1/*
2 * KCMStyle
3 * Copyright (C) 2002 Karol Szwed <gallium@kde.org>
4 * Copyright (C) 2002 Daniel Molkentin <molkentin@kde.org>
5 * Copyright (C) 2007 Urs Wolfer <uwolfer @ kde.org>
6 * Copyright (C) 2009 by Davide Bettio <davide.bettio@kdemail.net>
7
8 * Portions Copyright (C) 2007 Paolo Capriotti <p.capriotti@gmail.com>
9 * Portions Copyright (C) 2007 Ivan Cukic <ivan.cukic+kde@gmail.com>
10 * Portions Copyright (C) 2008 by Petri Damsten <damu@iki.fi>
11 * Portions Copyright (C) 2000 TrollTech AS.
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public
15 * License version 2 as published by the Free Software Foundation.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; see the file COPYING. If not, write to
24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 * Boston, MA 02110-1301, USA.
26 */
27
28#include "kcmstyle.h"
29
30#ifdef Q_WS_X11
31#include <config-X11.h>
32#endif
33
34#include "styleconfdialog.h"
35#include "ui_stylepreview.h"
36
37#include <kaboutdata.h>
38#include <kapplication.h>
39#include <kcombobox.h>
40#include <kmessagebox.h>
41#include <kstyle.h>
42#include <kstandarddirs.h>
43#include <kautostart.h>
44#include <KDebug>
45#include <KNotification>
46#include <KLibrary>
47#include <KColorScheme>
48#include <KStandardDirs>
49#include <knewstuff3/downloaddialog.h>
50#ifdef Q_WS_X11
51#include <kdecoration.h>
52#endif
53
54
55#include <QtCore/QFile>
56#include <QtCore/QSettings>
57#include <QtGui/QAbstractItemView>
58#include <QtGui/QLabel>
59#include <QtGui/QPainter>
60#include <QtGui/QPixmapCache>
61#include <QtGui/QStyleFactory>
62#include <QtGui/QFormLayout>
63#include <QtGui/QStandardItemModel>
64#include <QtGui/QStyle>
65#include <QtDBus/QtDBus>
66
67#ifdef Q_WS_X11
68#include <QX11Info>
69#endif
70
71#include "../krdb/krdb.h"
72
73#ifdef Q_WS_X11
74#include <X11/Xlib.h>
75#endif
76
77// X11 namespace cleanup
78#undef Below
79#undef KeyPress
80#undef KeyRelease
81
82
83/**** DLL Interface for kcontrol ****/
84
85#include <kpluginfactory.h>
86#include <kpluginloader.h>
87
88K_PLUGIN_FACTORY(KCMStyleFactory, registerPlugin<KCMStyle>();)
89K_EXPORT_PLUGIN(KCMStyleFactory("kcmstyle"))
90
91
92extern "C"
93{
94 KDE_EXPORT void kcminit_style()
95 {
96 uint flags = KRdbExportQtSettings | KRdbExportQtColors | KRdbExportXftSettings | KRdbExportGtkTheme;
97 KConfig _config( "kcmdisplayrc", KConfig::NoGlobals );
98 KConfigGroup config(&_config, "X11");
99
100 // This key is written by the "colors" module.
101 bool exportKDEColors = config.readEntry("exportKDEColors", true);
102 if (exportKDEColors)
103 flags |= KRdbExportColors;
104 runRdb( flags );
105
106 // Write some Qt root property.
107#ifdef Q_WS_X11
108#ifndef __osf__ // this crashes under Tru64 randomly -- will fix later
109 QByteArray properties;
110 QDataStream d(&properties, QIODevice::WriteOnly);
111 d.setVersion( 3 ); // Qt2 apps need this.
112 d << kapp->palette() << KGlobalSettings::generalFont();
113 Atom a = XInternAtom(QX11Info::display(), "_QT_DESKTOP_PROPERTIES", false);
114
115 // do it for all root windows - multihead support
116 int screen_count = ScreenCount(QX11Info::display());
117 for (int i = 0; i < screen_count; i++)
118 XChangeProperty(QX11Info::display(), RootWindow(QX11Info::display(), i),
119 a, a, 8, PropModeReplace,
120 (unsigned char*) properties.data(), properties.size());
121#endif
122#endif
123 }
124}
125
126class StylePreview : public QWidget, public Ui::StylePreview
127{
128public:
129 StylePreview(QWidget *parent = 0)
130 : QWidget(parent)
131 {
132 setupUi(this);
133
134 // Ensure that the user can't toy with the child widgets.
135 // Method borrowed from Qt's qtconfig.
136 QList<QWidget*> widgets = findChildren<QWidget*>();
137 foreach (QWidget* widget, widgets)
138 {
139 widget->installEventFilter(this);
140 widget->setFocusPolicy(Qt::NoFocus);
141 }
142 }
143
144 bool eventFilter( QObject* /* obj */, QEvent* ev )
145 {
146 switch( ev->type() )
147 {
148 case QEvent::MouseButtonPress:
149 case QEvent::MouseButtonRelease:
150 case QEvent::MouseButtonDblClick:
151 case QEvent::MouseMove:
152 case QEvent::KeyPress:
153 case QEvent::KeyRelease:
154 case QEvent::Enter:
155 case QEvent::Leave:
156 case QEvent::Wheel:
157 case QEvent::ContextMenu:
158 return true; // ignore
159 default:
160 break;
161 }
162 return false;
163 }
164};
165
166
167KCMStyle::KCMStyle( QWidget* parent, const QVariantList& )
168 : KCModule( KCMStyleFactory::componentData(), parent ), appliedStyle(NULL)
169{
170 setQuickHelp( i18n("<h1>Style</h1>"
171 "This module allows you to modify the visual appearance "
172 "of user interface elements, such as the widget style "
173 "and effects."));
174
175 m_bStyleDirty= false;
176 m_bEffectsDirty = false;
177
178
179 KGlobal::dirs()->addResourceType("themes", "data", "kstyle/themes");
180
181 KAboutData *about =
182 new KAboutData( I18N_NOOP("kcmstyle"), 0,
183 ki18n("KDE Style Module"),
184 0, KLocalizedString(), KAboutData::License_GPL,
185 ki18n("(c) 2002 Karol Szwed, Daniel Molkentin"));
186
187 about->addAuthor(ki18n("Karol Szwed"), KLocalizedString(), "gallium@kde.org");
188 about->addAuthor(ki18n("Daniel Molkentin"), KLocalizedString(), "molkentin@kde.org");
189 about->addAuthor(ki18n("Ralf Nolden"), KLocalizedString(), "nolden@kde.org");
190 setAboutData( about );
191
192 // Setup pages and mainLayout
193 mainLayout = new QVBoxLayout( this );
194 mainLayout->setMargin(0);
195
196 tabWidget = new QTabWidget( this );
197 mainLayout->addWidget( tabWidget );
198
199 // Add Page1 (Applications Style)
200 // -----------------
201 //gbWidgetStyle = new QGroupBox( i18n("Widget Style"), page1 );
202 page1 = new QWidget;
203 page1Layout = new QVBoxLayout( page1 );
204
205 QWidget* gbWidgetStyle = new QWidget( page1 );
206 QVBoxLayout *widgetLayout = new QVBoxLayout(gbWidgetStyle);
207
208 gbWidgetStyleLayout = new QVBoxLayout;
209 widgetLayout->addLayout( gbWidgetStyleLayout );
210 gbWidgetStyleLayout->setAlignment( Qt::AlignTop );
211 hbLayout = new QHBoxLayout( );
212 hbLayout->setObjectName( "hbLayout" );
213
214 QLabel* label=new QLabel(i18n("Widget style:"),this);
215 hbLayout->addWidget( label );
216
217 cbStyle = new KComboBox( gbWidgetStyle );
218 cbStyle->setObjectName( "cbStyle" );
219 cbStyle->setEditable( false );
220 hbLayout->addWidget( cbStyle );
221 hbLayout->setStretchFactor( cbStyle, 1 );
222 label->setBuddy(cbStyle);
223
224 pbConfigStyle = new QPushButton( i18n("Con&figure..."), gbWidgetStyle );
225 pbConfigStyle->setEnabled( false );
226 hbLayout->addWidget( pbConfigStyle );
227
228 gbWidgetStyleLayout->addLayout( hbLayout );
229
230 lblStyleDesc = new QLabel( gbWidgetStyle );
231 gbWidgetStyleLayout->addWidget( lblStyleDesc );
232
233 QGroupBox *gbPreview = new QGroupBox( i18n( "Preview" ), page1 );
234 QVBoxLayout *previewLayout = new QVBoxLayout(gbPreview);
235 previewLayout->setMargin( 0 );
236 stylePreview = new StylePreview( gbPreview );
237 gbPreview->layout()->addWidget( stylePreview );
238
239 page1Layout->addWidget( gbWidgetStyle );
240 page1Layout->addWidget( gbPreview );
241 page1Layout->addStretch();
242
243 connect( cbStyle, SIGNAL(activated(int)), this, SLOT(styleChanged()) );
244 connect( cbStyle, SIGNAL(activated(int)), this, SLOT(updateConfigButton()));
245 connect( pbConfigStyle, SIGNAL(clicked()), this, SLOT(styleSpecificConfig()));
246
247 // Add Page2 (Effects)
248 // -------------------
249 page2 = new QWidget;
250 fineTuningUi.setupUi(page2);
251
252 fineTuningUi.comboGraphicEffectsLevel->setObjectName( "cbGraphicEffectsLevel" );
253 fineTuningUi.comboGraphicEffectsLevel->setEditable( false );
254 fineTuningUi.comboGraphicEffectsLevel->addItem(i18n("Low display resolution and Low CPU"), KGlobalSettings::NoEffects);
255 fineTuningUi.comboGraphicEffectsLevel->addItem(i18n("High display resolution and Low CPU"), KGlobalSettings::GradientEffects);
256 fineTuningUi.comboGraphicEffectsLevel->addItem(i18n("Low display resolution and High CPU"), KGlobalSettings::SimpleAnimationEffects);
257 fineTuningUi.comboGraphicEffectsLevel->addItem(i18n("High display resolution and High CPU"), (int) (KGlobalSettings::SimpleAnimationEffects | KGlobalSettings::GradientEffects));
258 fineTuningUi.comboGraphicEffectsLevel->addItem(i18n("Low display resolution and Very High CPU"), KGlobalSettings::ComplexAnimationEffects);
259 fineTuningUi.comboGraphicEffectsLevel->addItem(i18n("High display resolution and Very High CPU"), (int) (KGlobalSettings::ComplexAnimationEffects | KGlobalSettings::GradientEffects));
260
261 connect(cbStyle, SIGNAL(activated(int)), this, SLOT(setStyleDirty()));
262 connect(fineTuningUi.cbIconsOnButtons, SIGNAL(toggled(bool)), this, SLOT(setEffectsDirty()));
263 connect(fineTuningUi.cbIconsInMenus, SIGNAL(toggled(bool)), this, SLOT(setEffectsDirty()));
264 connect(fineTuningUi.comboGraphicEffectsLevel, SIGNAL(activated(int)), this, SLOT(setEffectsDirty()));
265 connect(fineTuningUi.comboToolbarIcons, SIGNAL(activated(int)), this, SLOT(setEffectsDirty()));
266 connect(fineTuningUi.comboSecondaryToolbarIcons, SIGNAL(activated(int)), this, SLOT(setEffectsDirty()));
267 connect(fineTuningUi.comboMenubarStyle, SIGNAL(activated(int)), this, SLOT(setEffectsDirty()));
268
269 addWhatsThis();
270
271 if (!QFile::exists(QLibraryInfo::location(QLibraryInfo::PluginsPath) + "/menubar/libappmenu-qt.so")) {
272 fineTuningUi.menubarBox->hide();
273 }
274
275 // Insert the pages into the tabWidget
276 tabWidget->addTab(page1, i18nc("@title:tab", "&Applications"));
277 tabWidget->addTab(page2, i18nc("@title:tab", "&Fine Tuning"));
278}
279
280
281KCMStyle::~KCMStyle()
282{
283 qDeleteAll(styleEntries);
284 delete appliedStyle;
285}
286
287void KCMStyle::updateConfigButton()
288{
289 if (!styleEntries[currentStyle()] || styleEntries[currentStyle()]->configPage.isEmpty()) {
290 pbConfigStyle->setEnabled(false);
291 return;
292 }
293
294 // We don't check whether it's loadable here -
295 // lets us report an error and not waste time
296 // loading things if the user doesn't click the button
297 pbConfigStyle->setEnabled( true );
298}
299
300void KCMStyle::styleSpecificConfig()
301{
302 QString libname = styleEntries[currentStyle()]->configPage;
303
304 KLibrary library(libname, KCMStyleFactory::componentData());
305 if (!library.load()) {
306 KMessageBox::detailedError(this,
307 i18n("There was an error loading the configuration dialog for this style."),
308 library.errorString(),
309 i18n("Unable to Load Dialog"));
310 return;
311 }
312
313 KLibrary::void_function_ptr allocPtr = library.resolveFunction("allocate_kstyle_config");
314
315 if (!allocPtr)
316 {
317 KMessageBox::detailedError(this,
318 i18n("There was an error loading the configuration dialog for this style."),
319 library.errorString(),
320 i18n("Unable to Load Dialog"));
321 return;
322 }
323
324 //Create the container dialog
325 StyleConfigDialog* dial = new StyleConfigDialog(this, styleEntries[currentStyle()]->name);
326
327 typedef QWidget*(* factoryRoutine)( QWidget* parent );
328
329 //Get the factory, and make the widget.
330 factoryRoutine factory = (factoryRoutine)(allocPtr); //Grmbl. So here I am on my
331 //"never use C casts" moralizing streak, and I find that one can't go void* -> function ptr
332 //even with a reinterpret_cast.
333
334 QWidget* pluginConfig = factory( dial );
335
336 //Insert it in...
337 dial->setMainWidget( pluginConfig );
338
339 //..and connect it to the wrapper
340 connect(pluginConfig, SIGNAL(changed(bool)), dial, SLOT(setDirty(bool)));
341 connect(dial, SIGNAL(defaults()), pluginConfig, SLOT(defaults()));
342 connect(dial, SIGNAL(save()), pluginConfig, SLOT(save()));
343
344 if (dial->exec() == QDialog::Accepted && dial->isDirty() ) {
345 // Force re-rendering of the preview, to apply settings
346 switchStyle(currentStyle(), true);
347
348 //For now, ask all KDE apps to recreate their styles to apply the setitngs
349 KGlobalSettings::self()->emitChange(KGlobalSettings::StyleChanged);
350
351 // We call setStyleDirty here to make sure we force style re-creation
352 setStyleDirty();
353 }
354
355 delete dial;
356}
357
358void KCMStyle::changeEvent( QEvent *event )
359{
360 KCModule::changeEvent( event );
361 if ( event->type() == QEvent::PaletteChange ) {
362 // Force re-rendering of the preview, to apply new palette
363 switchStyle(currentStyle(), true);
364 }
365}
366
367void KCMStyle::load()
368{
369 KConfig config( "kdeglobals", KConfig::FullConfig );
370
371 loadStyle( config );
372 loadEffects( config );
373
374 m_bStyleDirty= false;
375 m_bEffectsDirty = false;
376 //Enable/disable the button for the initial style
377 updateConfigButton();
378
379 emit changed( false );
380}
381
382
383void KCMStyle::save()
384{
385 // Don't do anything if we don't need to.
386 if ( !(m_bStyleDirty | m_bEffectsDirty ) )
387 return;
388
389 const bool showMenuIcons = !QApplication::testAttribute(Qt::AA_DontShowIconsInMenus);
390 if (fineTuningUi.cbIconsInMenus->isChecked() != showMenuIcons) {
391 KMessageBox::information(this,
392 i18n("<p>Changes to the visibility of menu icons will only affect newly started applications.</p>"),
393 i18nc("@title:window", "Menu Icons Changed"), "MenuIconsChanged");
394 }
395
396 // Save effects.
397 KConfig _config("kdeglobals", KConfig::NoGlobals);
398 KConfigGroup config(&_config, "KDE");
399 // Effects page
400 config.writeEntry( "ShowIconsOnPushButtons", fineTuningUi.cbIconsOnButtons->isChecked());
401 config.writeEntry( "ShowIconsInMenuItems", fineTuningUi.cbIconsInMenus->isChecked());
402 KConfigGroup g( &_config, "KDE-Global GUI Settings" );
403 g.writeEntry( "GraphicEffectsLevel", fineTuningUi.comboGraphicEffectsLevel->itemData(fineTuningUi.comboGraphicEffectsLevel->currentIndex()));
404
405 KConfigGroup generalGroup(&_config, "General");
406 generalGroup.writeEntry("widgetStyle", currentStyle());
407
408 KConfigGroup toolbarStyleGroup(&_config, "Toolbar style");
409 toolbarStyleGroup.writeEntry("ToolButtonStyle",
410 toolbarButtonText(fineTuningUi.comboToolbarIcons->currentIndex()));
411 toolbarStyleGroup.writeEntry("ToolButtonStyleOtherToolbars",
412 toolbarButtonText(fineTuningUi.comboSecondaryToolbarIcons->currentIndex()));
413
414 // menubar page
415 KConfigGroup menuBarStyleGroup(&_config, "Appmenu Style");
416
417 // load kded module if needed
418 bool load = false;
419 QList<QVariant> args;
420
421 QString style = menuBarStyleText(fineTuningUi.comboMenubarStyle->currentIndex());
422
423 QString previous = menuBarStyleGroup.readEntry("Style", "InApplication");
424 menuBarStyleGroup.writeEntry("Style", style);
425 _config.sync();
426
427 QDBusMessage method = QDBusMessage::createMethodCall("org.kde.kded",
428 "/modules/appmenu",
429 "org.kde.kded",
430 "reconfigure");
431 QDBusConnection::sessionBus().asyncCall(method);
432
433 if (previous == "InApplication" && style != "InApplication") {
434 load = true;
435 KNotification *notification = new KNotification("reload", 0);
436 notification->setComponentData(KComponentData("kcmstyle"));
437 notification->setText(i18n("Settings changes will take effect only on application restart"));
438 notification->sendEvent();
439 }
440
441#ifdef Q_WS_X11
442 // If user select ButtonVertical, we add (if needed) a button to titlebar
443 if (style == "ButtonVertical") {
444 KConfig _kwinConfig("kwinrc", KConfig::NoGlobals);
445 KConfigGroup kwinConfig(&_kwinConfig, "Style");
446 QString buttonsOnLeft = kwinConfig.readEntry("ButtonsOnLeft", KDecorationOptions::defaultTitleButtonsLeft());
447 QString buttonsOnRight = kwinConfig.readEntry("ButtonsOnRight", KDecorationOptions::defaultTitleButtonsRight());
448 qDebug() << buttonsOnLeft << buttonsOnRight;
449 if (!buttonsOnLeft.contains("N") && !buttonsOnRight.contains("N")) {
450 buttonsOnLeft = "N" + buttonsOnLeft;
451 }
452 kwinConfig.writeEntry("ButtonsOnLeft", buttonsOnLeft);
453 kwinConfig.writeEntry("CustomButtonPositions", "true");
454 }
455#endif
456
457 args = QList<QVariant>() << "appmenu" << (style != "InApplication");
458 method = QDBusMessage::createMethodCall("org.kde.kded",
459 "/kded",
460 "org.kde.kded",
461 "setModuleAutoloading");
462 method.setArguments(args);
463 QDBusConnection::sessionBus().asyncCall(method);
464
465 args = QList<QVariant>() << "appmenu";
466 if (load) {
467 method = QDBusMessage::createMethodCall("org.kde.kded",
468 "/kded",
469 "org.kde.kded",
470 "loadModule");
471 QDBusMessage method = QDBusMessage::createMethodCall("org.kde.kded", "/modules/appmenu", "org.kde.kded", "reconfigure");
472 QDBusConnection::sessionBus().asyncCall(method);
473 } else if (style == "InApplication") {
474 method = QDBusMessage::createMethodCall("org.kde.kded",
475 "/kded",
476 "org.kde.kded",
477 "unloadModule");
478 }
479 method.setArguments(args);
480 QDBusConnection::sessionBus().asyncCall(method);
481
482 // Export the changes we made to qtrc, and update all qt-only
483 // applications on the fly, ensuring that we still follow the user's
484 // export fonts/colors settings.
485 if (m_bStyleDirty | m_bEffectsDirty) // Export only if necessary
486 {
487 uint flags = KRdbExportQtSettings | KRdbExportGtkTheme;
488 KConfig _kconfig( "kcmdisplayrc", KConfig::NoGlobals );
489 KConfigGroup kconfig(&_kconfig, "X11");
490 bool exportKDEColors = kconfig.readEntry("exportKDEColors", true);
491 if (exportKDEColors)
492 flags |= KRdbExportColors;
493 runRdb( flags );
494 }
495
496 // Now allow KDE apps to reconfigure themselves.
497 if ( m_bStyleDirty )
498 KGlobalSettings::self()->emitChange(KGlobalSettings::StyleChanged);
499
500 if ( m_bEffectsDirty ) {
501 KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_STYLE);
502 // ##### FIXME - Doesn't apply all settings correctly due to bugs in
503 // KApplication/KToolbar
504 KGlobalSettings::self()->emitChange(KGlobalSettings::ToolbarStyleChanged);
505
506#ifdef Q_WS_X11
507 // Send signal to all kwin instances
508 QDBusMessage message =
509 QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig");
510 QDBusConnection::sessionBus().send(message);
511#endif
512 }
513
514 // Clean up
515 m_bStyleDirty = false;
516 m_bEffectsDirty = false;
517 emit changed( false );
518}
519
520
521bool KCMStyle::findStyle( const QString& str, int& combobox_item )
522{
523 StyleEntry* se = styleEntries[str.toLower()];
524
525 QString name = se ? se->name : str;
526
527 combobox_item = 0;
528
529 //look up name
530 for( int i = 0; i < cbStyle->count(); i++ )
531 {
532 if ( cbStyle->itemText(i) == name )
533 {
534 combobox_item = i;
535 return true;
536 }
537 }
538
539 return false;
540}
541
542
543void KCMStyle::defaults()
544{
545 // Select default style
546 int item = 0;
547 bool found;
548
549 found = findStyle( KStyle::defaultStyle(), item );
550 if (!found)
551 found = findStyle( "plastique", item );
552 if (!found)
553 found = findStyle( "windows", item );
554 if (!found)
555 found = findStyle( "platinum", item );
556 if (!found)
557 found = findStyle( "motif", item );
558
559 cbStyle->setCurrentIndex( item );
560
561 m_bStyleDirty = true;
562 switchStyle( currentStyle() ); // make resets visible
563
564 // Effects
565 fineTuningUi.comboToolbarIcons->setCurrentIndex(toolbarButtonIndex("TextBesideIcon"));
566 fineTuningUi.comboSecondaryToolbarIcons->setCurrentIndex(toolbarButtonIndex("TextBesideIcon"));
567 fineTuningUi.comboMenubarStyle->setCurrentIndex(menuBarStyleIndex("InApplication"));
568 fineTuningUi.cbIconsOnButtons->setChecked(true);
569 fineTuningUi.cbIconsInMenus->setChecked(true);
570 fineTuningUi.comboGraphicEffectsLevel->setCurrentIndex(fineTuningUi.comboGraphicEffectsLevel->findData(((int) KGlobalSettings::graphicEffectsLevelDefault())));
571 emit changed(true);
572}
573
574void KCMStyle::setEffectsDirty()
575{
576 m_bEffectsDirty = true;
577 emit changed(true);
578}
579
580void KCMStyle::setStyleDirty()
581{
582 m_bStyleDirty = true;
583 emit changed(true);
584}
585
586// ----------------------------------------------------------------
587// All the Style Switching / Preview stuff
588// ----------------------------------------------------------------
589
590void KCMStyle::loadStyle( KConfig& config )
591{
592 cbStyle->clear();
593 // Create a dictionary of WidgetStyle to Name and Desc. mappings,
594 // as well as the config page info
595 qDeleteAll(styleEntries);
596 styleEntries.clear();
597
598 QString strWidgetStyle;
599 QStringList list = KGlobal::dirs()->findAllResources("themes", "*.themerc",
600 KStandardDirs::Recursive |
601 KStandardDirs::NoDuplicates);
602 for (QStringList::iterator it = list.begin(); it != list.end(); ++it)
603 {
604 KConfig config( *it, KConfig::SimpleConfig);
605 if ( !(config.hasGroup("KDE") && config.hasGroup("Misc")) )
606 continue;
607
608 KConfigGroup configGroup = config.group("KDE");
609
610 strWidgetStyle = configGroup.readEntry("WidgetStyle");
611 if (strWidgetStyle.isNull())
612 continue;
613
614 // We have a widgetstyle, so lets read the i18n entries for it...
615 StyleEntry* entry = new StyleEntry;
616 configGroup = config.group("Misc");
617 entry->name = configGroup.readEntry("Name");
618 entry->desc = configGroup.readEntry("Comment", i18n("No description available."));
619 entry->configPage = configGroup.readEntry("ConfigPage", QString());
620
621 // Check if this style should be shown
622 configGroup = config.group("Desktop Entry");
623 entry->hidden = configGroup.readEntry("Hidden", false);
624
625 // Insert the entry into our dictionary.
626 styleEntries.insert(strWidgetStyle.toLower(), entry);
627 }
628
629 // Obtain all style names
630 QStringList allStyles = QStyleFactory::keys();
631
632 // Get translated names, remove all hidden style entries.
633 QStringList styles;
634 StyleEntry* entry;
635 for (QStringList::iterator it = allStyles.begin(); it != allStyles.end(); ++it)
636 {
637 QString id = (*it).toLower();
638 // Find the entry.
639 if ( (entry = styleEntries[id]) != 0 )
640 {
641 // Do not add hidden entries
642 if (entry->hidden)
643 continue;
644
645 styles += entry->name;
646
647 nameToStyleKey[entry->name] = id;
648 }
649 else
650 {
651 styles += (*it); //Fall back to the key (but in original case)
652 nameToStyleKey[*it] = id;
653 }
654 }
655
656 // Sort the style list, and add it to the combobox
657 styles.sort();
658 cbStyle->addItems( styles );
659
660 // Find out which style is currently being used
661 KConfigGroup configGroup = config.group( "General" );
662 QString defaultStyle = KStyle::defaultStyle();
663 QString cfgStyle = configGroup.readEntry( "widgetStyle", defaultStyle );
664
665 // Select the current style
666 // Do not use cbStyle->listBox() as this may be NULL for some styles when
667 // they use QPopupMenus for the drop-down list!
668
669 // ##### Since Trolltech likes to seemingly copy & paste code,
670 // QStringList::findItem() doesn't have a Qt::StringComparisonMode field.
671 // We roll our own (yuck)
672 cfgStyle = cfgStyle.toLower();
673 int item = 0;
674 for( int i = 0; i < cbStyle->count(); i++ )
675 {
676 QString id = nameToStyleKey[cbStyle->itemText(i)];
677 item = i;
678 if ( id == cfgStyle ) // ExactMatch
679 break;
680 else if ( id.contains( cfgStyle ) )
681 break;
682 else if ( id.contains( QApplication::style()->metaObject()->className() ) )
683 break;
684 item = 0;
685 }
686 cbStyle->setCurrentIndex( item );
687 m_bStyleDirty = false;
688
689 switchStyle( currentStyle() ); // make resets visible
690}
691
692QString KCMStyle::currentStyle()
693{
694 return nameToStyleKey[cbStyle->currentText()];
695}
696
697
698void KCMStyle::styleChanged()
699{
700 switchStyle( currentStyle() );
701}
702
703
704void KCMStyle::switchStyle(const QString& styleName, bool force)
705{
706 // Don't flicker the preview if the same style is chosen in the cb
707 if (!force && appliedStyle && appliedStyle->objectName() == styleName)
708 return;
709
710 // Create an instance of the new style...
711 QStyle* style = QStyleFactory::create(styleName);
712 if (!style)
713 return;
714
715 // Prevent Qt from wrongly caching radio button images
716 QPixmapCache::clear();
717
718 setStyleRecursive( stylePreview, style );
719
720 // this flickers, but reliably draws the widgets correctly.
721 stylePreview->resize( stylePreview->sizeHint() );
722
723 delete appliedStyle;
724 appliedStyle = style;
725
726 // Set the correct style description
727 StyleEntry* entry = styleEntries[ styleName ];
728 QString desc;
729 desc = i18n("Description: %1", entry ? entry->desc : i18n("No description available.") );
730 lblStyleDesc->setText( desc );
731}
732
733void KCMStyle::setStyleRecursive(QWidget* w, QStyle* s)
734{
735 // Don't let broken styles kill the palette
736 // for other styles being previewed. (e.g SGI style)
737 w->setPalette(QPalette());
738
739 QPalette newPalette(KGlobalSettings::createApplicationPalette());
740 s->polish( newPalette );
741 w->setPalette(newPalette);
742
743 // Apply the new style.
744 w->setStyle(s);
745
746 // Recursively update all children.
747 const QObjectList children = w->children();
748
749 // Apply the style to each child widget.
750 foreach (QObject* child, children)
751 {
752 if (child->isWidgetType())
753 setStyleRecursive((QWidget *) child, s);
754 }
755}
756
757// ----------------------------------------------------------------
758// All the Effects stuff
759// ----------------------------------------------------------------
760QString KCMStyle::toolbarButtonText(int index)
761{
762 switch (index) {
763 case 1:
764 return "TextOnly";
765 case 2:
766 return "TextBesideIcon";
767 case 3:
768 return "TextUnderIcon";
769 default:
770 break;
771 }
772
773 return "NoText";
774}
775
776int KCMStyle::toolbarButtonIndex(const QString &text)
777{
778 if (text == "TextOnly") {
779 return 1;
780 } else if (text == "TextBesideIcon") {
781 return 2;
782 } else if (text == "TextUnderIcon") {
783 return 3;
784 }
785
786 return 0;
787}
788
789QString KCMStyle::menuBarStyleText(int index)
790{
791 switch (index) {
792 case 1:
793 return "ButtonVertical";
794 case 2:
795 return "TopMenuBar";
796 case 3:
797 return "Others";
798 }
799
800 return "InApplication";
801}
802
803int KCMStyle::menuBarStyleIndex(const QString &text)
804{
805 if (text == "ButtonVertical") {
806 return 1;
807 } else if (text == "TopMenuBar") {
808 return 2;
809 } else if (text == "Others") {
810 return 3;
811 }
812
813 return 0;
814}
815
816void KCMStyle::loadEffects( KConfig& config )
817{
818 // KDE's Part via KConfig
819 KConfigGroup configGroup = config.group("Toolbar style");
820
821 QString tbIcon = configGroup.readEntry("ToolButtonStyle", "TextBesideIcon");
822 fineTuningUi.comboToolbarIcons->setCurrentIndex(toolbarButtonIndex(tbIcon));
823 tbIcon = configGroup.readEntry("ToolButtonStyleOtherToolbars", "TextBesideIcon");
824 fineTuningUi.comboSecondaryToolbarIcons->setCurrentIndex(toolbarButtonIndex(tbIcon));
825
826 configGroup = config.group("Appmenu Style");
827 QString menuBarStyle = configGroup.readEntry("Style", "InApplication");
828 fineTuningUi.comboMenubarStyle->setCurrentIndex(menuBarStyleIndex(menuBarStyle));
829
830 configGroup = config.group("KDE");
831 fineTuningUi.cbIconsOnButtons->setChecked(configGroup.readEntry("ShowIconsOnPushButtons", true));
832 fineTuningUi.cbIconsInMenus->setChecked(configGroup.readEntry("ShowIconsInMenuItems", true));
833
834 KConfigGroup graphicConfigGroup = config.group("KDE-Global GUI Settings");
835 fineTuningUi.comboGraphicEffectsLevel->setCurrentIndex(fineTuningUi.comboGraphicEffectsLevel->findData(graphicConfigGroup.readEntry("GraphicEffectsLevel", ((int) KGlobalSettings::graphicEffectsLevel()))));
836
837 m_bEffectsDirty = false;
838}
839
840void KCMStyle::addWhatsThis()
841{
842 // Page1
843 cbStyle->setWhatsThis( i18n("Here you can choose from a list of"
844 " predefined widget styles (e.g. the way buttons are drawn) which"
845 " may or may not be combined with a theme (additional information"
846 " like a marble texture or a gradient).") );
847 stylePreview->setWhatsThis( i18n("This area shows a preview of the currently selected style "
848 "without having to apply it to the whole desktop.") );
849 // Page2
850 page2->setWhatsThis( i18n("This page allows you to choose details about the widget style options") );
851 fineTuningUi.comboToolbarIcons->setWhatsThis( i18n( "<p><b>No Text:</b> Shows only icons on toolbar buttons. "
852 "Best option for low resolutions.</p>"
853 "<p><b>Text Only: </b>Shows only text on toolbar buttons.</p>"
854 "<p><b>Text Beside Icons: </b> Shows icons and text on toolbar buttons. "
855 "Text is aligned beside the icon.</p>"
856 "<b>Text Below Icons: </b> Shows icons and text on toolbar buttons. "
857 "Text is aligned below the icon.") );
858 fineTuningUi.cbIconsOnButtons->setWhatsThis( i18n( "If you enable this option, KDE Applications will "
859 "show small icons alongside some important buttons.") );
860 fineTuningUi.cbIconsInMenus->setWhatsThis( i18n( "If you enable this option, KDE Applications will "
861 "show small icons alongside most menu items.") );
862 fineTuningUi.comboGraphicEffectsLevel->setWhatsThis( i18n( "If you enable this option, KDE Applications will "
863 "run internal animations.") );
864}
865
866#include "kcmstyle.moc"
867
868// vim: set noet ts=4:
869