diff options
Diffstat (limited to 'kstyles/kthemestyle')
-rw-r--r-- | kstyles/kthemestyle/Makefile.am | 29 | ||||
-rw-r--r-- | kstyles/kthemestyle/kstyledirs.cpp | 48 | ||||
-rw-r--r-- | kstyles/kthemestyle/kstyledirs.h | 82 | ||||
-rw-r--r-- | kstyles/kthemestyle/kthemebase.cpp | 1826 | ||||
-rw-r--r-- | kstyles/kthemestyle/kthemebase.h | 853 | ||||
-rw-r--r-- | kstyles/kthemestyle/kthemestyle.cpp | 2384 | ||||
-rw-r--r-- | kstyles/kthemestyle/kthemestyle.h | 230 |
7 files changed, 5452 insertions, 0 deletions
diff --git a/kstyles/kthemestyle/Makefile.am b/kstyles/kthemestyle/Makefile.am new file mode 100644 index 000000000..0edb271b4 --- /dev/null +++ b/kstyles/kthemestyle/Makefile.am @@ -0,0 +1,29 @@ + +# This file is part of the KDE libraries +# Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org) +# (C) 1997 Stephan Kulow (coolo@kde.org) + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. + +# This library 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 +# Library General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this library; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. + +AM_CPPFLAGS = -DQT_PLUGIN + +INCLUDES = -I$(top_srcdir)/kdefx $(all_includes) +noinst_HEADERS = kthemestyle.h kthemebase.h kstyledirs.h +kde_style_LTLIBRARIES = kthemestyle.la +kthemestyle_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kthemestyle_la_LIBADD = ../../kdefx/libkdefx.la ../../kdecore/libkdecore.la +kthemestyle_la_SOURCES = kthemebase.cpp kthemestyle.cpp kstyledirs.cpp +kthemestyle_la_METASOURCES = AUTO diff --git a/kstyles/kthemestyle/kstyledirs.cpp b/kstyles/kthemestyle/kstyledirs.cpp new file mode 100644 index 000000000..b77d4d56b --- /dev/null +++ b/kstyles/kthemestyle/kstyledirs.cpp @@ -0,0 +1,48 @@ +/* + $Id$ + + Simple helper routines for style's use of KStandardDirs with QSettings, etc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + + This file is part of the KDE libraries +*/ + +#include <qfile.h> +#include <kstandarddirs.h> +#include "kstyledirs.h" + +KStyleDirs* KStyleDirs::instance = 0; + +KStyleDirs::KStyleDirs() +{ + addResourceType( "themepixmap", KStandardDirs::kde_default( "data" ) + "kstyle/pixmaps/" ); + addResourceType( "themerc", KStandardDirs::kde_default( "data" ) + "kstyle/themes/" ); +} + +KStyleDirs::~KStyleDirs() +{ +} + +void KStyleDirs::addToSearch( const char* type, QSettings& s ) const +{ + const QStringList & dirs = resourceDirs(type); + for ( int c = dirs.size()-1; c >= 0 ; c-- ) + { + s.insertSearchPath( QSettings::Unix, dirs[ c ]); + } +} + diff --git a/kstyles/kthemestyle/kstyledirs.h b/kstyles/kthemestyle/kstyledirs.h new file mode 100644 index 000000000..b23457a07 --- /dev/null +++ b/kstyles/kthemestyle/kstyledirs.h @@ -0,0 +1,82 @@ +/* + $Id$ + + This file is part of the KDE libraries + (c) 2002 Maksim Orlovich <mo002j@mail.rochester.edu>, + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KSTYLE_DIRS_H +#define KSTYLE_DIRS_H + + +#include <qsettings.h> +#include <qstringlist.h> +#include <kstandarddirs.h> + +/** +* @short Access to the standard KDE directories for the pixmap style +* @author Maksim Orlovich<mo002j@mail.rochester.edu> is responsible for this file, + but all the interesting work is done by KStandardDirs +* @version $Id$ +* +* This class provides a this wrapper for styles around KStandardDirs, +* permitting integration with QSettings and easy loading of pixmaps +* +* It add share/apps/kstyle/themes as "themerc", +* share/apps/kstyle/pixmaps "themepixmap" +*/ +class KStyleDirs: public KStandardDirs +{ +public: + static KStyleDirs* dirs() + { + if ( !instance) + instance = new KStyleDirs; + return instance; + } + + static void release() + { + delete instance; + instance = 0; + } + + /** + Adds all of KDE directories of type type to the seach path of q. + + For example, when one does the following: + QSettings settings; + KStyleDirs dirs; + dirs.addToSearch("config",settings); + + The one can do settings.readEntry("kstyle/KDE/WidgetStyle") to access a settings in kstylerc. + */ + void addToSearch( const char* type, QSettings& q) const; //Better name? + +protected: + static KStyleDirs* instance; + /** + Creates an instance of the class, and calculates the path information. + */ + KStyleDirs(); + KStyleDirs(const KStyleDirs&); + KStyleDirs& operator= (const KStyleDirs&); + + virtual ~KStyleDirs(); +}; + +#endif diff --git a/kstyles/kthemestyle/kthemebase.cpp b/kstyles/kthemestyle/kthemebase.cpp new file mode 100644 index 000000000..bd463e28e --- /dev/null +++ b/kstyles/kthemestyle/kthemebase.cpp @@ -0,0 +1,1826 @@ +/* + $Id$ + + This file is part of the KDE libraries + Copyright (C) 1999 Daniel M. Duley <mosfet@kde.org> + + KDE3 port (C) 2001-2002 Maksim Orlovich <mo002j@mail.rochester.edu> + Port version 0.9.7 + + Palette setup code is from KApplication, + Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org) + Copyright (C) 1998, 1999, 2000 KDE Team + + Includes code portions from the KDE HighColor style. + + KDE3 HighColor Style + Copyright (C) 2001 Karol Szwed <gallium@kde.org> + (C) 2001 Fredrik Höglund <fredrik@kde.org> + + Drawing routines adapted from the KDE2 HCStyle, + Copyright (C) 2000 Daniel M. Duley <mosfet@kde.org> + (C) 2000 Dirk Mueller <mueller@kde.org> + (C) 2001 Martijn Klingens <klingens@kde.org> + + + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kthemebase.h" +#include <kpixmapeffect.h> +#include <qimage.h> +#include <qpainter.h> +#include <qbitmap.h> +#include <stdlib.h> + +#include <qsettings.h> +#include <qapplication.h> +#include <qscrollbar.h> + +typedef QMap<QString, QString> Prop; + +template class QIntCache<KThemePixmap> +; + +/* +Bugs: +Can't delete old slider image when calculating the rotated one for some reason. +*/ + +//Shamelessly stolen from KConfigBase +static QColor readColorEntry( QSettings* s, const char *pKey, + const QColor* pDefault ) +{ + QColor aRetColor; + int nRed = 0, nGreen = 0, nBlue = 0; + + QString aValue = s->readEntry( pKey ); + if ( !aValue.isEmpty() ) + { + if ( aValue.at( 0 ) == '#' ) + { + aRetColor.setNamedColor( aValue ); + } + else + { + bool bOK; + // find first part (red) + int nIndex = aValue.find( ',' ); + if ( nIndex == -1 ) + { + // return a sensible default -- Bernd + if ( pDefault ) + aRetColor = *pDefault; + return aRetColor; + } + + nRed = aValue.left( nIndex ).toInt( &bOK ); + + // find second part (green) + int nOldIndex = nIndex; + nIndex = aValue.find( ',', nOldIndex + 1 ); + + if ( nIndex == -1 ) + { + // return a sensible default -- Bernd + if ( pDefault ) + aRetColor = *pDefault; + return aRetColor; + } + nGreen = aValue.mid( nOldIndex + 1, + nIndex - nOldIndex - 1 ).toInt( &bOK ); + + // find third part (blue) + nBlue = aValue.right( aValue.length() - nIndex - 1 ).toInt( &bOK ); + + aRetColor.setRgb( nRed, nGreen, nBlue ); + } + } + else + { + + if ( pDefault ) + aRetColor = *pDefault; + } + + return aRetColor; +} + + +static const char * const widgetEntries[] = + { // unsunken widgets (see header) + "PushButton", "ComboBox", "HSBarSlider", "VSBarSlider", "Bevel", "ToolButton", + "ScrollButton", "HScrollDeco", "VScrollDeco", "ComboDeco", "MenuItem", "Tab", + "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", + // sunken widgets + "PushButtonDown", "ComboBoxDown", "HSBarSliderDown", "VSBarSliderDown", + "BevelDown", "ToolButtonDown", "ScrollButtonDown", "HScrollDecoDown", + "VScrollDecoDown", "ComboDecoDown", "MenuItemDown", "TabDown", "SunkenArrowUp", + "SunkenArrowDown", "SunkenArrowLeft", "SunkenArrowRight", + // everything else + "HScrollGroove", "VScrollGroove", "Slider", "SliderGroove", "CheckBoxDown", + "CheckBox", "CheckBoxTri", "RadioDown", "Radio", "HBarHandle", "VBarHandle", + "ToolBar", "Splitter", "CheckMark", "MenuBar", "DisableArrowUp", + "DisableArrowDown", "DisableArrowLeft", "DisableArrowRight", "ProgressBar", + "ProgressBackground", "MenuBarItem", "Background", "RotSlider", + "RotInactiveTab", "RotActiveTab", + }; + +#define INHERIT_ITEMS 16 + + +class KThemeBasePrivate +{ +public: + /** Color overrides flags..*/ + bool overrideForeground; + bool overrideBackground; + bool overrideSelectForeground; + bool overrideSelectBackground; + bool overrideWindowForeground; + bool overrideWindowBackground; + + /** + * Colors to override defaults with.. + */ + QColor overrideForegroundCol; + QColor overrideBackgroundCol; + QColor overrideSelectForegroundCol; + QColor overrideSelectBackgroundCol; + QColor overrideWindowForegroundCol; + QColor overrideWindowBackgroundCol; + + int contrast; + + + QMap <QString, QMap<QString, QString> > props; + + QMap<const QPixmap*, QColor> colorCache; + + /* + A heuristic routine that tries to determine the avergae color of the image + Wouldn't work for things like sliders, etc. + */ + QColor pixmapAveColor( const QPixmap* p ) + { + if ( colorCache.contains( p ) ) + return colorCache[ p ]; + + QImage to_ave = p->convertToImage(); + double h = 0, s = 0, v = 0; + int count = 0; + int dh, ds, dv; + for ( int x = 0; x < p->width(); x++ ) + { + QColor pix( to_ave.pixel( x, p->height() / 2 ) ); + pix.hsv( &dh, &ds, &dv ); + h += dh; + s += ds; + v += dv; + count++; + } + + for ( int y = 0; y < p->height(); y++ ) + { + QColor pix( to_ave.pixel( p->width() / 2, y ) ); + pix.hsv( &dh, &ds, &dv ); + h += dh; + s += ds; + v += dv; + count++; + } + colorCache[ p ] = QColor( int( h / count + 0.5 ), int( s / count + 0.5 ), int( v / count + 0.5 ), QColor::Hsv ); + return colorCache[ p ]; + } +}; + + + +// This is used to encode the keys. I used to use masks but I think this +// bitfield is nicer :) I don't know why C++ coders don't use these more.. +// (mosfet) +struct kthemeKeyData +{ +unsigned int id : + 6; +unsigned int width : + 12; +unsigned int height : + 12; +unsigned int border : + 1; +unsigned int mask : + 1; +}; + +union kthemeKey{ + kthemeKeyData data; + unsigned int cacheKey; +}; + +void KThemeBase::generateBorderPix( int i ) +{ + // separate pixmap into separate components + if ( pbPixmaps[ i ] ) + { + // evidently I have to do masks manually... + const QBitmap * srcMask = pbPixmaps[ i ] ->mask(); + QBitmap destMask( pbWidth[ i ], pbWidth[ i ] ); + QPixmap tmp( pbWidth[ i ], pbWidth[ i ] ); + + bitBlt( &tmp, 0, 0, pbPixmaps[ i ], 0, 0, pbWidth[ i ], pbWidth[ i ], + Qt::CopyROP, false ); + if ( srcMask ) + { + bitBlt( &destMask, 0, 0, srcMask, 0, 0, pbWidth[ i ], pbWidth[ i ], + Qt::CopyROP, false ); + tmp.setMask( destMask ); + } + pbPixmaps[ i ] ->setBorder( KThemePixmap::TopLeft, tmp ); + + bitBlt( &tmp, 0, 0, pbPixmaps[ i ], pbPixmaps[ i ] ->width() - pbWidth[ i ], 0, + pbWidth[ i ], pbWidth[ i ], Qt::CopyROP, false ); + if ( srcMask ) + { + bitBlt( &destMask, 0, 0, srcMask, pbPixmaps[ i ] ->width() - pbWidth[ i ], + 0, pbWidth[ i ], pbWidth[ i ], Qt::CopyROP, false ); + tmp.setMask( destMask ); + } + pbPixmaps[ i ] ->setBorder( KThemePixmap::TopRight, tmp ); + + bitBlt( &tmp, 0, 0, pbPixmaps[ i ], 0, pbPixmaps[ i ] ->height() - pbWidth[ i ], + pbWidth[ i ], pbWidth[ i ], Qt::CopyROP, false ); + if ( srcMask ) + { + bitBlt( &destMask, 0, 0, srcMask, 0, pbPixmaps[ i ] ->height() - pbWidth[ i ], + pbWidth[ i ], pbWidth[ i ], Qt::CopyROP, false ); + tmp.setMask( destMask ); + } + pbPixmaps[ i ] ->setBorder( KThemePixmap::BottomLeft, tmp ); + + bitBlt( &tmp, 0, 0, pbPixmaps[ i ], pbPixmaps[ i ] ->width() - pbWidth[ i ], + pbPixmaps[ i ] ->height() - pbWidth[ i ], pbWidth[ i ], pbWidth[ i ], + Qt::CopyROP, false ); + if ( srcMask ) + { + bitBlt( &destMask, 0, 0, srcMask, pbPixmaps[ i ] ->width() - pbWidth[ i ], + pbPixmaps[ i ] ->height() - pbWidth[ i ], pbWidth[ i ], pbWidth[ i ], + Qt::CopyROP, false ); + tmp.setMask( destMask ); + } + pbPixmaps[ i ] ->setBorder( KThemePixmap::BottomRight, tmp ); + + tmp.resize( pbPixmaps[ i ] ->width() - pbWidth[ i ] * 2, pbWidth[ i ] ); + destMask.resize( pbPixmaps[ i ] ->width() - pbWidth[ i ] * 2, pbWidth[ i ] ); + bitBlt( &tmp, 0, 0, pbPixmaps[ i ], pbWidth[ i ], 0, + pbPixmaps[ i ] ->width() - pbWidth[ i ] * 2, pbWidth[ i ], Qt::CopyROP, false ); + if ( srcMask ) + { + bitBlt( &destMask, 0, 0, srcMask, pbWidth[ i ], 0, + pbPixmaps[ i ] ->width() - pbWidth[ i ] * 2, pbWidth[ i ], + Qt::CopyROP, false ); + tmp.setMask( destMask ); + } + pbPixmaps[ i ] ->setBorder( KThemePixmap::Top, tmp ); + + bitBlt( &tmp, 0, 0, pbPixmaps[ i ], pbWidth[ i ], + pbPixmaps[ i ] ->height() - pbWidth[ i ], + pbPixmaps[ i ] ->width() - pbWidth[ i ] * 2, pbWidth[ i ], Qt::CopyROP, false ); + if ( srcMask ) + { + bitBlt( &destMask, 0, 0, srcMask, pbWidth[ i ], + pbPixmaps[ i ] ->height() - pbWidth[ i ], + pbPixmaps[ i ] ->width() - pbWidth[ i ] * 2, pbWidth[ i ], Qt::CopyROP, false ); + tmp.setMask( destMask ); + } + pbPixmaps[ i ] ->setBorder( KThemePixmap::Bottom, tmp ); + + tmp.resize( pbWidth[ i ], pbPixmaps[ i ] ->height() - pbWidth[ i ] * 2 ); + destMask.resize( pbWidth[ i ], pbPixmaps[ i ] ->height() - pbWidth[ i ] * 2 ); + bitBlt( &tmp, 0, 0, pbPixmaps[ i ], 0, pbWidth[ i ], pbWidth[ i ], + pbPixmaps[ i ] ->height() - pbWidth[ i ] * 2, Qt::CopyROP, false ); + if ( srcMask ) + { + bitBlt( &destMask, 0, 0, srcMask, 0, pbWidth[ i ], pbWidth[ i ], + pbPixmaps[ i ] ->height() - pbWidth[ i ] * 2, Qt::CopyROP, false ); + tmp.setMask( destMask ); + } + + pbPixmaps[ i ] ->setBorder( KThemePixmap::Left, tmp ); + + bitBlt( &tmp, 0, 0, pbPixmaps[ i ], pbPixmaps[ i ] ->width() - pbWidth[ i ], + pbWidth[ i ], pbWidth[ i ], pbPixmaps[ i ] ->height() - pbWidth[ i ] * 2, + Qt::CopyROP, false ); + if ( srcMask ) + { + bitBlt( &destMask, 0, 0, srcMask, pbPixmaps[ i ] ->width() - pbWidth[ i ], + pbWidth[ i ], pbWidth[ i ], pbPixmaps[ i ] ->height() - pbWidth[ i ] * 2, + Qt::CopyROP, false ); + tmp.setMask( destMask ); + } + pbPixmaps[ i ] ->setBorder( KThemePixmap::Right, tmp ); + } + else + qWarning( "KThemeBase: Tried making border from empty pixmap\n" ); +} + + +void KThemeBase::copyWidgetConfig( int sourceID, int destID, QString *pixnames, + QString *brdnames ) +{ + scaleHints[ destID ] = scaleHints[ sourceID ]; + gradients[ destID ] = gradients[ sourceID ]; + blends[ destID ] = blends[ sourceID ]; + bContrasts[ destID ] = bContrasts[ sourceID ]; + borders[ destID ] = borders[ sourceID ]; + highlights[ destID ] = highlights[ sourceID ]; + + if ( grLowColors[ sourceID ] ) + grLowColors[ destID ] = new QColor( *grLowColors[ sourceID ] ); + else + grLowColors[ destID ] = NULL; + + if ( grHighColors[ sourceID ] ) + grHighColors[ destID ] = new QColor( *grHighColors[ sourceID ] ); + else + grHighColors[ destID ] = NULL; + + if ( colors[ sourceID ] ) + colors[ destID ] = new QColorGroup( *colors[ sourceID ] ); + else + colors[ destID ] = NULL; + + // pixmap + pixnames[ destID ] = pixnames[ sourceID ]; + duplicate[ destID ] = false; + pixmaps[ destID ] = NULL; + images[ destID ] = NULL; + if ( !pixnames[ destID ].isEmpty() ) + { + if ( scaleHints[ sourceID ] == TileScale && blends[ sourceID ] == 0.0 ) + { + pixmaps[ destID ] = pixmaps[ sourceID ]; + duplicate[ destID ] = true; + } + if ( !duplicate[ destID ] ) + { + pixmaps[ destID ] = loadPixmap( pixnames[ destID ] ); + if ( scaleHints[ destID ] == TileScale && blends[ destID ] == 0.0 ) + images[ destID ] = NULL; + else + images[ destID ] = loadImage( pixnames[ destID ] ); + } + } + + // border pixmap + pbDuplicate[ destID ] = false; + pbPixmaps[ destID ] = NULL; + pbWidth[ destID ] = pbWidth[ sourceID ]; + brdnames[ destID ] = brdnames[ sourceID ]; + if ( !brdnames[ destID ].isEmpty() ) + { + pbPixmaps[ destID ] = pbPixmaps[ sourceID ]; + pbDuplicate[ destID ] = true; + } + + if ( sourceID == ActiveTab && destID == InactiveTab ) + aTabLine = iTabLine; + else if ( sourceID == InactiveTab && destID == ActiveTab ) + iTabLine = aTabLine; +} + +void KThemeBase::readConfig( Qt::GUIStyle /*style*/ ) +{ +#define PREBLEND_ITEMS 12 + static const WidgetType preBlend[] = + { + Slider, IndicatorOn, IndicatorOff, + ExIndicatorOn, ExIndicatorOff, HScrollDeco, VScrollDeco, HScrollDecoDown, + VScrollDecoDown, ComboDeco, ComboDecoDown, CheckMark + }; + + int i; + QString tmpStr; + QString pixnames[ WIDGETS ]; // used for duplicate check + QString brdnames[ WIDGETS ]; + bool loaded[ WIDGETS ]; // used for preloading for CopyWidget + + QSettings config; + if (configDirName.isEmpty() || configDirName == ".") + { + KStyleDirs::dirs()->addToSearch( "themerc", config ); + } + else config.insertSearchPath( QSettings::Unix, configDirName ); + + applyConfigFile( config ); + + d->contrast = config.readNumEntry( configFileName + "KDE/contrast", 7 ); + + + + for ( i = 0; i < INHERIT_ITEMS; ++i ) + applyResourceGroup( &config, i ); + for ( ; i < INHERIT_ITEMS*2; ++i ) + { + if ( config.entryList( configFileName + widgetEntries[ i ] ).size() ) + applyResourceGroup( &config, i ); +#ifndef Q_WS_QWS //FIXME + + else + { + Prop& copyProp = d->props[ widgetEntries[ i ] ]; + copyProp[ "CopyWidget" ] = QString( widgetEntries[ i - INHERIT_ITEMS ] ); + } +#endif + + } + for ( ; i < WIDGETS; ++i ) + applyResourceGroup( &config, i ); + applyMiscResourceGroup( &config ); + + // initialize defaults that may not be read + for ( i = 0; i < WIDGETS; ++i ) + loaded[ i ] = false; + btnXShift = btnYShift = focus3DOffset = 0; + aTabLine = iTabLine = true; + roundedButton = roundedCombo = roundedSlider = focus3D = false; + splitterWidth = 10; + + //Handle the rotated background separately.. + d->props[ widgetEntries[ RotSliderGroove ] ] = d->props[ widgetEntries[ SliderGroove ] ]; + d->props[ widgetEntries[ RotInactiveTab ] ] = d->props[ widgetEntries[ InactiveTab ] ]; + d->props[ widgetEntries[ RotActiveTab ] ] = d->props[ widgetEntries[ ActiveTab ] ]; + + // misc items + readMiscResourceGroup(); + + + for ( i = 0; i < WIDGETS; ++i ) + readResourceGroup( i, pixnames, brdnames, loaded ); + + if ( pixmaps[ RotSliderGroove ] ) + { + QWMatrix r270; //TODO: 90 if reverse? + r270.rotate( 270 ); + KThemePixmap* bf = new KThemePixmap( pixmaps[ RotSliderGroove ], pixmaps[ RotSliderGroove ] ->xForm( r270 ) ); // + pixmaps[ RotSliderGroove ] = bf; + if ( images[ RotSliderGroove ] ) + { + delete images[ RotSliderGroove ]; + images[ RotSliderGroove ] = new QImage( bf->convertToImage() ); + } + } + + if ( pixmaps[ RotActiveTab ] ) + { + QWMatrix r180; + r180.rotate( 180 ); + KThemePixmap* bf = new KThemePixmap( pixmaps[ RotActiveTab ], pixmaps[ RotActiveTab ] ->xForm( r180 ) ); + + pixmaps[ RotActiveTab ] = bf; + if ( images[ RotActiveTab ] ) + { + delete images[ RotActiveTab ]; + images[ RotActiveTab ] = new QImage( bf->convertToImage() ); + } + } + + if ( pixmaps[ RotInactiveTab ] ) + { + QWMatrix r180; + r180.rotate( 180 ); + KThemePixmap* bf = new KThemePixmap( pixmaps[ RotInactiveTab ], pixmaps[ RotInactiveTab ] ->xForm( r180 ) ); + + pixmaps[ RotInactiveTab ] = bf; + if ( images[ RotInactiveTab ] ) + { + delete images[ RotInactiveTab ]; + images[ RotInactiveTab ] = new QImage( bf->convertToImage() ); + } + } + + // Handle preblend items + for ( i = 0; i < PREBLEND_ITEMS; ++i ) + { + if ( pixmaps[ preBlend[ i ] ] != NULL && blends[ preBlend[ i ] ] != 0.0 ) + blend( preBlend[ i ] ); + } + + d->props.clear(); +} + +KThemeBase::KThemeBase( const QString& dir, const QString & configFile ) + : KStyle( FilledFrameWorkaround ), configFileName( configFile ) +{ + d = new KThemeBasePrivate; + if ( configFileName.isEmpty() ) + configFileName = "kstylerc"; + + + configDirName = dir; + //Strip of rc from the configFileName + if ( configFileName.endsWith( "rc" ) ) + { + configFileName.truncate( configFileName.length() - 2 ); //Get rid of rc.. + } + //else SCREAM!! + + + configFileName = "/" + configFileName + "/"; + + readConfig( Qt::WindowsStyle ); + cache = new KThemeCache( cacheSize ); + + switch ( scrollBarLayout() ) + { + case SBBottomLeft: + setScrollBarType( NextStyleScrollBar ); + break; + case SBBottomRight: + setScrollBarType( PlatinumStyleScrollBar ); + break; + case SBOpposite: + break; + //Do nothing, this type already set.. + } + ; +} + +void KThemeBase::applyConfigFile( QSettings& config ) +{ + QStringList keys = config.entryList( configFileName ); + + if ( keys.contains( "foreground" ) ) + { + d->overrideForeground = true; + d->overrideForegroundCol = readColorEntry( &config, ( configFileName + "foreground" ).latin1(), 0 ); + } + else + d->overrideForeground = false; + + if ( keys.contains( "background" ) ) + { + d->overrideBackground = true; + d->overrideBackgroundCol = readColorEntry( &config, ( configFileName + "background" ).latin1(), 0 ); + } + else + d->overrideBackground = false; + + + + if ( keys.contains( "selectForeground" ) ) + { + d->overrideSelectForeground = true; + d->overrideSelectForegroundCol = readColorEntry( &config, ( configFileName + "selectForeground" ).latin1(), 0 ); + } + else + d->overrideSelectForeground = false; + + if ( keys.contains( "selectBackground" ) ) + { + d->overrideSelectBackground = true; + d->overrideSelectBackgroundCol = readColorEntry( &config, ( configFileName + "selectBackground" ).latin1(), 0 ); + } + else + d->overrideSelectBackground = false; + + if ( keys.contains( "windowBackground" ) ) + { + d->overrideWindowBackground = true; + d->overrideWindowBackgroundCol = readColorEntry( &config, ( configFileName + "windowBackground" ).latin1(), 0 ); + } + else + d->overrideWindowBackground = false; + + + if ( keys.contains( "windowForeground" ) ) + { + d->overrideWindowForeground = true; + d->overrideWindowForegroundCol = readColorEntry( &config, ( configFileName + "windowForeground" ).latin1(), 0 ); + } + else + d->overrideWindowForeground = false; + + +#ifndef Q_WS_QWS //FIXME + + for ( int input = 0; input < WIDGETS; ++input ) + { + d->props.erase( widgetEntries[ input ] ); + } + d->props.erase( "Misc" ); +#endif +} + +KThemeBase::~KThemeBase() +{ + int i; + for ( i = 0; i < WIDGETS; ++i ) + { + if ( !duplicate[ i ] ) + { + if ( images[ i ] ) + delete images[ i ]; + if ( pixmaps[ i ] ) + delete pixmaps[ i ]; + } + if ( !pbDuplicate[ i ] && pbPixmaps[ i ] ) + delete pbPixmaps[ i ]; + if ( colors[ i ] ) + delete( colors[ i ] ); + if ( grLowColors[ i ] ) + delete( grLowColors[ i ] ); + if ( grHighColors[ i ] ) + delete( grHighColors[ i ] ); + } + KStyleDirs::release(); + delete cache; + delete d; +} + +QImage* KThemeBase::loadImage( const QString &name ) +{ + QImage * image = new QImage; + QString path = KStyleDirs::dirs()->findResource( "themepixmap",name ); + image->load( path ); + if ( !image->isNull() ) + return ( image ); + qWarning( "KThemeBase: Unable to load image %s\n", name.latin1() ); + delete image; + return ( NULL ); +} + +KThemePixmap* KThemeBase::loadPixmap( const QString &name ) +{ + KThemePixmap * pixmap = new KThemePixmap( false ); + QString path = KStyleDirs::dirs()->findResource( "themepixmap", name ); + pixmap->load( path ); + if ( !pixmap->isNull() ) + return pixmap; + qWarning( "KThemeBase: Unable to load pixmap %s\n", name.latin1() ); + delete pixmap; + return ( NULL ); +} + + +KThemePixmap* KThemeBase::scale( int w, int h, WidgetType widget ) const +{ + if ( scaleHints[ widget ] == FullScale ) + { + if ( !pixmaps[ widget ] || pixmaps[ widget ] ->width() != w || + pixmaps[ widget ] ->height() != h ) + { + KThemePixmap * cachePix = cache->pixmap( w, h, widget ); + if ( cachePix ) + { + cachePix = new KThemePixmap( *cachePix ); + if ( pixmaps[ widget ] ) + cache->insert( pixmaps[ widget ], KThemeCache::FullScale, + widget ); + else + qWarning( "We would have inserted a null pixmap!\n" ); + pixmaps[ widget ] = cachePix; + } + else + { + cache->insert( pixmaps[ widget ], KThemeCache::FullScale, widget ); + QImage tmpImg = images[ widget ] ->smoothScale( w, h ); + pixmaps[ widget ] = new KThemePixmap; + pixmaps[ widget ] ->convertFromImage( tmpImg ); + if ( blends[ widget ] != 0.0 ) + blend( widget ); + } + } + } + else if ( scaleHints[ widget ] == HorizontalScale ) + { + if ( pixmaps[ widget ] ->width() != w ) + { + KThemePixmap * cachePix = cache->horizontalPixmap( w, widget ); + if ( cachePix ) + { + cachePix = new KThemePixmap( *cachePix ); + if ( pixmaps[ widget ] ) + cache->insert( pixmaps[ widget ], KThemeCache::HorizontalScale, widget ); + else + qWarning( "We would have inserted a null pixmap!\n" ); + pixmaps[ widget ] = cachePix; + } + else + { + cache->insert( pixmaps[ widget ], KThemeCache::HorizontalScale, widget ); + QImage tmpImg = images[ widget ] -> + smoothScale( w, images[ widget ] ->height() ); + pixmaps[ widget ] = new KThemePixmap; + pixmaps[ widget ] ->convertFromImage( tmpImg ); + if ( blends[ widget ] != 0.0 ) + blend( widget ); + } + } + } + else if ( scaleHints[ widget ] == VerticalScale ) + { + if ( pixmaps[ widget ] ->height() != h ) + { + KThemePixmap * cachePix = cache->verticalPixmap( w, widget ); + if ( cachePix ) + { + cachePix = new KThemePixmap( *cachePix ); + if ( pixmaps[ widget ] ) + cache->insert( pixmaps[ widget ], KThemeCache::VerticalScale, widget ); + else + qWarning( "We would have inserted a null pixmap!\n" ); + pixmaps[ widget ] = cachePix; + } + else + { + cache->insert( pixmaps[ widget ], KThemeCache::VerticalScale, widget ); + QImage tmpImg = + images[ widget ] ->smoothScale( images[ widget ] ->width(), h ); + pixmaps[ widget ] = new KThemePixmap; + pixmaps[ widget ] ->convertFromImage( tmpImg ); + if ( blends[ widget ] != 0.0 ) + blend( widget ); + } + } + } + // If blended tile here so the blend is scaled properly + else if ( scaleHints[ widget ] == TileScale && blends[ widget ] != 0.0 ) + { + if ( !pixmaps[ widget ] || pixmaps[ widget ] ->width() != w || + pixmaps[ widget ] ->height() != h ) + { + KThemePixmap * cachePix = cache->pixmap( w, h, widget ); + if ( cachePix ) + { + cachePix = new KThemePixmap( *cachePix ); + cache->insert( pixmaps[ widget ], KThemeCache::FullScale, widget ); + pixmaps[ widget ] = cachePix; + } + else + { + cache->insert( pixmaps[ widget ], KThemeCache::FullScale, widget ); + QPixmap tile; + tile.convertFromImage( *images[ widget ] ); + pixmaps[ widget ] = new KThemePixmap; + pixmaps[ widget ] ->resize( w, h ); + QPainter p( pixmaps[ widget ] ); + p.drawTiledPixmap( 0, 0, w, h, tile ); + if ( blends[ widget ] != 0.0 ) + blend( widget ); + } + } + } + return ( pixmaps[ widget ] ); +} + +KThemePixmap* KThemeBase::scaleBorder( int w, int h, WidgetType widget ) const +{ + KThemePixmap * pixmap = NULL; + if ( !pbPixmaps[ widget ] && !pbWidth[ widget ] ) + return ( NULL ); + pixmap = cache->pixmap( w, h, widget, true ); + if ( pixmap ) + { + pixmap = new KThemePixmap( *pixmap ); + } + else + { + pixmap = new KThemePixmap(); + pixmap->resize( w, h ); + QBitmap mask; + mask.resize( w, h ); + mask.fill( color0 ); + QPainter mPainter; + mPainter.begin( &mask ); + + QPixmap *tmp = borderPixmap( widget ) ->border( KThemePixmap::TopLeft ); + const QBitmap *srcMask = tmp->mask(); + int bdWidth = tmp->width(); + + bitBlt( pixmap, 0, 0, tmp, 0, 0, bdWidth, bdWidth, + Qt::CopyROP, false ); + if ( srcMask ) + bitBlt( &mask, 0, 0, srcMask, 0, 0, bdWidth, bdWidth, + Qt::CopyROP, false ); + else + mPainter.fillRect( 0, 0, bdWidth, bdWidth, color1 ); + + + tmp = borderPixmap( widget ) ->border( KThemePixmap::TopRight ); + srcMask = tmp->mask(); + bitBlt( pixmap, w - bdWidth, 0, tmp, 0, 0, bdWidth, + bdWidth, Qt::CopyROP, false ); + if ( srcMask ) + bitBlt( &mask, w - bdWidth, 0, srcMask, 0, 0, bdWidth, + bdWidth, Qt::CopyROP, false ); + else + mPainter.fillRect( w - bdWidth, 0, bdWidth, bdWidth, color1 ); + + tmp = borderPixmap( widget ) ->border( KThemePixmap::BottomLeft ); + srcMask = tmp->mask(); + bitBlt( pixmap, 0, h - bdWidth, tmp, 0, 0, bdWidth, + bdWidth, Qt::CopyROP, false ); + if ( srcMask ) + bitBlt( &mask, 0, h - bdWidth, srcMask, 0, 0, bdWidth, + bdWidth, Qt::CopyROP, false ); + else + mPainter.fillRect( 0, h - bdWidth, bdWidth, bdWidth, color1 ); + + tmp = borderPixmap( widget ) ->border( KThemePixmap::BottomRight ); + srcMask = tmp->mask(); + bitBlt( pixmap, w - bdWidth, h - bdWidth, tmp, 0, 0, + bdWidth, bdWidth, Qt::CopyROP, false ); + if ( srcMask ) + bitBlt( &mask, w - bdWidth, h - bdWidth, srcMask, 0, 0, + bdWidth, bdWidth, Qt::CopyROP, false ); + else + mPainter.fillRect( w - bdWidth, h - bdWidth, bdWidth, bdWidth, color1 ); + + QPainter p; + p.begin( pixmap ); + if ( w - bdWidth * 2 > 0 ) + { + tmp = borderPixmap( widget ) ->border( KThemePixmap::Top ); + srcMask = tmp->mask(); + p.drawTiledPixmap( bdWidth, 0, w - bdWidth * 2, bdWidth, *tmp ); + if ( srcMask ) + bitBlt( &mask, bdWidth, 0, srcMask, 0, 0, + w - bdWidth * 2, bdWidth, Qt::CopyROP, false ); + else + mPainter.fillRect( bdWidth, 0, w - bdWidth * 2, bdWidth, color1 ); + + tmp = borderPixmap( widget ) ->border( KThemePixmap::Bottom ); + srcMask = tmp->mask(); + p.drawTiledPixmap( bdWidth, h - bdWidth, w - bdWidth * 2, bdWidth, + *tmp ); + if ( srcMask ) + bitBlt( &mask, bdWidth, h - bdWidth, srcMask, 0, 0, + w - bdWidth * 2, bdWidth, Qt::CopyROP, false ); + else + mPainter.fillRect( bdWidth, h - bdWidth, w - bdWidth * 2, bdWidth, + color1 ); + } + if ( h - bdWidth * 2 > 0 ) + { + tmp = borderPixmap( widget ) ->border( KThemePixmap::Left ); + srcMask = tmp->mask(); + p.drawTiledPixmap( 0, bdWidth, bdWidth, h - bdWidth * 2, *tmp ); + if ( srcMask ) + bitBlt( &mask, 0, bdWidth, srcMask, 0, 0, + bdWidth, h - bdWidth * 2, Qt::CopyROP, false ); + else + mPainter.fillRect( 0, bdWidth, bdWidth, h - bdWidth * 2, color1 ); + + tmp = borderPixmap( widget ) ->border( KThemePixmap::Right ); + srcMask = tmp->mask(); + p.drawTiledPixmap( w - bdWidth, bdWidth, bdWidth, h - bdWidth * 2, + *tmp ); + if ( srcMask ) + bitBlt( &mask, w - bdWidth, bdWidth, srcMask, 0, 0, + bdWidth, h - bdWidth * 2, Qt::CopyROP, false ); + else + mPainter.fillRect( w - bdWidth, bdWidth, bdWidth, h - bdWidth * 2, color1 ); + } + p.end(); + mPainter.end(); + pixmap->setMask( mask ); + cache->insert( pixmap, KThemeCache::FullScale, widget, true ); + if ( !pixmap->mask() ) + qWarning( "No mask for border pixmap!\n" ); + } + return ( pixmap ); +} + + +KThemePixmap* KThemeBase::blend( WidgetType widget ) const +{ + KPixmapEffect::GradientType g; + switch ( gradients[ widget ] ) + { + case GrHorizontal: + g = KPixmapEffect::HorizontalGradient; + break; + case GrVertical: + g = KPixmapEffect::VerticalGradient; + break; + case GrPyramid: + g = KPixmapEffect::PyramidGradient; + break; + case GrRectangle: + g = KPixmapEffect::RectangleGradient; + break; + case GrElliptic: + g = KPixmapEffect::EllipticGradient; + break; + default: + g = KPixmapEffect::DiagonalGradient; + break; + } + KPixmapEffect::blend( *pixmaps[ widget ], blends[ widget ], *grLowColors[ widget ], + g, false ); + return ( pixmaps[ widget ] ); +} + +KThemePixmap* KThemeBase::gradient( int w, int h, WidgetType widget ) const +{ + if ( gradients[ widget ] == GrVertical ) + { + if ( !pixmaps[ widget ] || pixmaps[ widget ] ->height() != h ) + { + KThemePixmap * cachePix = cache->verticalPixmap( h, widget ); + if ( cachePix ) + { + cachePix = new KThemePixmap( *cachePix ); + if ( pixmaps[ widget ] ) + cache->insert( pixmaps[ widget ], KThemeCache::VerticalScale, + widget ); + pixmaps[ widget ] = cachePix; + } + else + { + if ( pixmaps[ widget ] ) + cache->insert( pixmaps[ widget ], KThemeCache::VerticalScale, + widget ); + pixmaps[ widget ] = new KThemePixmap; + pixmaps[ widget ] ->resize( w, h ); + KPixmapEffect::gradient( *pixmaps[ widget ], *grHighColors[ widget ], + *grLowColors[ widget ], + KPixmapEffect::VerticalGradient ); + } + } + } + else if ( gradients[ widget ] == GrHorizontal ) + { + if ( !pixmaps[ widget ] || pixmaps[ widget ] ->width() != w ) + { + KThemePixmap * cachePix = cache->horizontalPixmap( w, widget ); + if ( cachePix ) + { + cachePix = new KThemePixmap( *cachePix ); + if ( pixmaps[ widget ] ) + cache->insert( pixmaps[ widget ], + KThemeCache::HorizontalScale, widget ); + pixmaps[ widget ] = cachePix; + } + else + { + if ( pixmaps[ widget ] ) + cache->insert( pixmaps[ widget ], + KThemeCache::HorizontalScale, widget ); + pixmaps[ widget ] = new KThemePixmap; + pixmaps[ widget ] ->resize( w, h ); + KPixmapEffect::gradient( *pixmaps[ widget ], *grHighColors[ widget ], + *grLowColors[ widget ], + KPixmapEffect::HorizontalGradient ); + } + } + } + else if ( gradients[ widget ] == GrReverseBevel ) + { + if ( !pixmaps[ widget ] || pixmaps[ widget ] ->width() != w || + pixmaps[ widget ] ->height() != h ) + { + KThemePixmap * cachePix = cache->pixmap( w, h, widget ); + if ( cachePix ) + { + cachePix = new KThemePixmap( *cachePix ); + if ( pixmaps[ widget ] ) + cache->insert( pixmaps[ widget ], KThemeCache::FullScale, + widget ); + pixmaps[ widget ] = cachePix; + } + else + { + if ( pixmaps[ widget ] ) + cache->insert( pixmaps[ widget ], KThemeCache::FullScale, + widget ); + pixmaps[ widget ] = new KThemePixmap; + pixmaps[ widget ] ->resize( w, h ); + + KPixmap s; + int offset = decoWidth( widget ); + s.resize( w - offset * 2, h - offset * 2 ); + QColor lc( *grLowColors[ widget ] ); + QColor hc( *grHighColors[ widget ] ); + if ( bevelContrast( widget ) ) + { + int bc = bevelContrast( widget ); + // want single increments, not factors like light()/dark() + lc.setRgb( lc.red() - bc, lc.green() - bc, lc.blue() - bc ); + hc.setRgb( hc.red() + bc, hc.green() + bc, hc.blue() + bc ); + } + KPixmapEffect::gradient( *pixmaps[ widget ], + lc, hc, + KPixmapEffect::DiagonalGradient ); + KPixmapEffect::gradient( s, *grHighColors[ widget ], + *grLowColors[ widget ], + KPixmapEffect::DiagonalGradient ); + bitBlt( pixmaps[ widget ], offset, offset, &s, 0, 0, w - offset * 2, + h - offset * 2, Qt::CopyROP ); + } + } + } + else + { + KPixmapEffect::GradientType g; + switch ( gradients[ widget ] ) + { + case GrPyramid: + g = KPixmapEffect::PyramidGradient; + break; + case GrRectangle: + g = KPixmapEffect::RectangleGradient; + break; + case GrElliptic: + g = KPixmapEffect::EllipticGradient; + break; + default: + g = KPixmapEffect::DiagonalGradient; + break; + } + if ( !pixmaps[ widget ] || pixmaps[ widget ] ->width() != w || + pixmaps[ widget ] ->height() != h ) + { + KThemePixmap * cachePix = cache->pixmap( w, h, widget ); + if ( cachePix ) + { + cachePix = new KThemePixmap( *cachePix ); + if ( pixmaps[ widget ] ) + cache->insert( pixmaps[ widget ], KThemeCache::FullScale, + widget ); + pixmaps[ widget ] = cachePix; + } + else + { + if ( pixmaps[ widget ] ) + cache->insert( pixmaps[ widget ], KThemeCache::FullScale, + widget ); + pixmaps[ widget ] = new KThemePixmap; + pixmaps[ widget ] ->resize( w, h ); + KPixmapEffect::gradient( *pixmaps[ widget ], *grHighColors[ widget ], + *grLowColors[ widget ], g ); + } + } + } + return ( pixmaps[ widget ] ); +} + +KThemePixmap* KThemeBase::scalePixmap( int w, int h, WidgetType widget ) const +{ + + if ( gradients[ widget ] && blends[ widget ] == 0.0 ) + return ( gradient( w, h, widget ) ); + + return ( scale( w, h, widget ) ); +} + +QColorGroup* KThemeBase::makeColorGroup( const QColor &fg, const QColor &bg, + Qt::GUIStyle ) +{ + if ( shading == Motif ) + { + int highlightVal, lowlightVal; + highlightVal = 100 + ( 2 * d->contrast + 4 ) * 16 / 10; + lowlightVal = 100 + ( ( 2 * d->contrast + 4 ) * 10 ); + return ( new QColorGroup( fg, bg, bg.light( highlightVal ), + bg.dark( lowlightVal ), bg.dark( 120 ), + fg, QApplication::palette().active().base() ) ); + } + else + return ( new QColorGroup( fg, bg, bg.light( 150 ), bg.dark(), + bg.dark( 120 ), fg, + QApplication::palette().active().base() ) ); +} + + +void KThemeBase::applyMiscResourceGroup( QSettings *config ) +{ +#ifndef Q_WS_QWS //FIXME + d->props.erase( "Misc" ); // clear the old property + + QString base = configFileName + "Misc/"; + + Prop& prop = d->props[ "Misc" ]; + QString tmpStr; + + tmpStr = config->readEntry( base + "SButtonPosition" ); + if ( tmpStr == "BottomLeft" ) + prop[ "SButtonPosition" ] = QString::number( ( int ) SBBottomLeft ); + else if ( tmpStr == "BottomRight" ) + prop[ "SButtonPosition" ] = QString::number( ( int ) SBBottomRight ); + else + { + if ( tmpStr != "Opposite" && !tmpStr.isEmpty() ) + qWarning( "KThemeBase: Unrecognized sb button option %s, using Opposite.\n", tmpStr.latin1() ); + ; + prop[ "SButtonPosition" ] = QString::number( ( int ) SBOpposite ); + } + tmpStr = config->readEntry( base + "ArrowType" ); + if ( tmpStr == "Small" ) + prop[ "ArrowType" ] = QString::number( ( int ) SmallArrow ); + else if ( tmpStr == "3D" ) + prop[ "ArrowType" ] = QString::number( ( int ) MotifArrow ); + else + { + if ( tmpStr != "Normal" && !tmpStr.isEmpty() ) + qWarning( "KThemeBase: Unrecognized arrow option %s, using Normal.\n", tmpStr.latin1() ); + prop[ "ArrowType" ] = QString::number( ( int ) LargeArrow ); + } + tmpStr = config->readEntry( base + "ShadeStyle" ); + if ( tmpStr == "Motif" ) + prop[ "ShadeStyle" ] = QString::number( ( int ) Motif ); + else if ( tmpStr == "Next" ) + prop[ "ShadeStyle" ] = QString::number( ( int ) Next ); + else if ( tmpStr == "KDE" ) + prop[ "ShadeStyle" ] = QString::number( ( int ) KDE ); + else + prop[ "ShadeStyle" ] = QString::number( ( int ) Windows ); + + prop[ "FrameWidth" ] = QString::number( config->readNumEntry( base + "FrameWidth", 2 ) ); + prop[ "Cache" ] = QString::number( config->readNumEntry( base + "Cache", 1024 ) ); + prop[ "ScrollBarExtent" ] = QString::number( config->readNumEntry( base + "ScrollBarExtent", 16 ) ); +#endif +} + +static int readNumEntry( Prop& prop, QString setting, int def ) +{ + bool ok; + QString s_val = prop[ setting ]; + int val = s_val.toInt( &ok ); + if ( ok ) + return val; + return def; +} + +static QColor readColorEntry( Prop& prop, QString setting, const QColor& def ) +{ + QString s_val = prop[ setting ]; + if ( !s_val.isEmpty() ) + { + QColor c( s_val ); + return c; + } + return def; +} + +void KThemeBase::readMiscResourceGroup() +{ +#ifndef Q_WS_QWS //FIXME + Prop & prop = d->props[ "Misc" ]; + + sbPlacement = ( SButton ) readNumEntry( prop, "SButtonPosition", + ( int ) SBOpposite ); + arrowStyle = ( ArrowStyle ) readNumEntry( prop, "ArrowType", + ( int ) LargeArrow ); + shading = ( ShadeStyle ) readNumEntry( prop, "ShadeStyle", ( int ) Windows ); + defaultFrame = readNumEntry( prop, "FrameWidth", 2 ); + cacheSize = readNumEntry( prop, "Cache", 1024 ); + sbExtent = readNumEntry( prop, "ScrollBarExtent", 16 ); +#endif +} + + +void KThemeBase::applyResourceGroup( QSettings *config, int i ) +{ +#ifndef Q_WS_QWS //FIXME + QString tmpStr; + int tmpVal; + + // clear the old property + d->props.erase( widgetEntries[ i ] ); + + QString base = configFileName + widgetEntries[ i ] + "/"; + + Prop& prop = d->props[ widgetEntries[ i ] ]; + + tmpStr = config->readEntry( base + "CopyWidget", "" ); + prop[ "CopyWidget" ] = tmpStr; + if ( !tmpStr.isEmpty() ) + { + return ; + } + + tmpStr = config->readEntry( base + "Scale" ); + if ( tmpStr == "Full" ) + tmpVal = ( int ) FullScale; + else if ( tmpStr == "Horizontal" ) + tmpVal = ( int ) HorizontalScale; + else if ( tmpStr == "Vertical" ) + tmpVal = ( int ) VerticalScale; + else + { + if ( tmpStr != "Tile" && !tmpStr.isEmpty() ) + qWarning( "KThemeBase: Unrecognized scale option %s, using Tile.\n", tmpStr.latin1() ); + tmpVal = ( int ) TileScale; + } + prop[ "ScaleHint" ] = QString::number( tmpVal ); + + // Gradient type + tmpStr = config->readEntry( base + "Gradient" ); + if ( tmpStr == "Diagonal" ) + tmpVal = ( int ) GrDiagonal; + else if ( tmpStr == "Horizontal" ) + tmpVal = ( int ) GrHorizontal; + else if ( tmpStr == "Vertical" ) + tmpVal = ( int ) GrVertical; + else if ( tmpStr == "Pyramid" ) + tmpVal = ( int ) GrPyramid; + else if ( tmpStr == "Rectangle" ) + tmpVal = ( int ) GrRectangle; + else if ( tmpStr == "Elliptic" ) + tmpVal = ( int ) GrElliptic; + else if ( tmpStr == "ReverseBevel" ) + tmpVal = ( int ) GrReverseBevel; + else + { + if ( tmpStr != "None" && !tmpStr.isEmpty() ) + qWarning( "KThemeBase: Unrecognized gradient option %s, using None.\n", tmpStr.latin1() ); + tmpVal = ( int ) GrNone; + } + prop[ "Gradient" ] = QString::number( tmpVal ); + + // Blend intensity + tmpStr.setNum( config->readDoubleEntry( base + "BlendIntensity", 0.0 ) ); + prop[ "Blend" ] = tmpStr; + + // Bevel contrast + prop[ "BContrast" ] = QString::number( config->readNumEntry( base + "BevelContrast", 0 ) ); + + // Border width + prop[ "Border" ] = QString::number( config->readNumEntry( base + "Border", 1 ) ); + + // Highlight width + prop[ "Highlight" ] = QString::number( config->readNumEntry( base + "Highlight", 1 ) ); + + QStringList keys = config->entryList( base ); + + // Gradient low color or blend background + if ( keys.contains( "GradientLow" ) ) + prop[ "GrLow" ] = readColorEntry( config, QString( base + "GradientLow" ).latin1(), + &QApplication::palette().active().background() ).name(); + + // Gradient high color + if ( keys.contains( "GradientHigh" ) ) + prop[ "GrHigh" ] = readColorEntry( config, QString( base + "GradientHigh" ).latin1(), + &QApplication::palette().active().foreground() ).name(); + + // Extended color attributes + if ( keys.contains( "Foreground" ) || keys.contains( "Background" ) ) + { + QColor fg, bg; + if ( keys.contains( "Background" ) ) + bg = readColorEntry( config, QString( base + "Background" ).latin1(), &bg ); + if ( keys.contains( "Foreground" ) ) + fg = readColorEntry( config, QString( base + "Foreground" ).latin1(), &fg ); + prop[ "Foreground" ] = fg.name(); + prop[ "Background" ] = bg.name(); + + } + else + colors[ i ] = NULL; + + // Pixmap + tmpStr = config->readEntry( base + "Pixmap", "" ); + if ( !tmpStr.isEmpty() ) + prop[ "Pixmap" ] = tmpStr; + // Pixmap border + tmpStr = config->readEntry( base + "PixmapBorder", "" ); + if ( !tmpStr.isEmpty() ) + { + prop[ "PixmapBorder" ] = tmpStr; + prop[ "PixmapBWidth" ] = QString::number( + config->readNumEntry( base + "PixmapBWidth", 0 ) ); + } + + // Various widget specific settings. This was more efficient when bunched + // together in the misc group, but this makes an easier to read config. + if ( i == SliderGroove ) + prop[ "SmallGroove" ] = QString::number( + config->readBoolEntry( base + "SmallGroove", false ) ); + else if ( i == ActiveTab || i == InactiveTab ) + prop[ "BottomLine" ] = QString::number( + config->readBoolEntry( base + "BottomLine", true ) ); + else if ( i == Splitter ) + prop[ "Width" ] = QString::number( config->readNumEntry( base + "Width", 10 ) ); + else if ( i == ComboBox || i == ComboBoxDown ) + { + if ( keys.contains( "Round" ) ) + prop[ "Round" ] = QString::number( config->readBoolEntry( base + "Round", false ) ); + else + prop[ "Round" ] = "5000"; // invalid, used w/multiple groups + + } + else if ( i == PushButton || i == PushButtonDown ) + { + if ( keys.contains( "XShift" ) ) + prop[ "XShift" ] = QString::number( config->readNumEntry( base + "XShift", 0 ) ); + else + prop[ "XShift" ] = "5000"; + if ( keys.contains( "YShift" ) ) + prop[ "YShift" ] = QString::number( config->readNumEntry( base + "YShift", 0 ) ); + else + prop[ "YShift" ] = "5000"; + if ( keys.contains( "3DFocusRect" ) ) + prop[ "3DFRect" ] = QString::number( config-> + readBoolEntry( base + "3DFocusRect", false ) ); + else + prop[ "3DFRect" ] = "5000"; + if ( keys.contains( "3DFocusOffset" ) ) + prop[ "3DFOffset" ] = QString::number( config-> + readBoolEntry( base + "3DFocusOffset", 0 ) ); + else + prop[ "3DFOffset" ] = "5000"; + if ( keys.contains( "Round" ) ) + prop[ "Round" ] = QString::number( config->readBoolEntry( base + "Round", false ) ); + else + prop[ "Round" ] = "5000"; + } +#endif +} + +void KThemeBase::readResourceGroup( int i, QString *pixnames, QString *brdnames, + bool *loadArray ) +{ +#ifndef Q_WS_QWS //FIXME + if ( loadArray[ i ] == true ) + { + return ; // already been preloaded. + } + + int tmpVal; + Prop prop = d->props[ widgetEntries[ i ] ]; + QString tmpStr; + + tmpStr = prop[ "CopyWidget" ]; + if ( !tmpStr.isEmpty() ) + { // Duplicate another widget's config + int sIndex; + loadArray[ i ] = true; + for ( sIndex = 0; sIndex < WIDGETS; ++sIndex ) + { + if ( tmpStr == widgetEntries[ sIndex ] ) + { + if ( !loadArray[ sIndex ] ) // hasn't been loaded yet + readResourceGroup( sIndex, pixnames, brdnames, + loadArray ); + break; + } + } + if ( loadArray[ sIndex ] ) + { + copyWidgetConfig( sIndex, i, pixnames, brdnames ); + } + else + qWarning( "KThemeBase: Unable to identify source widget for %s\n", widgetEntries[ i ] ); + return ; + } + // special inheritance for disabled arrows (these are tri-state unlike + // the rest of what we handle). + for ( tmpVal = DisArrowUp; tmpVal <= DisArrowRight; ++tmpVal ) + { + if ( tmpVal == i ) + { + tmpStr = prop[ "Pixmap" ]; + if ( tmpStr.isEmpty() ) + { + copyWidgetConfig( ArrowUp + ( tmpVal - DisArrowUp ), i, pixnames, + brdnames ); + return ; + } + } + } + + // Scale hint + scaleHints[ i ] = ( ScaleHint ) readNumEntry( prop, "ScaleHint", ( int ) TileScale ); + gradients[ i ] = ( Gradient ) readNumEntry( prop, "Gradient", ( int ) GrNone ); + + // Blend intensity + tmpStr = prop[ "Blend" ]; + if ( tmpStr.isEmpty() ) + tmpStr = QString::fromLatin1( "0.0" ); + blends[ i ] = tmpStr.toFloat(); + + // Bevel contrast + bContrasts[ i ] = readNumEntry( prop, "BContrast", 0 ); + + // Border width + borders[ i ] = readNumEntry( prop, "Border", 1 ); + + // Highlight width + highlights[ i ] = readNumEntry( prop, "Highlight", 1 ); + + // Gradient low color or blend background + if ( gradients[ i ] != GrNone || blends[ i ] != 0.0 ) + grLowColors[ i ] = + new QColor( readColorEntry( prop, "GrLow", + QApplication::palette().active(). + background() ) ); + else + grLowColors[ i ] = NULL; + + // Gradient high color + if ( gradients[ i ] != GrNone ) + grHighColors[ i ] = + new QColor( readColorEntry( prop, "GrHigh", + QApplication::palette().active(). + background() ) ); + else + grHighColors[ i ] = NULL; + + // Extended color attributes + QColor fg, bg; + fg = readColorEntry( prop, "Foreground", fg ); + bg = readColorEntry( prop, "Background", bg ); + if ( fg.isValid() || bg.isValid() ) + { + if ( !fg.isValid() ) + fg = QApplication::palette().active().foreground(); + if ( !bg.isValid() ) + bg = QApplication::palette().active().background(); + colors[ i ] = makeColorGroup( fg, bg, Qt::WindowsStyle ); + } + else + colors[ i ] = NULL; + + // Pixmap + int existing; + tmpStr = prop[ "Pixmap" ]; + pixnames[ i ] = tmpStr; + duplicate[ i ] = false; + pixmaps[ i ] = NULL; + images[ i ] = NULL; + // Scan for duplicate pixmaps(two identical pixmaps, tile scale, no blend, + // no pixmapped border) + if ( !tmpStr.isEmpty() ) + { + for ( existing = 0; existing < i; ++existing ) + { + if ( tmpStr == pixnames[ existing ] && scaleHints[ i ] == TileScale && + scaleHints[ existing ] == TileScale && blends[ existing ] == 0.0 && + blends[ i ] == 0.0 ) + { + pixmaps[ i ] = pixmaps[ existing ]; + duplicate[ i ] = true; + break; + } + } + } + // load + if ( !duplicate[ i ] && !tmpStr.isEmpty() ) + { + pixmaps[ i ] = loadPixmap( tmpStr ); + // load and save images for scaled/blended widgets for speed. + if ( scaleHints[ i ] == TileScale && blends[ i ] == 0.0 ) + images[ i ] = NULL; + else + images[ i ] = loadImage( tmpStr ); + } + + // Pixmap border + tmpStr = prop[ "PixmapBorder" ]; + brdnames[ i ] = tmpStr; + pbDuplicate[ i ] = false; + pbPixmaps[ i ] = NULL; + pbWidth[ i ] = 0; + if ( !tmpStr.isEmpty() ) + { + pbWidth[ i ] = readNumEntry( prop, "PixmapBWidth", 0 ); + if ( pbWidth[ i ] == 0 ) + { + qWarning( "KThemeBase: No border width specified for pixmapped border widget %s\n", + widgetEntries[ i ] ); + qWarning( "KThemeBase: Using default of 2.\n" ); + pbWidth[ i ] = 2; + } + // duplicate check + for ( existing = 0; existing < i; ++existing ) + { + if ( tmpStr == brdnames[ existing ] ) + { + pbPixmaps[ i ] = pbPixmaps[ existing ]; + pbDuplicate[ i ] = true; + break; + } + } + } + // load + if ( !pbDuplicate[ i ] && !tmpStr.isEmpty() ) + pbPixmaps[ i ] = loadPixmap( tmpStr ); + + if ( pbPixmaps[ i ] && !pbDuplicate[ i ] ) + generateBorderPix( i ); + + // Various widget specific settings. This was more efficient when bunched + // together in the misc group, but this makes an easier to read config. + if ( i == SliderGroove ) + roundedSlider = readNumEntry( prop, "SmallGroove", false ); + else if ( i == ActiveTab ) + aTabLine = readNumEntry( prop, "BottomLine", true ); + else if ( i == InactiveTab ) + iTabLine = readNumEntry( prop, "BottomLine", true ); + else if ( i == Splitter ) + splitterWidth = readNumEntry( prop, "Width", 10 ); + else if ( i == ComboBox || i == ComboBoxDown ) + { + tmpVal = readNumEntry( prop, "Round", 5000 ); + if ( tmpVal != 5000 ) + roundedCombo = tmpVal; + } + else if ( i == PushButton || i == PushButtonDown ) + { + tmpVal = readNumEntry( prop, "XShift", 0 ); + if ( tmpVal != 5000 ) + btnXShift = tmpVal; + tmpVal = readNumEntry( prop, "YShift", 0 ); + if ( tmpVal != 5000 ) + btnYShift = tmpVal; + tmpVal = readNumEntry( prop, "3DFRect", false ); + if ( tmpVal != 5000 ) + focus3D = tmpVal; + tmpVal = readNumEntry( prop, "3DFOffset", 0 ); + if ( tmpVal != 5000 ) + focus3DOffset = tmpVal; + tmpVal = readNumEntry( prop, "Round", false ); + if ( tmpVal != 5000 ) + roundedButton = tmpVal; + } + loadArray[ i ] = true; +#endif +} + + +QPalette KThemeBase::overridePalette( const QPalette& pal ) +{ + + //Read current settings for colors.. + QColor background = pal.active().background(); + QColor foreground = pal.active().foreground(); + QColor button = background; //CHECKME + QColor highlight = pal.active().highlight(); + QColor highlightedText = pal.active().highlightedText(); //CHECKME + QColor base = pal.active().base(); //config->readColorEntry( "windowBackground", &white ); + QColor baseText = pal.active().text(); //CHECKME + + //See whether there are any immediate overrides. + if ( d->overrideBackground ) + background = d->overrideBackgroundCol; + + if ( d->overrideForeground ) + foreground = d->overrideForegroundCol; + + if ( d->overrideSelectBackground ) + highlight = d->overrideSelectBackgroundCol; + if ( d->overrideSelectForeground ) + highlightedText = d->overrideSelectForegroundCol; + + if ( d->overrideWindowBackground ) + base = d->overrideWindowBackgroundCol; + if ( d->overrideWindowForeground ) + baseText = d->overrideWindowForegroundCol; + + //Now, try to get the button color from the pixmap + if ( uncached( Bevel ) ) + button = d->pixmapAveColor( uncached( Bevel ) ); + + QColor buttonText = foreground; + + int highlightVal, lowlightVal; + highlightVal = 100 + ( 2 * d->contrast + 4 ) * 16 / 10; + lowlightVal = 100 + ( 2 * d->contrast + 4 ) * 10; + + + if ( isPixmap( Background ) || isColor( Background ) ) + { + if ( isColor( Background ) ) + { + background = colorGroup( pal.active(), Background ) + ->background(); + } + if ( isPixmap( Background ) ) + { + background = d->pixmapAveColor( uncached( Background ) ); + } + + + QColorGroup pre( foreground, button, background.light( highlightVal ), + background.dark( lowlightVal ), background.dark( 120 ), + baseText, buttonText /*CHECKME: BrightText*/, base, background ); + + buttonText = colorGroup( pre, PushButton ) ->foreground(); + } + + QColor disfg = foreground; + int h, s, v; + disfg.hsv( &h, &s, &v ); + if ( v > 128 ) + // dark bg, light fg - need a darker disabled fg + disfg = disfg.dark( lowlightVal ); + else if ( disfg != black ) + // light bg, dark fg - need a lighter disabled fg - but only if !black + disfg = disfg.light( highlightVal ); + else + // black fg - use darkgray disabled fg + disfg = Qt::darkGray; + + + QColorGroup disabledgrp( disfg, background, //TODO:Convert this to the new ctor. + background.light( highlightVal ), + background.dark( lowlightVal ), + background.dark( 120 ), + background.dark( 120 ), base ); + + + QColorGroup colgrp( foreground, button, background.light( highlightVal ), + background.dark( lowlightVal ), background.dark( 120 ), + baseText, buttonText /*CHECKME: BrightText*/, base, background ); + + + + colgrp.setColor( QColorGroup::Highlight, highlight ); + colgrp.setColor( QColorGroup::HighlightedText, highlightedText ); + colgrp.setColor( QColorGroup::ButtonText, buttonText ); + colgrp.setColor( QColorGroup::Midlight, button.light( 110 ) ); + + + disabledgrp.setColor( QColorGroup::Base, base ); + disabledgrp.setColor( QColorGroup::Button, button ); + disabledgrp.setColor( QColorGroup::ButtonText, buttonText ); + disabledgrp.setColor( QColorGroup::Midlight, button.light( 110 ) ); + + QPalette newPal( colgrp, disabledgrp, colgrp ); + + return newPal; + +} + +KThemePixmap::KThemePixmap( bool timer ) + : KPixmap() +{ + if ( timer ) + { + t = new QTime; + t->start(); + } + else + t = NULL; + int i; + for ( i = 0; i < 8; ++i ) + b[ i ] = NULL; +} + +KThemePixmap::KThemePixmap( const KThemePixmap &p ) + : KPixmap( p ) +{ + if ( p.t ) + { + t = new QTime; + t->start(); + } + else + t = NULL; + int i; + for ( i = 0; i < 8; ++i ) + if ( p.b[ i ] ) + b[ i ] = new QPixmap( *p.b[ i ] ); + else + b[ i ] = NULL; +} + +KThemePixmap::KThemePixmap( const KThemePixmap &p, const QPixmap &p2 ) + : KPixmap( p2 ) +{ + if ( p.t ) + { + t = new QTime; + t->start(); + } + else + t = NULL; + int i; + for ( i = 0; i < 8; ++i ) + if ( p.b[ i ] ) + b[ i ] = new QPixmap( *p.b[ i ] ); + else + b[ i ] = NULL; +} + + + +KThemePixmap::~KThemePixmap() +{ + if ( t ) + delete t; + int i; + for ( i = 0; i < 8; ++i ) + if ( b[ i ] ) + delete b[ i ]; +} + +KThemeCache::KThemeCache( int maxSize, QObject *parent, const char *name ) + : QObject( parent, name ) +{ + cache.setMaxCost( maxSize * 1024 ); + cache.setAutoDelete( true ); + flushTimer.start( 300000 ); // 5 minutes + connect( &flushTimer, SIGNAL( timeout() ), SLOT( flushTimeout() ) ); +} + +void KThemeCache::flushTimeout() +{ + QIntCacheIterator<KThemePixmap> it( cache ); + while ( it.current() ) + { + if ( it.current() ->isOld() ) + cache.remove( it.currentKey() ); + else + ++it; + } +} + +KThemePixmap* KThemeCache::pixmap( int w, int h, int widgetID, bool border, + bool mask ) +{ + + kthemeKey key; + key.cacheKey = 0; // shut up, gcc + key.data.id = widgetID; + key.data.width = w; + key.data.height = h; + key.data.border = border; + key.data.mask = mask; + + KThemePixmap *pix = cache.find( ( unsigned long ) key.cacheKey ); + if ( pix ) + pix->updateAccessed(); + return ( pix ); +} + +KThemePixmap* KThemeCache::horizontalPixmap( int w, int widgetID ) +{ + kthemeKey key; + key.cacheKey = 0; // shut up, gcc + key.data.id = widgetID; + key.data.width = w; + key.data.height = 0; + key.data.border = false; + key.data.mask = false; + KThemePixmap *pix = cache.find( ( unsigned long ) key.cacheKey ); + if ( pix ) + pix->updateAccessed(); + return ( pix ); +} + +KThemePixmap* KThemeCache::verticalPixmap( int h, int widgetID ) +{ + kthemeKey key; + key.cacheKey = 0; // shut up, gcc + key.data.id = widgetID; + key.data.width = 0; + key.data.height = h; + key.data.border = false; + key.data.mask = false; + KThemePixmap *pix = cache.find( ( unsigned long ) key.cacheKey ); + if ( pix ) + pix->updateAccessed(); + return ( pix ); +} + +bool KThemeCache::insert( KThemePixmap *pixmap, ScaleHint scale, int widgetID, + bool border, bool mask ) +{ + kthemeKey key; + key.cacheKey = 0; // shut up, gcc + key.data.id = widgetID; + key.data.width = ( scale == FullScale || scale == HorizontalScale ) ? + pixmap->width() : 0; + key.data.height = ( scale == FullScale || scale == VerticalScale ) ? + pixmap->height() : 0; + key.data.border = border; + key.data.mask = mask; + + if ( cache.find( ( unsigned long ) key.cacheKey, true ) != NULL ) + { + return ( true ); // a pixmap of this scale is already in there + } + return ( cache.insert( ( unsigned long ) key.cacheKey, pixmap, + pixmap->width() * pixmap->height() * pixmap->depth() / 8 ) ); +} + + + + +#include "kthemebase.moc" diff --git a/kstyles/kthemestyle/kthemebase.h b/kstyles/kthemestyle/kthemebase.h new file mode 100644 index 000000000..8f8041464 --- /dev/null +++ b/kstyles/kthemestyle/kthemebase.h @@ -0,0 +1,853 @@ +/* + $Id$ + + This file is part of the KDE libraries + Copyright (C) 1999 Daniel M. Duley <mosfet@kde.org> + + KDE3 port (C) 2001 Maksim Orlovich <mo002j@mail.rochester.edu> + + Palette setup code is from KApplication, +Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org) +Copyright (C) 1998, 1999, 2000 KDE Team + + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KTHEMEBASE_H +#define KTHEMEBASE_H + +#include <qtimer.h> +#include <qdatetime.h> +#include <kpixmap.h> +#include <qintcache.h> +#include <qstring.h> +#include <kstyle.h> +#include <qsettings.h> +#include <qpalette.h> // for QColorGroup +#include "kstyledirs.h" +#include <qmap.h> + +class QImage; + + + +/** + * This class adds simple time management to KPixmap for use in flushing + * KThemeCache. + * + * @author Daniel M. Duley <mosfet@kde.org> + */ +class KThemePixmap : public KPixmap +{ +public: + enum BorderType{Top = 0, Bottom, Left, Right, TopLeft, TopRight, BottomLeft, + BottomRight}; + + KThemePixmap( bool timer = true ); + KThemePixmap( const KThemePixmap &p ); + KThemePixmap( const KThemePixmap &p, const QPixmap& rp ); + ~KThemePixmap(); + QPixmap* border( BorderType type ); + void setBorder( BorderType type, const QPixmap &p ); + void updateAccessed(); + bool isOld(); +protected: + QTime *t; + QPixmap *b[ 8 ]; + +private: + class KThemePixmapPrivate; + KThemePixmapPrivate *d; +}; + +inline QPixmap* KThemePixmap::border( BorderType type ) +{ + return ( b[ type ] ); +} + +inline void KThemePixmap::setBorder( BorderType type, const QPixmap &p ) +{ + if ( b[ type ] ) + { + qWarning( "KThemePixmap: Overwriting existing border!" ); + delete( b[ type ] ); + } + b[ type ] = new QPixmap( p ); +} + +inline void KThemePixmap::updateAccessed() +{ + if ( t ) + t->start(); +} + +inline bool KThemePixmap::isOld() +{ + return ( t ? t->elapsed() >= 300000 : false ); +} + +/** + * A very simple pixmap cache for theme plugins. QPixmapCache is not used + * since it uses QString keys which are not needed. All the information we + * need can be encoded in a numeric key. Using QIntCache instead allows us to + * skip the string operations. + * + * This class is mostly just inline methods that do bit operations on a key + * composed of the widget ID, width and/or height, and then calls + * QIntCache::find(). + * + * One other thing to note is that full, horizontal, and vertically scaled + * pixmaps are not used interchangeably. For example, if you insert a fully + * scaled pixmap that is 32x32 then request a horizontally scaled pixmap with + * a width of 32, they will not match. This is because a pixmap that has been + * inserted into the cache has already been scaled at some point and it is + * very likely the vertical height was not originally 32. Thus the pixmap + * will be wrong when drawn, even though the horizontal width matches. + * + * @author Daniel M. Duley <mosfet@kde.org> + * + */ +class KThemeCache : public QObject +{ + Q_OBJECT +public: + /** + * The scale hints supported by the cache. Note that Tiled is not here + * since tiled pixmaps are kept only once in KThemeBase. + */ + enum ScaleHint{FullScale, HorizontalScale, VerticalScale}; + /** + * The constructor. + * + * @param maxSize The maximum size of the cache in kilobytes. + * @param parent The parent object. + * @param name The name of the object. + */ + KThemeCache( int maxSize, QObject *parent = 0, const char *name = 0 ); + /** + * Inserts a new pixmap into the cache. + * + * @param pixmap The pixmap to insert. + * @param scale The scaling type of the pixmap. + * @param widgetID The widget ID of the pixmap, usually from KThemeBase's + * WidgetType enum. + * @param border True if the pixmap has a border. + * @param mask True if the pixmap has a mask. + * + * @return True if the insert was successful, false otherwise. + */ + bool insert( KThemePixmap *pixmap, ScaleHint scale, int widgetID, + bool border = false, bool mask = false ); + /** + * Returns a fully scaled pixmap. + * + * @param w The pixmap width to search for. + * @param h The pixmap height to search for. + * @param widgetID The widget ID to search for. + * @param border True if the pixmap has a border. + * @param mask True if the pixmap has a mask. + * + * @return True if a pixmap matching the width, height, and widget ID of + * the pixmap exists, NULL otherwise. + */ + KThemePixmap* pixmap( int w, int h, int widgetID, bool border = false, + bool mask = false ); + /** + * Returns a horizontally scaled pixmap. + * + * @param w The pixmap width to search for. + * @param widgetID The widget ID to search for. + * + * @return True if a pixmap matching the width and widget ID of + * the pixmap exists, NULL otherwise. + */ + KThemePixmap* horizontalPixmap( int w, int widgetID ); + /** + * Returns a vertically scaled pixmap. + * + * @param h The pixmap height to search for. + * @param widgetID The widget ID to search for. + * + * @return True if a pixmap matching the height and widget ID of + * the pixmap exists, NULL otherwise. + */ + KThemePixmap* verticalPixmap( int h, int widgetID ); +protected slots: + void flushTimeout(); +protected: + QIntCache<KThemePixmap> cache; + QTimer flushTimer; + +private: + class KThemeCachePrivate; + KThemeCachePrivate *d; +}; + + + +class KThemeBasePrivate; +/** + * This is a base class for KDE themed styles. It implements a cache, + * configuration file parsing, pixmap scaling, gradients, and a lot + * of inline methods for accessing user specified parameters. + * + * Note that this class *does not* actually implement any themes. It just + * provides the groundwork for doing so. The only reason to use this class + * directly is if you plan to reimplement all of the widgets. Otherwise, + * refer to KThemeStyle for a fully themed style you can derive from. + * + * @author Daniel M. Duley <mosfet@kde.org> + */ +class KThemeBase: public KStyle +{ + Q_OBJECT +public: + /** + * Constructs a new KThemeBase object. + */ + KThemeBase( const QString &dirs, const QString &configFile ); + ~KThemeBase(); + /** + * Describes if a pixmap should be scaled fully, horizontally, vertically, + * or not at all and tiled. + */ + enum ScaleHint{FullScale, HorizontalScale, VerticalScale, TileScale}; + /** + * The default arrow types. + */ + enum ArrowStyle{MotifArrow, LargeArrow, SmallArrow}; + /** + * The default frame shading styles. + */ + enum ShadeStyle{Motif, Windows, Next, KDE}; + /** + * The default scrollbar button layout. BottomLeft is like what Next + * uses, BottomRight is like Platinum, and Opposite it like Windows and + * Motif. + */ + enum SButton{SBBottomLeft, SBBottomRight, SBOpposite}; + /** + * The gradient types. Horizontal is left to right, Vertical is top to + * bottom, and diagonal is upper-left to bottom-right. + */ + enum Gradient{GrNone, GrHorizontal, GrVertical, GrDiagonal, GrPyramid, + GrRectangle, GrElliptic, GrReverseBevel}; + /** + * This provides a list of widget types that KThemeBase recognizes. + */ + /* Internal note: The order here is important. Some widgets inherit + * properties. This is usually for when you have two settings for the + * same widget, ie: on(sunken), and off. The on settings will inherit + * the properties of the off one when nothing is specified in the config. + * + * In order to be able to handle this while still having everything in + * one group that is easy to loop from we have the following order: + * unsunked(off) items, sunken(on)items, and then the ones that don't + * matter. INHERIT_ITEMS define the number of widgets that have inheritence + * so if 0 == PushButtonOff then INHERIT_ITEMS should == PushButtonOn + * and so on. WIDGETS define the total number of widgets. + */ + enum WidgetType{ + // Off (unsunken widgets) + PushButton = 0, ComboBox, HScrollBarSlider, VScrollBarSlider, Bevel, + ToolButton, ScrollButton, HScrollDeco, VScrollDeco, + ComboDeco, MenuItem, InactiveTab, ArrowUp, ArrowDown, ArrowLeft, + ArrowRight, + // On (sunken widgets) + PushButtonDown, ComboBoxDown, HScrollBarSliderDown, + VScrollBarSliderDown, BevelDown, ToolButtonDown, ScrollButtonDown, + HScrollDecoDown, VScrollDecoDown, ComboDecoDown, MenuItemDown, + ActiveTab, SunkenArrowUp, SunkenArrowDown, SunkenArrowLeft, + SunkenArrowRight, + // Everything else (indicators must have separate settings) + HScrollGroove, VScrollGroove, Slider, SliderGroove, IndicatorOn, + IndicatorOff, IndicatorTri, ExIndicatorOn, ExIndicatorOff, HBarHandle, VBarHandle, + ToolBar, Splitter, CheckMark, MenuBar, DisArrowUp, DisArrowDown, + DisArrowLeft, DisArrowRight, ProgressBar, ProgressBg, MenuBarItem, + Background, RotSliderGroove, RotInactiveTab, RotActiveTab, WIDGETS}; + + /** + * The scaling type specified by the KConfig file. + * + * @param widget A Widgets enum value. + * + * @return A ScaleHint enum value. + */ + ScaleHint scaleHint( WidgetType widget ) const; + /** + * The gradient type specified by the KConfig file. + * + * @param widget A Widgets enum value. + * + * @return A Gradient enum value. + */ + Gradient gradientHint( WidgetType widget ) const; + /** + * The color group specified for a given widget. + * If a color group is set in the theme configuration + * that is used, otherwise defaultColor is returned. + * + * @param defaultGroup The colorGroup to set if one is available. + * + * @param widget The widget whose color group to retrieve. + * + */ + const QColorGroup* colorGroup( const QColorGroup &defaultGroup, + WidgetType widget ) const; + + QBrush pixmapBrush( const QColorGroup &group, QColorGroup::ColorRole role, + int w, int h, WidgetType widget ); + /** + * True if the widget has a pixmap or gradient specified. + */ + bool isPixmap( WidgetType widget ) const; + /** + * True if the widget has a color group specified. + */ + bool isColor( WidgetType widget ) const; + /** + * True if the user specified a 3D focus rectangle + */ + bool is3DFocus() const; + /** + * If the user specified a 3D focus rectangle, they may also specify an + * offset from the default rectangle to use when drawing it. This returns + * the specified offset. + */ + int focusOffset() const; + /** + * The border width of the specified widget. + */ + int borderWidth( WidgetType widget ) const; + /** + * Pixmap border width of the specified widget. + */ + int pixBorderWidth( WidgetType widget ) const; + /** + * Returns the border pixmap if enabled for the specified widget. This + * will contain the originial pixmap, plus the edges separated in + * KThemePixmap::border() if valid. If invalid it will return NULL. + */ + KThemePixmap* borderPixmap( WidgetType widget ) const; + /** + * The highlight width of the specified widget. + */ + int highlightWidth( WidgetType widget ) const; + /** + * The border plus highlight width of the widget. + */ + int decoWidth( WidgetType widget ) const; + /** + * The extent (width for vertical, height for horizontal) requested + * for the scrollbars. + */ + int getSBExtent() const; + /** + * The scrollbar button layout. + */ + SButton scrollBarLayout() const; + /** + * The arrow type. + */ + ArrowStyle arrowType() const; + /** + * The shading type. + */ + ShadeStyle shade() const; + /** + * The frame width. + */ + int frameWidth() const; + /** + * The splitter width. + */ + int splitWidth() const; + /** + * The contrast for some bevel effects such as reverse gradient. + */ + int bevelContrast( WidgetType widget ) const; + /** + * The button text X shift. + */ + int buttonXShift() const; + /** + * The button text Y shift. + */ + int buttonYShift() const; + /** + * Returns either the slider length of the slider pixmap if available, + * otherwise the length specified in the config file. + */ + int sliderButtonLength() const; + /** + * True if rounded buttons are requested. + */ + bool roundButton() const; + /** + * True if rounded comboboxes are requested. + */ + bool roundComboBox() const; + /** + * True if rounded slider grooves are requested. + */ + bool roundSlider() const; + /** + * True if a line should be drawn on the bottom of active tabs. + */ + bool activeTabLine() const; + /** + * True if a line should be drawn on the bottom of inactive tabs. + */ + bool inactiveTabLine() const; + /** + * Returns the current uncached pixmap for the given widget. This will + * usually be either the last scaled or gradient pixmap if those have + * been specified in the config file, the original pixmap if not, or NULL + * if no pixmap has been specified. + */ + KThemePixmap* uncached( WidgetType widget ) const; + /** + * Returns the pixmap for the given widget at the specified width and + * height. This will return NULL if no pixmap or gradient is specified. + * It may also return a different sized pixmap if the scaling + * is set to Tiled. When using this method, you should call it using + * the needed width and height then use QPainter::drawTiledPixmap to + * paint it. Doing this, if the pixmap is scaled it will be the proper + * size, otherwise it will be tiled. + * + * @param w Requested width. + * @param h Requested height. + * @param widget Widget type. + * @return The pixmap or NULL if one is not specified. + */ + virtual KThemePixmap *scalePixmap( int w, int h, WidgetType widget ) const; +protected: + /** + * This method reads a configuration file and sets things up so + * overrideColorGroup works. Modiying user's config files within + * a style is evil, IMHO (SadEagle). On the other hand, this will + * make it simply ignore settings. + * + * @param config The configuration file to apply. + */ + void applyConfigFile( QSettings & config ); + + /* + * Generates a new palette based on the values for which have been specified explicitly + * in the .themerc file. + */ + QPalette overridePalette( const QPalette& pal ); + + /** + * Returns a QImage for the given widget if the widget is scaled, NULL + * otherwise. QImages of the original pixmap are stored for scaled + * widgets in order to facilitate fast and accurate smooth-scaling. This + * also saves us a conversion from a pixmap to an image then back again. + */ + QImage* image( WidgetType widget ) const; + /** + * Returns the gradient high color if one is specified, NULL otherwise. + */ + QColor* gradientHigh( WidgetType widget ) const; + /** + * Returns the gradient low color if one is specified, NULL otherwise. + */ + QColor* gradientLow( WidgetType widget ) const; + /** + * Reads in all the configuration file entries supported. + * + * @param colorStyle The style for the color groups. In KDE, colors were + * calculated a little differently for Motif vs Windows styles. This + * is obsolete. + */ + void readConfig( Qt::GUIStyle colorStyle = Qt::WindowsStyle ); + void readWidgetConfig( int i, QSettings *config, QString *pixnames, + QString *brdnames, bool *loadArray ); + void copyWidgetConfig( int sourceID, int destID, QString *pixnames, + QString *brdnames ); + /** + * Makes a full color group based on the given foreground and background + * colors. This is the same code used by KDE (kapp.cpp) in previous + * versions. + */ + QColorGroup* makeColorGroup( const QColor &fg, const QColor &bg, + Qt::GUIStyle style = Qt::WindowsStyle ); + KThemePixmap* scale( int w, int h, WidgetType widget ) const; + KThemePixmap* scaleBorder( int w, int h, WidgetType type ) const; + KThemePixmap* gradient( int w, int h, WidgetType widget ) const ; + KThemePixmap* blend( WidgetType widget ) const; + void generateBorderPix( int i ); + void applyResourceGroup( QSettings *config, int i ); + void applyMiscResourceGroup( QSettings *config ); + void readResourceGroup( int i, QString *pixnames, QString *brdnames, + bool *loadArray ); + void readMiscResourceGroup(); + /** + * Attempts to load a pixmap from the default KThemeBase locations. + */ + KThemePixmap* loadPixmap( const QString &name ); + /** + * Attempts to load a image from the default KThemeBase locations. + */ + QImage* loadImage( const QString &name ); + + + /** + These are included for fuuture extension purposes.. + */ + virtual int pixelMetric ( PixelMetric metric, const QWidget * widget = 0 ) const + { + return KStyle::pixelMetric( metric, widget ); + } + + virtual void drawPrimitive ( PrimitiveElement pe, QPainter * p, const QRect & r, const QColorGroup & cg, + SFlags flags = Style_Default, + const QStyleOption& option = QStyleOption::Default ) const + { + KStyle::drawPrimitive ( pe, p, r, cg, + flags, option ); + } + + + virtual void drawControl( ControlElement element, + QPainter *p, + const QWidget *widget, + const QRect &r, + const QColorGroup &cg, + SFlags how = Style_Default, + const QStyleOption& opt = QStyleOption::Default ) const + { + KStyle::drawControl( element, p, widget, + r, cg, how, opt ); + } + + virtual void drawControlMask( ControlElement element, + QPainter *p, + const QWidget *widget, + const QRect &r, + const QStyleOption& opt = QStyleOption::Default ) const + { + KStyle::drawControlMask( element, p, widget, r, opt ); + } + + + virtual void drawComplexControl( ComplexControl control, + QPainter *p, + const QWidget* widget, + const QRect &r, + const QColorGroup &cg, + SFlags flags = Style_Default, + SCFlags controls = SC_All, + SCFlags active = SC_None, + const QStyleOption& opt = QStyleOption::Default ) const + { + KStyle::drawComplexControl( control, p, widget, r, cg, flags, controls, active, opt ); + } + + + virtual void drawKStylePrimitive( KStylePrimitive kpe, + QPainter* p, + const QWidget* widget, + const QRect &r, + const QColorGroup &cg, + SFlags flags = Style_Default, + const QStyleOption& opt = QStyleOption::Default ) const + { + KStyle::drawKStylePrimitive( kpe, + p, widget, r, + cg, flags, opt ); + } + + + virtual int styleHint( StyleHint sh, + const QWidget *widget = 0, + const QStyleOption& opt = QStyleOption::Default, + QStyleHintReturn* returnData = 0 ) const + { + return KStyle::styleHint( sh, + widget, + opt, + returnData ); + } + + virtual QSize sizeFromContents( ContentsType contents, + const QWidget *widget, + const QSize &contentsSize, + const QStyleOption& opt = QStyleOption::Default ) const + { + return KStyle::sizeFromContents( contents, + widget, contentsSize, opt ); + } + +private: + KThemeBasePrivate *d; + + SButton sbPlacement; + ArrowStyle arrowStyle; + ShadeStyle shading; + int defaultFrame; + int btnXShift, btnYShift; + int sliderLen; + int splitterWidth; + int focus3DOffset; + int sbExtent; + bool smallGroove; + bool roundedButton, roundedCombo, roundedSlider; + bool aTabLine, iTabLine; + bool focus3D; + KThemeCache *cache; + int cacheSize; + QString configFileName; + QString configDirName; + + /** + * The theme pixmaps. Many of these may be NULL if no pixmap is specified. + * There may also be duplicate pixmap pointers if more than one widget + * uses the same tiled pixmap. If a pixmap is tiled, it is kept here and + * this acts as a cache. Otherwise this will hold whatever the last scaled + * pixmap was. + */ + mutable KThemePixmap *pixmaps[ WIDGETS ]; + /** + * The theme images. These are for scaled images and are kept in order + * to maintain fast smoothscaling. + */ + mutable QImage *images[ WIDGETS ]; + /** + * The border widths + */ + mutable unsigned char borders[ WIDGETS ]; + /** + * The highlight widths + */ + mutable unsigned char highlights[ WIDGETS ]; + /** + * The scale hints for pixmaps and gradients. + */ + mutable ScaleHint scaleHints[ WIDGETS ]; + /** + * All the color groups. + */ + mutable QColorGroup *colors[ WIDGETS ]; + /** + * Gradient low colors (or blend background). + */ + mutable QColor *grLowColors[ WIDGETS ]; + /** + * Gradient high colors. + */ + mutable QColor *grHighColors[ WIDGETS ]; + /** + * Gradient types. + */ + mutable Gradient gradients[ WIDGETS ]; + /** + * Blend intensity factors + */ + mutable float blends[ WIDGETS ]; + /** + * Bevel contrasts + */ + mutable unsigned char bContrasts[ WIDGETS ]; + /** + * Duplicate pixmap entries (used during destruction). + */ + mutable bool duplicate[ WIDGETS ]; + /** + * Pixmapped border widths + */ + mutable int pbWidth[ WIDGETS ]; + /** + * Pixmapped borders + */ + mutable KThemePixmap *pbPixmaps[ WIDGETS ]; + /** + * Duplicate border pixmapped border entries + */ + mutable bool pbDuplicate[ WIDGETS ]; + +}; + +inline bool KThemeBase::isPixmap( WidgetType widget ) const +{ + return ( pixmaps[ widget ] != NULL || gradients[ widget ] != GrNone ); +} + +inline bool KThemeBase::isColor( WidgetType widget ) const +{ + return ( colors[ widget ] != NULL ); +} + +inline bool KThemeBase::is3DFocus() const +{ + return ( focus3D ); +} + +inline int KThemeBase::focusOffset() const +{ + return ( focus3DOffset ); +} + +inline int KThemeBase::bevelContrast( WidgetType widget ) const +{ + return ( bContrasts[ widget ] ); +} + +inline KThemeBase::ScaleHint KThemeBase::scaleHint( WidgetType widget ) const +{ + return ( ( widget < WIDGETS ) ? scaleHints[ widget ] : TileScale ); +} + +inline KThemeBase::Gradient KThemeBase::gradientHint( WidgetType widget ) const +{ + return ( ( widget < WIDGETS ) ? gradients[ widget ] : GrNone ); +} + +inline KThemePixmap* KThemeBase::uncached( WidgetType widget ) const +{ + return ( pixmaps[ widget ] ); +} + +inline QBrush KThemeBase::pixmapBrush( const QColorGroup &group, + QColorGroup::ColorRole role, + int w, int h, WidgetType widget ) +{ + if ( pixmaps[ widget ] || images[ widget ] ) + return ( QBrush( group.color( role ), *scalePixmap( w, h, widget ) ) ); + else + return ( group.color( role ) ); +} + +inline const QColorGroup* KThemeBase::colorGroup( const QColorGroup &defaultGroup, + WidgetType widget ) const +{ + return ( ( colors[ widget ] ) ? colors[ widget ] : &defaultGroup ); +} + +inline int KThemeBase::borderWidth( WidgetType widget ) const +{ + return ( pbWidth[ widget ] ? pbWidth[ widget ] : borders[ widget ] ); +} + +inline int KThemeBase::pixBorderWidth( WidgetType widget ) const +{ + return ( pbWidth[ widget ] ); +} + +inline int KThemeBase::highlightWidth( WidgetType widget ) const +{ + return ( pbWidth[ widget ] ? 0 : highlights[ widget ] ); +} + +inline int KThemeBase::decoWidth( WidgetType widget ) const +{ + return ( pbWidth[ widget ] ? pbWidth[ widget ] : borders[ widget ] + highlights[ widget ] ); +} + +inline QColor* KThemeBase::gradientHigh( WidgetType widget ) const +{ + return ( grHighColors[ widget ] ); +} + +inline QColor* KThemeBase::gradientLow( WidgetType widget ) const +{ + return ( grLowColors[ widget ] ); +} + +inline QImage* KThemeBase::image( WidgetType widget ) const +{ + return ( images[ widget ] ); +} + +inline KThemeBase::SButton KThemeBase::scrollBarLayout() const +{ + return ( sbPlacement ); +} + +inline KThemeBase::ArrowStyle KThemeBase::arrowType() const +{ + return ( arrowStyle ); +} + +inline KThemeBase::ShadeStyle KThemeBase::shade() const +{ + return ( shading ); +} + +inline int KThemeBase::frameWidth() const +{ + return ( defaultFrame ); +} + +inline int KThemeBase::buttonXShift() const +{ + return ( btnXShift ); +} + +inline int KThemeBase::splitWidth() const +{ + return ( splitterWidth ); +} + +inline int KThemeBase::buttonYShift() const +{ + return ( btnYShift ); +} + +inline int KThemeBase::sliderButtonLength() const +{ + if ( isPixmap( Slider ) ) + return ( uncached( Slider ) ->width() ); + else + return ( sliderLen ); +} + +inline bool KThemeBase::roundButton() const +{ + return ( roundedButton ); +} + +inline bool KThemeBase::roundComboBox() const +{ + return ( roundedCombo ); +} + +inline bool KThemeBase::roundSlider() const +{ + return ( roundedSlider ); +} + +inline bool KThemeBase::activeTabLine() const +{ + return ( aTabLine ); +} + +inline bool KThemeBase::inactiveTabLine() const +{ + return ( iTabLine ); +} + +inline int KThemeBase::getSBExtent() const +{ + return ( sbExtent ); +} + +inline KThemePixmap* KThemeBase::borderPixmap( WidgetType widget ) const +{ + return ( pbPixmaps[ widget ] ); +} + +#endif diff --git a/kstyles/kthemestyle/kthemestyle.cpp b/kstyles/kthemestyle/kthemestyle.cpp new file mode 100644 index 000000000..cdb419772 --- /dev/null +++ b/kstyles/kthemestyle/kthemestyle.cpp @@ -0,0 +1,2384 @@ +/* + $Id$ + + This file is part of the KDE libraries + Copyright (C) 1999 Daniel M. Duley <mosfet@kde.org> + + KDE3 port (C) 2001-2002 Maksim Orlovich <mo002j@mail.rochester.edu> +Port version 0.9.7 + + Includes code portions from the dotNET style, and the KDE HighColor style. + + dotNET Style + Copyright (C) 2001, Chris Lee <lee@azsites.com> + Carsten Pfeiffer <pfeiffer@kde.org> + + KDE3 HighColor Style + Copyright (C) 2001 Karol Szwed <gallium@kde.org> + (C) 2001 Fredrik Höglund <fredrik@kde.org> + + Drawing routines adapted from the KDE2 HCStyle, + Copyright (C) 2000 Daniel M. Duley <mosfet@kde.org> + (C) 2000 Dirk Mueller <mueller@kde.org> + (C) 2001 Martijn Klingens <klingens@kde.org> + + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kthemestyle.h" +#include "kthemebase.h" +#include <qstyleplugin.h> +#include <qstylefactory.h> +#include <kimageeffect.h> + +#include <qbitmap.h> +#include <qcheckbox.h> +#include <qlabel.h> +#define INCLUDE_MENUITEM_DEF +#include <qmenudata.h> +#include <qpopupmenu.h> +#include <qpalette.h> +#include <qtabbar.h> +#include <qtoolbutton.h> +#include <kglobalsettings.h> +#include <kdrawutil.h> +#include <qdrawutil.h> +#include <qprogressbar.h> +#include <qdir.h> +#include <qapplication.h> +#include <qmenubar.h> +#include <qrangecontrol.h> +#include <qslider.h> +#include <qtooltip.h> +#include <qobjectlist.h> +#include <qradiobutton.h> +#include <qstatusbar.h> +#include "kstyledirs.h" + +#include <qimage.h> + +#include <limits.h> + +#ifdef __GLIBC__ +#include <dlfcn.h> +#endif + +static const QCOORD u_arrow[] = { -1, -3, 0, -3, -2, -2, 1, -2, -3, -1, 2, -1, -4, 0, 3, 0, -4, 1, 3, 1}; +static const QCOORD d_arrow[] = { -4, -2, 3, -2, -4, -1, 3, -1, -3, 0, 2, 0, -2, 1, 1, 1, -1, 2, 0, 2}; +static const QCOORD l_arrow[] = { -3, -1, -3, 0, -2, -2, -2, 1, -1, -3, -1, 2, 0, -4, 0, 3, 1, -4, 1, 3}; +static const QCOORD r_arrow[] = { -2, -4, -2, 3, -1, -4, -1, 3, 0, -3, 0, 2, 1, -2, 1, 1, 2, -1, 2, 0}; + +const QCOORD win_style_u_arrow[] = { 0, -2, 0, -2, -1, -1, 1, -1, -2, 0, 2, 0, -3, 1, 3, 1 }; +const QCOORD win_style_d_arrow[] = { -3, -2, 3, -2, -2, -1, 2, -1, -1, 0, 1, 0, 0, 1, 0, 1 }; +const QCOORD win_style_l_arrow[] = { 1, -3, 1, -3, 0, -2, 1, -2, -1, -1, 1, -1, -2, 0, 1, 0, -1, 1, 1, 1, 0, 2, 1, 2, 1, 3, 1, 3 }; +const QCOORD win_style_r_arrow[] = { -2, -3, -2, -3, -2, -2, -1, -2, -2, -1, 0, -1, -2, 0, 1, 0, -2, 1, 0, 1, -2, 2, -1, 2, -2, 3, -2, 3 }; + + +#define QCOORDARRLEN(x) sizeof(x)/(sizeof(QCOORD)*2) + + +static const int itemFrame = 2; +static const int itemHMargin = 3; +static const int itemVMargin = 1; +static const int arrowHMargin = 6; +static const int rightBorder = 12; + + +/* +BUGS: +Sliders flash a bit -- anything else? + +TODO: +Nicer disabled buttons. +Sliders are not disabled properly +*/ + + +class KThemeStylePlugin : public QStylePlugin +{ +public: + + KThemeStylePlugin() + { +#ifdef __GLIBC__ + dlopen("kthemestyle.so",RTLD_LAZY); + //####### Keep reference count up so kdecore w. fast-malloc doesn't get unloaded + //####### (Fixes exit crashes with qt-only apps that occur on Linux) + //####### This should be rethought after 3.0, + //####### as it relies on the implementation-specific behavior + //####### of the glibc libdl (finding already loaded libraries based on the + //####### soname) +#endif + } + + ~KThemeStylePlugin() + {} + + QStringList keys() const + { + QSettings cfg; + KStyleDirs::dirs()->addToSearch( "config", cfg ); + + QStringList keys; + bool ok; + + keys = cfg.readListEntry( "/kthemestyle/themes", &ok); + if ( !ok ) + qWarning( "KThemeStyle cache seems corrupt!\n" ); //Too bad one can't i18n this :-( + + return keys; + } + + QStyle* create( const QString& key ) + { + QSettings cfg; + KStyleDirs::dirs()->addToSearch( "config", cfg ); + + QString file = cfg.readEntry( "/kthemestyle/" + key + "/file" ); + if ( !key.isEmpty() ) + { + QFileInfo fi( file ); + return new KThemeStyle( fi.dirPath(), fi.fileName() ); + } + + return 0; + } +}; + +KDE_Q_EXPORT_PLUGIN( KThemeStylePlugin ) + + +void kDrawWindowsArrow ( QPainter *p, const QStyle* style, QStyle::PrimitiveElement pe, bool down, + int x, int y, int w, int h, + const QColorGroup &cg, bool enabled ) +{ + QPointArray a; + switch ( pe ) + { + case QStyle::PE_ArrowUp: + a.setPoints( QCOORDARRLEN( win_style_u_arrow ), win_style_u_arrow ); + break; + + case QStyle::PE_ArrowDown: + a.setPoints( QCOORDARRLEN( win_style_d_arrow ), win_style_d_arrow ); + break; + + case QStyle::PE_ArrowLeft: + a.setPoints( QCOORDARRLEN( win_style_l_arrow ), win_style_l_arrow ); + break; + default: + a.setPoints( QCOORDARRLEN( win_style_r_arrow ), win_style_r_arrow ); + } + + p->save(); + if ( down ) + { + p->translate( style->pixelMetric( QStyle::PM_ButtonShiftHorizontal ), + style->pixelMetric( QStyle::PM_ButtonShiftVertical ) ); + } + + if ( enabled ) + { + a.translate( x + w / 2, y + h / 2 ); + p->setPen( cg.buttonText() ); + p->drawLineSegments( a ); + } + else + { + a.translate( x + w / 2 + 1, y + h / 2 + 1 ); + p->setPen( cg.light() ); + p->drawLineSegments( a ); + a.translate( -1, -1 ); + p->setPen( cg.mid() ); + p->drawLineSegments( a ); + } + + p->restore(); + +} + + + +QSize KThemeStyle::sizeFromContents( ContentsType contents, + const QWidget* widget, + const QSize &contentSize, + const QStyleOption& opt ) const +{ + switch ( contents ) + { + // PUSHBUTTON SIZE + // ------------------------------------------------------------------ + case CT_PushButton: + { + const QPushButton * button = ( const QPushButton* ) widget; + int w = contentSize.width(); + int h = contentSize.height(); + int bm = pixelMetric( PM_ButtonMargin, widget ); + int fw = pixelMetric( PM_DefaultFrameWidth, widget ) * 2; + + w += bm + fw + 6; // ### Add 6 to make way for bold font. + h += bm + fw; + + // Ensure we stick to standard width and heights. + if ( button->isDefault() || button->autoDefault() ) + { + if ( w < 80 && !button->text().isEmpty() ) + w = 80; + } + + if ( h < 22 ) + h = 22; + + return QSize( w, h ); + } + + // POPUPMENU ITEM SIZE + // ----------------------------------------------------------------- + case CT_PopupMenuItem: + { + if ( ! widget || opt.isDefault() ) + return contentSize; + + const QPopupMenu *popup = ( const QPopupMenu * ) widget; + bool checkable = popup->isCheckable(); + QMenuItem *mi = opt.menuItem(); + int maxpmw = opt.maxIconWidth(); + int w = contentSize.width(), h = contentSize.height(); + + if ( mi->custom() ) + { + w = mi->custom() ->sizeHint().width(); + h = mi->custom() ->sizeHint().height(); + if ( ! mi->custom() ->fullSpan() ) + h += 2 * itemVMargin + 2 * itemFrame; + } + else if ( mi->widget() ) + {} + else if ( mi->isSeparator() ) + { + w = 10; // Arbitrary + h = 2; + } + else + { + if ( mi->pixmap() ) + h = QMAX( h, mi->pixmap() ->height() + 2 * itemFrame ); + else + h = QMAX( h, popup->fontMetrics().height() + + 2 * itemVMargin + 2 * itemFrame ); + + if ( mi->iconSet() ) + h = QMAX( h, mi->iconSet() ->pixmap( + QIconSet::Small, QIconSet::Normal ).height() + + 2 * itemFrame ); + } + + if ( ! mi->text().isNull() && mi->text().find( '\t' ) >= 0 ) + w += 12; + else if ( mi->popup() ) + w += 2 * arrowHMargin; + + if ( maxpmw ) + w += maxpmw + 6; + if ( checkable && maxpmw < 20 ) + w += 20 - maxpmw; + if ( checkable || maxpmw > 0 ) + w += 12; + + w += rightBorder; + + return QSize( w, h ); + } + + default: + return KThemeBase::sizeFromContents( contents, widget, contentSize, opt ); + } +} + + +QRect KThemeStyle::subRect(SubRect sr, const QWidget* widget) const +{ + if (sr == SR_CheckBoxFocusRect) + { + const QCheckBox* cb = static_cast<const QCheckBox*>(widget); + + //Only checkbox, no label + if (cb->text().isEmpty() && (cb->pixmap() == 0) ) + { + QRect bounding = cb->rect(); + + int cw = pixelMetric(PM_IndicatorWidth, widget); + int ch = pixelMetric(PM_IndicatorHeight, widget); + + QRect checkbox(bounding.x() + 2, bounding.y() + 2 + (bounding.height() - ch)/2, cw - 4, ch - 4); + + return checkbox; + } + } + return KStyle::subRect(sr, widget); +} + +int KThemeStyle::pixelMetric ( PixelMetric metric, const QWidget * widget ) const +{ + switch ( metric ) + { + case PM_MenuBarFrameWidth: + return 1; + case PM_DefaultFrameWidth: + return ( frameWidth() ); + + case PM_ButtonMargin: + return decoWidth( PushButton ) > decoWidth( PushButtonDown ) ? + 3 + decoWidth( PushButton ) : 3 + decoWidth( PushButtonDown ); + + case PM_ScrollBarExtent: + case PM_SliderThickness: //Should this be 16 always? + return getSBExtent(); + + case PM_ButtonDefaultIndicator: + return 0; + + case PM_ButtonShiftHorizontal: + return buttonXShift(); + + case PM_ButtonShiftVertical: + return buttonYShift(); + + case PM_ExclusiveIndicatorWidth: + if ( isPixmap( ExIndicatorOn ) ) + return ( uncached( ExIndicatorOn ) ->size().width() ); + else + return KThemeBase::pixelMetric ( metric, widget ); + + case PM_ExclusiveIndicatorHeight: + if ( isPixmap( ExIndicatorOn ) ) + return ( uncached( ExIndicatorOn ) ->size().height() ); + else + return KThemeBase::pixelMetric ( metric, widget ); + + + case PM_IndicatorWidth: + if ( isPixmap( IndicatorOn ) ) + return ( uncached( IndicatorOn ) ->size().width() ); + else + return KThemeBase::pixelMetric ( metric, widget ); + + case PM_IndicatorHeight: + if ( isPixmap( IndicatorOn ) ) + return ( uncached( IndicatorOn ) ->size().height() ); + else + return KThemeBase::pixelMetric ( metric, widget ); + + case PM_SliderLength: + return ( sliderButtonLength() ); + + case PM_SplitterWidth: + return ( splitWidth() ); + + default: + return KThemeBase::pixelMetric ( metric, widget ); + } +} + + + +KThemeStyle::KThemeStyle( const QString& configDir, const QString &configFile ) + : KThemeBase( configDir, configFile ), paletteSaved( false ), polishLock( false ), menuCache( 0 ), vsliderCache( 0 ), + brushHandle( 0 ), brushHandleSet( false ), kickerMode( false ) +{ + mtfstyle = QStyleFactory::create( "Motif" ); + if ( !mtfstyle ) + mtfstyle = QStyleFactory::create( *( QStyleFactory::keys().begin() ) ); +} + +KThemeStyle::~KThemeStyle() +{ + delete vsliderCache; + delete menuCache; + +} + + +void KThemeStyle::polish( QApplication * app ) +{ + if (!qstrcmp(app->argv()[0], "kicker")) + kickerMode = true; +} + + +void KThemeStyle::polish( QPalette &p ) +{ + if ( polishLock ) + { + return ; //Palette polishing disabled ... + } + + + + if ( !paletteSaved ) + { + oldPalette = p; + paletteSaved = true; + } + + p = overridePalette( p ); + + if ( isPixmap( Background ) ) + { + QBrush bgBrush( p.color( QPalette::Normal, + QColorGroup::Background ), + *uncached( Background ) ); + brushHandle = uncached( Background )->handle(); + brushHandleSet = true; + p.setBrush( QColorGroup::Background, bgBrush ); + } + +} + +void KThemeStyle::paletteChanged() +{ + QPalette p = QApplication::palette(); + polish( p ); + QApplication::setPalette( p ); +} + + +void KThemeStyle::unPolish( QApplication *app ) +{ + app->setPalette( oldPalette, true ); +} + +bool KThemeStyle::eventFilter( QObject* object, QEvent* event ) +{ + if( object->inherits("KActiveLabel")) + { + if(event->type() == QEvent::Move || event->type() == QEvent::Resize || + event->type() == QEvent::Show) + { + QWidget *w = static_cast<QWidget*>(object); + QPoint pos(0, 0); + pos = w->mapTo(w->topLevelWidget(), pos); + QPixmap pix(uncached( Background )->size()); + QPainter p; + p.begin(&pix); + p.drawTiledPixmap(0, 0, + uncached( Background )->width(), + uncached( Background )->height() , + *uncached( Background ), + pos.x(), pos.y()); + p.end(); + QPalette pal(w->palette()); + QBrush brush( pal.color( QPalette::Normal, + QColorGroup::Background), + pix ); + pal.setBrush(QColorGroup::Base, brush); + w->setPalette(pal); + } + } + if (!qstrcmp(object->name(), "kde toolbar widget") && object->inherits("QLabel")) + { + QWidget* lb = static_cast<QWidget*>(object); + if (lb->backgroundMode() == Qt::PaletteButton) + lb->setBackgroundMode(Qt::PaletteBackground); + lb->removeEventFilter(this); + } + + return KStyle::eventFilter(object, event); +} + +void KThemeStyle::polish( QWidget *w ) +{ + if (::qt_cast<QStatusBar*>(w)) + w->setPaletteBackgroundColor(QApplication::palette().color(QPalette::Normal, QColorGroup::Background)); + + if (::qt_cast<QLabel*>(w) && !qstrcmp(w->name(), "kde toolbar widget")) + w->installEventFilter(this); + + if (w->backgroundPixmap() && !w->isTopLevel() && + (!kickerMode || + (!w->inherits("TaskBar") && !w->inherits("TaskBarContainer") && !w->inherits("TaskbarApplet") && !w->inherits("ContainerArea") && !w->inherits("AppletHandle")))) + { + //The brushHandle check verifies that the bg pixmap is actually the brush.. + if (!brushHandleSet || brushHandle == w->backgroundPixmap()->handle()) + { + w->setBackgroundOrigin( QWidget::WindowOrigin ); + } + } + + if (w->inherits("KActiveLabel")) + { + if (uncached( Background )) + w->installEventFilter(this); + } + + if ( w->inherits( "QTipLabel" ) ) + { + polishLock = true; + + QColorGroup clrGroup( Qt::black, QColor( 255, 255, 220 ), + QColor( 96, 96, 96 ), Qt::black, Qt::black, + Qt::black, QColor( 255, 255, 220 ) ); + QPalette toolTip ( clrGroup, clrGroup, clrGroup ); + + QToolTip::setPalette( toolTip ); + polishLock = false; + } + + if ( w->inherits( "KonqIconViewWidget" ) ) //Konqueror background hack/workaround + { + w->setPalette( oldPalette ); + return ; + } + + if ( ::qt_cast<QMenuBar*>(w) ) + { + w->setBackgroundMode( QWidget::NoBackground ); + } + else if ( w->inherits( "KToolBarSeparator" ) || w->inherits( "QToolBarSeparator" ) ) + { + w->setBackgroundMode( QWidget::PaletteBackground ); + } + else if ( ::qt_cast<QPopupMenu*>(w) ) + { + popupPalette = w->palette(); + if ( isColor( MenuItem ) || isColor( MenuItemDown ) ) + { + QPalette newPal( w->palette() ); + if ( isColor( MenuItem ) ) + { + newPal.setActive( *colorGroup( newPal.active(), MenuItem ) ); + newPal.setDisabled( *colorGroup( newPal.active(), MenuItem ) ); + } + if ( isColor( MenuItemDown ) ) + { + newPal.setActive( *colorGroup( newPal.active(), MenuItemDown ) ); + } + w->setPalette( newPal ); + } + + w->setBackgroundMode( QWidget::NoBackground ); + } + else if ( ::qt_cast<QCheckBox*>(w) ) + { + if ( isColor( IndicatorOff ) || isColor( IndicatorOn ) ) + { + QPalette newPal( w->palette() ); + if ( isColor( IndicatorOff ) ) + { + newPal.setActive( *colorGroup( newPal.active(), IndicatorOff ) ); + newPal.setDisabled( *colorGroup( newPal.active(), IndicatorOff ) ); + } + if ( isColor( IndicatorOn ) ) + newPal.setActive( *colorGroup( newPal.active(), IndicatorOn ) ); + w->setPalette( newPal ); + } + } + else if ( ::qt_cast<QRadioButton*>(w) ) + { + if ( isColor( ExIndicatorOff ) || isColor( ExIndicatorOn ) ) + { + QPalette newPal( w->palette() ); + if ( isColor( ExIndicatorOff ) ) + { + newPal.setActive( *colorGroup( newPal.active(), ExIndicatorOff ) ); + newPal.setDisabled( *colorGroup( newPal.active(), + ExIndicatorOff ) ); + } + if ( isColor( ExIndicatorOn ) ) + newPal.setActive( *colorGroup( newPal.active(), ExIndicatorOn ) ); + w->setPalette( newPal ); + } + } + + KStyle::polish( w ); +} + +void KThemeStyle::unPolish( QWidget* w ) +{ + if (w->backgroundPixmap() && !w->isTopLevel()) + { + //The brushHandle check verifies that the bg pixmap is actually the brush.. + if (!brushHandleSet || brushHandle ==w->backgroundPixmap()->handle()) + { + w->setBackgroundOrigin( QWidget::WidgetOrigin ); + } + } + + //Toolbar labels should nornally be PaletteButton + if ( ::qt_cast<QLabel*>(w) && !qstrcmp(w->name(), "kde toolbar widget")) + w->setBackgroundMode( QWidget::PaletteButton ); + + //The same for menu bars, popup menus + else if ( ::qt_cast<QMenuBar*>(w) || ::qt_cast<QPopupMenu*>(w) ) + w->setBackgroundMode( QWidget::PaletteButton ); + + //For toolbar internal separators, return to button, too (can't use qt_cast here since don't have access to the class) + else if ( w->inherits( "KToolBarSeparator" ) || w->inherits( "QToolBarSeparator" ) ) + w->setBackgroundMode( QWidget::PaletteButton ); + + //For scrollbars, we don't do much, since the widget queries the style on the switch + + //Drop some custom palettes. ### this really should check the serial number to be 100% correct. + if ( ::qt_cast<QPopupMenu*>(w) || ::qt_cast<QCheckBox*>(w) || ::qt_cast<QRadioButton*>(w) || ::qt_cast<QStatusBar*>(w) ) + w->unsetPalette(); + + KStyle::unPolish( w ); +} + + +void KThemeStyle::drawBaseButton( QPainter *p, int x, int y, int w, int h, + const QColorGroup &g, bool sunken, bool + rounded, WidgetType type ) const +{ + int offset = borderPixmap( type ) ? 0 : decoWidth( type ) ; //##### This is wrong, but the code relies on it.. + QPen oldPen = p->pen(); + + // handle reverse bevel here since it uses decowidth differently + if ( gradientHint( type ) == GrReverseBevel ) + { + int i; + bitBlt( p->device(), x, y, scalePixmap( w, h, type ), 0, 0, w, h, + Qt::CopyROP, true ); + p->setPen( g.text() ); + for ( i = 0; i < borderWidth( type ); ++i, ++x, ++y, w -= 2, h -= 2 ) + p->drawRect( x, y, w, h ); + } + // same with KDE style borders + else if ( !borderPixmap( type ) && shade() == KDE ) + { + kDrawBeButton( p, x, y, w, h, g, sunken ); + if ( isPixmap( type ) ) + p->drawTiledPixmap( x + 4, y + 4, w - 6, h - 6, + *scalePixmap( w - 6, h - 6, + type ) ); + else + p->fillRect( x + 4, y + 4, w - 6, h - offset * 6, + g.brush( QColorGroup::Button ) ); + + } + else + { + if ( ( w - offset * 2 ) > 0 && ( h - offset * 2 ) > 0 ) + { + if ( isPixmap( type ) ) + if ( rounded ) + p->drawTiledPixmap( x, y, w, h, *scalePixmap( w, h, type ) ); + else + p->drawTiledPixmap( x + offset, y + offset, w - offset * 2, + h - offset * 2, + *scalePixmap( w - offset * 2, h - offset * 2, + type ) ); + else if ( 1 ) //##### TODO - Get this optimization working... !borderPixmap( type ) || (( w - decoWidth(type) * 2 ) > 0 && ( h - decoWidth(type) * 2 ) > 0) ) + //Sometimes border covers the whole thing - in that case, avoid drawing the base. + { + p->fillRect( x + offset, y + offset, w - offset * 2, h - offset * 2, + g.brush( QColorGroup::Button ) ); + } + } + if ( borderPixmap( type ) ) + { + bitBlt( p->device(), x, y, scaleBorder( w, h, type ), 0, 0, w, h, + Qt::CopyROP, false ); + } + else + drawShade( p, x, y, w, h, g, sunken, rounded, + highlightWidth( type ), borderWidth( type ), shade() ); + } + p->setPen( oldPen ); +} + +void KThemeStyle::drawPrimitive ( PrimitiveElement pe, QPainter * p, const QRect & r, const QColorGroup & g_base, + SFlags flags, const QStyleOption & opt ) const +{ + bool handled = false; + int x, y, w, h; + r.rect( &x, &y, &w, &h ); + + bool sunken = ( flags & Style_Sunken ); + bool enabled = ( flags & Style_Enabled ); + bool down = ( flags & Style_Down ); + bool on = flags & Style_On; + QColorGroup g = g_base; + + switch ( pe ) + { + case PE_ArrowUp: + case PE_ArrowDown: + case PE_ArrowRight: + case PE_ArrowLeft: + { + QRect r( x, y, w, h ); + if ( r.width() > 12 ) + { + r.setRect( r.x() + ( r.width() - 12 ) / 2, r.y(), 12, r.height() ); + } + if ( r.height() > 12 ) + { + r.setRect( r.x(), r.y() + ( r.height() - 12 ) / 2, r.width(), 12 ); + } + r.rect( &x, &y, &w, &h ); + // Handles pixmapped arrows. A little inefficient because you can specify + // some as pixmaps and some as default types. + WidgetType widget; + switch ( pe ) + { + case PE_ArrowUp: + widget = enabled ? down ? SunkenArrowUp : ArrowUp : DisArrowUp; + break; + case PE_ArrowDown: + widget = enabled ? down ? SunkenArrowDown : ArrowDown : DisArrowDown; + break; + case PE_ArrowLeft: + widget = enabled ? down ? SunkenArrowLeft : ArrowLeft : DisArrowLeft; + break; + case PE_ArrowRight: + default: + widget = enabled ? down ? SunkenArrowRight : ArrowRight : DisArrowRight; + break; + } + if ( isPixmap( widget ) ) + { + bitBlt( p->device(), x + ( w - uncached( widget ) ->width() ) / 2, + y + ( h - uncached( widget ) ->height() ) / 2, + uncached( widget ) ); + + return ; + } + const QColorGroup *cg = colorGroup( g, widget ); + // Standard arrow types + if ( arrowType() == MotifArrow ) + { + mtfstyle->drawPrimitive( pe, p, r, g, flags, opt ); + + handled = true; + } + else if ( arrowType() == SmallArrow ) + { + // #### FIXME: This should be like the Platinum style - uses HighColor look for now + QPointArray a; + + switch ( pe ) + { + case PE_ArrowUp: + a.setPoints( QCOORDARRLEN( u_arrow ), u_arrow ); + break; + + case PE_ArrowDown: + a.setPoints( QCOORDARRLEN( d_arrow ), d_arrow ); + break; + + case PE_ArrowLeft: + a.setPoints( QCOORDARRLEN( l_arrow ), l_arrow ); + break; + + default: + a.setPoints( QCOORDARRLEN( r_arrow ), r_arrow ); + } + + p->save(); + + if ( flags & Style_Down ) + p->translate( pixelMetric( PM_ButtonShiftHorizontal ), + pixelMetric( PM_ButtonShiftVertical ) ); + + if ( flags & Style_Enabled ) + { + a.translate( r.x() + r.width() / 2, r.y() + r.height() / 2 ); + p->setPen( cg->buttonText() ); + p->drawLineSegments( a ); + } + else + { + a.translate( r.x() + r.width() / 2 + 1, r.y() + r.height() / 2 + 1 ); + p->setPen( cg->mid() ); + p->drawLineSegments( a ); + } + p->restore(); + } + else + { + QPointArray a; + int x2 = x + w - 1, y2 = y + h - 1; + switch ( pe ) + { + case PE_ArrowUp: + a.setPoints( 4, x, y2, x2, y2, x + w / 2, y, x, y2 ); + break; + case PE_ArrowDown: + a.setPoints( 4, x, y, x2, y, x + w / 2, y2, x, y ); + break; + case PE_ArrowLeft: + a.setPoints( 4, x2, y, x2, y2, x, y + h / 2, x2, y ); + break; + default: + a.setPoints( 4, x, y, x, y2, x2, y + h / 2, x, y ); + break; + } + QBrush oldBrush = p->brush(); + QPen oldPen = p->pen(); + p->setBrush( cg->brush( QColorGroup::Shadow ) ); + p->setPen( cg->shadow() ); + p->drawPolygon( a ); + p->setBrush( oldBrush ); + p->setPen( oldPen ); + handled = true; + } + break; + + } + case PE_HeaderSection: + { + sunken = false; //Never mind this one + } + case PE_ButtonBevel: + { + WidgetType type = ( sunken || on || down ) ? BevelDown : Bevel; + drawBaseButton( p, x, y, w, h, *colorGroup( g, type ), ( sunken || on || down ), false, type ); + handled = true; + break; + } + case PE_ButtonCommand: + { + drawBaseButton( p, x, y, w, h, g, ( sunken || on || down ), roundButton(), ( sunken || on || down ) ? + PushButtonDown : PushButton ); + handled = true; + break; + } + case PE_PanelDockWindow: + { + drawBaseButton( p, x, y, w, h, *colorGroup( g, ToolBar ), false, false, + ToolBar ); + handled = true; + break; + } + case PE_CheckMark: + { + if ( isPixmap( CheckMark ) ) + { + if ( flags & Style_Enabled || flags & Style_On ) + bitBlt( p->device(), x + ( w - uncached( CheckMark ) ->width() ) / 2, + y + ( h - uncached( CheckMark ) ->height() ) / 2, + uncached( CheckMark ) ); + handled = true; + } + else //Small hack to ensure the checkmark gets painter proper color.. + { + g.setColor( QColorGroup::Text, g.buttonText() ); + } + break; + } + case PE_ExclusiveIndicator: + { + if ( isPixmap( ( flags & Style_On || flags & Style_Down ) ? ExIndicatorOn : ExIndicatorOff ) ) + { + p->drawPixmap( x, y, *uncached( ( flags & Style_On || flags & Style_Down ) ? ExIndicatorOn : + ExIndicatorOff ) ); + handled = true; + } + + break; + } + case PE_ExclusiveIndicatorMask: + { + if ( isPixmap( ( flags & Style_On || flags & Style_Down ) ? ExIndicatorOn : ExIndicatorOff ) ) + { + const QBitmap * mask = uncached( ( flags & Style_On || flags & Style_Down ) ? ExIndicatorOn : ExIndicatorOff ) ->mask(); + if ( mask ) + { + p->setPen( Qt::color1 ); + p->drawPixmap( x, y, *mask ); + } + else + p->fillRect( x, y, w, h, QBrush( color1, SolidPattern ) ); + handled = true; + } + break; + } + + case PE_IndicatorMask: + { + if ( isPixmap( ( flags & Style_On ) ? IndicatorOn : IndicatorOff ) ) + { + const QBitmap * mask = uncached( ( flags & Style_On ) ? IndicatorOn : + IndicatorOff ) ->mask(); + if ( mask ) + { + p->setPen( Qt::color1 ); + p->drawPixmap( x, y, *mask ); + } + else + p->fillRect( x, y, w, h, QBrush( color1, SolidPattern ) ); + handled = true; + } + break; + } + case PE_Indicator: + { + if ( isPixmap( ( flags & Style_On || flags & Style_Down ) ? + IndicatorOn : IndicatorOff ) ) + { + p->drawPixmap( x, y, *uncached( ( flags & Style_On || flags & Style_Down ) ? + IndicatorOn : IndicatorOff ) ); + handled = true; + } + break; + } + case PE_Splitter: + { + drawBaseButton( p, x, y, w, h, *colorGroup( g, Splitter ), false, false, + Splitter ); + handled = true; + break; + } + case PE_FocusRect: + { + if ( is3DFocus() ) + { + p->setPen( g.dark() ); + int i = focusOffset(); + p->drawLine( r.x() + i, r.y() + 1 + i, r.x() + i, r.bottom() - 1 - i ); + p->drawLine( r.x() + 1 + i, r.y() + i, r.right() - 1 - i, r.y() + i ); + p->setPen( g.light() ); + p->drawLine( r.right() - i, r.y() + 1 + i, r.right() - i, r.bottom() - 1 - i ); + p->drawLine( r.x() + 1 + i, r.bottom() - i, r.right() - 1 - i, r.bottom() - i ); + handled = true; + } + else + { + handled = true; + p->drawWinFocusRect(r); + } + break; + } + case PE_PanelMenuBar: + { + QPixmap* cache = makeMenuBarCache(w, h); + p->drawPixmap( x, y, *cache); + handled = true; + break; + } + case PE_ScrollBarAddPage: + case PE_ScrollBarSubPage: + { + WidgetType widget = ( flags & Style_Horizontal ) ? HScrollGroove : VScrollGroove; + + if ( !isPixmap( widget ) ) + { + p->fillRect( r, colorGroup( g, widget ) ->brush( QColorGroup::Background ) ); + // Do the borders and frame + drawShade( p, r.x(), r.y(), r.width(), + r.height(), *colorGroup( g, widget ), true, false, + highlightWidth( widget ), borderWidth( widget ), shade() ); + } + else + { + // If the groove is pixmapped we make a full-sized image (it gets + // cached) then bitBlt it to the appropriate rect. + p->drawTiledPixmap( r.x(), r.y(), r.width(), r.height(), + *scalePixmap( r.width(), r.height(), + widget ) ); + drawShade( p, r.x(), r.y(), r.width(), + r.height(), *colorGroup( g, widget ), true, false, + highlightWidth( widget ), borderWidth( widget ), shade() ); + } + + handled = true; + break; + } + case PE_ScrollBarAddLine: + { + bool horizontal = ( flags & Style_Horizontal ); + drawBaseButton( p, r.x(), r.y(), r.width(), r.height(), + *colorGroup( g, down ? ScrollButtonDown : ScrollButton ), + down, false, down ? ScrollButtonDown : ScrollButton ); + + drawPrimitive( ( horizontal ) ? PE_ArrowRight : PE_ArrowDown, p , + QRect( r.x() + 3, r.y() + 3, r.width() - 6, r.height() - 6 ), + *colorGroup( g, down ? ScrollButtonDown : ScrollButton ), + flags ); + + handled = true; + break; + } + case PE_ScrollBarSubLine: + { + bool horizontal = ( flags & Style_Horizontal ); + drawBaseButton( p, r.x(), r.y(), r.width(), r.height(), + *colorGroup( g, down ? ScrollButtonDown : ScrollButton ), + down, false, down ? ScrollButtonDown : ScrollButton ); + + drawPrimitive( ( horizontal ) ? PE_ArrowLeft : PE_ArrowUp, p , + QRect( r.x() + 3, r.y() + 3, r.width() - 6, r.height() - 6 ), + *colorGroup( g, down ? ScrollButtonDown : ScrollButton ), + flags ); + handled = true; + break; + } + case PE_ScrollBarSlider: + { + bool active = ( flags & Style_Active ) || ( flags & Style_Down ); //activeControl == QStyle::AddLine; + bool horizontal = ( flags & Style_Horizontal ); + int offsetH = horizontal ? 0: decoWidth(VScrollGroove) ; + int offsetV = horizontal ? decoWidth(HScrollGroove):0; + + WidgetType widget = horizontal ? + active ? HScrollBarSliderDown : HScrollBarSlider : + active ? VScrollBarSliderDown : VScrollBarSlider; + drawBaseButton( p, r.x()+offsetH, r.y()+offsetV, r.width()-2*offsetH, + r.height()-2*offsetV, *colorGroup( g, widget ), active, false, + widget ); + + int spaceW = horizontal ? r.width() - decoWidth( widget ) - 4 : + r.width(); + int spaceH = horizontal ? r.height() : + r.height() - decoWidth( widget ) - 4; + + widget = active ? horizontal ? HScrollDecoDown : VScrollDecoDown : + horizontal ? HScrollDeco : VScrollDeco; + if ( isPixmap( widget ) ) + { + if ( spaceW >= uncached( widget ) ->width() && + spaceH >= uncached( widget ) ->height() ) + { + bitBlt( p->device(), + r.x() + ( r.width() - uncached( widget ) ->width() ) / 2, + r.y() + ( r.height() - uncached( widget ) ->height() ) / 2, + uncached( widget ) ); + } + } + handled = true; + break; + + } + default: + handled = false; + } + + if ( !handled ) + KThemeBase::drawPrimitive ( pe, p, r, g, + flags, opt ); +} + + + +QPixmap* KThemeStyle::makeMenuBarCache(int w, int h) const +{ + if (menuCache) + { + if (menuCache->width() != w || menuCache->height() != h ) + { + delete menuCache; + } + else + return menuCache; + } + + const QColorGroup *g = colorGroup( QApplication::palette().active(), MenuBar); + + menuCache = new QPixmap ( w, h ); + QPainter p(menuCache); + drawBaseButton( &p, 0, 0, w, h, *g, false, false, MenuBar ); + p.end(); + return menuCache; +} + + +void KThemeStyle::drawControl( ControlElement element, + QPainter *p, + const QWidget *widget, + const QRect &r, + const QColorGroup &cg, + SFlags how , + const QStyleOption& opt ) const +{ + bool handled = false; + int x, y, w, h; + r.rect( &x, &y, &w, &h ); + + + switch ( element ) + { + case CE_PushButton: + { + const QPushButton * btn = ( const QPushButton* ) widget; + bool sunken = btn->isOn() || btn->isDown(); + int diw = pixelMetric( PM_ButtonDefaultIndicator, btn ); + drawBaseButton( p, diw, diw, w - 2 * diw, h - 2 * diw, + *colorGroup( btn->colorGroup(), sunken ? PushButtonDown : + PushButton ), sunken, roundButton(), + sunken ? PushButtonDown : PushButton ); + // TODO if diw, draw fancy default button indicator + handled = true; + break; + } + case CE_PushButtonLabel: + { + const QPushButton* button = ( const QPushButton* ) widget; + bool active = button->isOn() || button->isDown(); + int x, y, w, h; + r.rect( &x, &y, &w, &h ); + + // Shift button contents if pushed. + if ( active ) + { + x += pixelMetric( PM_ButtonShiftHorizontal, widget ); + y += pixelMetric( PM_ButtonShiftVertical, widget ); + how |= Style_Sunken; + } + + // Does the button have a popup menu? + if ( button->isMenuButton() ) + { + int dx = pixelMetric( PM_MenuButtonIndicator, widget ); + drawPrimitive( PE_ArrowDown, p, QRect( x + w - dx - 2, y + 2, dx, h - 4 ), + cg, how, opt ); + w -= dx; + } + + // Draw the icon if there is one + if ( button->iconSet() && !button->iconSet() ->isNull() ) + { + QIconSet::Mode mode = QIconSet::Disabled; + QIconSet::State state = QIconSet::Off; + + if ( button->isEnabled() ) + mode = button->hasFocus() ? QIconSet::Active : QIconSet::Normal; + if ( button->isToggleButton() && button->isOn() ) + state = QIconSet::On; + + QPixmap pixmap = button->iconSet() ->pixmap( QIconSet::Small, mode, state ); + + // Center the iconset if there's no text or pixmap + if (button->text().isEmpty() && !button->pixmap()) + p->drawPixmap( x + (w - pixmap.width()) / 2, + y + (h - pixmap.height()) / 2, pixmap ); + else + p->drawPixmap( x + 4, y + (h - pixmap.height()) / 2, pixmap ); + + int pw = pixmap.width(); + x += pw + 4; + w -= pw + 4; + } + + // Make the label indicate if the button is a default button or not + if ( active || button->isDefault() && button->isEnabled() ) + { + // Draw "fake" bold text - this enables the font metrics to remain + // the same as computed in QPushButton::sizeHint(), but gives + // a reasonable bold effect. + int i; + + // Text shadow + for ( i = 0; i < 2; i++ ) + drawItem( p, QRect( x + i + 1, y + 1, w, h ), AlignCenter | ShowPrefix, + button->colorGroup(), button->isEnabled(), NULL, + button->text(), -1, + active ? &button->colorGroup().dark() : &button->colorGroup().mid() ); + + // Normal Text + for ( i = 0; i < 2; i++ ) + drawItem( p, QRect( x + i, y, w, h ), AlignCenter | ShowPrefix, + button->colorGroup(), true, i == 0 ? button->pixmap() : NULL, + button->text(), -1, + active ? &button->colorGroup().light() : &button->colorGroup().buttonText() ); + } + else + { + if ( button->isEnabled() ) + { + drawItem( p, QRect( x, y, w, h ), AlignCenter | ShowPrefix, button->colorGroup(), + true, button->pixmap(), button->text(), -1, + active ? &button->colorGroup().light() : &button->colorGroup().buttonText() ); + } + else + { + //TODO: Handle reversed + drawItem( p, QRect( x + 1, y + 1, w, h ), AlignCenter | ShowPrefix, button->colorGroup(), + true, button->pixmap(), button->text(), -1, + &button->colorGroup().light() ); + + drawItem( p, QRect( x, y, w, h ), AlignCenter | ShowPrefix, button->colorGroup(), + true, button->pixmap(), button->text(), -1, + &button->colorGroup().buttonText() ); + } + } + + // Draw a focus rect if the button has focus + if ( how & Style_HasFocus ) + drawPrimitive( PE_FocusRect, p, + QStyle::visualRect( subRect( SR_PushButtonFocusRect, widget ), widget ), + cg, how ); + handled = true; + break; + } + + case CE_MenuBarEmptyArea: + { + //Expand to cover entire region + drawPrimitive(PE_PanelMenuBar, p, + QRect(0,0,r.width()+r.x()*2, r.height()+r.y()*2), + cg, Style_Default); + handled = true; + break; + } + + case CE_TabBarTab: + { + const QTabBar* tb = ( const QTabBar* ) widget; + QTabBar::Shape tbs = tb->shape(); + bool selected = how & Style_Selected; + WidgetType widget = selected ? ActiveTab : InactiveTab; + const QColorGroup *cg = colorGroup( tb->colorGroup(), widget ); + int i; + int x2 = x + w - 1, y2 = y + h - 1; + int bWidth = borderWidth( widget ); + int hWidth = highlightWidth( widget ); + handled = true; + if ( tbs == QTabBar::RoundedAbove || tbs == QTabBar::TriangularAbove ) + { + if ( !selected ) + { + p->fillRect( x, y, x2 - x + 1, 2, + tb->palette().active().brush( QColorGroup::Background ) ); + y += 2; + } + p->setPen( cg->text() ); + i = 0; + if ( i < bWidth ) + { + p->drawLine( x, y + 1, x, y2 ); + p->drawLine( x2, y + 1, x2, y2 ); + p->drawLine( x + 1, y, x2 - 1, y ); + if ( selected ? activeTabLine() : inactiveTabLine() ) + { + p->drawLine( x, y2, x2, y2 ); + --y2; + } + ++i, ++x, ++y, --x2; + } + for ( ; i < bWidth; ++i, ++x, ++y, --x2 ) + { + p->drawLine( x, y, x, y2 ); + p->drawLine( x2, y, x2, y2 ); + p->drawLine( x, y, x2, y ); + if ( selected ? activeTabLine() : inactiveTabLine() ) + { + p->drawLine( x, y2, x2, y2 ); + --y2; + } + } + i = 0; + if ( i < hWidth && bWidth == 0 ) + { + p->setPen( cg->light() ); + p->drawLine( x, y + 1, x, y2 ); + p->drawLine( x + 1, y, x2 - 1, y ); + p->setPen( cg->dark() ); + p->drawLine( x2, y + 1, x2, y2 ); + if ( selected ? activeTabLine() : inactiveTabLine() ) + { + p->drawLine( x, y2, x2, y2 ); + --y2; + } + ++i, ++x, ++y, --x2; + } + for ( ; i < hWidth; ++i, ++x, ++y, --x2 ) + { + p->setPen( cg->light() ); + p->drawLine( x, y, x, y2 ); + p->drawLine( x, y, x2, y ); + p->setPen( cg->dark() ); + p->drawLine( x2, y + 1, x2, y2 ); + if ( selected ? activeTabLine() : inactiveTabLine() ) + { + p->drawLine( x, y2, x2, y2 ); + --y2; + } + } + if ( isPixmap( widget ) ) + p->drawTiledPixmap( x, y, x2 - x + 1, y2 - y + 1, + *scalePixmap( x2 - x + 1, y2 - y + 1, widget ) ); + else + p->fillRect( x, y, x2 - x + 1, y2 - y + 1, cg->background() ); + } + else if ( tb->shape() == QTabBar::RoundedBelow || + tb->shape() == QTabBar::TriangularBelow ) + { + if ( widget == ActiveTab ) + widget = RotActiveTab; + else + widget = RotInactiveTab; + + if ( !selected ) + { + p->fillRect( x, y2 - 2, x2 - x + 1, 2, + tb->palette().active().brush( QColorGroup::Background ) ); + y2 -= 2; + } + p->setPen( cg->text() ); + i = 0; + if ( i < bWidth ) + { + p->drawLine( x, y, x, y2 - 1 ); + p->drawLine( x2, y, x2, y2 - 1 ); + p->drawLine( x + 1, y2, x2 - 1, y2 ); + if ( selected ? activeTabLine() : inactiveTabLine() ) + { + p->drawLine( x, y, x2, y ); + ++y; + } + } + for ( ; i < bWidth; ++i, ++x, --x2, --y2 ) + { + p->drawLine( x, y, x, y2 ); + p->drawLine( x2, y, x2, y2 ); + p->drawLine( x, y2, x2, y2 ); + if ( selected ? activeTabLine() : inactiveTabLine() ) + { + p->drawLine( x, y, x2, y ); + ++y; + } + } + i = 0; + if ( i < hWidth && bWidth == 0 ) + { + p->setPen( cg->dark() ); + p->drawLine( x + 1, y2, x2 - 1, y2 ); + p->drawLine( x2, y, x2, y2 - 1 ); + p->setPen( cg->light() ); + p->drawLine( x, y, x, y2 - 1 ); + if ( selected ? activeTabLine() : inactiveTabLine() ) + { + p->drawLine( x, y, x2, y ); + ++y; + } + ++i, ++x, --x2, --y2; + } + for ( ; i < hWidth; ++i, ++x, --x2, --y2 ) + { + p->setPen( cg->dark() ); + p->drawLine( x, y2, x2, y2 ); + p->drawLine( x2, y, x2, y2 ); + p->setPen( cg->light() ); + p->drawLine( x, y, x, y2 ); + if ( selected ? activeTabLine() : inactiveTabLine() ) + { + p->drawLine( x, y, x2, y ); + ++y; + } + } + if ( isPixmap( widget ) ) + p->drawTiledPixmap( x, y, x2 - x + 1, y2 - y + 1, + *scalePixmap( x2 - x + 1, y2 - y + 1, widget ) ); + else + p->fillRect( x, y, x2 - x + 1, y2 - y + 1, cg->background() ); + } + break; + } + case CE_MenuBarItem: + { + + r.rect( &x, &y, &w, &h ); + QMenuItem *mi = opt.menuItem(); + QMenuBar *mb = ( QMenuBar* ) widget; + QRect pr = mb->rect(); + bool active = how & Style_Active; + //bool focused = how & Style_HasFocus; + const QColorGroup *g = colorGroup( cg, active ? MenuBarItem : MenuBar ); + QColor btext = g->buttonText(); + + QPixmap* cache = makeMenuBarCache(pr.width(), pr.height()); + + QPixmap buf( w, pr.height() ); + + bitBlt(&buf, 0, 0, cache, x, y, w, pr.height()); + QPainter p2( &buf ); + + if ( active ) + { + drawBaseButton( &p2, 0, 0, w, h, *g, false, false, MenuBarItem ); + } + + p2.end(); + p->drawPixmap( x, y, buf, 0, 0, w, h ); + + drawItem( p, QRect(x,y,w,h), AlignCenter | AlignVCenter | ShowPrefix | DontClip | SingleLine, + *g, mi->isEnabled(), mi->pixmap(), mi->text(), + -1, &btext ); + handled = true; + break; + } + case CE_PopupMenuItem: + { + bool separator = false; + int x, y, w, h; + r.rect( &x, &y, &w, &h ); + + const QPopupMenu *popupmenu = ( const QPopupMenu * ) widget; + QMenuItem *mi = opt.menuItem(); + if ( mi ) + { + separator = mi->isSeparator(); + } + + int tab = opt.tabWidth(); + int checkcol = opt.maxIconWidth(); + bool enabled = (mi? mi->isEnabled():true); + bool checkable = popupmenu->isCheckable(); + bool active = how & Style_Active; + bool etchtext = styleHint( SH_EtchDisabledText, 0 ); + bool reverse = QApplication::reverseLayout(); + + const QColorGroup& cg_ours = *colorGroup( cg, active ? MenuItemDown : MenuItem ); + //QColor btext = cg_ours.buttonText(); + + + if ( checkable ) + checkcol = QMAX( checkcol, 20 ); + + // Are we a menu item separator? + if ( separator ) + { + p->setPen( cg_ours.dark() ); + p->drawLine( x, y, x + w, y ); + p->setPen( cg_ours.light() ); + p->drawLine( x, y + 1, x + w, y + 1 ); + break; + } + + // Draw the menu item background + if ( active ) + drawBaseButton( p, x, y, w, h, cg_ours, true, false, MenuItemDown ); + else + { + drawShade( p, x, y, w, h, *colorGroup( cg_ours, MenuItem ), false, false, + highlightWidth( MenuItem ), borderWidth( MenuItem ), + shade() ); + int dw = decoWidth( MenuItem ); + if ( !isPixmap( MenuItem ) ) + { + p->fillRect( + x + dw, y + dw, w - dw * 2, h - dw * 2, + cg_ours.brush( QColorGroup::Background ) ); + //cg.brush( QColorGroup::Background )); + //colorGroup( cg_ours, MenuItem ) ->brush( QColorGroup::Background ) ); + } + else + { + // process inactive item pixmaps as one large item + p->drawTiledPixmap( x + dw, y + dw, w - dw * 2, h - dw * 2, *scalePixmap + ( w, p->window().height(), MenuItem ), + x, y ); + } + } + + if (!mi) + break; + + // Do we have an icon? + if ( mi->iconSet() ) + { + QIconSet::Mode mode; + QRect cr = visualRect( QRect( x, y, checkcol, h ), r ); + + // Select the correct icon from the iconset + if ( active ) + mode = enabled ? QIconSet::Active : QIconSet::Disabled; + else + mode = enabled ? QIconSet::Normal : QIconSet::Disabled; + + // Do we have an icon and are checked at the same time? + // Then draw a "pressed" background behind the icon + if ( checkable && mi->isChecked() ) //!active && -- ?? + drawBaseButton( p, cr.x(), cr.y(), cr.width(), cr.height(), *colorGroup( cg_ours, BevelDown ), true, false, BevelDown ); + + // Draw the icon + QPixmap pixmap = mi->iconSet() ->pixmap( QIconSet::Small, mode ); + int pixw = pixmap.width(); + int pixh = pixmap.height(); + QRect pmr( 0, 0, pixw, pixh ); + pmr.moveCenter( cr.center() ); + p->setPen( cg_ours.highlightedText() ); + p->drawPixmap( pmr.topLeft(), pixmap ); + } + + // Are we checked? (This time without an icon) + else if ( checkable && mi->isChecked() ) + { + int cx = reverse ? x + w - checkcol : x; + + // We only have to draw the background if the menu item is inactive - + // if it's active the "pressed" background is already drawn + //if ( ! active ) + // qDrawShadePanel( p, cx, y, checkcol, h, cg_ours, true, 1, + // &cg_ours.brush(QColorGroup::Midlight) ); + + // Draw the checkmark + SFlags cflags = Style_Default; + cflags |= active ? Style_Enabled : Style_On; + + drawPrimitive( PE_CheckMark, p, QRect( cx + itemFrame, y + itemFrame, + checkcol - itemFrame * 2, h - itemFrame * 2 ), cg_ours, cflags ); + } + + // Time to draw the menu item label... + int xm = itemFrame + checkcol + itemHMargin; // X position margin + + int xp = reverse ? // X position + x + tab + rightBorder + itemHMargin + itemFrame - 1 : + x + xm; + + int offset = reverse ? -1 : 1; // Shadow offset for etched text + + // Label width (minus the width of the accelerator portion) + int tw = w - xm - tab - arrowHMargin - itemHMargin * 3 - itemFrame + 1; + + // Set the color for enabled and disabled text + // (used for both active and inactive menu items) + p->setPen( enabled ? cg_ours.buttonText() : cg_ours.mid() ); + + // This color will be used instead of the above if the menu item + // is active and disabled at the same time. (etched text) + QColor discol = cg_ours.mid(); + + // Does the menu item draw it's own label? + if ( mi->custom() ) + { + int m = itemVMargin; + // Save the painter state in case the custom + // paint method changes it in some way + p->save(); + + // Draw etched text if we're inactive and the menu item is disabled + if ( etchtext && !enabled && !active ) + { + p->setPen( cg_ours.light() ); + mi->custom() ->paint( p, cg_ours, active, enabled, xp + offset, y + m + 1, tw, h - 2 * m ); + p->setPen( discol ); + } + mi->custom() ->paint( p, cg_ours, active, enabled, xp, y + m, tw, h - 2 * m ); + p->restore(); + } + else + { + // The menu item doesn't draw it's own label + QString s = mi->text(); + + // Does the menu item have a text label? + if ( !s.isNull() ) + { + int t = s.find( '\t' ); + int m = itemVMargin; + int text_flags = AlignVCenter | ShowPrefix | DontClip | SingleLine; + text_flags |= reverse ? AlignRight : AlignLeft; + + // Does the menu item have a tabstop? (for the accelerator text) + if ( t >= 0 ) + { + int tabx = reverse ? x + rightBorder + itemHMargin + itemFrame : + x + w - tab - rightBorder - itemHMargin - itemFrame; + + + // Draw the right part of the label (accelerator text) + if ( etchtext && !enabled && !active ) + { + // Draw etched text if we're inactive and the menu item is disabled + p->setPen( cg_ours.light() ); + p->drawText( tabx + offset, y + m + 1, tab, h - 2 * m, text_flags, s.mid( t + 1 ) ); + p->setPen( discol ); + } + p->drawText( tabx, y + m, tab, h - 2 * m, text_flags, s.mid( t + 1 ) ); + s = s.left( t ); + } + + + // Draw the left part of the label (or the whole label + // if there's no accelerator) + if ( etchtext && !enabled && !active ) + { + // Etched text again for inactive disabled menu items... + p->setPen( cg_ours.light() ); + p->drawText( xp + offset, y + m + 1, tw, h - 2 * m, text_flags, s, t ); + p->setPen( discol ); + } + + p->drawText( xp, y + m, tw, h - 2 * m, text_flags, s, t ); + + } + + // The menu item doesn't have a text label + // Check if it has a pixmap instead + else if ( mi->pixmap() ) + { + QPixmap * pixmap = mi->pixmap(); + + // Draw the pixmap + if ( pixmap->depth() == 1 ) + p->setBackgroundMode( OpaqueMode ); + + int diffw = ( ( w - pixmap->width() ) / 2 ) + + ( ( w - pixmap->width() ) % 2 ); + p->drawPixmap( x + diffw, y + itemFrame, *pixmap ); + + if ( pixmap->depth() == 1 ) + p->setBackgroundMode( TransparentMode ); + } + } + + // Does the menu item have a submenu? + if ( mi->popup() ) + { + PrimitiveElement arrow = reverse ? PE_ArrowLeft : PE_ArrowRight; + int dim = 10 - itemFrame; //We're not very useful to inherit off, so just hardcode.. + QRect vr = visualRect( QRect( x + w - arrowHMargin - itemFrame - dim, + y + h / 2 - dim / 2, dim, dim ), r ); + + // Draw an arrow at the far end of the menu item + if ( active ) + { + if ( enabled ) + discol = cg_ours.buttonText(); + + QColorGroup g2( discol, cg_ours.highlight(), white, white, + enabled ? white : discol, discol, white ); + + drawPrimitive( arrow, p, vr, g2, Style_Enabled | Style_Down ); + } + else + drawPrimitive( arrow, p, vr, cg_ours, + enabled ? Style_Enabled : Style_Default ); + } + handled = true; + break; + } + case CE_ProgressBarGroove: + { + QBrush bg; + const QColorGroup * cg2 = colorGroup( cg, ProgressBg ); + qDrawWinPanel( p, r, *cg2, true ); + bg.setColor( cg2->color( QColorGroup::Background ) ); + if ( isPixmap( ProgressBg ) ) + bg.setPixmap( *uncached( ProgressBg ) ); + p->fillRect( x + 2, y + 2, w - 4, h - 4, bg ); + + handled = true; + break; + } + case CE_ProgressBarContents: + { + const QProgressBar* pb = (const QProgressBar*)widget; + QRect cr = subRect(SR_ProgressBarContents, widget); + double progress = pb->progress(); + bool reverse = QApplication::reverseLayout(); + int steps = pb->totalSteps(); + + int pstep = 0; + + if (!cr.isValid()) + return; + + // Draw progress bar + if (progress > 0 || steps == 0) + { + double pg = (steps == 0) ? 0.1 : progress / steps; + int width = QMIN(cr.width(), (int)(pg * cr.width())); + if (steps == 0) + { //Busy indicator + + if (width < 1) width = 1; //A busy indicator with width 0 is kind of useless + + int remWidth = cr.width() - width; //Never disappear completely + if (remWidth <= 0) remWidth = 1; //Do something non-crashy when too small... + + pstep = int(progress) % ( 2 * remWidth ); + + if ( pstep > remWidth ) + { + //Bounce about.. We're remWidth + some delta, we want to be remWidth - delta... + // - ( (remWidth + some delta) - 2* remWidth ) = - (some deleta - remWidth) = remWidth - some delta.. + pstep = - (pstep - 2 * remWidth ); + } + } + + if ( !reverse ) + drawBaseButton( p, x + pstep, y, width, h, *colorGroup( cg, ProgressBar ), false, false, ProgressBar ); + else + { + //TODO:Optimize + QPixmap buf( width, h ); + QPainter p2( &buf ); + drawBaseButton( &p2, 0, 0, width, h, *colorGroup( cg, ProgressBar ), false, false, ProgressBar ); + p2.end(); + QPixmap mirroredPix = QPixmap( buf.convertToImage().mirror( true, false ) ); + bitBlt( p->device(), x + w - width - pstep, y, &mirroredPix ); + } + } + + handled = true; + break; + } + default: + handled = false; + }; + + if ( !handled ) + KThemeBase::drawControl( element, + p, widget, r, cg, how, opt ); +} + + +void KThemeStyle::drawControlMask( ControlElement element, + QPainter *p, + const QWidget *widget, + const QRect &r, + const QStyleOption& opt ) const +{ + bool handled = false; + int x, y, w, h; + r.rect( &x, &y, &w, &h ); + + switch ( element ) + { + case CE_PushButton: + { + //Is this correct? + drawBaseMask( p, x, y, w, h, roundButton() ); + handled = true; + break; + } + default: + handled = false; + }; + + if ( !handled ) + KThemeBase::drawControlMask( element, + p, widget, r, opt ); + +} + + +void KThemeStyle::drawKStylePrimitive( KStylePrimitive kpe, + QPainter* p, + const QWidget* widget, + const QRect &r, + const QColorGroup &cg, + SFlags flags, + const QStyleOption& opt ) const +{ + bool handled = false; + int x, y, w, h; + r.rect( &x, &y, &w, &h ); + switch ( kpe ) + { + case KPE_SliderGroove: + { + if ( !roundSlider() ) + { + const QSlider * slider = ( const QSlider* ) widget; + bool horizontal = slider->orientation() == Horizontal; + if ( horizontal ) + { + drawBaseButton( p, x, y, w, h, *colorGroup( cg, SliderGroove ), true, + false, SliderGroove ); + } + else + { + drawBaseButton( p, x, y, w, h, *colorGroup( cg, RotSliderGroove ), true, + false, RotSliderGroove ); + } + } + else + { + //This code is from HighColorDefault.. + const QSlider* slider = ( const QSlider* ) widget; + bool horizontal = slider->orientation() == Horizontal; + int gcenter = ( horizontal ? r.height() : r.width() ) / 2; + + QRect gr; + if ( horizontal ) + gr = QRect( r.x(), r.y() + gcenter - 3, r.width(), 7 ); + else + gr = QRect( r.x() + gcenter - 3, r.y(), 7, r.height() ); + + int x, y, w, h; + gr.rect( &x, &y, &w, &h ); + int x2 = x + w - 1; + int y2 = y + h - 1; + + // Draw the slider groove. + p->setPen( cg.dark() ); + p->drawLine( x + 2, y, x2 - 2, y ); + p->drawLine( x, y + 2, x, y2 - 2 ); + p->fillRect( x + 2, y + 2, w - 4, h - 4, + slider->isEnabled() ? cg.dark() : cg.mid() ); + p->setPen( cg.shadow() ); + p->drawRect( x + 1, y + 1, w - 2, h - 2 ); + p->setPen( cg.light() ); + p->drawPoint( x + 1, y2 - 1 ); + p->drawPoint( x2 - 1, y2 - 1 ); + p->drawLine( x2, y + 2, x2, y2 - 2 ); + p->drawLine( x + 2, y2, x2 - 2, y2 ); + } + handled = true; + break; + } + case KPE_SliderHandle: + { + if ( isPixmap( Slider ) ) + { + const QSlider * slider = ( const QSlider* ) widget; + bool horizontal = slider->orientation() == Horizontal; + if ( horizontal ) + { + bitBlt( p->device(), x, y + ( h - uncached( Slider ) ->height() ) / 2, + uncached( Slider ) ); + } + else + { + if ( !vsliderCache ) + { + QWMatrix r270; + r270.rotate( 270 ); + vsliderCache = new QPixmap( uncached( Slider ) ->xForm( r270 ) ); + if ( uncached( Slider ) ->mask() ) + vsliderCache->setMask( uncached( Slider ) ->mask() ->xForm( r270 ) ); + } + bitBlt( p->device(), x + ( w - vsliderCache->width() ) / 2, y, + vsliderCache ); + } + } + else + { + //This code again from HighColor.. + //...except sans the gradient.. + const QSlider* slider = ( const QSlider* ) widget; + bool horizontal = slider->orientation() == Horizontal; + int x, y, w, h; + r.rect( &x, &y, &w, &h ); + int x2 = x + w - 1; + int y2 = y + h - 1; + + p->setPen( cg.mid() ); + p->drawLine( x + 1, y, x2 - 1, y ); + p->drawLine( x, y + 1, x, y2 - 1 ); + p->setPen( cg.shadow() ); + p->drawLine( x + 1, y2, x2 - 1, y2 ); + p->drawLine( x2, y + 1, x2, y2 - 1 ); + + p->setPen( cg.light() ); + p->drawLine( x + 1, y + 1, x2 - 1, y + 1 ); + p->drawLine( x + 1, y + 1, x + 1, y2 - 1 ); + p->setPen( cg.dark() ); + p->drawLine( x + 2, y2 - 1, x2 - 1, y2 - 1 ); + p->drawLine( x2 - 1, y + 2, x2 - 1, y2 - 1 ); + p->setPen( cg.midlight() ); + p->drawLine( x + 2, y + 2, x2 - 2, y + 2 ); + p->drawLine( x + 2, y + 2, x + 2, y2 - 2 ); + p->setPen( cg.mid() ); + p->drawLine( x + 3, y2 - 2, x2 - 2, y2 - 2 ); + p->drawLine( x2 - 2, y + 3, x2 - 2, y2 - 2 ); + p->fillRect( QRect( x + 3, y + 3, w - 6, h - 6 ), + cg.button() ); + + // Paint riffles + if ( horizontal ) + { + p->setPen( cg.light() ); + p->drawLine( x + 5, y + 4, x + 5, y2 - 4 ); + p->drawLine( x + 8, y + 4, x + 8, y2 - 4 ); + p->drawLine( x + 11, y + 4, x + 11, y2 - 4 ); + p->setPen( slider->isEnabled() ? cg.shadow() : cg.mid() ); + p->drawLine( x + 6, y + 4, x + 6, y2 - 4 ); + p->drawLine( x + 9, y + 4, x + 9, y2 - 4 ); + p->drawLine( x + 12, y + 4, x + 12, y2 - 4 ); + } + else + { + p->setPen( cg.light() ); + p->drawLine( x + 4, y + 5, x2 - 4, y + 5 ); + p->drawLine( x + 4, y + 8, x2 - 4, y + 8 ); + p->drawLine( x + 4, y + 11, x2 - 4, y + 11 ); + p->setPen( slider->isEnabled() ? cg.shadow() : cg.mid() ); + p->drawLine( x + 4, y + 6, x2 - 4, y + 6 ); + p->drawLine( x + 4, y + 9, x2 - 4, y + 9 ); + p->drawLine( x + 4, y + 12, x2 - 4, y + 12 ); + } + } + handled = true; + break; + } + //case KPE_DockWindowHandle: + case KPE_ToolBarHandle: + case KPE_GeneralHandle: + { + if ( w > h ) + drawBaseButton( p, x, y, w, h, *colorGroup( cg, HBarHandle ), false, false, + HBarHandle ); + else + drawBaseButton( p, x, y, w, h, *colorGroup( cg, VBarHandle ), false, false, + VBarHandle ); + + handled = true; + break; + } + default: + handled = false; + + } + + if ( !handled ) + { + KThemeBase::drawKStylePrimitive( kpe, p, widget, + r, cg, flags, opt ); + } + +} + + + + +void KThemeStyle::drawComplexControl ( ComplexControl control, QPainter * p, const QWidget * widget, + const QRect & r, const QColorGroup & g, SFlags how , + SCFlags controls, SCFlags active, + const QStyleOption & opt ) const +{ + bool handled = false; + int x, y, w, h; + r.rect( &x, &y, &w, &h ); + bool down = how & Style_Down; + bool on = how & Style_On; + + // bool enabled = ( how & Style_Enabled ); + + switch ( control ) + { + case CC_ToolButton: + { + const QToolButton * toolbutton = ( const QToolButton * ) widget; + QRect button, menu; + button = querySubControlMetrics( control, widget, SC_ToolButton, opt ); + menu = querySubControlMetrics( control, widget, SC_ToolButtonMenu, opt ); + + + if ( controls & SC_ToolButton ) + { + WidgetType widget = ( down || on ) ? ToolButtonDown : ToolButton; + + drawBaseButton( p, button.x(), button.y(), button.width(), button.height(), *colorGroup( g, widget ), down || on, false, + widget ); + + // int m = decoWidth( widget ); + } + + if ( controls & SC_ToolButtonMenu ) + { + drawPrimitive( PE_ArrowDown, p, menu, g, how ); + /* if ( enabled ) + kDrawWindowsArrow(p, this, PE_ArrowDown, false, menu.x(), menu.y(), menu.width(), menu.height(), + g, true ); + else + kDrawWindowsArrow(p, this, PE_ArrowDown, false, menu.x(), menu.y(), menu.width(), menu.height(), + g, false );*/ + } + + if ( toolbutton->hasFocus() && !toolbutton->focusProxy() ) + { + QRect fr = toolbutton->rect(); + fr.addCoords( 3, 3, -3, -3 ); + drawPrimitive( PE_FocusRect, p, fr, g ); + } + + handled = true; + break; + } + + case CC_ComboBox: + { + if ( controls & SC_ComboBoxFrame ) + { + //TODO: Anyway of detecting when the popup is there -- would look nicer if sunken then too.. + bool sunken = ( active == SC_ComboBoxArrow ); + //No frame, edit box and button for now? + WidgetType widget = sunken ? ComboBoxDown : ComboBox; + drawBaseButton( p, x, y, w, h, *colorGroup( g, widget ), sunken, + roundComboBox(), widget ); + + controls ^= SC_ComboBoxFrame; + } + + if ( controls & SC_ComboBoxArrow ) + { + bool sunken = ( active == SC_ComboBoxArrow ); + QRect ar = QStyle::visualRect( + querySubControlMetrics( CC_ComboBox, widget, SC_ComboBoxArrow ), + widget ); + ar.rect( &x, &y, &w, &h ); + WidgetType widget = sunken ? ComboBoxDown : ComboBox; + + if ( !sunken && isPixmap( ComboDeco ) ) + bitBlt( p->device(), + x + ( w - uncached( ComboDeco ) ->width() - decoWidth( ComboBox ) / 2 ), + y + ( h - uncached( ComboDeco ) ->height() ) / 2, + uncached( ComboDeco ) ); + else if ( sunken && isPixmap( ComboDecoDown ) ) + bitBlt( p->device(), + x + ( w - uncached( ComboDecoDown ) ->width() - decoWidth( ComboBoxDown ) ) / 2, + y + ( h - uncached( ComboDecoDown ) ->height() ) / 2, + uncached( ComboDecoDown ) ); + else + { + + mtfstyle->drawPrimitive( PE_ArrowDown, p, QRect( x, y, w, h ), *colorGroup( g, widget ), sunken ? ( how | Style_Sunken ) : how, opt ); + qDrawShadeRect( p, x, y, w, h, *colorGroup( g, widget ) ); //w-14, y+7+(h-15), 10, 3, + } + controls ^= SC_ComboBoxArrow; + } + break; + } + case CC_ScrollBar: + { + const QScrollBar *sb = ( const QScrollBar* ) widget; + bool maxedOut = ( sb->minValue() == sb->maxValue() ); + bool horizontal = ( sb->orientation() == Qt::Horizontal ); + SFlags sflags = ( ( horizontal ? Style_Horizontal : Style_Default ) | + ( maxedOut ? Style_Default : Style_Enabled ) ); + + //Here, we don't do add page, subpage, etc., + QRect addline, subline, subline2, groove, slider; + subline = querySubControlMetrics( control, widget, SC_ScrollBarSubLine, opt ); + addline = querySubControlMetrics( control, widget, SC_ScrollBarAddLine, opt ); + groove = querySubControlMetrics( control, widget, SC_ScrollBarGroove, opt ); + + slider = querySubControlMetrics( control, widget, SC_ScrollBarSlider, opt ); + subline2 = addline; + + QPixmap buf( sb->width(), sb->height() ); + QPainter p2( &buf ); + + if ( groove.isValid() ) + { + p2.fillRect( groove, QColor( 255, 0, 0 ) ); + drawPrimitive( PE_ScrollBarSubPage, &p2, groove, g, + sflags | ( ( active == SC_ScrollBarSubPage ) ? + Style_Down : Style_Default ) ); + } + + + // Draw the up/left button set + if ( subline.isValid() ) + { + drawPrimitive( PE_ScrollBarSubLine, &p2, subline, g, + sflags | ( active == SC_ScrollBarSubLine ? + Style_Down : Style_Default ) ); + } + + if ( addline.isValid() ) + drawPrimitive( PE_ScrollBarAddLine, &p2, addline, g, + sflags | ( ( active == SC_ScrollBarAddLine ) ? + Style_Down : Style_Default ) ); + + if ( slider.isValid() ) + { //(controls & SC_ScrollBarSlider) && + drawPrimitive( PE_ScrollBarSlider, &p2, slider, g, + sflags | ( ( active == SC_ScrollBarSlider ) ? + Style_Down : Style_Default ) ); + // Draw focus rect + if ( sb->hasFocus() ) + { + QRect fr( slider.x() + 2, slider.y() + 2, + slider.width() - 5, slider.height() - 5 ); + drawPrimitive( PE_FocusRect, &p2, fr, g, Style_Default ); + } + p2.end(); + bitBlt( p->device(), x, y, &buf ); + handled = true; + + } + break; + } + default: + handled = false; + } + + if ( !handled ) + { + KThemeBase::drawComplexControl ( control, p, widget, + r, g, how , + controls, active, + opt ); + } + +} + + +void KThemeStyle::drawBaseMask( QPainter *p, int x, int y, int w, int h, + bool round ) const +{ + // round edge fills + static const QCOORD btm_left_fill[] = + { + 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 0, 1, 1, 1, 2, 1, 3, 1, 4, 1, + 1, 2, 2, 2, 3, 2, 4, 2, 2, 3, 3, 3, 4, 3, 3, 4, 4, 4 + }; + + static const QCOORD btm_right_fill[] = + { + 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 0, 1, 1, 1, 2, 1, 3, 1, 4, + 1, 0, 2, 1, 2, 2, 2, 3, 2, 0, 3, 1, 3, 2, 3, 0, 4, 1, 4 + }; + + static const QCOORD top_left_fill[] = + { + 3, 0, 4, 0, 2, 1, 3, 1, 4, 1, 1, 2, 2, 2, 3, 2, 4, 2, 0, 3, + 1, 3, 2, 3, 3, 3, 4, 3, 0, 4, 1, 4, 2, 4, 3, 4, 4, 4 + }; + + static const QCOORD top_right_fill[] = + { + 0, 0, 1, 0, 0, 1, 1, 1, 2, 1, 0, 2, 1, 2, 2, 2, 3, 2, 0, + 3, 1, 3, 2, 3, 3, 3, 4, 3, 0, 4, 1, 4, 2, 4, 3, 4, 4, 4 + }; + + QBrush fillBrush( color1, SolidPattern ); + p->setPen( color1 ); + if ( round && w > 19 && h > 19 ) + { + int x2 = x + w - 1; + int y2 = y + h - 1; + QPointArray a( QCOORDARRLEN( top_left_fill ), top_left_fill ); + a.translate( 1, 1 ); + p->drawPoints( a ); + a.setPoints( QCOORDARRLEN( btm_left_fill ), btm_left_fill ); + a.translate( 1, h - 6 ); + p->drawPoints( a ); + a.setPoints( QCOORDARRLEN( top_right_fill ), top_right_fill ); + a.translate( w - 6, 1 ); + p->drawPoints( a ); + a.setPoints( QCOORDARRLEN( btm_right_fill ), btm_right_fill ); + a.translate( w - 6, h - 6 ); + p->drawPoints( a ); + + p->fillRect( x + 6, y, w - 12, h, fillBrush ); + p->fillRect( x, y + 6, x + 6, h - 12, fillBrush ); + p->fillRect( x2 - 6, y + 6, x2, h - 12, fillBrush ); + p->drawLine( x + 6, y, x2 - 6, y ); + p->drawLine( x + 6, y2, x2 - 6, y2 ); + p->drawLine( x, y + 6, x, y2 - 6 ); + p->drawLine( x2, y + 6, x2, y2 - 6 ); + + } + else + p->fillRect( x, y, w, h, fillBrush ); +} + +int KThemeStyle::styleHint( StyleHint sh, const QWidget *w, const QStyleOption &opt, QStyleHintReturn *shr ) const +{ + switch ( sh ) + { + case SH_EtchDisabledText: + case SH_Slider_SnapToValue: + case SH_PrintDialog_RightAlignButtons: + case SH_FontDialog_SelectAssociatedText: + case SH_PopupMenu_AllowActiveAndDisabled: + case SH_MenuBar_AltKeyNavigation: + case SH_MenuBar_MouseTracking: + case SH_PopupMenu_MouseTracking: + case SH_ComboBox_ListMouseTracking: + return 1; + + case SH_GUIStyle: + return WindowsStyle; + + case SH_ScrollBar_BackgroundMode: + return NoBackground; + + default: + return KThemeBase::styleHint( sh, w, opt, shr ); + }; +} + + + +/* This is where we draw the borders and highlights. The new round button + * code is a pain in the arse. We don't want to be calculating arcs so + * use a whole lotta QPointArray's ;-) The code is made a lot more complex + * because you can have variable width border and highlights... + * I may want to cache this if round buttons are used, but am concerned + * about excessive cache misses. This is a memory/speed tradeoff that I + * have to test. + */ +void KThemeStyle::drawShade( QPainter *p, int x, int y, int w, int h, + const QColorGroup &g, bool sunken, bool rounded, + int hWidth, int bWidth, ShadeStyle style ) const +{ + int i, sc, bc, x2, y2; + QPen highPen, lowPen; + + if ( style == Motif ) + { + highPen.setColor( sunken ? g.dark() : g.light() ); + lowPen.setColor( sunken ? g.light() : g.dark() ); + } + else + { + highPen.setColor( sunken ? g.shadow() : g.light() ); + lowPen.setColor( sunken ? g.light() : g.shadow() ); + } + + // Advanced round buttons + if ( rounded && w > 19 && h > 19 ) + { + x2 = x + w - 1, y2 = y + h - 1; + QPointArray bPntArray, hPntArray, lPntArray; + QPointArray bLineArray, hLineArray, lLineArray; + // borders + for ( i = 0, bc = 0; i < bWidth; ++i ) + { + bPntArray.putPoints( bc, 24, x + 4, y + 1, x + 5, y + 1, x + 3, y + 2, x + 2, y + 3, + x + 1, y + 4, x + 1, y + 5, x + 1, y2 - 5, x + 1, y2 - 4, x + 2, y2 - 3, + x2 - 5, y + 1, x2 - 4, y + 1, x2 - 3, y + 2, x2 - 5, y2 - 1, + x2 - 4, y2 - 1, x2 - 3, y2 - 2, x2 - 2, y2 - 3, x2 - 1, y2 - 5, + x2 - 1, y2 - 4, x + 3, y2 - 2, x + 4, y2 - 1, x + 5, y2 - 1, + x2 - 2, y + 3, x2 - 1, y + 4, x2 - 1, y + 5 ); + bc += 24; + // ellispe edges don't match exactly, so fill in blanks + if ( i < bWidth - 1 || hWidth != 0 ) + { + bPntArray.putPoints( bc, 20, x + 6, y + 1, x + 4, y + 2, x + 3, y + 3, + x + 2, y + 4, x + 1, y + 6, x2 - 6, y + 1, x2 - 4, y + 2, + x2 - 3, y + 3, x + 2, y2 - 4, x + 1, y2 - 6, x2 - 6, y2 - 1, + x2 - 4, y2 - 2, x2 - 3, y2 - 3, x2 - 2, y2 - 4, x2 - 1, y2 - 6, + x + 6, y2 - 1, x + 4, y2 - 2, x + 3, y2 - 3, x2 - 1, y + 6, + x2 - 2, y + 4 ); + bc += 20; + } + bLineArray.putPoints( i * 8, 8, x + 6, y, x2 - 6, y, x, y + 6, x, y2 - 6, + x + 6, y2, x2 - 6, y2, x2, y + 6, x2, y2 - 6 ); + ++x, ++y; + --x2, --y2; + } + // highlights + for ( i = 0, sc = 0; i < hWidth; ++i ) + { + hPntArray.putPoints( sc, 12, x + 4, y + 1, x + 5, y + 1, // top left + x + 3, y + 2, x + 2, y + 3, x + 1, y + 4, x + 1, y + 5, + x + 1, y2 - 5, x + 1, y2 - 4, x + 2, y2 - 3, // half corners + x2 - 5, y + 1, x2 - 4, y + 1, x2 - 3, y + 2 ); + lPntArray.putPoints( sc, 12, x2 - 5, y2 - 1, x2 - 4, y2 - 1, // btm right + x2 - 3, y2 - 2, x2 - 2, y2 - 3, x2 - 1, y2 - 5, x2 - 1, y2 - 4, + x + 3, y2 - 2, x + 4, y2 - 1, x + 5, y2 - 1, //half corners + x2 - 2, y + 3, x2 - 1, y + 4, x2 - 1, y + 5 ); + sc += 12; + if ( i < hWidth - 1 ) + { + hPntArray.putPoints( sc, 10, x + 6, y + 1, x + 4, y + 2, // top left + x + 3, y + 3, x + 2, y + 4, x + 1, y + 6, + x2 - 6, y + 1, x2 - 4, y + 2, // half corners + x2 - 3, y + 3, x + 2, y2 - 4, x + 1, y2 - 6 ); + lPntArray.putPoints( sc, 10, x2 - 6, y2 - 1, x2 - 4, y2 - 2, // btm right + x2 - 3, y2 - 3, x2 - 2, y2 - 4, x2 - 1, y2 - 6, + x + 6, y2 - 1, x + 4, y2 - 2, // half corners + x + 3, y2 - 3, x2 - 1, y + 6, x2 - 2, y + 4 ); + sc += 10; + } + hLineArray.putPoints( i * 4, 4, x + 6, y, x2 - 6, y, x, y + 6, x, y2 - 6 ); + lLineArray.putPoints( i * 4, 4, x + 6, y2, x2 - 6, y2, x2, y + 6, x2, y2 - 6 ); + ++x, ++y; + --x2, --y2; + } + p->setPen( Qt::black ); + p->drawPoints( bPntArray ); + p->drawLineSegments( bLineArray ); + p->setPen( highPen ); + p->drawPoints( hPntArray ); + p->drawLineSegments( hLineArray ); + p->setPen( lowPen ); + p->drawPoints( lPntArray ); + p->drawLineSegments( lLineArray ); + } + // Rectangular buttons + else + { + QPointArray highShade( hWidth * 4 ); + QPointArray lowShade( hWidth * 4 ); + + p->setPen( g.shadow() ); + for ( i = 0; i < bWidth && w > 2 && h > 2; ++i, ++x, ++y, w -= 2, h -= 2 ) + p->drawRect( x, y , w, h ); + + if ( !hWidth ) + return ; + + x2 = x + w - 1, y2 = y + h - 1; + for ( i = 0; i < hWidth; ++i, ++x, ++y, --x2, --y2 ) + { + highShade.putPoints( i * 4, 4, x, y, x2, y, x, y, x, y2 ); + lowShade.putPoints( i * 4, 4, x, y2, x2, y2, x2, y, x2, y2 ); + } + if ( style == Windows && hWidth > 1 ) + { + p->setPen( highPen ); + p->drawLineSegments( highShade, 0, 2 ); + p->setPen( lowPen ); + p->drawLineSegments( lowShade, 0, 2 ); + + p->setPen( ( sunken ) ? g.dark() : g.mid() ); + p->drawLineSegments( highShade, 4 ); + p->setPen( ( sunken ) ? g.mid() : g.dark() ); + p->drawLineSegments( lowShade, 4 ); + } + else + { + p->setPen( ( sunken ) ? g.dark() : g.light() ); + p->drawLineSegments( highShade ); + p->setPen( ( sunken ) ? g.light() : g.dark() ); + p->drawLineSegments( lowShade ); + } + } +} + + + + +int KThemeStyle::popupMenuItemHeight( bool /*checkable*/, QMenuItem *mi, + const QFontMetrics &fm ) +{ + int h2, h = 0; + int offset = QMAX( decoWidth( MenuItemDown ), decoWidth( MenuItem ) ) + 4; + + if ( mi->isSeparator() ) + return ( 2 ); + if ( mi->isChecked() ) + h = isPixmap( CheckMark ) ? uncached( CheckMark ) ->height() + offset : + offset + 16; + if ( mi->pixmap() ) + { + h2 = mi->pixmap() ->height() + offset; + h = h2 > h ? h2 : h; + } + if ( mi->iconSet() ) + { + h2 = mi->iconSet() -> + pixmap( QIconSet::Small, QIconSet::Normal ).height() + offset; + h = h2 > h ? h2 : h; + } + h2 = fm.height() + offset; + h = h2 > h ? h2 : h; + return ( h ); +} + +#include "kthemestyle.moc" +// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent on; diff --git a/kstyles/kthemestyle/kthemestyle.h b/kstyles/kthemestyle/kthemestyle.h new file mode 100644 index 000000000..9b7d54b60 --- /dev/null +++ b/kstyles/kthemestyle/kthemestyle.h @@ -0,0 +1,230 @@ +/* +$Id$ + +This file is part of the KDE libraries +Copyright (C) 1999 Daniel M. Duley <mosfet@kde.org> + +KDE3 port (C) 2001-2002 Maksim Orlovich <mo002j@mail.rochester.edu> +Port version 0.9.7 + +Includes code portions from the dotNET style, and the KDE HighColor style. + +dotNET Style + Copyright (C) 2001, Chris Lee <lee@azsites.com> + Carsten Pfeiffer <pfeiffer@kde.org> + +KDE3 HighColor Style +Copyright (C) 2001 Karol Szwed <gallium@kde.org> + (C) 2001 Fredrik Höglund <fredrik@kde.org> + +Drawing routines adapted from the KDE2 HCStyle, +Copyright (C) 2000 Daniel M. Duley <mosfet@kde.org> + (C) 2000 Dirk Mueller <mueller@kde.org> + (C) 2001 Martijn Klingens <klingens@kde.org> + + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KTHEMESTYLE_H +#define KTHEMESTYLE_H + +#include <qglobal.h> + +#include "kthemebase.h" +#include <qwindowdefs.h> +#include <qobject.h> +#include <qbutton.h> +#include <qpushbutton.h> +#include <qscrollbar.h> +#include <qtabbar.h> +#include <qstring.h> +#include <qintdict.h> +#include <qmap.h> + + +/** + * KDE themed styles. + * + * It provides methods for + * drawing most widgets with user-specified borders, highlights, pixmaps, + * etc. It also handles various other settings such as scrollbar types, + * rounded buttons, and shading types. For a full list of parameters this + * class handles refer to the KDE theme configuration documentation. + * + */ + +class KThemeStyle: public KThemeBase +{ + Q_OBJECT +public: + /** + * Constructs a new KThemeStyle object. + * + * @param configDir The directory which has the KConfig file. + * @param configFile A KConfig file to use as the theme configuration. + * Defaults to ~/.kderc. + */ + KThemeStyle( const QString& configDir, const QString &configFile = QString::null ); + ~KThemeStyle(); + + virtual int pixelMetric ( PixelMetric metric, const QWidget * widget = 0 ) const; + + virtual void drawPrimitive ( PrimitiveElement pe, QPainter * p, const QRect & r, const QColorGroup & cg, + SFlags flags = Style_Default, + const QStyleOption& = QStyleOption::Default ) const; + + virtual void drawControl( ControlElement element, + QPainter *p, + const QWidget *widget, + const QRect &r, + const QColorGroup &cg, + SFlags how = Style_Default, + const QStyleOption& = QStyleOption::Default ) const; + + virtual void drawControlMask( ControlElement element, + QPainter *p, + const QWidget *widget, + const QRect &r, + const QStyleOption& = QStyleOption::Default ) const; + + + virtual void drawComplexControl( ComplexControl control, + QPainter *p, + const QWidget* widget, + const QRect &r, + const QColorGroup &cg, + SFlags flags = Style_Default, + SCFlags controls = SC_All, + SCFlags active = SC_None, + const QStyleOption& = QStyleOption::Default ) const; + + virtual void drawKStylePrimitive( KStylePrimitive kpe, + QPainter* p, + const QWidget* widget, + const QRect &r, + const QColorGroup &cg, + SFlags flags = Style_Default, + const QStyleOption& = QStyleOption::Default ) const; + + + virtual int styleHint( StyleHint sh, + const QWidget *widget = 0, + const QStyleOption& = QStyleOption::Default, + QStyleHintReturn* returnData = 0 ) const; + + virtual QSize sizeFromContents( ContentsType contents, + const QWidget *widget, + const QSize &contentsSize, + const QStyleOption& = QStyleOption::Default ) const; + + virtual QRect subRect(SubRect, const QWidget *) const; + + virtual void polish( QWidget* ); + virtual void unPolish( QWidget* ); + virtual bool eventFilter( QObject* object, QEvent* event ); + /** + * By default this just sets the background brushes to the pixmapped + * background. + */ + virtual void polish( QApplication *app ); + virtual void unPolish( QApplication* ); + + /** \internal */ + // to make it possible for derived classes to overload this function + virtual void polish( QPalette& pal ); + + /** + * This is a convenience method for drawing widgets with + * borders, highlights, pixmaps, colors, etc... + * You specify the widget type and it will draw it according to the + * config file settings. + * + * @param x The x coordinate of the button's upper left hand corner. + * @param y The y coordinate of the buttons' upper left hand corner. + * @param w The button width. + * @param h The button height. + * @param p The QPainter to draw on. + * @param g The color group to use. + * @param sunken The button is drawn with a sunken style if @p true + * @param rounded @p true if the widget is rounded, @p false if rectangular. + * @param type The widget type to paint. + */ + virtual void drawBaseButton( QPainter *p, int x, int y, int w, int h, + const QColorGroup &g, bool sunken = false, + bool rounded = false, WidgetType type = Bevel ) const; + /** + * Draw a mask with for widgets that may be rounded. + * + *Currently used + * by pushbuttons and comboboxes. + * + * @param p The QPainter to draw on. + * @param x The x coordinate of the widget's upper left hand corner. + * @param y The y coordinate of the widget's upper left hand corner. + * @param w The widget width. + * @param h The widget height. + * @param rounded @p true if the widget is rounded, @p false if rectangular. + */ + virtual void drawBaseMask( QPainter *p, int x, int y, int w, int h, + bool rounded ) const; + + + + /** + * Draw a shaded rectangle using the given style. + * + * @param p The painter to draw on. + * @param g The color group to use. + * @param x The x coordinate of the rectangle's upper left hand corner. + * @param y The y coordinate of the rectangle's upper left hand corner. + * @param w The rectangle width. + * @param h The rectangle height. + * @param sunken Draws a sunken style if @p true. + * @param rounded Draws a rounded shape if @p true. Requires bWidth to be + * at least 1. + * @param hWidth The highlight width. + * @param bWidth The border width. + * @param style The shading style to use. + */ + virtual void drawShade( QPainter *p, int x, int y, int w, int h, + const QColorGroup &g, bool sunken, bool rounded, + int hWidth, int bWidth, ShadeStyle style ) const; + int popupMenuItemHeight( bool checkable, QMenuItem *mi, + const QFontMetrics &fm ); + +protected: + QPalette oldPalette, popupPalette, indiPalette, exIndiPalette; + bool paletteSaved; + bool polishLock; + QStyle *mtfstyle; + + QPixmap* makeMenuBarCache(int w, int h) const; + + mutable QPixmap* menuCache; + mutable QPixmap* vsliderCache; + + Qt::HANDLE brushHandle; + bool brushHandleSet; + bool kickerMode; + +protected slots: + void paletteChanged(); + + + +}; + + +#endif |