diff options
Diffstat (limited to 'kwin/clients')
103 files changed, 15392 insertions, 0 deletions
diff --git a/kwin/clients/Makefile.am b/kwin/clients/Makefile.am new file mode 100644 index 000000000..6d00b0001 --- /dev/null +++ b/kwin/clients/Makefile.am @@ -0,0 +1,8 @@ +SUBDIRS = plastik b2 default keramik laptop modernsystem quartz redmond web +# need to be ported: kwmtheme (almost done) +# only for testing: test + +messages: rc.cpp + $(EXTRACTRC) `find . -name \*.ui` >> rc.cpp + $(XGETTEXT) `find . -name \*.cpp` -o $(podir)/kwin_clients.pot + -rm rc.cpp diff --git a/kwin/clients/PORTING b/kwin/clients/PORTING new file mode 100644 index 000000000..9a5fb9f6b --- /dev/null +++ b/kwin/clients/PORTING @@ -0,0 +1,159 @@ +It's suggested you check sources of some KDE CVS decoration if in doubts or in need of an example. +Also, the API is documented in the .h header files. + +Makefile.am: +- Change kwin_ to kwin3_ (in LDFLAGS, LIBADD, kde_module_LTLIBRARIES, SOURCES). +- Make sure LDFLAGS contains $(KDE_PLUGIN) and -module . +- Add -lkdecorations to LIBADD. +- Do NOT rename the directory where the .desktop file is installed ( $(kde_datadir)/kwin/ ). + +.desktop file: +- Change kwin_ to kwin3_ in X-KDE-Library. + +Sources: +- There are no kwin/something.h includes, and don't use the KWinInternal namespace. +- Use QToolTip instead of KWinToolTip. +- Use QButton instead of KWinButton, QToolButton instead of KWinToolButton and QWidget + instead of KWinWidgetButton. +- For tooltips, use simply QToolTip::add(). +- Change Client* to MyClient* (or whatever is your main client class) in your MyButton. +- Pass parent->widget() to QButton constructor in your MyButton constructor. +- Make your MyClient class inherit from KDecoration instead of Client. +- Make MyClient constructor take KDecorationBridge* and KDecorationFactory* as arguments, + and pass these arguments to KDecoration constructor. +- Except for data members initialization, make the constructor empty, move everything + to void MyClient::init(). +- As the first thing in init(), call createMainWidget(); if your client class took some + flags such as WResizeNoErase, pass them to this function. +- Then, do 'widget()->installEventFilter( this );'. +- Implement MyClient::eventFilter() - as MyClient is now no longer QWidget, you need the event + filter to call all the functions that used to be called directly. Usually, it's something + like: +===== +bool MyClient::eventFilter( QObject* o, QEvent* e ) +{ + if ( o != widget() ) + return false; + + switch ( e->type() ) + { + case QEvent::Resize: + resizeEvent( static_cast< QResizeEvent* >( e ) ); + return true; + + case QEvent::Paint: + paintEvent( static_cast< QPaintEvent* >( e ) ); + return true; + + case QEvent::MouseButtonDblClick: + mouseDoubleClickEvent( static_cast< QMouseEvent* >( e ) ); + return true; + + case QEvent::Wheel: + wheelEvent( static_cast< QWheelEvent* >( e )); + return true; + + case QEvent::MouseButtonPress: + processMousePressEvent( static_cast< QMouseEvent* >( e ) ); + return true; + + case QEvent::Show: + showEvent( static_cast< QShowEvent* >( e ) ); + return true; + + default: + return false; + } +} +===== +- In MyClient, 'this' will have to be often replaced with 'widget()', pay special attention + to cases where this won't cause compile error (e.g. in connect() calls, which take QObject* ). +- Also, many calls may need 'widget()->' prepended. +- Layout is created in init(), so call createLayout() directly there (if it's implemented). +- Remove calls to Client methods (Client::resizeEvent() and so on). +- Replace Options:: with KDecorationOptions:: . +- Replace 'options' with 'options()' in MyClient (which is KDecoration::options()), if often used + outside of MyClient, you may want to create (this assumes your code is in its namespace): +===== +inline const KDecorationOptions* options() { return KDecoration::options(); } +===== +- Options for colors need 'Color' prepended (e.g. 'ColorButtonBg'). +- Replace miniIcon() with getting the right pixmap from icon() (usually + 'icon().pixmap( QIconSet::Small, QIconSet::Normal )' ). +- Replace stickyChange() with desktopChange(), and test isOnAllDesktops(). +- Replace Sticky with OnAllDestops. +- Replace iconify with minimize. +- Change activeChange(bool) to activeChange(), and use isActive() to check the state. + Similar for desktopChange, captionChange(), iconChange(), maximizeChange(). +- Replace 'contextHelp()' with 'showContextHelp()'. +- WindowWrapperShowEvent() is gone, simply use showEvent() filtered by the event filter if needed. +- Change 'animateIconifyOrDeiconify()' to 'animateMinize()', if it's empty, simply remove it. + Make sure it doesn't reenter the event loop (no kapp->processEvents()). +- Buttons should use explicit setCursor() if they don't want cursor set by mousePosition(). + I.e. usually call setCursor( ArrowCursor ) in your MyButton. +- In the part where you insert windowWrapper() into the layout, i.e. something like +===== + layout->addWidget( windowWrapper()); +===== + replace it with something like +===== + if( isPreview()) + layout->addWidget( new QLabel( i18n( "<center><b>MyDecoration</b></center>" ), widget())); + else + layout->addItem( new QSpacerItem( 0, 0 )); +===== +- Implement MyClient::minimumSize(). +- Handling maximization - to change vertical or horizontal maximalization, use e.g. + 'maximize( maximizeMode() ^ MaximizeVertical', to change normal maximalization, i.e. after + left-clicking on the button, use + 'maximize( maximizeMode() == MaximizeFull ? MaximizeRestore : MaximizeFull );' (which also + means that there's also no maximize() slot). + Also, if your decoration button has only two visual states representing the maximalization state, + it's recommended that it shows the maximized state only for MaximizeFull state. +- Make sure the decoration matches the window state after init() is finished, that is, that + the buttons represent correctly the maximalization, on-all-desktops etc. states. As the + simplest solution, you can call maximizeChange(), desktopChange(), etc. at the end + of init(). +- Use 'titlebarDblClickOperation()' for performing the application after doubleclicking + the titlebar. +- Implement borders() returning the width of the top,left,right and bottom border. You may + check values like 'maximizeMode() == MaximizeFull && !options()->moveResizeMaximizedWindows()' + to check whether you can disable some borders completely. + Note that your painting code must of course match these sizes. +- If your code uses XGrabServer() or XUnGrabServer(), replace them with (un)grabXServer(). +- In cases where you call some function from the KDecoration API that can possibly destroy + the decoration (e.g. showWindowMenu() or closeWindow()), make sure to use exists() if some more + code will follow this call. Refer to showWindowMenu() documentation for an example. +- Create class MyFactory inheriting from KDecorationFactory, and move the code that was + in 'extern "C"' to it: From init() to constructor, from deinit() to destructor, from allocate() + or create() to createDecoration(). Pass the KDecorationBridge* argument and 'this' to created + MyClient objects. If createDecoration() needs to know the window type (e.g. it used the tool + argument), use windowType() similarly like in KDecoration, and pass it the KDecorationBridge* + argument. +- Add something like this: +===== +extern "C" +{ + KDecorationFactory *create_factory() + { + return new MyNamespace::MyFactory(); + } +} +===== +- The reset handling has changed: There's no signal resetClients(), and no + slotResetAllClientsDelayed(). If your MyClient has some slotReset(), make it + reset( unsigned long ), where the argument is mask of things that have changed ( SettingXYZ ). + If you have some global function that handles resetting, make it + MyFactory::reset( unsigned long ). Try to minimize the effects of the changed things, + e.g. if only the color setting has changed, doing a repaint is often enough, and there's no need + to recreate the decorations. If you need to recreate the decorations, return true + from MyFactory::reset(), otherwise, you may call resetDecorations() to call reset() in all + MyClient instances. +- Implement resize() to resize the decoration to the given size + (usually 'widget()->resize( s );' is enough). +- Review mousePosition() if it's implemented. Position constants need 'Position' prepended, + e.g. Top -> PositionTop. +- Note that you cannot use "appdata" with KStandardDirs, as the decoration will be used + also in other applications than kwin. +- Implement all missing pure virtual functions. For mousePosition(), you may call + KDecoration::mousePosition() if it's sufficient. diff --git a/kwin/clients/REQUIREMENTS_FOR_CVS b/kwin/clients/REQUIREMENTS_FOR_CVS new file mode 100644 index 000000000..778e2fe2d --- /dev/null +++ b/kwin/clients/REQUIREMENTS_FOR_CVS @@ -0,0 +1,20 @@ +If you are looking to include a C++ KWin style client in CVS make sure you +follow the following requirements: + +A) You must follow the current color scheme for all decorations. *No* fixed +pixmaps are allowed for the clients. If you wish to draw your decorations +use as few shades as possible, then use kpixmap2bitmap in kdegraphics +to convert them into individual bitmaps. Once this is done you can +draw the bitmaps using a colorgroup with kColorBitmaps. + +If your client is just a set of pixmaps that doesn't follow any of the options +I suggest you make a KWM theme so the user gets those options to +configure the pixmaps and look. Making a plain pixmapped dedicated style +makes no sense since it is less configurable than KWM themes and cannot follow +client plugin options. + +B) You must follow at least the color settings in the Options class. + +Daniel M. Duley +mosfet@kde.org + diff --git a/kwin/clients/b2/Makefile.am b/kwin/clients/b2/Makefile.am new file mode 100644 index 000000000..88a9b1608 --- /dev/null +++ b/kwin/clients/b2/Makefile.am @@ -0,0 +1,23 @@ + +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +SUBDIRS = . config + +kde_module_LTLIBRARIES = kwin3_b2.la + +kwin3_b2_la_SOURCES = b2client.cpp +kwin3_b2_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +# kwin_b2_la_LDFLAGS = $(all_libraries) -avoid-version -module $(KDE_RPATH) $(KDE_MT_LDFLAGS) +kwin3_b2_la_LIBADD = ../../lib/libkdecorations.la + +METASOURCES = AUTO +noinst_HEADERS = b2client.h + +lnkdir = $(kde_datadir)/kwin/ +lnk_DATA = b2.desktop + +EXTRA_DIST = $(lnk_DATA) + +###KMAKE-start (don't edit or delete this block) + +###KMAKE-end diff --git a/kwin/clients/b2/b2.desktop b/kwin/clients/b2/b2.desktop new file mode 100644 index 000000000..c368d7fa7 --- /dev/null +++ b/kwin/clients/b2/b2.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Name=B II +Name[hi]=बी II +Name[lo]= B II +Name[te]=బి II +Name[th]=ชุดตกแต่ง B II +X-KDE-Library=kwin3_b2 diff --git a/kwin/clients/b2/b2client.cpp b/kwin/clients/b2/b2client.cpp new file mode 100644 index 000000000..6a5d821b2 --- /dev/null +++ b/kwin/clients/b2/b2client.cpp @@ -0,0 +1,1433 @@ +/* + * B-II KWin Client + * + * Changes: + * Customizable button positions by Karol Szwed <gallium@kde.org> + * + * Thin frame in fixed size windows, titlebar gradient support, accessibility + * improvements, customizable menu double click action and button hover + * effects are + * Copyright (c) 2003,2004 Luciano Montanaro <mikelima@cirulla.net> + */ + +#include "b2client.h" +#include <qapplication.h> +#include <qlayout.h> +#include <qdrawutil.h> +#include <kpixmapeffect.h> +#include <kimageeffect.h> +#include <kicontheme.h> +#include <kiconeffect.h> +#include <kdrawutil.h> +#include <klocale.h> +#include <kconfig.h> +#include <qbitmap.h> +#include <qlabel.h> +#include <qtooltip.h> + +#include <X11/Xlib.h> + +namespace B2 { + +#include "bitmaps.h" + +enum { + Norm = 0, + Hover, Down, INorm, IHover, IDown, + NumStates +}; + +enum { + P_CLOSE = 0, + P_MAX, P_NORMALIZE, P_ICONIFY, P_PINUP, P_MENU, P_HELP, P_SHADE, P_RESIZE, + P_NUM_BUTTON_TYPES +}; + +#define NUM_PIXMAPS (P_NUM_BUTTON_TYPES * NumStates) + +static KPixmap *pixmap[NUM_PIXMAPS]; + +// active +#define PIXMAP_A(i) (pixmap[(i) * NumStates + Norm]) +// active, hover +#define PIXMAP_AH(i) (pixmap[(i) * NumStates + Hover]) +// active, down +#define PIXMAP_AD(i) (pixmap[(i) * NumStates + Down]) +// inactive +#define PIXMAP_I(i) (pixmap[(i) * NumStates + INorm]) +// inactive, hover +#define PIXMAP_IH(i) (pixmap[(i) * NumStates + IHover]) +// inactive, down +#define PIXMAP_ID(i) (pixmap[(i) * NumStates + IDown]) + +static KPixmap* titleGradient[2] = {0, 0}; + +static int thickness = 4; // Frame thickness +static int buttonSize = 16; + +enum DblClickOperation { + NoOp = 0, + MinimizeOp, + ShadeOp, + CloseOp +}; + +static DblClickOperation menu_dbl_click_op = NoOp; + +static bool pixmaps_created = false; +static bool colored_frame = false; +static bool do_draw_handle = true; +static bool drawSmallBorders = false; + +// ===================================== + +extern "C" KDE_EXPORT KDecorationFactory* create_factory() +{ + return new B2::B2ClientFactory(); +} + +// ===================================== + +static inline const KDecorationOptions *options() +{ + return KDecoration::options(); +} + +static void redraw_pixmaps(); + +static void read_config(B2ClientFactory *f) +{ + // Force button size to be in a reasonable range. + // If the frame width is large, the button size must be large too. + buttonSize = (QFontMetrics(options()->font(true)).height() + 1) & 0x3e; + if (buttonSize < 16) buttonSize = 16; + + KConfig conf("kwinb2rc"); + conf.setGroup("General"); + colored_frame = conf.readBoolEntry("UseTitleBarBorderColors", false); + do_draw_handle = conf.readBoolEntry("DrawGrabHandle", true); + drawSmallBorders = !options()->moveResizeMaximizedWindows(); + + QString opString = conf.readEntry("MenuButtonDoubleClickOperation", "NoOp"); + if (opString == "Close") { + menu_dbl_click_op = B2::CloseOp; + } else if (opString == "Minimize") { + menu_dbl_click_op = B2::MinimizeOp; + } else if (opString == "Shade") { + menu_dbl_click_op = B2::ShadeOp; + } else { + menu_dbl_click_op = B2::NoOp; + } + + switch (options()->preferredBorderSize(f)) { + case KDecoration::BorderTiny: + thickness = 2; + break; + case KDecoration::BorderLarge: + thickness = 5; + break; + case KDecoration::BorderVeryLarge: + thickness = 8; + break; + case KDecoration::BorderHuge: + thickness = 12; + break; + case KDecoration::BorderVeryHuge: + case KDecoration::BorderOversized: + case KDecoration::BorderNormal: + default: + thickness = 4; + } +} + +static void drawB2Rect(KPixmap *pix, const QColor &primary, bool down) +{ + QPainter p(pix); + QColor hColor = primary.light(150); + QColor lColor = primary.dark(150); + + if (down) qSwap(hColor, lColor); + + if (QPixmap::defaultDepth() > 8) { + KPixmapEffect::gradient(*pix, hColor, lColor, + KPixmapEffect::DiagonalGradient); + } + else + pix->fill(primary); + int x2 = pix->width() - 1; + int y2 = pix->height() - 1; + p.setPen(lColor); + p.drawLine(0, 0, x2, 0); + p.drawLine(0, 0, 0, y2); + p.drawLine(1, x2 - 1, x2 - 1, y2 - 1); + p.drawLine(x2 - 1, 1, x2 - 1, y2 - 1); + p.setPen(hColor); + p.drawRect(1, 1, x2, y2); + +} + +QPixmap* kwin_get_menu_pix_hack() +{ + //return menu_pix; FIXME + return PIXMAP_A(P_MENU); +} + +static void create_pixmaps() +{ + if (pixmaps_created) + return; + pixmaps_created = true; + + int i; + int bsize = buttonSize - 2; + if (bsize < 16) bsize = 16; + + for (i = 0; i < NUM_PIXMAPS; i++) { + pixmap[i] = new KPixmap; + switch (i / NumStates) { + case P_MAX: // will be initialized by copying P_CLOSE + case P_RESIZE: + break; + case P_ICONIFY: + pixmap[i]->resize(10, 10); break; + case P_SHADE: + case P_CLOSE: + pixmap[i]->resize(bsize, bsize); break; + default: + pixmap[i]->resize(16, 16); break; + } + } + + // there seems to be no way to load X bitmaps from data properly, so + // we need to create new ones for each mask :P + QBitmap pinupMask(16, 16, pinup_mask_bits, true); + PIXMAP_A(P_PINUP)->setMask(pinupMask); + PIXMAP_I(P_PINUP)->setMask(pinupMask); + QBitmap pindownMask(16, 16, pindown_mask_bits, true); + PIXMAP_AD(P_PINUP)->setMask(pindownMask); + PIXMAP_ID(P_PINUP)->setMask(pindownMask); + + QBitmap menuMask(16, 16, menu_mask_bits, true); + for (i = 0; i < NumStates; i++) + pixmap[P_MENU * NumStates + i]->setMask(menuMask); + + QBitmap helpMask(16, 16, help_mask_bits, true); + for (i = 0; i < NumStates; i++) + pixmap[P_HELP * NumStates + i]->setMask(helpMask); + + QBitmap normalizeMask(16, 16, true); + // draw normalize icon mask + QPainter mask; + mask.begin(&normalizeMask); + + QBrush one(Qt::color1); + mask.fillRect(normalizeMask.width() - 12, normalizeMask.height() - 12, + 12, 12, one); + mask.fillRect(0, 0, 10, 10, one); + mask.end(); + + for (i = 0; i < NumStates; i++) + pixmap[P_NORMALIZE * NumStates + i]->setMask(normalizeMask); + + QBitmap shadeMask(bsize, bsize, true); + mask.begin(&shadeMask); + mask.fillRect(0, 0, bsize, 6, one); + mask.end(); + for (i = 0; i < NumStates; i++) + pixmap[P_SHADE * NumStates + i]->setMask(shadeMask); + + titleGradient[0] = 0; + titleGradient[1] = 0; + + redraw_pixmaps(); +} + +static void delete_pixmaps() +{ + for (int i = 0; i < NUM_PIXMAPS; i++) { + delete pixmap[i]; + pixmap[i] = 0; + } + for (int i = 0; i < 2; i++) { + delete titleGradient[i]; + titleGradient[i] = 0; + } + pixmaps_created = false; +} + +// ===================================== + +B2ClientFactory::B2ClientFactory() +{ + read_config(this); + create_pixmaps(); +} + +B2ClientFactory::~B2ClientFactory() +{ + delete_pixmaps(); +} + +KDecoration *B2ClientFactory::createDecoration(KDecorationBridge *b) +{ + return new B2::B2Client(b, this); +} + +bool B2ClientFactory::reset(unsigned long changed) +{ + bool needsReset = SettingColors ? true : false; + // TODO Do not recreate decorations if it is not needed. Look at + // ModernSystem for how to do that + read_config(this); + if (changed & SettingFont) { + delete_pixmaps(); + create_pixmaps(); + needsReset = true; + } + redraw_pixmaps(); + // For now just return true. + return needsReset; +} + +bool B2ClientFactory::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonMenu: + case AbilityButtonOnAllDesktops: + case AbilityButtonSpacer: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + case AbilityButtonAboveOthers: + case AbilityButtonBelowOthers: + case AbilityButtonShade: + case AbilityButtonResize: + return true; + default: + return false; + }; +} + +QValueList< B2ClientFactory::BorderSize > B2ClientFactory::borderSizes() const +{ + // the list must be sorted + return QValueList< BorderSize >() << BorderTiny << BorderNormal << + BorderLarge << BorderVeryLarge << BorderHuge; +} + +// ===================================== + +void B2Client::maxButtonClicked() +{ + maximize(button[BtnMax]->last_button); +} + +void B2Client::shadeButtonClicked() +{ + setShade(!isSetShade()); +} + +void B2Client::resizeButtonPressed() +{ + performWindowOperation(ResizeOp); +} + +B2Client::B2Client(KDecorationBridge *b, KDecorationFactory *f) + : KDecoration(b, f), bar_x_ofs(0), in_unobs(0) +{ +} + +void B2Client::init() +{ + const QString tips[] = { + i18n("Menu"), + isOnAllDesktops() ? + i18n("Not on all desktops") : i18n("On all desktops"), + i18n("Minimize"), i18n("Maximize"), + i18n("Close"), i18n("Help"), + isSetShade() ? i18n("Unshade") : i18n("Shade"), + i18n("Resize") + }; + + // Check this early, otherwise the preview will be rendered badly. + resizable = isResizable(); + + createMainWidget(WResizeNoErase | WRepaintNoErase); + widget()->installEventFilter(this); + + widget()->setBackgroundMode(NoBackground); + + // Set button pointers to NULL so we know what has been created + for (int i = 0; i < BtnCount; i++) + button[i] = NULL; + + g = new QGridLayout(widget(), 3, 3); + // Left and right border width + + leftSpacer = new QSpacerItem(thickness, 16, + QSizePolicy::Fixed, QSizePolicy::Expanding); + rightSpacer = new QSpacerItem(thickness, 16, + QSizePolicy::Fixed, QSizePolicy::Expanding); + + g->addItem(leftSpacer, 1, 0); + g->addItem(rightSpacer, 1, 2); + + // Top border height + topSpacer = new QSpacerItem(10, buttonSize + 4, + QSizePolicy::Expanding, QSizePolicy::Fixed); + g->addItem(topSpacer, 0, 1); + + // Bottom border height. + bottomSpacer = new QSpacerItem(10, + thickness + (mustDrawHandle() ? 4 : 0), + QSizePolicy::Expanding, QSizePolicy::Fixed); + g->addItem(bottomSpacer, 2, 1); + if (isPreview()) { + QLabel *previewLabel = new QLabel( + i18n("<b><center>B II preview</center></b>"), + widget()); + g->addWidget(previewLabel, 1, 1); + + } else { + g->addItem(new QSpacerItem(0, 0), 1, 1); + } + + // titlebar + g->setRowSpacing(0, buttonSize + 4); + + titlebar = new B2Titlebar(this); + titlebar->setMinimumWidth(buttonSize + 4); + titlebar->setFixedHeight(buttonSize + 4); + + QBoxLayout *titleLayout = new QBoxLayout(titlebar, + QBoxLayout::LeftToRight, 0, 1, 0); + titleLayout->addSpacing(3); + + if (options()->customButtonPositions()) { + addButtons(options()->titleButtonsLeft(), tips, titlebar, titleLayout); + titleLayout->addItem(titlebar->captionSpacer); + addButtons(options()->titleButtonsRight(), tips, titlebar, titleLayout); + } else { + addButtons("MSH", tips, titlebar, titleLayout); + titleLayout->addItem(titlebar->captionSpacer); + addButtons("IAX", tips, titlebar, titleLayout); + } + + titleLayout->addSpacing(3); + + QColor c = options()->colorGroup(KDecoration::ColorTitleBar, isActive()). + color(QColorGroup::Button); + + for (int i = 0; i < BtnCount; i++) { + if (button[i]) + button[i]->setBg(c); + } + + titlebar->updateGeometry(); + positionButtons(); + titlebar->recalcBuffer(); + titlebar->installEventFilter(this); +} + +void B2Client::addButtons(const QString& s, const QString tips[], + B2Titlebar* tb, QBoxLayout* titleLayout) +{ + if (s.length() <= 0) + return; + + for (unsigned int i = 0; i < s.length(); i++) { + switch (s[i].latin1()) { + case 'M': // Menu button + if (!button[BtnMenu]) { + button[BtnMenu] = new B2Button(this, tb, tips[BtnMenu], + LeftButton | RightButton); + button[BtnMenu]->setPixmaps(P_MENU); + button[BtnMenu]->setUseMiniIcon(); + connect(button[BtnMenu], SIGNAL(pressed()), + this, SLOT(menuButtonPressed())); + titleLayout->addWidget(button[BtnMenu]); + } + break; + case 'S': // Sticky button + if (!button[BtnSticky]) { + button[BtnSticky] = new B2Button(this, tb, tips[BtnSticky]); + button[BtnSticky]->setPixmaps(P_PINUP); + button[BtnSticky]->setToggle(); + button[BtnSticky]->setDown(isOnAllDesktops()); + connect(button[BtnSticky], SIGNAL(clicked()), + this, SLOT(toggleOnAllDesktops())); + titleLayout->addWidget(button[BtnSticky]); + } + break; + case 'H': // Help button + if (providesContextHelp() && (!button[BtnHelp])) { + button[BtnHelp] = new B2Button(this, tb, tips[BtnHelp]); + button[BtnHelp]->setPixmaps(P_HELP); + connect(button[BtnHelp], SIGNAL(clicked()), + this, SLOT(showContextHelp())); + titleLayout->addWidget(button[BtnHelp]); + } + break; + case 'I': // Minimize button + if (isMinimizable() && (!button[BtnIconify])) { + button[BtnIconify] = new B2Button(this, tb,tips[BtnIconify]); + button[BtnIconify]->setPixmaps(P_ICONIFY); + connect(button[BtnIconify], SIGNAL(clicked()), + this, SLOT(minimize())); + titleLayout->addWidget(button[BtnIconify]); + } + break; + case 'A': // Maximize button + if (isMaximizable() && (!button[BtnMax])) { + button[BtnMax] = new B2Button(this, tb, tips[BtnMax], + LeftButton | MidButton | RightButton); + button[BtnMax]->setPixmaps(maximizeMode() == MaximizeFull ? + P_NORMALIZE : P_MAX); + connect(button[BtnMax], SIGNAL(clicked()), + this, SLOT(maxButtonClicked())); + titleLayout->addWidget(button[BtnMax]); + } + break; + case 'X': // Close button + if (isCloseable() && !button[BtnClose]) { + button[BtnClose] = new B2Button(this, tb, tips[BtnClose]); + button[BtnClose]->setPixmaps(P_CLOSE); + connect(button[BtnClose], SIGNAL(clicked()), + this, SLOT(closeWindow())); + titleLayout->addWidget(button[BtnClose]); + } + break; + case 'L': // Shade button + if (isShadeable() && !button[BtnShade]) { + button[BtnShade] = new B2Button(this, tb, tips[BtnShade]); + button[BtnShade]->setPixmaps(P_SHADE); + connect(button[BtnShade], SIGNAL(clicked()), + this, SLOT(shadeButtonClicked())); + titleLayout->addWidget(button[BtnShade]); + } + break; + case 'R': // Resize button + if (resizable && !button[BtnResize]) { + button[BtnResize] = new B2Button(this, tb, tips[BtnResize]); + button[BtnResize]->setPixmaps(P_RESIZE); + connect(button[BtnResize], SIGNAL(pressed()), + this, SLOT(resizeButtonPressed())); + titleLayout->addWidget(button[BtnResize]); + } + break; + case '_': // Additional spacing + titleLayout->addSpacing(4); + break; + } + } +} + +bool B2Client::mustDrawHandle() const +{ + if (drawSmallBorders && (maximizeMode() & MaximizeVertical)) { + return false; + } else { + return do_draw_handle && resizable; + } +} + +void B2Client::iconChange() +{ + if (button[BtnMenu]) + button[BtnMenu]->repaint(false); +} + +// Gallium: New button show/hide magic for customizable +// button positions. +void B2Client::calcHiddenButtons() +{ + // Hide buttons in this order: + // Shade, Sticky, Help, Resize, Maximize, Minimize, Close, Menu + B2Button* btnArray[] = { + button[BtnShade], button[BtnSticky], button[BtnHelp], button[BtnResize], + button[BtnMax], button[BtnIconify], button[BtnClose], button[BtnMenu] + }; + int minWidth = 120; + int currentWidth = width(); + int count = 0; + int i; + + // Determine how many buttons we need to hide + while (currentWidth < minWidth) { + currentWidth += buttonSize + 1; // Allow for spacer (extra 1pix) + count++; + } + // Bound the number of buttons to hide + if (count > BtnCount) count = BtnCount; + + // Hide the required buttons + for (i = 0; i < count; i++) { + if (btnArray[i] && btnArray[i]->isVisible()) + btnArray[i]->hide(); + } + // Show the rest of the buttons + for (i = count; i < BtnCount; i++) { + if (btnArray[i] && (!btnArray[i]->isVisible())) + btnArray[i]->show(); + } +} + +void B2Client::resizeEvent(QResizeEvent * /*e*/) +{ + calcHiddenButtons(); + titlebar->layout()->activate(); + positionButtons(); + + /* may be the resize cut off some space occupied by titlebar, which + was moved, so instead of reducing it, we first try to move it */ + titleMoveAbs(bar_x_ofs); + + doShape(); + widget()->repaint(); // the frame is misrendered without this +} + +void B2Client::captionChange() +{ + positionButtons(); + titleMoveAbs(bar_x_ofs); + doShape(); + titlebar->recalcBuffer(); + titlebar->repaint(false); +} + +void B2Client::paintEvent(QPaintEvent* e) +{ + QPainter p(widget()); + + KDecoration::ColorType frameColorGroup = colored_frame ? + KDecoration::ColorTitleBar : KDecoration::ColorFrame; + + QRect t = titlebar->geometry(); + + // Frame height, this is used a lot of times + int fHeight = height() - t.height(); + + // distance from the bottom border - it is different if window is resizable + int bb = mustDrawHandle() ? 4 : 0; + int bDepth = thickness + bb; + + QColorGroup fillColor = options()->colorGroup(frameColorGroup, isActive()); + QBrush fillBrush(options()->color(frameColorGroup, isActive())); + + // outer frame rect + p.drawRect(0, t.bottom() - thickness + 1, + width(), fHeight - bb + thickness); + + if (thickness >= 2) { + // inner window rect + p.drawRect(thickness - 1, t.bottom(), + width() - 2 * (thickness - 1), fHeight - bDepth + 2); + + if (thickness >= 3) { + // frame shade panel + qDrawShadePanel(&p, 1, t.bottom() - thickness + 2, + width() - 2, fHeight - 2 - bb + thickness, fillColor, false); + if (thickness == 4) { + p.setPen(fillColor.background()); + p.drawRect(thickness - 2, t.bottom() - 1, + width() - 2 * (thickness - 2), fHeight + 4 - bDepth); + } else if (thickness > 4) { + qDrawShadePanel(&p, thickness - 2, + t.bottom() - 1, width() - 2 * (thickness - 2), + fHeight + 4 - bDepth, fillColor, true); + if (thickness >= 5) { + // draw frame interior + p.fillRect(2, t.bottom() - thickness + 3, + width() - 4, thickness - 4, fillBrush); + p.fillRect(2, height() - bDepth + 2, + width() - 4, thickness - 4, fillBrush); + p.fillRect(2, t.bottom() - 1, + thickness - 4, fHeight - bDepth + 4, fillBrush); + p.fillRect(width() - thickness + 2, t.bottom() - 1, + thickness - 4, fHeight - bDepth + 4, fillBrush); + } + } + } + } + + // bottom handle rect + if (mustDrawHandle()) { + p.setPen(Qt::black); + int hx = width() - 40; + int hw = 40; + + p.drawLine(width() - 1, height() - thickness - 4, + width() - 1, height() - 1); + p.drawLine(hx, height() - 1, width() - 1, height() - 1); + p.drawLine(hx, height() - 4, hx, height() - 1); + + p.fillRect(hx + 1, height() - thickness - 3, + hw - 2, thickness + 2, fillBrush); + + p.setPen(fillColor.dark()); + p.drawLine(width() - 2, height() - thickness - 4, + width() - 2, height() - 2); + p.drawLine(hx + 1, height() - 2, width() - 2, height() - 2); + + p.setPen(fillColor.light()); + p.drawLine(hx + 1, height() - thickness - 2, + hx + 1, height() - 3); + p.drawLine(hx + 1, height() - thickness - 3, + width() - 3, height() - thickness - 3); + } + + /* OK, we got a paint event, which means parts of us are now visible + which were not before. We try the titlebar if it is currently fully + obscured, and if yes, try to unobscure it, in the hope that some + of the parts which we just painted were in the titlebar area. + It can happen, that the titlebar, as it got the FullyObscured event + had no chance of becoming partly visible. The problem is, that + we now might have the space available, but the titlebar gets no + visibilitinotify events until its state changes, so we just try + */ + if (titlebar->isFullyObscured()) { + /* We first see, if our repaint contained the titlebar area */ + QRegion reg(QRect(0, 0, width(), buttonSize + 4)); + reg = reg.intersect(e->region()); + if (!reg.isEmpty()) + unobscureTitlebar(); + } +} + +void B2Client::doShape() +{ + QRect t = titlebar->geometry(); + QRegion mask(widget()->rect()); + // top to the tilebar right + if (bar_x_ofs) { + // left from bar + mask -= QRect(0, 0, bar_x_ofs, t.height() - thickness); + // top left point + mask -= QRect(0, t.height() - thickness, 1, 1); + } + if (t.right() < width() - 1) { + mask -= QRect(width() - 1, + t.height() - thickness, 1, 1); //top right point + mask -= QRect(t.right() + 1, 0, + width() - t.right() - 1, t.height() - thickness); + } + // bottom right point + mask -= QRect(width() - 1, height() - 1, 1, 1); + if (mustDrawHandle()) { + // bottom left point + mask -= QRect(0, height() - 5, 1, 1); + // handle left point + mask -= QRect(width() - 40, height() - 1, 1, 1); + // bottom left + mask -= QRect(0, height() - 4, width() - 40, 4); + } else { + // bottom left point + mask -= QRect(0, height() - 1, 1, 1); + } + + setMask(mask); +} + +void B2Client::showEvent(QShowEvent *) +{ + calcHiddenButtons(); + positionButtons(); + doShape(); +} + +KDecoration::Position B2Client::mousePosition(const QPoint& p) const +{ + const int range = 16; + QRect t = titlebar->geometry(); + t.setHeight(buttonSize + 4 - thickness); + int ly = t.bottom(); + int lx = t.right(); + int bb = mustDrawHandle() ? 0 : 5; + + if (p.x() > t.right()) { + if (p.y() <= ly + range && p.x() >= width() - range) + return PositionTopRight; + else if (p.y() <= ly + thickness) + return PositionTop; + } else if (p.x() < bar_x_ofs) { + if (p.y() <= ly + range && p.x() <= range) + return PositionTopLeft; + else if (p.y() <= ly + thickness) + return PositionTop; + } else if (p.y() < ly) { + if (p.x() > bar_x_ofs + thickness && + p.x() < lx - thickness && p.y() > thickness) + return KDecoration::mousePosition(p); + if (p.x() > bar_x_ofs + range && p.x() < lx - range) + return PositionTop; + if (p.y() <= range) { + if (p.x() <= bar_x_ofs + range) + return PositionTopLeft; + else return PositionTopRight; + } else { + if (p.x() <= bar_x_ofs + range) + return PositionLeft; + else return PositionRight; + } + } + + if (p.y() >= height() - 8 + bb) { + /* the normal Client:: only wants border of 4 pixels */ + if (p.x() <= range) return PositionBottomLeft; + if (p.x() >= width() - range) return PositionBottomRight; + return PositionBottom; + } + + return KDecoration::mousePosition(p); +} + +void B2Client::titleMoveAbs(int new_ofs) +{ + if (new_ofs < 0) new_ofs = 0; + if (new_ofs + titlebar->width() > width()) { + new_ofs = width() - titlebar->width(); + } + if (bar_x_ofs != new_ofs) { + bar_x_ofs = new_ofs; + positionButtons(); + doShape(); + widget()->repaint(0, 0, width(), buttonSize + 4, false); + titlebar->repaint(false); + } +} + +void B2Client::titleMoveRel(int xdiff) +{ + titleMoveAbs(bar_x_ofs + xdiff); +} + +void B2Client::desktopChange() +{ + bool on = isOnAllDesktops(); + if (B2Button *b = button[BtnSticky]) { + b->setDown(on); + QToolTip::remove(b); + QToolTip::add(b, + on ? i18n("Not on all desktops") : i18n("On all desktops")); + } +} + +void B2Client::maximizeChange() +{ + bool m = maximizeMode() == MaximizeFull; + if (button[BtnMax]) { + button[BtnMax]->setPixmaps(m ? P_NORMALIZE : P_MAX); + button[BtnMax]->repaint(); + QToolTip::remove(button[BtnMax]); + QToolTip::add(button[BtnMax], + m ? i18n("Restore") : i18n("Maximize")); + } + bottomSpacer->changeSize(10, thickness + (mustDrawHandle() ? 4 : 0), + QSizePolicy::Expanding, QSizePolicy::Minimum); + + g->activate(); + doShape(); + widget()->repaint(false); +} + +void B2Client::activeChange() +{ + widget()->repaint(false); + titlebar->repaint(false); + + QColor c = options()->colorGroup( + KDecoration::ColorTitleBar, isActive()).color(QColorGroup::Button); + + for (int i = 0; i < BtnCount; i++) + if (button[i]) { + button[i]->setBg(c); + button[i]->repaint(false); + } +} + +void B2Client::shadeChange() +{ + bottomSpacer->changeSize(10, thickness + (mustDrawHandle() ? 4 : 0), + QSizePolicy::Expanding, QSizePolicy::Minimum); + g->activate(); + doShape(); + if (B2Button *b = button[BtnShade]) { + QToolTip::remove(b); + QToolTip::add(b, isSetShade() ? i18n("Unshade") : i18n("Shade")); + } +} + +QSize B2Client::minimumSize() const +{ + int left, right, top, bottom; + borders(left, right, top, bottom); + return QSize(left + right + 2 * buttonSize, top + bottom); +} + +void B2Client::resize(const QSize& s) +{ + widget()->resize(s); +} + +void B2Client::borders(int &left, int &right, int &top, int &bottom) const +{ + left = right = thickness; + top = buttonSize + 4; + bottom = thickness + (mustDrawHandle() ? 4 : 0); +} + +void B2Client::menuButtonPressed() +{ + static B2Client *lastClient = NULL; + + bool dbl = (lastClient == this && + time.elapsed() <= QApplication::doubleClickInterval()); + lastClient = this; + time.start(); + if (!dbl) { + KDecorationFactory* f = factory(); + QRect menuRect = button[BtnMenu]->rect(); + QPoint menuTop = button[BtnMenu]->mapToGlobal(menuRect.topLeft()); + QPoint menuBottom = button[BtnMenu]->mapToGlobal(menuRect.bottomRight()); + showWindowMenu(QRect(menuTop, menuBottom)); + if (!f->exists(this)) // 'this' was destroyed + return; + button[BtnMenu]->setDown(false); + } else { + switch (menu_dbl_click_op) { + case B2::MinimizeOp: + minimize(); + break; + case B2::ShadeOp: + setShade(!isSetShade()); + break; + case B2::CloseOp: + closeWindow(); + break; + case B2::NoOp: + default: + break; + } + } +} + +void B2Client::unobscureTitlebar() +{ + /* we just noticed, that we got obscured by other windows + so we look at all windows above us (stacking_order) merging their + masks, intersecting it with our titlebar area, and see if we can + find a place not covered by any window */ + if (in_unobs) { + return; + } + in_unobs = 1; + QRegion reg(QRect(0,0,width(), buttonSize + 4)); + reg = unobscuredRegion(reg); + if (!reg.isEmpty()) { + // there is at least _one_ pixel from our title area, which is not + // obscured, we use the first rect we find + // for a first test, we use boundingRect(), later we may refine + // to rect(), and search for the nearest, or biggest, or smthg. + titleMoveAbs(reg.boundingRect().x()); + } + in_unobs = 0; +} + +static void redraw_pixmaps() +{ + int i; + QColorGroup aGrp = options()->colorGroup(KDecoration::ColorButtonBg, true); + QColorGroup iGrp = options()->colorGroup(KDecoration::ColorButtonBg, false); + + // close + drawB2Rect(PIXMAP_A(P_CLOSE), aGrp.button(), false); + drawB2Rect(PIXMAP_AH(P_CLOSE), aGrp.button(), true); + drawB2Rect(PIXMAP_AD(P_CLOSE), aGrp.button(), true); + + drawB2Rect(PIXMAP_I(P_CLOSE), iGrp.button(), false); + drawB2Rect(PIXMAP_IH(P_CLOSE), iGrp.button(), true); + drawB2Rect(PIXMAP_ID(P_CLOSE), iGrp.button(), true); + + // shade + KPixmap thinBox; + thinBox.resize(buttonSize - 2, 6); + for (i = 0; i < NumStates; i++) { + bool is_act = (i < 2); + bool is_down = ((i & 1) == 1); + KPixmap *pix = pixmap[P_SHADE * NumStates + i]; + QColor color = is_act ? aGrp.button() : iGrp.button(); + drawB2Rect(&thinBox, color, is_down); + pix->fill(Qt::black); + bitBlt(pix, 0, 0, &thinBox, + 0, 0, thinBox.width(), thinBox.height(), Qt::CopyROP, true); + } + + // maximize + for (i = 0; i < NumStates; i++) { + *pixmap[P_MAX * NumStates + i] = *pixmap[P_CLOSE * NumStates + i]; + pixmap[P_MAX * NumStates + i]->detach(); + } + + // normalize + iconify + KPixmap smallBox; + smallBox.resize(10, 10); + KPixmap largeBox; + largeBox.resize(12, 12); + + for (i = 0; i < NumStates; i++) { + bool is_act = (i < 3); + bool is_down = (i == Down || i == IDown); + KPixmap *pix = pixmap[P_NORMALIZE * NumStates + i]; + drawB2Rect(&smallBox, is_act ? aGrp.button() : iGrp.button(), is_down); + drawB2Rect(&largeBox, is_act ? aGrp.button() : iGrp.button(), is_down); + pix->fill(options()->color(KDecoration::ColorTitleBar, is_act)); + bitBlt(pix, pix->width() - 12, pix->width() - 12, &largeBox, + 0, 0, 12, 12, Qt::CopyROP, true); + bitBlt(pix, 0, 0, &smallBox, 0, 0, 10, 10, Qt::CopyROP, true); + + bitBlt(pixmap[P_ICONIFY * NumStates + i], 0, 0, + &smallBox, 0, 0, 10, 10, Qt::CopyROP, true); + } + + // resize + for (i = 0; i < NumStates; i++) { + bool is_act = (i < 3); + bool is_down = (i == Down || i == IDown); + *pixmap[P_RESIZE * NumStates + i] = *pixmap[P_CLOSE * NumStates + i]; + pixmap[P_RESIZE * NumStates + i]->detach(); + drawB2Rect(&smallBox, is_act ? aGrp.button() : iGrp.button(), is_down); + bitBlt(pixmap[P_RESIZE * NumStates + i], + 0, 0, &smallBox, 0, 0, 10, 10, Qt::CopyROP, true); + } + + + QPainter p; + // x for close + menu + help + for (int j = 0; j < 3; j++) { + int pix; + unsigned const char *light, *dark; + switch (j) { + case 0: + pix = P_CLOSE; light = close_white_bits; dark = close_dgray_bits; + break; + case 1: + pix = P_MENU; light = menu_white_bits; dark = menu_dgray_bits; + break; + default: + pix = P_HELP; light = help_light_bits; dark = help_dark_bits; + break; + } + int off = (pixmap[pix * NumStates]->width() - 16) / 2; + for (i = 0; i < NumStates; i++) { + p.begin(pixmap[pix * NumStates + i]); + kColorBitmaps(&p, (i < 3) ? aGrp : iGrp, off, off, 16, 16, true, + light, NULL, NULL, dark, NULL, NULL); + p.end(); + } + } + + // pin + for (i = 0; i < NumStates; i++) { + bool isDown = (i == Down || i == IDown); + unsigned const char *white = isDown ? pindown_white_bits : pinup_white_bits; + unsigned const char *gray = isDown ? pindown_gray_bits : pinup_gray_bits; + unsigned const char *dgray =isDown ? pindown_dgray_bits : pinup_dgray_bits; + p.begin(pixmap[P_PINUP * NumStates + i]); + kColorBitmaps(&p, (i < 3) ? aGrp : iGrp, 0, 0, 16, 16, true, white, + gray, NULL, dgray, NULL, NULL); + p.end(); + } + + // Apply the hilight effect to the 'Hover' icons + KIconEffect ie; + QPixmap hilighted; + for (i = 0; i < P_NUM_BUTTON_TYPES; i++) { + int offset = i * NumStates; + hilighted = ie.apply(*pixmap[offset + Norm], + KIcon::Small, KIcon::ActiveState); + *pixmap[offset + Hover] = hilighted; + + hilighted = ie.apply(*pixmap[offset + INorm], + KIcon::Small, KIcon::ActiveState); + *pixmap[offset + IHover] = hilighted; + } + + + // Create the titlebar gradients + if (QPixmap::defaultDepth() > 8) { + QColor titleColor[4] = { + options()->color(KDecoration::ColorTitleBar, true), + options()->color(KDecoration::ColorFrame, true), + + options()->color(KDecoration::ColorTitleBlend, false), + options()->color(KDecoration::ColorTitleBar, false) + }; + + if (colored_frame) { + titleColor[0] = options()->color(KDecoration::ColorTitleBlend, true); + titleColor[1] = options()->color(KDecoration::ColorTitleBar, true); + } + + for (i = 0; i < 2; i++) { + if (titleColor[2 * i] != titleColor[2 * i + 1]) { + if (!titleGradient[i]) { + titleGradient[i] = new KPixmap; + } + titleGradient[i]->resize(64, buttonSize + 3); + KPixmapEffect::gradient(*titleGradient[i], + titleColor[2 * i], titleColor[2 * i + 1], + KPixmapEffect::VerticalGradient); + } else { + delete titleGradient[i]; + titleGradient[i] = 0; + } + } + } +} + +void B2Client::positionButtons() +{ + QFontMetrics fm(options()->font(isActive())); + QString cap = caption(); + if (cap.length() < 5) // make sure the titlebar has sufficiently wide + cap = "XXXXX"; // area for dragging the window + int textLen = fm.width(cap); + + QRect t = titlebar->captionSpacer->geometry(); + int titleWidth = titlebar->width() - t.width() + textLen + 2; + if (titleWidth > width()) titleWidth = width(); + + titlebar->resize(titleWidth, buttonSize + 4); + titlebar->move(bar_x_ofs, 0); +} + +// Transparent bound stuff. + +static QRect *visible_bound; +static QPointArray bound_shape; + +bool B2Client::drawbound(const QRect& geom, bool clear) +{ + if (clear) { + if (!visible_bound) return true; + } + + if (!visible_bound) { + visible_bound = new QRect(geom); + QRect t = titlebar->geometry(); + int frameTop = geom.top() + t.bottom(); + int barLeft = geom.left() + bar_x_ofs; + int barRight = barLeft + t.width() - 1; + if (barRight > geom.right()) barRight = geom.right(); + // line width is 5 pixels, so compensate for the 2 outer pixels (#88657) + QRect g = geom; + g.setLeft( g.left() + 2 ); + g.setTop( g.top() + 2 ); + g.setRight( g.right() - 2 ); + g.setBottom( g.bottom() - 2 ); + frameTop += 2; + barLeft += 2; + barRight -= 2; + + bound_shape.putPoints(0, 8, + g.left(), frameTop, + barLeft, frameTop, + barLeft, g.top(), + barRight, g.top(), + barRight, frameTop, + g.right(), frameTop, + g.right(), g.bottom(), + g.left(), g.bottom()); + } else { + *visible_bound = geom; + } + QPainter p(workspaceWidget()); + p.setPen(QPen(Qt::white, 5)); + p.setRasterOp(Qt::XorROP); + p.drawPolygon(bound_shape); + + if (clear) { + delete visible_bound; + visible_bound = 0; + } + return true; +} + +bool B2Client::eventFilter(QObject *o, QEvent *e) +{ + if (o != widget()) + return false; + switch (e->type()) { + case QEvent::Resize: + resizeEvent(static_cast< QResizeEvent* >(e)); + return true; + case QEvent::Paint: + paintEvent(static_cast< QPaintEvent* >(e)); + return true; + case QEvent::MouseButtonDblClick: + titlebar->mouseDoubleClickEvent(static_cast< QMouseEvent* >(e)); + return true; + case QEvent::Wheel: + titlebar->wheelEvent(static_cast< QWheelEvent* >(e)); + return true; + case QEvent::MouseButtonPress: + processMousePressEvent(static_cast< QMouseEvent* >(e)); + return true; + case QEvent::Show: + showEvent(static_cast< QShowEvent* >(e)); + return true; + default: + break; + } + return false; +} + +// ===================================== + +B2Button::B2Button(B2Client *_client, QWidget *parent, + const QString& tip, const int realizeBtns) + : QButton(parent, 0), hover(false) +{ + setBackgroundMode(NoBackground); + setCursor(arrowCursor); + realizeButtons = realizeBtns; + client = _client; + useMiniIcon = false; + setFixedSize(buttonSize, buttonSize); + QToolTip::add(this, tip); +} + + +QSize B2Button::sizeHint() const +{ + return QSize(buttonSize, buttonSize); +} + +QSizePolicy B2Button::sizePolicy() const +{ + return(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); +} + +void B2Button::drawButton(QPainter *p) +{ + KPixmap* gradient = titleGradient[client->isActive() ? 0 : 1]; + if (gradient) { + p->drawTiledPixmap(0, 0, buttonSize, buttonSize, *gradient, 0, 2); + } else { + p->fillRect(rect(), bg); + } + if (useMiniIcon) { + QPixmap miniIcon = client->icon().pixmap(QIconSet::Small, + client->isActive() ? QIconSet::Normal : QIconSet::Disabled); + p->drawPixmap((width() - miniIcon.width()) / 2, + (height() - miniIcon.height()) / 2, miniIcon); + } else { + int type; + if (client->isActive()) { + if (isOn() || isDown()) + type = Down; + else if (hover) + type = Hover; + else + type = Norm; + } else { + if (isOn() || isDown()) + type = IDown; + else if (hover) + type = IHover; + else + type = INorm; + } + p->drawPixmap((width() - icon[type]->width()) / 2, + (height() - icon[type]->height()) / 2, *icon[type]); + } +} + +void B2Button::setPixmaps(int button_id) +{ + button_id *= NumStates; + for (int i = 0; i < NumStates; i++) { + icon[i] = B2::pixmap[button_id + i]; + } + repaint(false); +} + +void B2Button::mousePressEvent(QMouseEvent * e) +{ + last_button = e->button(); + QMouseEvent me(e->type(), e->pos(), e->globalPos(), + (e->button() & realizeButtons) ? LeftButton : NoButton, + e->state()); + QButton::mousePressEvent(&me); +} + +void B2Button::mouseReleaseEvent(QMouseEvent * e) +{ + last_button = e->button(); + QMouseEvent me(e->type(), e->pos(), e->globalPos(), + (e->button() & realizeButtons) ? LeftButton : NoButton, + e->state()); + QButton::mouseReleaseEvent(&me); +} + +void B2Button::enterEvent(QEvent *e) +{ + hover = true; + repaint(false); + QButton::enterEvent(e); +} + +void B2Button::leaveEvent(QEvent *e) +{ + hover = false; + repaint(false); + QButton::leaveEvent(e); +} + +// ===================================== + +B2Titlebar::B2Titlebar(B2Client *parent) + : QWidget(parent->widget(), 0, WStyle_Customize | WRepaintNoErase), + client(parent), + set_x11mask(false), isfullyobscured(false), shift_move(false) +{ + setBackgroundMode(NoBackground); + captionSpacer = new QSpacerItem(buttonSize, buttonSize + 4, + QSizePolicy::Expanding, QSizePolicy::Fixed); +} + +bool B2Titlebar::x11Event(XEvent *e) +{ + if (!set_x11mask) { + set_x11mask = true; + XSelectInput(qt_xdisplay(), winId(), + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + KeymapStateMask | + ButtonMotionMask | + EnterWindowMask | LeaveWindowMask | + FocusChangeMask | + ExposureMask | + PropertyChangeMask | + StructureNotifyMask | SubstructureRedirectMask | + VisibilityChangeMask); + } + switch (e->type) { + case VisibilityNotify: + isfullyobscured = false; + if (e->xvisibility.state == VisibilityFullyObscured) { + isfullyobscured = true; + client->unobscureTitlebar(); + } + break; + default: + break; + } + return QWidget::x11Event(e); +} + +void B2Titlebar::drawTitlebar(QPainter &p, bool state) +{ + KPixmap* gradient = titleGradient[state ? 0 : 1]; + + QRect t = rect(); + // black titlebar frame + p.setPen(Qt::black); + p.drawLine(0, 0, 0, t.bottom()); + p.drawLine(0, 0, t.right(), 0); + p.drawLine(t.right(), 0, t.right(), t.bottom()); + + // titlebar fill + const QColorGroup cg = + options()->colorGroup(KDecoration::ColorTitleBar, state); + QBrush brush(cg.background()); + if (gradient) brush.setPixmap(*gradient); + qDrawShadeRect(&p, 1, 1, t.right() - 1, t.height() - 1, + cg, false, 1, 0, &brush); + + // and the caption + p.setPen(options()->color(KDecoration::ColorFont, state)); + p.setFont(options()->font(state)); + t = captionSpacer->geometry(); + p.drawText(t, AlignLeft | AlignVCenter, client->caption()); +} + +void B2Titlebar::recalcBuffer() +{ + titleBuffer.resize(width(), height()); + + QPainter p(&titleBuffer); + drawTitlebar(p, true); + oldTitle = caption(); +} + +void B2Titlebar::resizeEvent(QResizeEvent *) +{ + recalcBuffer(); + repaint(false); +} + + +void B2Titlebar::paintEvent(QPaintEvent *) +{ + if(client->isActive()) + bitBlt(this, 0, 0, &titleBuffer, 0, 0, titleBuffer.width(), + titleBuffer.height(), Qt::CopyROP, true); + else { + QPainter p(this); + drawTitlebar(p, false); + } +} + +void B2Titlebar::mouseDoubleClickEvent(QMouseEvent *e) +{ + if (e->button() == LeftButton && e->y() < height()) { + client->titlebarDblClickOperation(); + } +} + +void B2Titlebar::wheelEvent(QWheelEvent *e) +{ + if (client->isSetShade() || rect().contains(e->pos())) + client->titlebarMouseWheelOperation( e->delta()); +} + +void B2Titlebar::mousePressEvent(QMouseEvent * e) +{ + shift_move = e->state() & ShiftButton; + if (shift_move) { + moveOffset = e->globalPos(); + } else { + e->ignore(); + } +} + +void B2Titlebar::mouseReleaseEvent(QMouseEvent * e) +{ + if (shift_move) shift_move = false; + else e->ignore(); +} + +void B2Titlebar::mouseMoveEvent(QMouseEvent * e) +{ + if (shift_move) { + int oldx = mapFromGlobal(moveOffset).x(); + int xdiff = e->globalPos().x() - moveOffset.x(); + moveOffset = e->globalPos(); + if (oldx >= 0 && oldx <= rect().right()) { + client->titleMoveRel(xdiff); + } + } else { + e->ignore(); + } +} + +} // namespace B2 + +#include "b2client.moc" + +// vim: sw=4 + diff --git a/kwin/clients/b2/b2client.h b/kwin/clients/b2/b2client.h new file mode 100644 index 000000000..008b65af3 --- /dev/null +++ b/kwin/clients/b2/b2client.h @@ -0,0 +1,166 @@ +/* + * B-II KWin Client + * + * Changes: + * Customizable button positions by Karol Szwed <gallium@kde.org> + * Ported to the kde3.2 API by Luciano Montanaro <mikelima@cirulla.net> + */ + +#ifndef __B2CLIENT_H +#define __B2CLIENT_H + +#include <qvariant.h> +#include <qdatetime.h> +#include <qbutton.h> +#include <qbitmap.h> +#include <kpixmap.h> +#include <kdecoration.h> +#include <kdecorationfactory.h> + +class QSpacerItem; +class QBoxLayout; +class QGridLayout; + +namespace B2 { + +class B2Client; + +class B2Button : public QButton +{ +public: + B2Button(B2Client *_client=0, QWidget *parent=0, const QString& tip=NULL, const int realizeBtns = LeftButton); + ~B2Button() {}; + + void setBg(const QColor &c){bg = c;} + void setPixmaps(KPixmap *pix, KPixmap *pixDown, KPixmap *iPix, + KPixmap *iPixDown); + void setPixmaps(int button_id); + void setToggle(){setToggleType(Toggle);} + void setActive(bool on){setOn(on);} + void setUseMiniIcon(){useMiniIcon = true;} + QSize sizeHint() const; + QSizePolicy sizePolicy() const; +protected: + virtual void drawButton(QPainter *p); + void drawButtonLabel(QPainter *){;} + + void mousePressEvent( QMouseEvent* e ); + void mouseReleaseEvent( QMouseEvent* e ); +private: + void enterEvent(QEvent *e); + void leaveEvent(QEvent *e); + + bool useMiniIcon; + KPixmap *icon[6]; + QColor bg; //only use one color (the rest is pixmap) so forget QPalette ;) + +public: + B2Client* client; + ButtonState last_button; + int realizeButtons; + bool hover; +}; + +class B2Titlebar : public QWidget +{ + friend class B2Client; +public: + B2Titlebar(B2Client *parent); + ~B2Titlebar(){;} + bool isFullyObscured() const {return isfullyobscured;} + void recalcBuffer(); + QSpacerItem *captionSpacer; +protected: + void paintEvent( QPaintEvent* ); + bool x11Event(XEvent *e); + void mouseDoubleClickEvent( QMouseEvent * ); + void wheelEvent(QWheelEvent *); + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseMoveEvent(QMouseEvent *); + void resizeEvent(QResizeEvent *ev); +private: + void drawTitlebar(QPainter &p, bool state); + + B2Client *client; + QString oldTitle; + KPixmap titleBuffer; + QPoint moveOffset; + bool set_x11mask; + bool isfullyobscured; + bool shift_move; +}; + +class B2Client : public KDecoration +{ + Q_OBJECT + friend class B2Titlebar; +public: + B2Client(KDecorationBridge *b, KDecorationFactory *f); + ~B2Client(){;} + void init(); + void unobscureTitlebar(); + void titleMoveAbs(int new_ofs); + void titleMoveRel(int xdiff); + // transparent stuff + virtual bool drawbound(const QRect& geom, bool clear); +protected: + void resizeEvent( QResizeEvent* ); + void paintEvent( QPaintEvent* ); + void showEvent( QShowEvent* ); + void windowWrapperShowEvent( QShowEvent* ); + void captionChange(); + void desktopChange(); + void shadeChange(); + void activeChange(); + void maximizeChange(); + void iconChange(); + void doShape(); + Position mousePosition( const QPoint& p ) const; + void resize(const QSize&); + void borders(int &, int &, int &, int &) const; + QSize minimumSize() const; + bool eventFilter(QObject *, QEvent *); +private slots: + void menuButtonPressed(); + //void slotReset(); + void maxButtonClicked(); + void shadeButtonClicked(); + void resizeButtonPressed(); +private: + void addButtons(const QString& s, const QString tips[], + B2Titlebar* tb, QBoxLayout* titleLayout); + void positionButtons(); + void calcHiddenButtons(); + bool mustDrawHandle() const; + + enum ButtonType{BtnMenu=0, BtnSticky, BtnIconify, BtnMax, BtnClose, + BtnHelp, BtnShade, BtnResize, BtnCount}; + B2Button* button[BtnCount]; + QGridLayout *g; + // Border spacers + QSpacerItem *topSpacer; + QSpacerItem *bottomSpacer; + QSpacerItem *leftSpacer; + QSpacerItem *rightSpacer; + B2Titlebar *titlebar; + int bar_x_ofs; + int in_unobs; + QTime time; + bool resizable; +}; + +class B2ClientFactory : public QObject, public KDecorationFactory +{ +public: + B2ClientFactory(); + virtual ~B2ClientFactory(); + virtual KDecoration *createDecoration(KDecorationBridge *); + virtual bool reset(unsigned long changed); + virtual bool supports( Ability ability ); + QValueList< B2ClientFactory::BorderSize > borderSizes() const; +}; + +} + +#endif diff --git a/kwin/clients/b2/bitmaps.h b/kwin/clients/b2/bitmaps.h new file mode 100644 index 000000000..2f610f4b2 --- /dev/null +++ b/kwin/clients/b2/bitmaps.h @@ -0,0 +1,98 @@ +#ifndef __STDCLIENT_BITMAPS_H +#define __STDCLIENT_BITMAPS_H + +/** + * The standard client has the capability to color it's titlebar buttons + * according to the new color scheme. In order to do this it needs a bitmap + * for each shade which it draws into a pixmap with the appropriate color. + * These are all the bitmaps. + */ + +static const unsigned char close_white_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x04, 0x10, 0x04, 0x08, 0x08, 0x04, 0x10, 0x02, + 0x20, 0x01, 0x40, 0x00, 0x40, 0x00, 0x20, 0x01, 0x10, 0x02, 0x08, 0x04, + 0x04, 0x08, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char close_dgray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x18, 0x30, 0x30, 0x18, 0x60, 0x0c, + 0xc0, 0x06, 0x80, 0x03, 0x80, 0x03, 0xc0, 0x06, 0x60, 0x0c, 0x30, 0x18, + 0x18, 0x30, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char menu_white_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x3f, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char menu_dgray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0xf8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char menu_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x3f, 0x04, 0x20, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_white_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x1f, 0xa0, 0x03, + 0xb0, 0x01, 0x30, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_gray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, + 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_dgray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x10, 0x70, 0x20, 0x50, 0x20, + 0x48, 0x30, 0xc8, 0x38, 0x08, 0x1f, 0x08, 0x18, 0x10, 0x1c, 0x10, 0x0e, + 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x1f, 0xf0, 0x3f, 0xf0, 0x3f, + 0xf8, 0x3f, 0xf8, 0x3f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf0, 0x1f, 0xf0, 0x0f, + 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_white_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x11, + 0x3f, 0x15, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_gray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x0a, 0xbf, 0x0a, 0x80, 0x15, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_dgray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x20, 0x40, 0x31, 0x40, 0x2e, + 0x40, 0x20, 0x40, 0x20, 0x7f, 0x2a, 0x40, 0x3f, 0xc0, 0x31, 0xc0, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x20, 0xc0, 0x31, 0xc0, 0x3f, + 0xff, 0x3f, 0xff, 0x3f, 0xff, 0x3f, 0xc0, 0x3f, 0xc0, 0x31, 0xc0, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char help_mask_bits[] = { + 0x00,0x00,0x00,0x00,0xe0,0x03,0xf0,0x07,0x70,0x0e,0x60,0x0e,0x00,0x0f,0x80, + 0x07,0xc0,0x03,0xc0,0x01,0x80,0x01,0xc0,0x00,0xc0,0x01,0x80,0x01,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x4c,0x0b,0x08,0x58,0x65,0x09,0x08,0x90,0x00,0x00, + 0x00,0x09,0x04,0x00,0x00,0x72,0x6f,0x6f,0x74,0x00,0x24,0x31,0x24,0x47,0x6b, + 0x65,0x44,0x78,0x63 }; + +static const unsigned char help_dark_bits[] = { + 0x00,0x00,0x00,0x00,0xe0,0x03,0x30,0x06,0x30,0x06,0x00,0x06,0x00,0x03,0x80, + 0x01,0xc0,0x00,0xc0,0x00,0x00,0x00,0xc0,0x00,0xc0,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x65,0x64,0x28,0x29,0x00,0x00,0x00,0x00,0x90,0x00,0x00, + 0x00,0x21,0x00,0x00,0x00,0x34,0xfe,0x12,0x2b,0x00,0x00,0xff,0xff,0x58,0xc0, + 0x01,0x2b,0x45,0xfe }; + +static const unsigned char help_light_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x01,0x40,0x08,0x60,0x08,0x00,0x0c,0x00, + 0x06,0x00,0x03,0x00,0x01,0x80,0x01,0x00,0x00,0x00,0x01,0x80,0x01,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x4c,0x0b,0x08,0x58,0x65,0x09,0x08,0x90,0x00,0x00, + 0x00,0x09,0x04,0x00,0x00,0x72,0x6f,0x6f,0x74,0x00,0x24,0x31,0x24,0x47,0x6b, + 0x65,0x44,0x78,0x63 }; + +#endif + diff --git a/kwin/clients/b2/config/Makefile.am b/kwin/clients/b2/config/Makefile.am new file mode 100644 index 000000000..9ad21f7d4 --- /dev/null +++ b/kwin/clients/b2/config/Makefile.am @@ -0,0 +1,16 @@ +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = kwin_b2_config.la + +kwin_b2_config_la_SOURCES = config.cpp +kwin_b2_config_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin_b2_config_la_LIBADD = $(LIB_KDEUI) + +METASOURCES = AUTO +noinst_HEADERS = config.h + +lnkdir = $(kde_datadir)/kwin/ + +###KMAKE-start (don't edit or delete this block) + +###KMAKE-end diff --git a/kwin/clients/b2/config/config.cpp b/kwin/clients/b2/config/config.cpp new file mode 100644 index 000000000..d16a90307 --- /dev/null +++ b/kwin/clients/b2/config/config.cpp @@ -0,0 +1,165 @@ +/* + * This file contains the B2 configuration widget + * + * Copyright (c) 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + */ + +#include "config.h" +#include <kglobal.h> +#include <qwhatsthis.h> +#include <qvbox.h> +#include <klocale.h> + + +extern "C" +{ + KDE_EXPORT QObject* allocate_config( KConfig* conf, QWidget* parent ) + { + return(new B2Config(conf, parent)); + } +} + + +/* NOTE: + * 'conf' is a pointer to the kwindecoration modules open kwin config, + * and is by default set to the "Style" group. + * + * 'parent' is the parent of the QObject, which is a VBox inside the + * Configure tab in kwindecoration + */ + +B2Config::B2Config( KConfig* conf, QWidget* parent ) + : QObject( parent ) +{ + KGlobal::locale()->insertCatalogue("kwin_b2_config"); + b2Config = new KConfig("kwinb2rc"); + gb = new QVBox(parent); + + cbColorBorder = new QCheckBox( + i18n("Draw window frames using &titlebar colors"), gb); + QWhatsThis::add(cbColorBorder, + i18n("When selected, the window borders " + "are drawn using the titlebar colors; otherwise, they are " + "drawn using normal border colors.")); + + // Grab Handle + showGrabHandleCb = new QCheckBox( + i18n("Draw &resize handle"), gb); + QWhatsThis::add(showGrabHandleCb, + i18n("When selected, decorations are drawn with a \"grab handle\" " + "in the bottom right corner of the windows; " + "otherwise, no grab handle is drawn.")); + + // Double click menu option support + actionsGB = new QHGroupBox(i18n("Actions Settings"), gb); + QLabel *menuDblClickLabel = new QLabel(actionsGB); + menuDblClickLabel->setText(i18n("Double click on menu button:")); + menuDblClickOp = new QComboBox(actionsGB); + menuDblClickOp->insertItem(i18n("Do Nothing")); + menuDblClickOp->insertItem(i18n("Minimize Window")); + menuDblClickOp->insertItem(i18n("Shade Window")); + menuDblClickOp->insertItem(i18n("Close Window")); + + QWhatsThis::add(menuDblClickOp, + i18n("An action can be associated to a double click " + "of the menu button. Leave it to none if in doubt.")); + + // Load configuration options + load(conf); + + // Ensure we track user changes properly + connect(cbColorBorder, SIGNAL(clicked()), + this, SLOT(slotSelectionChanged())); + connect(showGrabHandleCb, SIGNAL(clicked()), + this, SLOT(slotSelectionChanged())); + connect(menuDblClickOp, SIGNAL(activated(int)), + this, SLOT(slotSelectionChanged())); + // Make the widgets visible in kwindecoration + gb->show(); +} + + +B2Config::~B2Config() +{ + delete b2Config; + delete gb; +} + + +void B2Config::slotSelectionChanged() +{ + emit changed(); +} + + +// Loads the configurable options from the kwinrc config file +// It is passed the open config from kwindecoration to improve efficiency +void B2Config::load(KConfig * /*conf*/) +{ + b2Config->setGroup("General"); + + bool override = b2Config->readBoolEntry("UseTitleBarBorderColors", false); + cbColorBorder->setChecked(override); + + override = b2Config->readBoolEntry( "DrawGrabHandle", true ); + showGrabHandleCb->setChecked(override); + + QString returnString = b2Config->readEntry( + "MenuButtonDoubleClickOperation", "NoOp"); + + int op; + if (returnString == "Close") { + op = 3; + } else if (returnString == "Shade") { + op = 2; + } else if (returnString == "Minimize") { + op = 1; + } else { + op = 0; + } + + menuDblClickOp->setCurrentItem(op); + +} + +static QString opToString(int op) +{ + switch (op) { + case 1: + return "Minimize"; + case 2: + return "Shade"; + case 3: + return "Close"; + case 0: + default: + return "NoOp"; + } +} + + +// Saves the configurable options to the kwinrc config file +void B2Config::save(KConfig * /*conf*/) +{ + b2Config->setGroup("General"); + b2Config->writeEntry("UseTitleBarBorderColors", cbColorBorder->isChecked()); + b2Config->writeEntry("DrawGrabHandle", showGrabHandleCb->isChecked()); + b2Config->writeEntry("MenuButtonDoubleClickOperation", + opToString(menuDblClickOp->currentItem())); + // Ensure others trying to read this config get updated + b2Config->sync(); +} + + +// Sets UI widget defaults which must correspond to style defaults +void B2Config::defaults() +{ + cbColorBorder->setChecked(false); + showGrabHandleCb->setChecked(true); + menuDblClickOp->setCurrentItem(0); +} + +#include "config.moc" +// vim: ts=4 diff --git a/kwin/clients/b2/config/config.h b/kwin/clients/b2/config/config.h new file mode 100644 index 000000000..9985f3f4f --- /dev/null +++ b/kwin/clients/b2/config/config.h @@ -0,0 +1,50 @@ +/* + * This file contains the B2 configuration widget + * + * Copyright (c) 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + */ + +#ifndef _KDE_B2CONFIG_H +#define _KDE_B2CONFIG_H + +#include <qcheckbox.h> +#include <qgroupbox.h> +#include <qhgroupbox.h> +#include <qlabel.h> +#include <qcombobox.h> +#include <kconfig.h> + +class B2Config: public QObject +{ + Q_OBJECT + + public: + B2Config( KConfig* conf, QWidget* parent ); + ~B2Config(); + + // These public signals/slots work similar to KCM modules + signals: + void changed(); + + public slots: + void load( KConfig* conf ); + void save( KConfig* conf ); + void defaults(); + + protected slots: + void slotSelectionChanged(); // Internal use + + private: + KConfig* b2Config; + QCheckBox* cbColorBorder; + QCheckBox* showGrabHandleCb; + QHGroupBox* actionsGB; + QComboBox* menuDblClickOp; + QWidget* gb; +}; + +#endif + +// vim: ts=4 diff --git a/kwin/clients/default/Makefile.am b/kwin/clients/default/Makefile.am new file mode 100644 index 000000000..3f9b1e7e7 --- /dev/null +++ b/kwin/clients/default/Makefile.am @@ -0,0 +1,14 @@ + +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +SUBDIRS = . config + +kde_module_LTLIBRARIES = kwin3_default.la + +kwin3_default_la_SOURCES = kdedefault.cpp +kwin3_default_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_default_la_LIBADD = $(LIB_KDECORE) ../../lib/libkdecorations.la + +METASOURCES = AUTO +noinst_HEADERS = kdedefault.h + diff --git a/kwin/clients/default/config/Makefile.am b/kwin/clients/default/config/Makefile.am new file mode 100644 index 000000000..a6e09522a --- /dev/null +++ b/kwin/clients/default/config/Makefile.am @@ -0,0 +1,16 @@ +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = kwin_default_config.la + +kwin_default_config_la_SOURCES = config.cpp +kwin_default_config_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin_default_config_la_LIBADD = $(LIB_KDEUI) + +METASOURCES = AUTO +noinst_HEADERS = config.h + +lnkdir = $(kde_datadir)/kwin/ + +###KMAKE-start (don't edit or delete this block) + +###KMAKE-end diff --git a/kwin/clients/default/config/config.cpp b/kwin/clients/default/config/config.cpp new file mode 100644 index 000000000..2ad494fa9 --- /dev/null +++ b/kwin/clients/default/config/config.cpp @@ -0,0 +1,131 @@ +/* + * + * KDE2 Default configuration widget + * + * Copyright (c) 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + */ + +#include "config.h" +#include <kglobal.h> +#include <qwhatsthis.h> +#include <kdialog.h> +#include <klocale.h> +#include <qpixmap.h> +#include <qvbox.h> + +extern "C" +{ + KDE_EXPORT QObject* allocate_config( KConfig* conf, QWidget* parent ) + { + return(new KDEDefaultConfig(conf, parent)); + } +} + +// NOTE: +// 'conf' is a pointer to the kwindecoration modules open kwin config, +// and is by default set to the "Style" group. +// 'parent' is the parent of the QObject, which is a VBox inside the +// Configure tab in kwindecoration + +KDEDefaultConfig::KDEDefaultConfig( KConfig* conf, QWidget* parent ) + : QObject( parent ) +{ + KGlobal::locale()->insertCatalogue("kwin_clients"); + highcolor = QPixmap::defaultDepth() > 8; + gb = new QVBox( parent ); + gb->setSpacing( KDialog::spacingHint() ); + + cbShowStipple = new QCheckBox( i18n("Draw titlebar &stipple effect"), gb ); + QWhatsThis::add( cbShowStipple, + i18n("When selected, active titlebars are drawn " + "with a stipple (dotted) effect; otherwise, they are " + "drawn without the stipple.")); + + cbShowGrabBar = new QCheckBox( i18n("Draw g&rab bar below windows"), gb ); + QWhatsThis::add( cbShowGrabBar, + i18n("When selected, decorations are drawn with a \"grab bar\" " + "below windows; otherwise, no grab bar is drawn.")); + + // Only show the gradient checkbox for highcolor displays + if (highcolor) + { + cbUseGradients = new QCheckBox( i18n("Draw &gradients"), gb ); + QWhatsThis::add( cbUseGradients, + i18n("When selected, decorations are drawn with gradients " + "for high-color displays; otherwise, no gradients are drawn.") ); + } + + // Load configuration options + load( conf ); + + // Ensure we track user changes properly + connect( cbShowStipple, SIGNAL(clicked()), + this, SLOT(slotSelectionChanged()) ); + connect( cbShowGrabBar, SIGNAL(clicked()), + this, SLOT(slotSelectionChanged()) ); + if (highcolor) + connect( cbUseGradients, SIGNAL(clicked()), + this, SLOT(slotSelectionChanged()) ); + + // Make the widgets visible in kwindecoration + gb->show(); +} + + +KDEDefaultConfig::~KDEDefaultConfig() +{ + delete gb; +} + + +void KDEDefaultConfig::slotSelectionChanged() +{ + emit changed(); +} + + +// Loads the configurable options from the kwinrc config file +// It is passed the open config from kwindecoration to improve efficiency +void KDEDefaultConfig::load( KConfig* conf ) +{ + conf->setGroup("KDEDefault"); + bool override = conf->readBoolEntry( "ShowTitleBarStipple", true ); + cbShowStipple->setChecked( override ); + + override = conf->readBoolEntry( "ShowGrabBar", true ); + cbShowGrabBar->setChecked( override ); + + if (highcolor) { + override = conf->readBoolEntry( "UseGradients", true ); + cbUseGradients->setChecked( override ); + } +} + + +// Saves the configurable options to the kwinrc config file +void KDEDefaultConfig::save( KConfig* conf ) +{ + conf->setGroup("KDEDefault"); + conf->writeEntry( "ShowTitleBarStipple", cbShowStipple->isChecked() ); + conf->writeEntry( "ShowGrabBar", cbShowGrabBar->isChecked() ); + + if (highcolor) + conf->writeEntry( "UseGradients", cbUseGradients->isChecked() ); + // No need to conf->sync() - kwindecoration will do it for us +} + + +// Sets UI widget defaults which must correspond to style defaults +void KDEDefaultConfig::defaults() +{ + cbShowStipple->setChecked( true ); + cbShowGrabBar->setChecked( true ); + + if (highcolor) + cbUseGradients->setChecked( true ); +} + +#include "config.moc" +// vim: ts=4 diff --git a/kwin/clients/default/config/config.h b/kwin/clients/default/config/config.h new file mode 100644 index 000000000..248d851df --- /dev/null +++ b/kwin/clients/default/config/config.h @@ -0,0 +1,49 @@ +/* + * + * KDE2 Default configuration widget + * + * Copyright (c) 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + */ + +#ifndef _KDE_DEFAULT_CONFIG_H +#define _KDE_DEFAULT_CONFIG_H + +#include <qcheckbox.h> +#include <qgroupbox.h> +#include <kconfig.h> +#include <qhbox.h> +#include <qlabel.h> +#include <qvbox.h> + +class KDEDefaultConfig: public QObject +{ + Q_OBJECT + + public: + KDEDefaultConfig( KConfig* conf, QWidget* parent ); + ~KDEDefaultConfig(); + + // These public signals/slots work similar to KCM modules + signals: + void changed(); + + public slots: + void load( KConfig* conf ); + void save( KConfig* conf ); + void defaults(); + + protected slots: + void slotSelectionChanged(); // Internal use + + private: + QCheckBox* cbShowStipple; + QCheckBox* cbShowGrabBar; + QCheckBox* cbUseGradients; + QVBox* gb; + bool highcolor; +}; + +#endif +// vim: ts=4 diff --git a/kwin/clients/default/kdedefault.cpp b/kwin/clients/default/kdedefault.cpp new file mode 100644 index 000000000..aeb1b2065 --- /dev/null +++ b/kwin/clients/default/kdedefault.cpp @@ -0,0 +1,1069 @@ +/* + * + * KDE2 Default KWin client + * + * Copyright (C) 1999, 2001 Daniel Duley <mosfet@kde.org> + * Matthias Ettrich <ettrich@kde.org> + * Karol Szwed <gallium@kde.org> + * + * Draws mini titlebars for tool windows. + * Many features are now customizable. + */ + +#include "kdedefault.h" + +#include <kconfig.h> +#include <kglobal.h> +#include <kpixmapeffect.h> +#include <kimageeffect.h> +#include <kdrawutil.h> +#include <klocale.h> +#include <qlayout.h> +#include <qdrawutil.h> +#include <qbitmap.h> +#include <qimage.h> +#include <qtooltip.h> +#include <qapplication.h> +#include <qlabel.h> +#include <kdebug.h> + +namespace Default +{ + +static const unsigned char iconify_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char close_bits[] = { + 0x00, 0x00, 0x84, 0x00, 0xce, 0x01, 0xfc, 0x00, 0x78, 0x00, 0x78, 0x00, + 0xfc, 0x00, 0xce, 0x01, 0x84, 0x00, 0x00, 0x00}; + +static const unsigned char maximize_bits[] = { + 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0x86, 0x01, 0x86, 0x01, 0x86, 0x01, + 0x86, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0x00, 0x00}; + +static const unsigned char minmax_bits[] = { + 0x7f, 0x00, 0x7f, 0x00, 0x63, 0x00, 0xfb, 0x03, 0xfb, 0x03, 0x1f, 0x03, + 0x1f, 0x03, 0x18, 0x03, 0xf8, 0x03, 0xf8, 0x03}; + +static const unsigned char question_bits[] = { + 0x00, 0x00, 0x78, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00}; + +static const unsigned char above_on_bits[] = { + 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0x30, 0x00, 0xfc, 0x00, 0x78, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char above_off_bits[] = { + 0x30, 0x00, 0x78, 0x00, 0xfc, 0x00, 0x30, 0x00, 0xfe, 0x01, 0xfe, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char below_on_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x78, 0x00, 0xfc, 0x00, + 0x30, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0x00, 0x00 }; + +static const unsigned char below_off_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, + 0x30, 0x00, 0xfc, 0x00, 0x78, 0x00, 0x30, 0x00 }; + +static const unsigned char shade_on_bits[] = { + 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0xfe, 0x01, 0x00, 0x00 }; + +static const unsigned char shade_off_bits[] = { + 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char pindown_white_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x1f, 0xa0, 0x03, + 0xb0, 0x01, 0x30, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_gray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, + 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_dgray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x10, 0x70, 0x20, 0x50, 0x20, + 0x48, 0x30, 0xc8, 0x38, 0x08, 0x1f, 0x08, 0x18, 0x10, 0x1c, 0x10, 0x0e, + 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x1f, 0xf0, 0x3f, 0xf0, 0x3f, + 0xf8, 0x3f, 0xf8, 0x3f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf0, 0x1f, 0xf0, 0x0f, + 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_white_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x11, + 0x3f, 0x15, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_gray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x0a, 0xbf, 0x0a, 0x80, 0x15, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_dgray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x20, 0x40, 0x31, 0x40, 0x2e, + 0x40, 0x20, 0x40, 0x20, 0x7f, 0x2a, 0x40, 0x3f, 0xc0, 0x31, 0xc0, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x20, 0xc0, 0x31, 0xc0, 0x3f, + 0xff, 0x3f, 0xff, 0x3f, 0xff, 0x3f, 0xc0, 0x3f, 0xc0, 0x31, 0xc0, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +// =========================================================================== + +static QPixmap* titlePix; +static KPixmap* titleBuffer; +static KPixmap* aUpperGradient; +static KPixmap* iUpperGradient; + +static KPixmap* pinDownPix; +static KPixmap* pinUpPix; +static KPixmap* ipinDownPix; +static KPixmap* ipinUpPix; + +static KPixmap* rightBtnUpPix[2]; +static KPixmap* rightBtnDownPix[2]; +static KPixmap* irightBtnUpPix[2]; +static KPixmap* irightBtnDownPix[2]; + +static KPixmap* leftBtnUpPix[2]; +static KPixmap* leftBtnDownPix[2]; +static KPixmap* ileftBtnUpPix[2]; +static KPixmap* ileftBtnDownPix[2]; + +static KDEDefaultHandler* clientHandler; +static int toolTitleHeight; +static int normalTitleHeight; +static int borderWidth; +static int grabBorderWidth; +static bool KDEDefault_initialized = false; +static bool useGradients; +static bool showGrabBar; +static bool showTitleBarStipple; + + +// =========================================================================== + +KDEDefaultHandler::KDEDefaultHandler() +{ + clientHandler = this; + readConfig( false ); + createPixmaps(); + KDEDefault_initialized = true; +} + + +KDEDefaultHandler::~KDEDefaultHandler() +{ + KDEDefault_initialized = false; + freePixmaps(); + clientHandler = NULL; +} + +KDecoration* KDEDefaultHandler::createDecoration( KDecorationBridge* b ) +{ + return new KDEDefaultClient( b, this ); +} + +bool KDEDefaultHandler::reset( unsigned long changed ) +{ + KDEDefault_initialized = false; + changed |= readConfig( true ); + if( changed & SettingColors ) + { // pixmaps need to be recreated + freePixmaps(); + createPixmaps(); + } + KDEDefault_initialized = true; + // SettingButtons is handled by KCommonDecoration + bool need_recreate = ( changed & ( SettingDecoration | SettingFont | SettingBorder )) != 0; + if( need_recreate ) // something else than colors changed + return true; + resetDecorations( changed ); + return false; +} + + +unsigned long KDEDefaultHandler::readConfig( bool update ) +{ + unsigned long changed = 0; + KConfig* conf = KGlobal::config(); + conf->setGroup("KDEDefault"); + + bool new_showGrabBar = conf->readBoolEntry("ShowGrabBar", true); + bool new_showTitleBarStipple = conf->readBoolEntry("ShowTitleBarStipple", true); + bool new_useGradients = conf->readBoolEntry("UseGradients", true); + int new_titleHeight = QFontMetrics(options()->font(true)).height(); + int new_toolTitleHeight = QFontMetrics(options()->font(true, true)).height()-2; + + int new_borderWidth; + switch(options()->preferredBorderSize(this)) { + case BorderLarge: + new_borderWidth = 8; + break; + case BorderVeryLarge: + new_borderWidth = 12; + break; + case BorderHuge: + new_borderWidth = 18; + break; + case BorderVeryHuge: + new_borderWidth = 27; + break; + case BorderOversized: + new_borderWidth = 40; + break; + case BorderTiny: + case BorderNormal: + default: + new_borderWidth = 4; + } + + if (new_titleHeight < 16) new_titleHeight = 16; + if (new_titleHeight < new_borderWidth) new_titleHeight = new_borderWidth; + if (new_toolTitleHeight < 12) new_toolTitleHeight = 12; + if (new_toolTitleHeight < new_borderWidth) new_toolTitleHeight = new_borderWidth; + + if( update ) + { + if( new_showGrabBar != showGrabBar + || new_titleHeight != normalTitleHeight + || new_toolTitleHeight != toolTitleHeight + || new_borderWidth != borderWidth ) + changed |= SettingDecoration; // need recreating the decoration + if( new_showTitleBarStipple != showTitleBarStipple + || new_useGradients != useGradients + || new_titleHeight != normalTitleHeight + || new_toolTitleHeight != toolTitleHeight ) + changed |= SettingColors; // just recreate the pixmaps and repaint + } + + showGrabBar = new_showGrabBar; + showTitleBarStipple = new_showTitleBarStipple; + useGradients = new_useGradients; + normalTitleHeight = new_titleHeight; + toolTitleHeight = new_toolTitleHeight; + borderWidth = new_borderWidth; + grabBorderWidth = (borderWidth > 15) ? borderWidth + 15 : 2*borderWidth; + return changed; +} + + +// This paints the button pixmaps upon loading the style. +void KDEDefaultHandler::createPixmaps() +{ + bool highcolor = useGradients && (QPixmap::defaultDepth() > 8); + + // Make the titlebar stipple optional + if (showTitleBarStipple) + { + QPainter p; + QPainter maskPainter; + int i, x, y; + titlePix = new QPixmap(132, normalTitleHeight+2); + QBitmap mask(132, normalTitleHeight+2); + mask.fill(Qt::color0); + + p.begin(titlePix); + maskPainter.begin(&mask); + maskPainter.setPen(Qt::color1); + for(i=0, y=2; i < 9; ++i, y+=4) + for(x=1; x <= 132; x+=3) + { + p.setPen(options()->color(ColorTitleBar, true).light(150)); + p.drawPoint(x, y); + maskPainter.drawPoint(x, y); + p.setPen(options()->color(ColorTitleBar, true).dark(150)); + p.drawPoint(x+1, y+1); + maskPainter.drawPoint(x+1, y+1); + } + maskPainter.end(); + p.end(); + titlePix->setMask(mask); + } else + titlePix = NULL; + + QColor activeTitleColor1(options()->color(ColorTitleBar, true)); + QColor activeTitleColor2(options()->color(ColorTitleBlend, true)); + + QColor inactiveTitleColor1(options()->color(ColorTitleBar, false)); + QColor inactiveTitleColor2(options()->color(ColorTitleBlend, false)); + + // Create titlebar gradient images if required + aUpperGradient = NULL; + iUpperGradient = NULL; + + if(highcolor) + { + // Create the titlebar gradients + if (activeTitleColor1 != activeTitleColor2) + { + aUpperGradient = new KPixmap; + aUpperGradient->resize(128, normalTitleHeight+2); + KPixmapEffect::gradient(*aUpperGradient, + activeTitleColor1, + activeTitleColor2, + KPixmapEffect::VerticalGradient); + } + + if (inactiveTitleColor1 != inactiveTitleColor2) + { + iUpperGradient = new KPixmap; + iUpperGradient->resize(128, normalTitleHeight+2); + + KPixmapEffect::gradient(*iUpperGradient, + inactiveTitleColor1, + inactiveTitleColor2, + KPixmapEffect::VerticalGradient); + } + } + + // Set the sticky pin pixmaps; + QColorGroup g; + QPainter p; + + // Active pins + g = options()->colorGroup( ColorButtonBg, true ); + pinUpPix = new KPixmap(); + pinUpPix->resize(16, 16); + p.begin( pinUpPix ); + kColorBitmaps( &p, g, 0, 0, 16, 16, true, pinup_white_bits, + pinup_gray_bits, NULL, NULL, pinup_dgray_bits, NULL ); + p.end(); + pinUpPix->setMask( QBitmap(16, 16, pinup_mask_bits, true) ); + + pinDownPix = new KPixmap(); + pinDownPix->resize(16, 16); + p.begin( pinDownPix ); + kColorBitmaps( &p, g, 0, 0, 16, 16, true, pindown_white_bits, + pindown_gray_bits, NULL, NULL, pindown_dgray_bits, NULL ); + p.end(); + pinDownPix->setMask( QBitmap(16, 16, pindown_mask_bits, true) ); + + // Inactive pins + g = options()->colorGroup( ColorButtonBg, false ); + ipinUpPix = new KPixmap(); + ipinUpPix->resize(16, 16); + p.begin( ipinUpPix ); + kColorBitmaps( &p, g, 0, 0, 16, 16, true, pinup_white_bits, + pinup_gray_bits, NULL, NULL, pinup_dgray_bits, NULL ); + p.end(); + ipinUpPix->setMask( QBitmap(16, 16, pinup_mask_bits, true) ); + + ipinDownPix = new KPixmap(); + ipinDownPix->resize(16, 16); + p.begin( ipinDownPix ); + kColorBitmaps( &p, g, 0, 0, 16, 16, true, pindown_white_bits, + pindown_gray_bits, NULL, NULL, pindown_dgray_bits, NULL ); + p.end(); + ipinDownPix->setMask( QBitmap(16, 16, pindown_mask_bits, true) ); + + // Create a title buffer for flicker-free painting + titleBuffer = new KPixmap(); + + // Cache all possible button states + leftBtnUpPix[true] = new KPixmap(); + leftBtnUpPix[true]->resize(normalTitleHeight, normalTitleHeight); + leftBtnDownPix[true] = new KPixmap(); + leftBtnDownPix[true]->resize(normalTitleHeight, normalTitleHeight); + ileftBtnUpPix[true] = new KPixmap(); + ileftBtnUpPix[true]->resize(normalTitleHeight, normalTitleHeight); + ileftBtnDownPix[true] = new KPixmap(); + ileftBtnDownPix[true]->resize(normalTitleHeight, normalTitleHeight); + + rightBtnUpPix[true] = new KPixmap(); + rightBtnUpPix[true]->resize(normalTitleHeight, normalTitleHeight); + rightBtnDownPix[true] = new KPixmap(); + rightBtnDownPix[true]->resize(normalTitleHeight, normalTitleHeight); + irightBtnUpPix[true] = new KPixmap(); + irightBtnUpPix[true]->resize(normalTitleHeight, normalTitleHeight); + irightBtnDownPix[true] = new KPixmap(); + irightBtnDownPix[true]->resize(normalTitleHeight, normalTitleHeight); + + leftBtnUpPix[false] = new KPixmap(); + leftBtnUpPix[false]->resize(toolTitleHeight, normalTitleHeight); + leftBtnDownPix[false] = new KPixmap(); + leftBtnDownPix[false]->resize(toolTitleHeight, normalTitleHeight); + ileftBtnUpPix[false] = new KPixmap(); + ileftBtnUpPix[false]->resize(normalTitleHeight, normalTitleHeight); + ileftBtnDownPix[false] = new KPixmap(); + ileftBtnDownPix[false]->resize(normalTitleHeight, normalTitleHeight); + + rightBtnUpPix[false] = new KPixmap(); + rightBtnUpPix[false]->resize(toolTitleHeight, toolTitleHeight); + rightBtnDownPix[false] = new KPixmap(); + rightBtnDownPix[false]->resize(toolTitleHeight, toolTitleHeight); + irightBtnUpPix[false] = new KPixmap(); + irightBtnUpPix[false]->resize(toolTitleHeight, toolTitleHeight); + irightBtnDownPix[false] = new KPixmap(); + irightBtnDownPix[false]->resize(toolTitleHeight, toolTitleHeight); + + // Draw the button state pixmaps + g = options()->colorGroup( ColorTitleBar, true ); + drawButtonBackground( leftBtnUpPix[true], g, false ); + drawButtonBackground( leftBtnDownPix[true], g, true ); + drawButtonBackground( leftBtnUpPix[false], g, false ); + drawButtonBackground( leftBtnDownPix[false], g, true ); + + g = options()->colorGroup( ColorButtonBg, true ); + drawButtonBackground( rightBtnUpPix[true], g, false ); + drawButtonBackground( rightBtnDownPix[true], g, true ); + drawButtonBackground( rightBtnUpPix[false], g, false ); + drawButtonBackground( rightBtnDownPix[false], g, true ); + + g = options()->colorGroup( ColorTitleBar, false ); + drawButtonBackground( ileftBtnUpPix[true], g, false ); + drawButtonBackground( ileftBtnDownPix[true], g, true ); + drawButtonBackground( ileftBtnUpPix[false], g, false ); + drawButtonBackground( ileftBtnDownPix[false], g, true ); + + g = options()->colorGroup( ColorButtonBg, false ); + drawButtonBackground( irightBtnUpPix[true], g, false ); + drawButtonBackground( irightBtnDownPix[true], g, true ); + drawButtonBackground( irightBtnUpPix[false], g, false ); + drawButtonBackground( irightBtnDownPix[false], g, true ); +} + + +void KDEDefaultHandler::freePixmaps() +{ + // Free button pixmaps + if (rightBtnUpPix[true]) + delete rightBtnUpPix[true]; + if(rightBtnDownPix[true]) + delete rightBtnDownPix[true]; + if (irightBtnUpPix[true]) + delete irightBtnUpPix[true]; + if (irightBtnDownPix[true]) + delete irightBtnDownPix[true]; + + if (leftBtnUpPix[true]) + delete leftBtnUpPix[true]; + if(leftBtnDownPix[true]) + delete leftBtnDownPix[true]; + if (ileftBtnUpPix[true]) + delete ileftBtnUpPix[true]; + if (ileftBtnDownPix[true]) + delete ileftBtnDownPix[true]; + + if (rightBtnUpPix[false]) + delete rightBtnUpPix[false]; + if(rightBtnDownPix[false]) + delete rightBtnDownPix[false]; + if (irightBtnUpPix[false]) + delete irightBtnUpPix[false]; + if (irightBtnDownPix[false]) + delete irightBtnDownPix[false]; + + if (leftBtnUpPix[false]) + delete leftBtnUpPix[false]; + if(leftBtnDownPix[false]) + delete leftBtnDownPix[false]; + if (ileftBtnUpPix[false]) + delete ileftBtnUpPix[false]; + if (ileftBtnDownPix[false]) + delete ileftBtnDownPix[false]; + + // Title images + if (titleBuffer) + delete titleBuffer; + if (titlePix) + delete titlePix; + if (aUpperGradient) + delete aUpperGradient; + if (iUpperGradient) + delete iUpperGradient; + + // Sticky pin images + if (pinUpPix) + delete pinUpPix; + if (ipinUpPix) + delete ipinUpPix; + if (pinDownPix) + delete pinDownPix; + if (ipinDownPix) + delete ipinDownPix; +} + + +void KDEDefaultHandler::drawButtonBackground(KPixmap *pix, + const QColorGroup &g, bool sunken) +{ + QPainter p; + int w = pix->width(); + int h = pix->height(); + int x2 = w-1; + int y2 = h-1; + + bool highcolor = useGradients && (QPixmap::defaultDepth() > 8); + QColor c = g.background(); + + // Fill the background with a gradient if possible + if (highcolor) + KPixmapEffect::gradient(*pix, c.light(130), c.dark(130), + KPixmapEffect::VerticalGradient); + else + pix->fill(c); + + p.begin(pix); + // outer frame + p.setPen(g.mid()); + p.drawLine(0, 0, x2, 0); + p.drawLine(0, 0, 0, y2); + p.setPen(g.light()); + p.drawLine(x2, 0, x2, y2); + p.drawLine(0, x2, y2, x2); + p.setPen(g.dark()); + p.drawRect(1, 1, w-2, h-2); + p.setPen(sunken ? g.mid() : g.light()); + p.drawLine(2, 2, x2-2, 2); + p.drawLine(2, 2, 2, y2-2); + p.setPen(sunken ? g.light() : g.mid()); + p.drawLine(x2-2, 2, x2-2, y2-2); + p.drawLine(2, x2-2, y2-2, x2-2); +} + +QValueList< KDEDefaultHandler::BorderSize > KDEDefaultHandler::borderSizes() const +{ // the list must be sorted + return QValueList< BorderSize >() << BorderNormal << BorderLarge << + BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized; +} + +bool KDEDefaultHandler::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonMenu: + case AbilityButtonOnAllDesktops: + case AbilityButtonSpacer: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + case AbilityButtonAboveOthers: + case AbilityButtonBelowOthers: + case AbilityButtonShade: + return true; + default: + return false; + }; +} + +// =========================================================================== + +KDEDefaultButton::KDEDefaultButton(ButtonType type, KDEDefaultClient *parent, const char *name) + : KCommonDecorationButton(type, parent, name) +{ + setBackgroundMode( QWidget::NoBackground ); + + isMouseOver = false; + deco = NULL; + large = !decoration()->isToolWindow(); +} + + +KDEDefaultButton::~KDEDefaultButton() +{ + if (deco) + delete deco; +} + + +void KDEDefaultButton::reset(unsigned long changed) +{ + if (changed&DecorationReset || changed&ManualReset || changed&SizeChange || changed&StateChange) { + switch (type() ) { + case CloseButton: + setBitmap(close_bits); + break; + case HelpButton: + setBitmap(question_bits); + break; + case MinButton: + setBitmap(iconify_bits); + break; + case MaxButton: + setBitmap( isOn() ? minmax_bits : maximize_bits ); + break; + case OnAllDesktopsButton: + setBitmap(0); + break; + case ShadeButton: + setBitmap( isOn() ? shade_on_bits : shade_off_bits ); + break; + case AboveButton: + setBitmap( isOn() ? above_on_bits : above_off_bits ); + break; + case BelowButton: + setBitmap( isOn() ? below_on_bits : below_off_bits ); + break; + default: + setBitmap(0); + break; + } + + this->update(); + } +} + + +void KDEDefaultButton::setBitmap(const unsigned char *bitmap) +{ + delete deco; + deco = 0; + + if (bitmap) { + deco = new QBitmap(10, 10, bitmap, true); + deco->setMask( *deco ); + } +} + + +void KDEDefaultButton::drawButton(QPainter *p) +{ + if (!KDEDefault_initialized) + return; + + const bool active = decoration()->isActive(); + + if (deco) { + // Fill the button background with an appropriate button image + KPixmap btnbg; + + if (isLeft() ) { + if (isDown()) + btnbg = active ? + *leftBtnDownPix[large] : *ileftBtnDownPix[large]; + else + btnbg = active ? + *leftBtnUpPix[large] : *ileftBtnUpPix[large]; + } else { + if (isDown()) + btnbg = active ? + *rightBtnDownPix[large] : *irightBtnDownPix[large]; + else + btnbg = active ? + *rightBtnUpPix[large] : *irightBtnUpPix[large]; + } + + p->drawPixmap( 0, 0, btnbg ); + + } else if ( isLeft() ) { + + // Fill the button background with an appropriate color/gradient + // This is for sticky and menu buttons + KPixmap* grad = active ? aUpperGradient : iUpperGradient; + if (!grad) { + QColor c = KDecoration::options()->color(KDecoration::ColorTitleBar, active); + p->fillRect(0, 0, width(), height(), c ); + } else + p->drawPixmap( 0, 0, *grad, 0,1, width(), height() ); + + } else { + // Draw a plain background for menus or sticky buttons on RHS + QColor c = KDecoration::options()->color(KDecoration::ColorFrame, active); + p->fillRect(0, 0, width(), height(), c); + } + + + // If we have a decoration bitmap, then draw that + // otherwise we paint a menu button (with mini icon), or a sticky button. + if( deco ) { + // Select the appropriate button decoration color + bool darkDeco = qGray( KDecoration::options()->color( + isLeft() ? KDecoration::ColorTitleBar : KDecoration::ColorButtonBg, + active).rgb() ) > 127; + + if (isMouseOver) + p->setPen( darkDeco ? Qt::darkGray : Qt::lightGray ); + else + p->setPen( darkDeco ? Qt::black : Qt::white ); + + int xOff = (width()-10)/2; + int yOff = (height()-10)/2; + p->drawPixmap(isDown() ? xOff+1: xOff, isDown() ? yOff+1 : yOff, *deco); + + } else { + KPixmap btnpix; + + if (type()==OnAllDesktopsButton) { + if (active) + btnpix = isOn() ? *pinDownPix : *pinUpPix; + else + btnpix = isOn() ? *ipinDownPix : *ipinUpPix; + } else + btnpix = decoration()->icon().pixmap( QIconSet::Small, QIconSet::Normal ); + + // Intensify the image if required + if (isMouseOver) { + btnpix = KPixmapEffect::intensity(btnpix, 0.8); + } + + // Smooth scale the pixmap for small titlebars + // This is slow, but we assume this isn't done too often + if ( width() < 16 ) { + btnpix.convertFromImage(btnpix.convertToImage().smoothScale(12, 12)); + p->drawPixmap( 0, 0, btnpix ); + } + else + p->drawPixmap( width()/2-8, height()/2-8, btnpix ); + } +} + + +void KDEDefaultButton::enterEvent(QEvent *e) +{ + isMouseOver=true; + repaint(false); + QButton::enterEvent(e); +} + + +void KDEDefaultButton::leaveEvent(QEvent *e) +{ + isMouseOver=false; + repaint(false); + QButton::leaveEvent(e); +} + + +// =========================================================================== + +KDEDefaultClient::KDEDefaultClient( KDecorationBridge* b, KDecorationFactory* f ) + : KCommonDecoration( b, f )/*, + m_closing(false)*/ +{ +} + +QString KDEDefaultClient::visibleName() const +{ + return i18n("KDE2"); +} + +QString KDEDefaultClient::defaultButtonsLeft() const +{ + return "MS"; +} + +QString KDEDefaultClient::defaultButtonsRight() const +{ + return "HIAX"; +} + +bool KDEDefaultClient::decorationBehaviour(DecorationBehaviour behaviour) const +{ + switch (behaviour) { + case DB_MenuClose: + return true; + case DB_WindowMask: + return true; + case DB_ButtonHide: + return true; + + default: + return KCommonDecoration::decorationBehaviour(behaviour); + } +} + +int KDEDefaultClient::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const +{ + switch (lm) { + case LM_BorderLeft: + case LM_BorderRight: + return borderWidth; + + case LM_BorderBottom: + return mustDrawHandle() ? grabBorderWidth : borderWidth; + + case LM_TitleEdgeLeft: + case LM_TitleEdgeRight: + return borderWidth; + + case LM_TitleEdgeTop: + return 3; + + case LM_TitleEdgeBottom: + return 1; + + case LM_TitleBorderLeft: + case LM_TitleBorderRight: + return 1; + + case LM_TitleHeight: + return titleHeight; + + case LM_ButtonWidth: + case LM_ButtonHeight: + return titleHeight; + + case LM_ButtonSpacing: + return 0; + + case LM_ExplicitButtonSpacer: + if ( !isToolWindow() ) + return borderWidth/2; + // fall though + default: + return KCommonDecoration::layoutMetric(lm, respectWindowState, btn); + } +} + +KCommonDecorationButton *KDEDefaultClient::createButton(ButtonType type) +{ + switch (type) { + case MenuButton: + return new KDEDefaultButton(MenuButton, this, "menu"); + case OnAllDesktopsButton: + return new KDEDefaultButton(OnAllDesktopsButton, this, "on_all_desktops"); + case HelpButton: + return new KDEDefaultButton(HelpButton, this, "help"); + case MinButton: + return new KDEDefaultButton(MinButton, this, "minimize"); + case MaxButton: + return new KDEDefaultButton(MaxButton, this, "maximize"); + case CloseButton: + return new KDEDefaultButton(CloseButton, this, "close"); + case AboveButton: + return new KDEDefaultButton(AboveButton, this, "above"); + case BelowButton: + return new KDEDefaultButton(BelowButton, this, "below"); + case ShadeButton: + return new KDEDefaultButton(ShadeButton, this, "shade"); + + default: + return 0; + } +} + +void KDEDefaultClient::init() +{ + // Finally, toolWindows look small + if ( isToolWindow() ) { + titleHeight = toolTitleHeight; + } + else { + titleHeight = normalTitleHeight; + } + + KCommonDecoration::init(); +} + +void KDEDefaultClient::reset( unsigned long changed) +{ + widget()->repaint(); + + KCommonDecoration::reset(changed); +} + +bool KDEDefaultClient::mustDrawHandle() const +{ + bool drawSmallBorders = !options()->moveResizeMaximizedWindows(); + if (drawSmallBorders && (maximizeMode() & MaximizeVertical)) { + return false; + } else { + return showGrabBar && isResizable(); + } +} + +void KDEDefaultClient::paintEvent( QPaintEvent* ) +{ + if (!KDEDefault_initialized) + return; + + QColorGroup g; + int offset; + + KPixmap* upperGradient = isActive() ? aUpperGradient : iUpperGradient; + + QPainter p(widget()); + + // Obtain widget bounds. + QRect r(widget()->rect()); + int x = r.x(); + int y = r.y(); + int x2 = r.width() - 1; + int y2 = r.height() - 1; + int w = r.width(); + int h = r.height(); + + // Determine where to place the extended left titlebar + int leftFrameStart = (h > 42) ? y+titleHeight+26: y+titleHeight; + + // Determine where to make the titlebar color transition + r = titleRect(); + int rightOffset = r.x()+r.width()+1; + + // Create a disposable pixmap buffer for the titlebar + // very early before drawing begins so there is no lag + // during painting pixels. + titleBuffer->resize( rightOffset-3, titleHeight+1 ); + + // Draw an outer black frame + p.setPen(Qt::black); + p.drawRect(x,y,w,h); + + // Draw part of the frame that is the titlebar color + g = options()->colorGroup(ColorTitleBar, isActive()); + p.setPen(g.light()); + p.drawLine(x+1, y+1, rightOffset-1, y+1); + p.drawLine(x+1, y+1, x+1, leftFrameStart+borderWidth-4); + + // Draw titlebar colour separator line + p.setPen(g.dark()); + p.drawLine(rightOffset-1, y+1, rightOffset-1, titleHeight+2); + + p.fillRect(x+2, y+titleHeight+3, + borderWidth-4, leftFrameStart+borderWidth-y-titleHeight-8, + options()->color(ColorTitleBar, isActive() )); + + // Finish drawing the titlebar extension + p.setPen(Qt::black); + p.drawLine(x+1, leftFrameStart+borderWidth-4, x+borderWidth-2, leftFrameStart-1); + p.setPen(g.mid()); + p.drawLine(x+borderWidth-2, y+titleHeight+3, x+borderWidth-2, leftFrameStart-2); + + // Fill out the border edges + g = options()->colorGroup(ColorFrame, isActive()); + p.setPen(g.light()); + p.drawLine(rightOffset, y+1, x2-1, y+1); + p.drawLine(x+1, leftFrameStart+borderWidth-3, x+1, y2-1); + p.setPen(g.dark()); + p.drawLine(x2-1, y+1, x2-1, y2-1); + p.drawLine(x+1, y2-1, x2-1, y2-1); + + p.setPen(options()->color(ColorFrame, isActive())); + QPointArray a; + QBrush brush( options()->color(ColorFrame, isActive()), Qt::SolidPattern ); + p.setBrush( brush ); // use solid, yellow brush + a.setPoints( 4, x+2, leftFrameStart+borderWidth-4, + x+borderWidth-2, leftFrameStart, + x+borderWidth-2, y2-2, + x+2, y2-2); + p.drawPolygon( a ); + p.fillRect(x2-borderWidth+2, y+titleHeight+3, + borderWidth-3, y2-y-titleHeight-4, + options()->color(ColorFrame, isActive() )); + + // Draw the bottom handle if required + if (mustDrawHandle()) + { + if(w > 50) + { + qDrawShadePanel(&p, x+1, y2-grabBorderWidth+2, 2*borderWidth+12, grabBorderWidth-2, + g, false, 1, &g.brush(QColorGroup::Mid)); + qDrawShadePanel(&p, x+2*borderWidth+13, y2-grabBorderWidth+2, w-4*borderWidth-26, grabBorderWidth-2, + g, false, 1, isActive() ? + &g.brush(QColorGroup::Background) : + &g.brush(QColorGroup::Mid)); + qDrawShadePanel(&p, x2-2*borderWidth-12, y2-grabBorderWidth+2, 2*borderWidth+12, grabBorderWidth-2, + g, false, 1, &g.brush(QColorGroup::Mid)); + } else + qDrawShadePanel(&p, x+1, y2-grabBorderWidth+2, w-2, grabBorderWidth-2, + g, false, 1, isActive() ? + &g.brush(QColorGroup::Background) : + &g.brush(QColorGroup::Mid)); + offset = grabBorderWidth; + } else + { + p.fillRect(x+2, y2-borderWidth+2, w-4, borderWidth-3, + options()->color(ColorFrame, isActive() )); + offset = borderWidth; + } + + // Draw a frame around the wrapped widget. + p.setPen( g.dark() ); + p.drawRect( x+borderWidth-1, y+titleHeight+3, w-2*borderWidth+2, h-titleHeight-offset-2 ); + + // Draw the title bar. + r = titleRect(); + + // Obtain titlebar blend colours + QColor c1 = options()->color(ColorTitleBar, isActive() ); + QColor c2 = options()->color(ColorFrame, isActive() ); + + // Fill with frame color behind RHS buttons + p.fillRect( rightOffset, y+2, x2-rightOffset-1, titleHeight+1, c2); + + QPainter p2( titleBuffer, this ); + + // Draw the titlebar gradient + if (upperGradient) + p2.drawTiledPixmap(0, 0, rightOffset-3, titleHeight+1, *upperGradient); + else + p2.fillRect(0, 0, rightOffset-3, titleHeight+1, c1); + + // Draw the title text on the pixmap, and with a smaller font + // for toolwindows than the default. + QFont fnt = options()->font(true); + + if ( isToolWindow() ) + fnt.setPointSize( fnt.pointSize()-2 ); // Shrink font by 2pt + + p2.setFont( fnt ); + + // Draw the titlebar stipple if active and available + if (isActive() && titlePix) + { + QFontMetrics fm(fnt); + int captionWidth = fm.width(caption()); + if (caption().isRightToLeft()) + p2.drawTiledPixmap( r.x(), 0, r.width()-captionWidth-4, + titleHeight+1, *titlePix ); + else + p2.drawTiledPixmap( r.x()+captionWidth+3, 0, r.width()-captionWidth-4, + titleHeight+1, *titlePix ); + } + + p2.setPen( options()->color(ColorFont, isActive()) ); + p2.drawText(r.x(), 1, r.width()-1, r.height(), + (caption().isRightToLeft() ? AlignRight : AlignLeft) | AlignVCenter, + caption() ); + + bitBlt( widget(), 2, 2, titleBuffer ); + + p2.end(); + + // Ensure a shaded window has no unpainted areas + // Is this still needed? +#if 1 + p.setPen(c2); + p.drawLine(x+borderWidth, y+titleHeight+4, x2-borderWidth, y+titleHeight+4); +#endif +} + +QRegion KDEDefaultClient::cornerShape(WindowCorner corner) +{ + switch (corner) { + case WC_TopLeft: + return QRect(0, 0, 1, 1); + + case WC_TopRight: + return QRect(width()-1, 0, 1, 1); + + case WC_BottomLeft: + return QRect(0, height()-1, 1, 1); + + case WC_BottomRight: + return QRect(width()-1, height()-1, 1, 1); + + default: + return QRegion(); + } +} + +} // namespace + +// Extended KWin plugin interface +extern "C" KDE_EXPORT KDecorationFactory* create_factory() +{ + return new Default::KDEDefaultHandler(); +} + +// vim: ts=4 +// kate: space-indent off; tab-width 4; diff --git a/kwin/clients/default/kdedefault.h b/kwin/clients/default/kdedefault.h new file mode 100644 index 000000000..53ad23fa2 --- /dev/null +++ b/kwin/clients/default/kdedefault.h @@ -0,0 +1,103 @@ +/* + * + * KDE2 Default KWin client + * + * Copyright (C) 1999, 2001 Daniel Duley <mosfet@kde.org> + * Matthias Ettrich <ettrich@kde.org> + * Karol Szwed <gallium@kde.org> + * + * Draws mini titlebars for tool windows. + * Many features are now customizable. + */ + +#ifndef _KDE_DEFAULT_H +#define _KDE_DEFAULT_H + +#include <qbutton.h> +#include <qbitmap.h> +#include <qdatetime.h> +#include <kpixmap.h> +#include <kcommondecoration.h> +#include <kdecorationfactory.h> + +class QSpacerItem; +class QBoxLayout; +class QGridLayout; + +namespace Default { + +class KDEDefaultClient; + +class KDEDefaultHandler: public KDecorationFactory +{ + public: + KDEDefaultHandler(); + ~KDEDefaultHandler(); + KDecoration* createDecoration( KDecorationBridge* b ); + bool reset( unsigned long changed ); + virtual QValueList< BorderSize > borderSizes() const; + virtual bool supports( Ability ability ); + + private: + unsigned long readConfig( bool update ); + void createPixmaps(); + void freePixmaps(); + void drawButtonBackground(KPixmap *pix, + const QColorGroup &g, bool sunken); +}; + + +// class KDEDefaultButton : public QButton, public KDecorationDefines +class KDEDefaultButton : public KCommonDecorationButton +{ + public: + KDEDefaultButton(ButtonType type, KDEDefaultClient *parent, const char *name); + ~KDEDefaultButton(); + + void reset(unsigned long changed); + + void setBitmap(const unsigned char *bitmap); + + protected: + void enterEvent(QEvent *); + void leaveEvent(QEvent *); + void drawButton(QPainter *p); + void drawButtonLabel(QPainter*) {;} + + QBitmap* deco; + bool large; + bool isMouseOver; +}; + + +class KDEDefaultClient : public KCommonDecoration +{ + public: + KDEDefaultClient( KDecorationBridge* b, KDecorationFactory* f ); + ~KDEDefaultClient() {;} + + virtual QString visibleName() const; + virtual QString defaultButtonsLeft() const; + virtual QString defaultButtonsRight() const; + virtual bool decorationBehaviour(DecorationBehaviour behaviour) const; + virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton * = 0) const; + virtual KCommonDecorationButton *createButton(ButtonType type); + + virtual QRegion cornerShape(WindowCorner corner); + + void init(); + void reset( unsigned long changed ); + + protected: + void paintEvent( QPaintEvent* ); + + private: + bool mustDrawHandle() const; + int titleHeight; +}; + +} + +#endif +// vim: ts=4 +// kate: space-indent off; tab-width 4; diff --git a/kwin/clients/keramik/Makefile.am b/kwin/clients/keramik/Makefile.am new file mode 100644 index 000000000..d5969643c --- /dev/null +++ b/kwin/clients/keramik/Makefile.am @@ -0,0 +1,44 @@ +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +SUBDIRS = . config + +noinst_PROGRAMS = embedtool + +noinst_HEADERS = tiles.h + +embedtool_SOURCES = embedtool.cpp +embedtool_LDADD = $(LIB_QT) +embedtool_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +kde_module_LTLIBRARIES = kwin3_keramik.la + +kwin3_keramik_la_SOURCES = keramik.cpp +kwin3_keramik_la_COMPILE_FIRST = tiles.h +kwin3_keramik_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_keramik_la_LIBADD = $(LIB_KDEUI) ../../lib/libkdecorations.la +#kwin3_keramik_la_LDFLAGS = $(all_libraries) -avoid-version -module $(KDE_RPATH) $(KDE_MT_LDFLAGS) + +METASOURCES = AUTO +noinst_headers = keramik.h tiles.h + +lnkdir = $(kde_datadir)/kwin +lnk_DATA = keramik.desktop + +EXTRA_DIST = $(lnk_DATA) + +tiles.h: pics/caption-large-left.png pics/caption-small-right.png pics/titlebar-center.png \ + pics/titlebutton-square.png pics/border-left.png pics/caption-large-right.png \ + pics/grabbar-center.png pics/titlebar-left.png pics/border-right.png \ + pics/caption-small-center.png pics/grabbar-left.png pics/titlebar-right.png \ + pics/caption-large-center.png pics/caption-small-left.png pics/grabbar-right.png \ + pics/titlebutton-round.png pics/bottom-left.png pics/bottom-right.png \ + pics/bottom-center.png \ + pics/titlebutton-square-large.png pics/titlebutton-square-huge.png \ + pics/titlebutton-round-large.png pics/titlebutton-round-huge.png + +tiles.h: embedtool + pics=`ls $(srcdir)/pics/*.png 2>/dev/null` ;\ + ./embedtool $$pics + +keramik.lo: tiles.h + diff --git a/kwin/clients/keramik/config/Makefile.am b/kwin/clients/keramik/config/Makefile.am new file mode 100644 index 000000000..3021635ca --- /dev/null +++ b/kwin/clients/keramik/config/Makefile.am @@ -0,0 +1,13 @@ +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = kwin_keramik_config.la + +kwin_keramik_config_la_SOURCES = config.cpp keramikconfig.ui +kwin_keramik_config_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin_keramik_config_la_LIBADD = $(LIB_KDEUI) + +METASOURCES = AUTO +noinst_HEADERS = config.h keramikconfig.h + +lnkdir = $(kde_datadir)/kwin + diff --git a/kwin/clients/keramik/config/config.cpp b/kwin/clients/keramik/config/config.cpp new file mode 100644 index 000000000..c548ca184 --- /dev/null +++ b/kwin/clients/keramik/config/config.cpp @@ -0,0 +1,110 @@ +/* + * + * Keramik KWin client configuration module + * + * Copyright (C) 2002 Fredrik Hglund <fredrik@kde.org> + * + * Based on the Quartz configuration module, + * Copyright (c) 2001 Karol Szwed <gallium@kde.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the license, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <kglobal.h> +#include <klocale.h> + +#include <qcheckbox.h> + +#include "config.h" +#include "config.moc" + +extern "C" +{ + KDE_EXPORT QObject* allocate_config( KConfig* conf, QWidget* parent ) + { + return ( new KeramikConfig( conf, parent ) ); + } +} + + +/* NOTE: + * 'conf' is a pointer to the kwindecoration modules open kwin config, + * and is by default set to the "Style" group. + * + * 'parent' is the parent of the QObject, which is a VBox inside the + * Configure tab in kwindecoration + */ + +KeramikConfig::KeramikConfig( KConfig* conf, QWidget* parent ) + : QObject( parent ) +{ + KGlobal::locale()->insertCatalogue("kwin_clients"); + c = new KConfig( "kwinkeramikrc" ); + + ui = new KeramikConfigUI( parent ); + connect( ui->showAppIcons, SIGNAL(clicked()), SIGNAL(changed()) ); + connect( ui->smallCaptions, SIGNAL(clicked()), SIGNAL(changed()) ); + connect( ui->largeGrabBars, SIGNAL(clicked()), SIGNAL(changed()) ); + connect( ui->useShadowedText, SIGNAL(clicked()), SIGNAL(changed()) ); + + load( conf ); + ui->show(); +} + + +KeramikConfig::~KeramikConfig() +{ + delete ui; + delete c; +} + + +// Loads the configurable options from the kwinrc config file +// It is passed the open config from kwindecoration to improve efficiency +void KeramikConfig::load( KConfig* ) +{ + c->setGroup("General"); + ui->showAppIcons->setChecked( c->readBoolEntry("ShowAppIcons", true) ); + ui->smallCaptions->setChecked( c->readBoolEntry("SmallCaptionBubbles", false) ); + ui->largeGrabBars->setChecked( c->readBoolEntry("LargeGrabBars", true) ); + ui->useShadowedText->setChecked( c->readBoolEntry("UseShadowedText", true) ); +} + + +// Saves the configurable options to the kwinrc config file +void KeramikConfig::save( KConfig* ) +{ + c->setGroup( "General" ); + c->writeEntry( "ShowAppIcons", ui->showAppIcons->isChecked() ); + c->writeEntry( "SmallCaptionBubbles", ui->smallCaptions->isChecked() ); + c->writeEntry( "LargeGrabBars", ui->largeGrabBars->isChecked() ); + c->writeEntry( "UseShadowedText", ui->useShadowedText->isChecked() ); + c->sync(); +} + + +// Sets UI widget defaults which must correspond to style defaults +void KeramikConfig::defaults() +{ + ui->showAppIcons->setChecked( true ); + ui->smallCaptions->setChecked( false ); + ui->largeGrabBars->setChecked( true ); + ui->useShadowedText->setChecked( true ); + + emit changed(); +} + +// vim: set noet ts=4 sw=4: diff --git a/kwin/clients/keramik/config/config.h b/kwin/clients/keramik/config/config.h new file mode 100644 index 000000000..71090531d --- /dev/null +++ b/kwin/clients/keramik/config/config.h @@ -0,0 +1,57 @@ +/* + * Keramik KWin client configuration module + * + * Copyright (C) 2002 Fredrik Hglund <fredrik@kde.org> + * + * Based on the Quartz configuration module, + * Copyright (c) 2001 Karol Szwed <gallium@kde.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the license, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __KWIN_KERAMIK_CONFIG_H +#define __KWIN_KERAMIK_CONFIG_H + +#include <kconfig.h> + +#include "keramikconfig.h" + +class KeramikConfig: public QObject +{ + Q_OBJECT + + public: + KeramikConfig( KConfig* conf, QWidget* parent ); + ~KeramikConfig(); + + // These public signals/slots work similar to KCM modules + signals: + void changed(); + + public slots: + void load( KConfig* conf ); + void save( KConfig* conf ); + void defaults(); + + private: + KeramikConfigUI *ui; + KConfig *c; +}; + + +#endif + +// vim: set noet ts=4 sw=4: diff --git a/kwin/clients/keramik/config/keramikconfig.ui b/kwin/clients/keramik/config/keramikconfig.ui new file mode 100644 index 000000000..f074a00b8 --- /dev/null +++ b/kwin/clients/keramik/config/keramikconfig.ui @@ -0,0 +1,76 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>KeramikConfigUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KeramikConfigUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>287</width> + <height>102</height> + </rect> + </property> + <property name="caption"> + <string>Keramik</string> + </property> + <vbox> + <property name="margin"> + <cstring>0</cstring> + </property> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>showAppIcons</cstring> + </property> + <property name="text"> + <string>Display the window &icon in the caption bubble</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if you want the window icon to be displayed in the caption bubble next to the titlebar text.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>smallCaptions</cstring> + </property> + <property name="text"> + <string>Draw &small caption bubbles on active windows</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if you want the caption bubble to have the same size on active windows that it has on inactive ones. This option is useful for laptops or low resolution displays where you want maximize the amount of space available to the window contents.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>largeGrabBars</cstring> + </property> + <property name="text"> + <string>Draw g&rab bars below windows</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if you want a grab bar to be drawn below windows. When this option is not selected only a thin border will be drawn in its place.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>useShadowedText</cstring> + </property> + <property name="text"> + <string>Use shadowed &text</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if you want the titlebar text to have a 3D look with a shadow behind it.</string> + </property> + </widget> + </vbox> +</widget> +<includes> + <include location="global" impldecl="in implementation">kdialog.h</include> +</includes> +<layoutdefaults spacing="6" margin="11"/> +<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/> +</UI> diff --git a/kwin/clients/keramik/embedtool.cpp b/kwin/clients/keramik/embedtool.cpp new file mode 100644 index 000000000..b0e5f1c72 --- /dev/null +++ b/kwin/clients/keramik/embedtool.cpp @@ -0,0 +1,230 @@ +/* + * Keramik KWin embed tool (version 1.0) + * + * Copyright (C) 2002 Fredrik Hglund <fredrik@kde.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the license, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <qimage.h> +#include <qtextstream.h> +#include <qregexp.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qdatetime.h> + +#include <iostream> + +static int primes[] = { + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, + 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, + 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, + 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, + 179, 181, 191, 193, 197, 199, 211, 223, 227, 229 +}; + +struct EmbedImage { + QString string; + int width; + int height; + bool alpha; + QString name; +}; + +class KeramikEmbedder { +public: + KeramikEmbedder(); + ~KeramikEmbedder(); + + void embed( const char * ); + void writeIndex(); + +private: + QFile *file; + QPtrList<EmbedImage> *index; + QTextStream stream; +}; + +KeramikEmbedder::KeramikEmbedder() +{ + QDateTime date( QDateTime::currentDateTime() ); + QString datestring( date.toString() ); + + file = new QFile( "tiles.h" ); + file->open( IO_WriteOnly | IO_Truncate ); + + stream.setDevice( file ); + + stream << "/*\n"; + stream << " * Generated by embedtool 1.0 on " << datestring << endl; + stream << " */\n\n"; + + stream << "#ifndef __TILES_H\n"; + stream << "#define __TILES_H\n\n"; + stream << "#include <qimage.h>\n"; + stream << "#include <qdict.h>\n\n"; + stream << "namespace Keramik {\n\n"; + + index = new QPtrList<EmbedImage>; + index->setAutoDelete( true ); +} + +KeramikEmbedder::~KeramikEmbedder() +{ + stream << "} // namespace Keramik\n\n"; + stream << "#endif // __TILES_H\n\n"; + stream << "// vim: set noet ts=4 sw=4:\n"; + + file->close(); + delete file; + delete index; +} + +void KeramikEmbedder::embed( const char *name ) +{ + QFileInfo fileinfo( name ); + QString basename( fileinfo.baseName() ); + QString codename( basename ); + QImage image( name ); + + codename = codename.replace( QRegExp("[^a-zA-Z0-9]"), "_" ); + + stream << "\tstatic const QRgb " << codename << "_data[] = {" << endl << "\t\t"; + stream.setf( QTextStream::hex | QTextStream::right ); + stream.fill( '0' ); + + int pixels = image.width() * image.height(); + Q_UINT32 *data = reinterpret_cast<Q_UINT32*>( image.bits() ); + bool hasAlpha = false; + + + for ( int i = 0, j = 0; i < pixels; i++ ) { + if ( qAlpha( *data ) && qAlpha( *data ) != 0xff ) + hasAlpha = true; + + stream << "0x" << qSetW(8) << *(data++); + + if ( i != pixels-1 ) { + stream << ','; + + if ( j++ > 4 ) { + j = 0; + stream << endl << "\t\t"; + } else + stream << ' '; + } + } + + stream.reset(); + + stream << endl << "\t}; // " << codename << "_data" << endl << endl; + + EmbedImage *imginfo = new EmbedImage; + imginfo->width = image.width(); + imginfo->height = image.height(); + imginfo->alpha = hasAlpha; + imginfo->name = codename; + imginfo->string = basename; + index->append( imginfo ); +} + +void KeramikEmbedder::writeIndex() +{ + stream << "\tstruct EmbedImage {\n"; + stream << "\t\tconst char *name;\n"; + stream << "\t\tint width;\n"; + stream << "\t\tint height;\n"; + stream << "\t\tbool alpha;\n"; + stream << "\t\tconst QRgb *data;\n"; + stream << "\t};\n\n"; + + uint i = 0; + stream << "\tstatic const EmbedImage image_db[] = {\n"; + for ( EmbedImage *image = index->first(); image; image = index->next() ) + { + stream << "\t\t{ \"" << image->string << "\", " + << image->width << ", " << image->height << + ", " << (image->alpha ? "true" : "false") + << ", " << image->name << "_data }"; + if ( i++ < index->count() - 1 ) + stream << ','; + stream << endl; + } + stream << "\t};\n\n"; + + uint prime = 0; + + for ( i = 0; i < 50; i++ ) + if ( (prime = primes[i]) >= index->count() ) + break; + + stream << "\tclass KeramikImageDb {\n"; + stream << "\tprivate:\n"; + stream << "\t\tstatic KeramikImageDb *m_inst;\n"; + stream << "\t\tQDict<QImage> *db;\n\n"; + stream << "\t\tKeramikImageDb() {\n"; + stream << "\t\t\tdb = new QDict<QImage>( " << prime << " );\n"; + stream << "\t\t\tdb->setAutoDelete( true );\n\n"; + stream << "\t\t\tfor ( int i = 0; i < " << index->count() << "; i++ ) {\n"; + stream << "\t\t\t\tQImage *img = new QImage( (uchar*)image_db[i].data,\n"; + stream << "\t\t\t\t\t\timage_db[i].width, image_db[i].height,\n"; + stream << "\t\t\t\t\t\t32, NULL, 0, QImage::LittleEndian );\n\n"; + stream << "\t\t\t\tif ( image_db[i].alpha )\n"; + stream << "\t\t\t\t\timg->setAlphaBuffer( true );\n\n"; + stream << "\t\t\t\tdb->insert( image_db[i].name, img );\n"; + stream << "\t\t\t}\n"; + stream << "\t\t}\n\n"; + stream << "\t\t~KeramikImageDb() {\n"; + stream << "\t\t\tdelete db;\n"; + stream << "\t\t}\n\n"; + stream << "\tpublic:\n"; + stream << "\t\tstatic KeramikImageDb* instance() {\n"; + stream << "\t\t\tif ( ! m_inst ) m_inst = new KeramikImageDb;\n"; + stream << "\t\t\treturn m_inst;\n"; + stream << "\t\t}\n\n"; + stream << "\t\tstatic void release() {\n"; + stream << "\t\t\tif ( m_inst ) delete m_inst;\n"; + stream << "\t\t\tm_inst = NULL;\n"; + stream << "\t\t}\n\n"; + stream << "\t\tQImage *image( const QString &name ) const {\n"; + stream << "\t\t\treturn db->find( name );\n"; + stream << "\t\t}\n\n"; + stream << "\t}; // class KeramikImageDb\n\n"; + stream << "\tKeramikImageDb *KeramikImageDb::m_inst = NULL;\n\n"; +} + +int main( int argv, char **argc ) +{ + if ( argv < 2 ) { + std::cout << "Insufficient arguments" << std::endl; + return 1; + } + + KeramikEmbedder embedder; + + for ( int i = 1; i < argv; i++ ) + { + std::cout << argc[i] << std::endl; + embedder.embed( argc[i] ); + } + + embedder.writeIndex(); + + return 0; +} + +// vim: set noet ts=4 sw=4: + diff --git a/kwin/clients/keramik/keramik.cpp b/kwin/clients/keramik/keramik.cpp new file mode 100644 index 000000000..57a51bce1 --- /dev/null +++ b/kwin/clients/keramik/keramik.cpp @@ -0,0 +1,1834 @@ +/* + * + * Keramik KWin client (version 0.8) + * + * Copyright (C) 2002 Fredrik H�lund <fredrik@kde.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the license, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <kconfig.h> +#include <klocale.h> +#include <kiconeffect.h> + +#include <qpainter.h> +#include <qlayout.h> +#include <qbitmap.h> +#include <qstyle.h> +#include <qtooltip.h> +#include <qwidget.h> +#include <qlabel.h> + +#include <X11/Xlib.h> + +#include "keramik.h" +#include "keramik.moc" + + + +// ------------------------------------------------------------------------------------------- + +static void flip( QPixmap *&pix ) +{ + QPixmap *tmp = new QPixmap( pix->xForm( QWMatrix(-1,0,0,1,pix->width(),0) ) ); + delete pix; + pix = tmp; +} + +static void flip( QBitmap *&pix ) +{ + QBitmap *tmp = new QBitmap( pix->xForm( QWMatrix(-1,0,0,1,pix->width(),0) ) ); + delete pix; + pix = tmp; +} + +namespace Keramik +{ + + const int buttonMargin = 9; // Margin between the window edge and the buttons + const int buttonSpacing = 4; // Spacing between the titlebar buttons + const int iconSpacing = 5; // Spacing between the icon and the text label + + // Default button layout + const char default_left[] = "M"; + const char default_right[] = "HIAX"; + + // Titlebar button bitmaps + const unsigned char menu_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0xf0, 0x07, 0x00, + 0xe0, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + + const unsigned char on_all_desktops_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0xf0, 0x0f, 0x00, + 0xf0, 0x0f, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + + const unsigned char not_on_all_desktops_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, + 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + + const unsigned char help_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0xf0, 0x07, 0x00, 0x30, 0x06, 0x00, 0x00, 0x07, 0x00, 0x80, 0x03, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + + const unsigned char minimize_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + + const unsigned char maximize_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x01, 0x00, 0xc0, 0x03, 0x00, 0xe0, 0x07, 0x00, 0xf0, 0x0f, 0x00, + 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + + const unsigned char restore_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, + 0xf0, 0x0f, 0x00, 0xe0, 0x07, 0x00, 0xc0, 0x03, 0x00, 0x80, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + + const unsigned char close_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x0c, 0x00, 0x70, 0x0e, 0x00, 0xe0, 0x07, 0x00, 0xc0, 0x03, 0x00, + 0xc0, 0x03, 0x00, 0xe0, 0x07, 0x00, 0x70, 0x0e, 0x00, 0x30, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00}; + + const unsigned char above_on_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, + 0x80, 0x01, 0x00, 0xe0, 0x07, 0x00, 0xc0, 0x03, 0x00, 0x80, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }; + + const unsigned char above_off_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0xc0, 0x03, 0x00, + 0xe0, 0x07, 0x00, 0x80, 0x01, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }; + + const unsigned char below_on_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0xc0, 0x03, 0x00, + 0xe0, 0x07, 0x00, 0x80, 0x01, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }; + + const unsigned char below_off_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, + 0x80, 0x01, 0x00, 0xe0, 0x07, 0x00, 0xc0, 0x03, 0x00, 0x80, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }; + + const unsigned char shade_on_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, + 0x10, 0x08, 0x00, 0x10, 0x08, 0x00, 0x10, 0x08, 0x00, 0x10, 0x08, 0x00, + 0x10, 0x08, 0x00, 0x10, 0x08, 0x00, 0x10, 0x08, 0x00, 0xf0, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }; + + const unsigned char shade_off_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 }; + + KeramikHandler *clientHandler = NULL; + bool keramik_initialized = false; + + + +// ------------------------------------------------------------------------------------------- + + + +KeramikHandler::KeramikHandler() +{ + for ( int i = 0; i < NumTiles; i++ ) { + activeTiles[i] = NULL; + inactiveTiles[i] = NULL; + } + + settings_cache = NULL; + + imageDb = KeramikImageDb::instance(); + + // Create the button deco bitmaps + buttonDecos[ Menu ] = new QBitmap( 17, 17, menu_bits, true ); + buttonDecos[ OnAllDesktops ] = new QBitmap( 17, 17, on_all_desktops_bits, true ); + buttonDecos[ NotOnAllDesktops ] = new QBitmap( 17, 17, not_on_all_desktops_bits, true ); + buttonDecos[ Help ] = new QBitmap( 17, 17, help_bits, true ); + buttonDecos[ Minimize ] = new QBitmap( 17, 17, minimize_bits, true ); + buttonDecos[ Maximize ] = new QBitmap( 17, 17, maximize_bits, true ); + buttonDecos[ Restore ] = new QBitmap( 17, 17, restore_bits, true ); + buttonDecos[ Close ] = new QBitmap( 17, 17, close_bits, true ); + buttonDecos[ AboveOn ] = new QBitmap( 17, 17, above_on_bits, true ); + buttonDecos[ AboveOff ] = new QBitmap( 17, 17, above_off_bits, true ); + buttonDecos[ BelowOn ] = new QBitmap( 17, 17, below_on_bits, true ); + buttonDecos[ BelowOff ] = new QBitmap( 17, 17, below_off_bits, true ); + buttonDecos[ ShadeOn ] = new QBitmap( 17, 17, shade_on_bits, true ); + buttonDecos[ ShadeOff ] = new QBitmap( 17, 17, shade_off_bits, true ); + + // Selfmask the bitmaps + for ( int i = 0; i < NumButtonDecos; i++ ) + buttonDecos[i]->setMask( *buttonDecos[i] ); + + // Flip the bitmaps horizontally in right-to-left mode + if ( QApplication::reverseLayout() ) { + for ( int i = 0; i < Help; ++i ) + ::flip( buttonDecos[i] ); + + for ( int i = Help + 1; i < NumButtonDecos; ++i ) + ::flip( buttonDecos[i] ); + } + + readConfig(); + createPixmaps(); + + keramik_initialized = true; +} + + +KeramikHandler::~KeramikHandler() +{ + keramik_initialized = false; + destroyPixmaps(); + + for ( int i = 0; i < NumButtonDecos; i++ ) + delete buttonDecos[i]; + + delete settings_cache; + + KeramikImageDb::release(); + imageDb = NULL; + clientHandler = NULL; +} + + +void KeramikHandler::createPixmaps() +{ + int heightOffset; + int widthOffset; + switch(options()->preferredBorderSize(this)) { + case BorderLarge: + widthOffset = 4; + heightOffset = 0; + break; + case BorderVeryLarge: + widthOffset = 8; + heightOffset = 0; + break; + case BorderHuge: + widthOffset = 14; + heightOffset = 0; + break; + case BorderVeryHuge: + widthOffset = 23; + heightOffset = 10; + break; + case BorderOversized: + widthOffset = 36; + heightOffset = 25; + break; + case BorderTiny: + case BorderNormal: + default: + widthOffset = 0; + heightOffset = 0; + } + int fontHeight = QFontMetrics(options()->font(true)).height(); + if (fontHeight > heightOffset + 20) + heightOffset = fontHeight - 20; + + QString size = (heightOffset < 8) ? "" : (heightOffset < 20) ? "-large" : "-huge"; + + QColor titleColor, captionColor, buttonColor; + QImage *titleCenter = NULL, *captionLeft = NULL, + *captionRight = NULL, *captionCenter = NULL; + + + // Active tiles + // ------------------------------------------------------------------------- + captionColor = KDecoration::options()->color( ColorTitleBar, true ); + titleColor = KDecoration::options()->color( ColorTitleBlend, true ); + + // Load the titlebar corners. + activeTiles[ TitleLeft ] = loadPixmap( "titlebar-left", titleColor ); + activeTiles[ TitleRight ] = loadPixmap( "titlebar-right", titleColor ); + + // Load the titlebar center tile image (this will be used as + // the background for the caption bubble tiles). + titleCenter = loadImage( "titlebar-center", titleColor ); + + // Load the small version of the caption bubble corner & center images. + captionLeft = loadImage( "caption-small-left", captionColor ); + captionRight = loadImage( "caption-small-right", captionColor ); + captionCenter = loadImage( "caption-small-center", captionColor ); + + // Create the caption bubble tiles (by blending the images onto the titlebar) + activeTiles[ CaptionSmallLeft ] = composite( captionLeft, titleCenter ); + activeTiles[ CaptionSmallRight ] = composite( captionRight, titleCenter ); + activeTiles[ CaptionSmallCenter ] = composite( captionCenter, titleCenter ); + + delete captionLeft; + delete captionRight; + delete captionCenter; + + // Now do the same with the large version + captionLeft = loadImage( "caption-large-left", captionColor ); + captionRight = loadImage( "caption-large-right", captionColor ); + captionCenter = loadImage( "caption-large-center", captionColor ); + + activeTiles[ CaptionLargeLeft ] = composite( captionLeft, titleCenter ); + activeTiles[ CaptionLargeRight ] = composite( captionRight, titleCenter ); + activeTiles[ CaptionLargeCenter ] = composite( captionCenter, titleCenter ); + + delete captionLeft; + delete captionRight; + delete captionCenter; + + // Create the titlebar center tile + activeTiles[ TitleCenter ] = new QPixmap( *titleCenter ); + + delete titleCenter; + + // Load the left & right border pixmaps + activeTiles[ BorderLeft ] = loadPixmap( "border-left", titleColor ); + activeTiles[ BorderRight ] = loadPixmap( "border-right", titleColor ); + + // Load the bottom grabbar pixmaps + if ( largeGrabBars ) { + activeTiles[ GrabBarLeft ] = loadPixmap( "grabbar-left", titleColor ); + activeTiles[ GrabBarRight ] = loadPixmap( "grabbar-right", titleColor ); + activeTiles[ GrabBarCenter ] = loadPixmap( "grabbar-center", titleColor ); + } else { + activeTiles[ GrabBarLeft ] = loadPixmap( "bottom-left", titleColor ); + activeTiles[ GrabBarRight ] = loadPixmap( "bottom-right", titleColor ); + activeTiles[ GrabBarCenter ] = loadPixmap( "bottom-center", titleColor ); + } + + // Inactive tiles + // ------------------------------------------------------------------------- + captionColor = KDecoration::options()->color( ColorTitleBar, false ); + titleColor = KDecoration::options()->color( ColorTitleBlend, false ); + + inactiveTiles[ TitleLeft ] = loadPixmap( "titlebar-left", titleColor ); + inactiveTiles[ TitleRight ] = loadPixmap( "titlebar-right", titleColor ); + + titleCenter = loadImage( "titlebar-center", titleColor ); + + captionLeft = loadImage( "caption-small-left", captionColor ); + captionRight = loadImage( "caption-small-right", captionColor ); + captionCenter = loadImage( "caption-small-center", captionColor ); + + inactiveTiles[ CaptionSmallLeft ] = composite( captionLeft, titleCenter ); + inactiveTiles[ CaptionSmallRight ] = composite( captionRight, titleCenter ); + inactiveTiles[ CaptionSmallCenter ] = composite( captionCenter, titleCenter ); + + delete captionLeft; + delete captionRight; + delete captionCenter; + + inactiveTiles[ TitleCenter ] = new QPixmap( *titleCenter ); + + delete titleCenter; + + inactiveTiles[ BorderLeft ] = loadPixmap( "border-left", titleColor ); + inactiveTiles[ BorderRight ] = loadPixmap( "border-right", titleColor ); + + if ( largeGrabBars ) { + inactiveTiles[ GrabBarLeft ] = loadPixmap( "grabbar-left", titleColor ); + inactiveTiles[ GrabBarRight ] = loadPixmap( "grabbar-right", titleColor ); + inactiveTiles[ GrabBarCenter ] = loadPixmap( "grabbar-center", titleColor ); + } else { + inactiveTiles[ GrabBarLeft ] = loadPixmap( "bottom-left", titleColor ); + inactiveTiles[ GrabBarRight ] = loadPixmap( "bottom-right", titleColor ); + inactiveTiles[ GrabBarCenter ] = loadPixmap( "bottom-center", titleColor ); + } + + // Buttons + // ------------------------------------------------------------------------- + buttonColor = QColor(); //KDecoration::options()->color( ButtonBg, true ); + + titleButtonRound = loadPixmap( "titlebutton-round"+size, buttonColor ); + titleButtonSquare = loadPixmap( "titlebutton-square"+size, buttonColor ); + + + // Prepare the tiles for use + // ------------------------------------------------------------------------- + if ( QApplication::reverseLayout() ) { + + // Fix lighting + flip( activeTiles[CaptionSmallLeft], activeTiles[CaptionSmallRight] ); + flip( inactiveTiles[CaptionSmallLeft], inactiveTiles[CaptionSmallRight] ); + + flip( activeTiles[CaptionLargeLeft], activeTiles[CaptionLargeRight] ); + + flip( activeTiles[TitleLeft], activeTiles[TitleRight] ); + flip( inactiveTiles[TitleLeft], inactiveTiles[TitleRight] ); + + flip( activeTiles[BorderLeft], activeTiles[BorderRight] ); + flip( inactiveTiles[BorderLeft], inactiveTiles[BorderRight] ); + + flip( activeTiles[GrabBarLeft], activeTiles[GrabBarRight] ); + flip( inactiveTiles[GrabBarLeft], inactiveTiles[GrabBarRight] ); + + ::flip( titleButtonRound ); + ::flip( titleButtonSquare ); + } + + // Pretile the center & border tiles for optimal performance + pretile( activeTiles[ CaptionSmallCenter ], 64, Qt::Horizontal ); + pretile( activeTiles[ CaptionLargeCenter ], 64, Qt::Horizontal ); + pretile( activeTiles[ TitleCenter ], 64, Qt::Horizontal ); + pretile( activeTiles[ GrabBarCenter ], 128, Qt::Horizontal ); + pretile( activeTiles[ BorderLeft ], 128, Qt::Vertical ); + pretile( activeTiles[ BorderRight ], 128, Qt::Vertical ); + + pretile( inactiveTiles[ CaptionSmallCenter ], 64, Qt::Horizontal ); + pretile( inactiveTiles[ TitleCenter ], 64, Qt::Horizontal ); + pretile( inactiveTiles[ GrabBarCenter ], 128, Qt::Horizontal ); + pretile( inactiveTiles[ BorderLeft ], 128, Qt::Vertical ); + pretile( inactiveTiles[ BorderRight ], 128, Qt::Vertical ); + + if (heightOffset > 0) { + addHeight (heightOffset, activeTiles[TitleLeft]); + addHeight (heightOffset, activeTiles[TitleCenter]); + addHeight (heightOffset, activeTiles[TitleRight]); + addHeight (heightOffset, activeTiles[CaptionSmallLeft]); + addHeight (heightOffset, activeTiles[CaptionSmallCenter]); + addHeight (heightOffset, activeTiles[CaptionSmallRight]); + addHeight (heightOffset, activeTiles[CaptionLargeLeft]); + addHeight (heightOffset, activeTiles[CaptionLargeCenter]); + addHeight (heightOffset, activeTiles[CaptionLargeRight]); + + addHeight (heightOffset, inactiveTiles[TitleLeft]); + addHeight (heightOffset, inactiveTiles[TitleCenter]); + addHeight (heightOffset, inactiveTiles[TitleRight]); + addHeight (heightOffset, inactiveTiles[CaptionSmallLeft]); + addHeight (heightOffset, inactiveTiles[CaptionSmallCenter]); + addHeight (heightOffset, inactiveTiles[CaptionSmallRight]); + } + + if (widthOffset > 0) { + addWidth (widthOffset, activeTiles[BorderLeft], true, activeTiles[GrabBarCenter]); + addWidth (widthOffset, activeTiles[BorderRight], false, activeTiles[GrabBarCenter]); + addWidth (widthOffset, inactiveTiles[BorderLeft], true, inactiveTiles[GrabBarCenter]); + addWidth (widthOffset, inactiveTiles[BorderRight], false, inactiveTiles[GrabBarCenter]); + + if (largeGrabBars) + widthOffset = widthOffset*3/2; + + addHeight (widthOffset, activeTiles[GrabBarLeft]); + addHeight (widthOffset, activeTiles[GrabBarCenter]); + addHeight (widthOffset, activeTiles[GrabBarRight]); + addHeight (widthOffset, inactiveTiles[GrabBarLeft]); + addHeight (widthOffset, inactiveTiles[GrabBarCenter]); + addHeight (widthOffset, inactiveTiles[GrabBarRight]); + } +} + + + +void KeramikHandler::destroyPixmaps() +{ + for ( int i = 0; i < NumTiles; i++ ) { + delete activeTiles[i]; + delete inactiveTiles[i]; + activeTiles[i] = NULL; + inactiveTiles[i] = NULL; + } + + delete titleButtonRound; + delete titleButtonSquare; +} + + +void KeramikHandler::addWidth (int width, QPixmap *&pix, bool left, QPixmap *bottomPix) { + int w = pix->width()+width; + int h = pix->height(); + + QPixmap *tmp = new QPixmap (w, h); + tmp->fill (); + QPainter p; + p.begin (tmp); + + for (int i = 0; i < h; i++) + p.drawPixmap (0, i, *bottomPix, i%2, 0, w,1); + + if (left) + p.drawPixmap(0, 0, *pix); + else + p.drawPixmap(width, 0, *pix); + + p.end(); + + delete pix; + pix = tmp; +} + + +void KeramikHandler::addHeight (int height, QPixmap *&pix) { + int w = pix->width(); + int h = pix->height()+height; + + QPixmap *tmp = new QPixmap (w, h); + QPainter p; + p.begin (tmp); + if (pix->height() > 10) { + p.drawPixmap(0, 0, *pix, 0, 0, w, 11); + for (int i = 0; i < height; i+=2) + p.drawPixmap(0, 11+i, *pix, 0, 11, w, 2); + p.drawPixmap(0, 11+height, *pix, 0, 11, w, -1); + } + else { + int lines = h-3; + int factor = pix->height()-3; + for (int i = 0; i < lines; i++) + p.drawPixmap(0, i, *pix, 0, i*factor/lines, w, 1); + p.drawPixmap(0, lines, *pix, 0, factor, w, 3); + } + p.end(); + + delete pix; + pix = tmp; +} + + +void KeramikHandler::flip( QPixmap *&pix1, QPixmap *&pix2 ) +{ + // Flip the pixmaps horizontally + QPixmap *tmp = new QPixmap( pix1->xForm( QWMatrix(-1,0,0,1,pix1->width(),0) ) ); + + delete pix1; + pix1 = new QPixmap( pix2->xForm( QWMatrix(-1,0,0,1,pix2->width(),0) ) ); + + delete pix2; + pix2 = tmp; +} + + +void KeramikHandler::pretile( QPixmap *&pix, int size, Qt::Orientation dir ) +{ + QPixmap *newpix; + QPainter p; + + if ( dir == Qt::Horizontal ) + newpix = new QPixmap( size, pix->height() ); + else + newpix = new QPixmap( pix->width(), size ); + + p.begin( newpix ); + p.drawTiledPixmap( newpix->rect(), *pix ) ; + p.end(); + + delete pix; + pix = newpix; +} + + +void KeramikHandler::readConfig() +{ + KConfig *c = new KConfig( "kwinkeramikrc" ); + + c->setGroup( "General" ); + showIcons = c->readBoolEntry( "ShowAppIcons", true ); + shadowedText = c->readBoolEntry( "UseShadowedText", true ); + smallCaptionBubbles = c->readBoolEntry( "SmallCaptionBubbles", false ); + largeGrabBars = c->readBoolEntry( "LargeGrabBars", true ); + + if ( ! settings_cache ) { + settings_cache = new SettingsCache; + settings_cache->largeGrabBars = largeGrabBars; + settings_cache->smallCaptionBubbles = smallCaptionBubbles; + } + + delete c; +} + + +QPixmap *KeramikHandler::composite( QImage *over, QImage *under ) +{ + QImage dest( over->width(), over->height(), 32 ); + int width = over->width(), height = over->height(); + + // Clear the destination image + Q_UINT32 *data = reinterpret_cast<Q_UINT32*>( dest.bits() ); + for (int i = 0; i < width * height; i++) + *(data++) = 0; + + // Copy the under image (bottom aligned) to the destination image + for (int y1 = height - under->height(), y2 = 0; y1 < height; y1++, y2++ ) + { + register Q_UINT32 *dst = reinterpret_cast<Q_UINT32*>( dest.scanLine(y1) ); + register Q_UINT32 *src = reinterpret_cast<Q_UINT32*>( under->scanLine(y2) ); + + for ( int x = 0; x < width; x++ ) + *(dst++) = *(src++); + } + + // Blend the over image onto the destination + register Q_UINT32 *dst = reinterpret_cast<Q_UINT32*>( dest.bits() ); + register Q_UINT32 *src = reinterpret_cast<Q_UINT32*>( over->bits() ); + for ( int i = 0; i < width * height; i++ ) + { + int r1 = qRed( *dst ), g1 = qGreen( *dst ), b1 = qBlue( *dst ); + int r2 = qRed( *src ), g2 = qGreen( *src ), b2 = qBlue( *src ); + int a = qAlpha( *src ); + + if ( a == 0xff ) + *dst = *src; + + else if ( a != 0x00 ) + *dst = qRgba( Q_UINT8( r1 + (((r2 - r1) * a) >> 8) ), + Q_UINT8( g1 + (((g2 - g1) * a) >> 8) ), + Q_UINT8( b1 + (((b2 - b1) * a) >> 8) ), + 0xff ); + + else if ( qAlpha(*dst) == 0x00 ) + *dst = 0; + + src++; dst++; + } + + // Create the final pixmap and return it + return new QPixmap( dest ); +} + + +QImage *KeramikHandler::loadImage( const QString &name, const QColor &col ) +{ + if ( col.isValid() ) { + QImage *img = new QImage( imageDb->image(name)->copy() ); + KIconEffect::colorize( *img, col, 1.0 ); + return img; + } else + return new QImage( imageDb->image(name)->copy() ); +} + + +QPixmap *KeramikHandler::loadPixmap( const QString &name, const QColor &col ) +{ + QImage *img = loadImage( name, col ); + QPixmap *pix = new QPixmap( *img ); + delete img; + + return pix; +} + + +bool KeramikHandler::reset( unsigned long changed ) +{ + keramik_initialized = false; + + bool needHardReset = false; + bool pixmapsInvalid = false; + + // Re-read the config file + readConfig(); + + if ( changed & SettingBorder ) + { + pixmapsInvalid = true; + needHardReset = true; + } + if ( changed & SettingFont ) + { + pixmapsInvalid = true; + needHardReset = true; + } + // Check if the color scheme has changed + if ( changed & SettingColors ) + { + pixmapsInvalid = true; + } + // Check if button positions have changed + + if ( changed & SettingButtons ) { + needHardReset = true; + } + + // Check if tooltips options have changed + if ( changed & SettingTooltips ) { + needHardReset = true; + } + + if ( (settings_cache->largeGrabBars != largeGrabBars) ) { + pixmapsInvalid = true; + needHardReset = true; + } + + if ( (settings_cache->smallCaptionBubbles != smallCaptionBubbles) ) { + needHardReset = true; + } + + // Update our config cache + settings_cache->largeGrabBars = largeGrabBars; + settings_cache->smallCaptionBubbles = smallCaptionBubbles; + + // Do we need to recreate the pixmaps? + if ( pixmapsInvalid ) { + destroyPixmaps(); + createPixmaps(); + } + + keramik_initialized = true; + + // Do we need to "hit the wooden hammer" ? + if ( !needHardReset ) + resetDecorations( changed ); + return needHardReset; +} + + +bool KeramikHandler::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonMenu: + case AbilityButtonOnAllDesktops: + case AbilityButtonSpacer: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + case AbilityButtonAboveOthers: + case AbilityButtonBelowOthers: + case AbilityButtonShade: + return true; + default: + return false; + }; +} + + +const QPixmap *KeramikHandler::tile( TilePixmap tilePix, bool active ) const +{ + return ( active ? activeTiles[ tilePix ] : inactiveTiles[ tilePix ] ); +} + +KDecoration* KeramikHandler::createDecoration( KDecorationBridge* bridge ) +{ + return new KeramikClient( bridge, this ); +} + +QValueList< KeramikHandler::BorderSize > KeramikHandler::borderSizes() const +{ // the list must be sorted + return QValueList< BorderSize >() << BorderNormal << BorderLarge << + BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized; +} + + +// ------------------------------------------------------------------------------------------- + + + +KeramikButton::KeramikButton( KeramikClient* c, const char *name, Button btn, const QString &tip, const int realizeBtns ) + : QButton( c->widget(), name ), + client( c ), button( btn ), hover( false ), lastbutton( NoButton ) +{ + realizeButtons = realizeBtns; + + QToolTip::add( this, tip ); // FRAME + setBackgroundMode( NoBackground ); + setCursor( arrowCursor ); + int size = clientHandler->roundButton()->height(); + setFixedSize( size, size ); + + setToggleButton( (button == OnAllDesktopsButton) ); +} + + +KeramikButton::~KeramikButton() +{ + // Empty. +} + + +void KeramikButton::enterEvent( QEvent *e ) +{ + QButton::enterEvent( e ); + + hover = true; + repaint( false ); +} + + +void KeramikButton::leaveEvent( QEvent *e ) +{ + QButton::leaveEvent( e ); + + hover = false; + repaint( false ); +} + + +void KeramikButton::mousePressEvent( QMouseEvent *e ) +{ + lastbutton = e->button(); + QMouseEvent me( e->type(), e->pos(), e->globalPos(), (e->button()&realizeButtons)?LeftButton:NoButton, e->state() ); + QButton::mousePressEvent( &me ); +} + + +void KeramikButton::mouseReleaseEvent( QMouseEvent *e ) +{ + lastbutton = e->button(); + QMouseEvent me( e->type(), e->pos(), e->globalPos(), (e->button()&realizeButtons)?LeftButton:NoButton, e->state() ); + QButton::mouseReleaseEvent( &me ); +} + + +void KeramikButton::drawButton( QPainter *p ) +{ + const QPixmap *pix; + const QBitmap *deco; + int size = clientHandler->roundButton()->height(); + + // Get the bevel from the client handler + if ( button == MenuButton || button == OnAllDesktopsButton || button == HelpButton ) + pix = clientHandler->roundButton(); + else + pix = clientHandler->squareButton(); + + // Draw the button background + const QPixmap *background = clientHandler->tile( TitleCenter, client->isActive() ); + p->drawPixmap( 0, 0, *background, + 0, (background->height()-size+1)/2, size, size ); + + if ( isDown() ) { + // Pressed + p->drawPixmap( QPoint(), *pix, QStyle::visualRect( QRect(2*size, 0, size, size), pix->rect() ) ); + p->translate( QApplication::reverseLayout() ? -1 : 1, 1 ); + } else if ( hover ) + // Mouse over + p->drawPixmap( QPoint(), *pix, QStyle::visualRect( QRect(size, 0, size, size), pix->rect() ) ); + else + // Normal + p->drawPixmap( QPoint(), *pix, QStyle::visualRect( QRect(0, 0, size, size), pix->rect() ) ); + + + // Draw the button deco on the bevel + switch ( button ) { + case MenuButton: + deco = clientHandler->buttonDeco( Menu ); + break; + + case OnAllDesktopsButton: + deco = clientHandler->buttonDeco( client->isOnAllDesktops() ? NotOnAllDesktops : OnAllDesktops ); + break; + + case HelpButton: + deco = clientHandler->buttonDeco( Help ); + // The '?' won't be flipped around in the ctor, so we need to + // shift it to the right to compensate for the button shadow + // being on the left side of the button in RTL mode. + if ( QApplication::reverseLayout() ) + p->translate( 2, 0 ); + break; + + case MinButton: + deco = clientHandler->buttonDeco( Minimize ); + break; + + case MaxButton: + deco = clientHandler->buttonDeco( client->maximizeMode() == KeramikClient::MaximizeFull ? Restore : Maximize ); + break; + + case CloseButton: + deco = clientHandler->buttonDeco( Close ); + break; + + case AboveButton: + deco = clientHandler->buttonDeco( client->keepAbove() ? AboveOn : AboveOff ); + break; + + case BelowButton: + deco = clientHandler->buttonDeco( client->keepBelow() ? BelowOn : BelowOff ); + break; + + case ShadeButton: + deco = clientHandler->buttonDeco( client->isSetShade() ? ShadeOn : ShadeOff ); + break; + + default: + deco = NULL; + } + + p->setPen( Qt::black ); // ### hardcoded color + p->drawPixmap( (size-17)/2, (size-17)/2, *deco ); +} + + + +// ------------------------------------------------------------------------------------------ + + + +KeramikClient::KeramikClient( KDecorationBridge* bridge, KDecorationFactory* factory ) + : KDecoration( bridge, factory ), + activeIcon( NULL ), inactiveIcon( NULL ), captionBufferDirty( true ), maskDirty( true ) +{ +} + +void KeramikClient::init() +{ + connect( this, SIGNAL( keepAboveChanged( bool )), SLOT( keepAboveChange( bool ))); + connect( this, SIGNAL( keepBelowChanged( bool )), SLOT( keepBelowChange( bool ))); + + createMainWidget( WStaticContents | WResizeNoErase | WRepaintNoErase ); + widget()->installEventFilter( this ); + + // Minimize flicker + widget()->setBackgroundMode( NoBackground ); + + for ( int i=0; i < NumButtons; i++ ) + button[i] = NULL; + + createLayout(); +} + +void KeramikClient::createLayout() +{ + + QVBoxLayout *mainLayout = new QVBoxLayout( widget() ); + QBoxLayout *titleLayout = new QBoxLayout( 0, QBoxLayout::LeftToRight, 0, 0, 0 ); + QHBoxLayout *windowLayout = new QHBoxLayout(); + + largeTitlebar = ( !maximizedVertical() && clientHandler->largeCaptionBubbles() ); + largeCaption = ( isActive() && largeTitlebar ); + + int grabBarHeight = clientHandler->grabBarHeight(); + int topSpacing = ( largeTitlebar ? 4 : 1 ); + int leftBorderWidth = clientHandler->tile( BorderLeft, true )->width(); + int rightBorderWidth = clientHandler->tile( BorderRight, true )->width(); + topSpacer = new QSpacerItem( 10, topSpacing, + QSizePolicy::Expanding, QSizePolicy::Minimum ); + + mainLayout->addItem( topSpacer ); + + mainLayout->addLayout( titleLayout ); // Titlebar + mainLayout->addLayout( windowLayout, 1 ); // Left border + window + right border + mainLayout->addSpacing( grabBarHeight ); // Bottom grab bar + + titleLayout->setSpacing( buttonSpacing ); + + titleLayout->addSpacing( buttonMargin ); // Left button margin + addButtons( titleLayout, options()->customButtonPositions() ? + options()->titleButtonsLeft() : QString(default_left) ); + + titlebar = new QSpacerItem( 10, clientHandler->titleBarHeight(largeTitlebar) + - topSpacing, QSizePolicy::Expanding, QSizePolicy::Minimum ); + titleLayout->addItem( titlebar ); + + titleLayout->addSpacing( buttonSpacing ); + addButtons( titleLayout, options()->customButtonPositions() ? + options()->titleButtonsRight() : QString(default_right) ); + titleLayout->addSpacing( buttonMargin - 1 ); // Right button margin + + windowLayout->addSpacing( leftBorderWidth ); // Left border + if( isPreview()) + windowLayout->addWidget( new QLabel( i18n( "<center><b>Keramik preview</b></center>" ), widget())); + else + windowLayout->addItem( new QSpacerItem( 0, 0 )); //no widget in the middle + windowLayout->addSpacing( rightBorderWidth ); // Right border +} + + +KeramikClient::~KeramikClient() +{ + delete activeIcon; + delete inactiveIcon; + + activeIcon = inactiveIcon = NULL; +} + + +void KeramikClient::reset( unsigned long ) +{ + if ( clientHandler->largeCaptionBubbles() && !largeTitlebar ) + { + // We're switching from small caption bubbles to large + if ( !maximizedVertical() ) { + topSpacer->changeSize( 10, 4, QSizePolicy::Expanding, QSizePolicy::Minimum ); + largeTitlebar = true; + largeCaption = isActive(); + + widget()->layout()->activate(); + + // Compensate for the titlebar size change + + // TODO This is wrong, this may break size increments (see bug #53784). + // FRAME + widget()->setGeometry( widget()->x(), widget()->y() - 3, width(), height() + 3 ); + } + } + else if ( !clientHandler->largeCaptionBubbles() && largeTitlebar ) + { + // We're switching from large caption bubbles to small + topSpacer->changeSize( 10, 1, QSizePolicy::Expanding, QSizePolicy::Minimum ); + largeTitlebar = largeCaption = false; + + widget()->layout()->activate(); + + // Compensate for the titlebar size change + // FRAME + widget()->setGeometry( widget()->x(), widget()->y() + 3, width(), height() - 3 ); + } + + calculateCaptionRect(); + + captionBufferDirty = maskDirty = true; + + // Only repaint the window if it's visible + // (i.e. not minimized and on the current desktop) + if ( widget()->isVisible() ) { + widget()->repaint( false ); + + for ( int i = 0; i < NumButtons; i++ ) + if ( button[i] ) button[i]->repaint( false ); + } +} + + +void KeramikClient::addButtons( QBoxLayout *layout, const QString &s ) +{ + for ( uint i=0; i < s.length(); i++ ) + { + switch ( s[i].latin1() ) + { + // Menu button + case 'M' : + if ( !button[MenuButton] ) { + button[MenuButton] = new KeramikButton( this, "menu", MenuButton, i18n("Menu"), LeftButton|RightButton ); + connect( button[MenuButton], SIGNAL( pressed() ), SLOT( menuButtonPressed() ) ); + layout->addWidget( button[MenuButton] ); + } + break; + + // OnAllDesktops button + case 'S' : + if ( !button[OnAllDesktopsButton] ) { + button[OnAllDesktopsButton] = new KeramikButton( this, "on_all_desktops", + OnAllDesktopsButton, isOnAllDesktops()?i18n("Not on all desktops"):i18n("On all desktops") ); + if(isOnAllDesktops()) + button[OnAllDesktopsButton]->toggle(); + connect( button[OnAllDesktopsButton], SIGNAL( clicked() ), SLOT( toggleOnAllDesktops() ) ); + layout->addWidget( button[OnAllDesktopsButton] ); + } + break; + + // Help button + case 'H' : + if ( !button[HelpButton] && providesContextHelp() ) { + button[HelpButton] = new KeramikButton( this, "help", HelpButton, i18n("Help") ); + connect( button[HelpButton], SIGNAL( clicked() ), SLOT( showContextHelp() ) ); + layout->addWidget( button[HelpButton] ); + } + break; + + // Minimize button + case 'I' : + if ( !button[MinButton] && isMinimizable() ) { + button[MinButton] = new KeramikButton( this, "minimize", MinButton, i18n("Minimize") ); + connect( button[MinButton], SIGNAL( clicked() ), SLOT( minimize() ) ); + layout->addWidget( button[MinButton] ); + } + break; + + // Maximize button + case 'A' : + if ( !button[MaxButton] && isMaximizable() ) { + button[MaxButton] = new KeramikButton( this, "maximize", MaxButton, i18n("Maximize"), LeftButton|MidButton|RightButton ); + connect( button[MaxButton], SIGNAL( clicked() ), SLOT( slotMaximize() ) ); + layout->addWidget( button[MaxButton] ); + } + break; + + // Close button + case 'X' : + if ( !button[CloseButton] && isCloseable() ) { + button[CloseButton] = new KeramikButton( this, "close", CloseButton, i18n("Close") ); + connect( button[CloseButton], SIGNAL( clicked() ), SLOT( closeWindow() ) ); + layout->addWidget( button[CloseButton] ); + } + break; + + // Above button + case 'F' : + if ( !button[AboveButton]) { + button[AboveButton] = new KeramikButton( this, "above", AboveButton, i18n("Keep Above Others") ); + connect( button[AboveButton], SIGNAL( clicked() ), SLOT( slotAbove() ) ); + layout->addWidget( button[AboveButton] ); + } + break; + + // Below button + case 'B' : + if ( !button[BelowButton]) { + button[BelowButton] = new KeramikButton( this, "below", BelowButton, i18n("Keep Below Others") ); + connect( button[BelowButton], SIGNAL( clicked() ), SLOT( slotBelow() ) ); + layout->addWidget( button[BelowButton] ); + } + break; + + // Shade button + case 'L' : + if ( !button[ShadeButton] && isShadeable() ) { + button[ShadeButton] = new KeramikButton( this, "shade", ShadeButton, + isSetShade() ? i18n("Unshade") : i18n( "Shade" )); + connect( button[ShadeButton], SIGNAL( clicked() ), SLOT( slotShade() ) ); + layout->addWidget( button[ShadeButton] ); + } + break; + + // Additional spacing + case '_' : + layout->addSpacing( buttonSpacing ); + break; + } + } +} + + +void KeramikClient::updateMask() +{ + if ( !keramik_initialized ) + return; + + // To maximize performance this code uses precalculated bounding rects + // to set the window mask. This saves us from having to allocate a 1bpp + // pixmap, paint the mask on it and then have the X server iterate + // over the pixels to compute the bounding rects from it. + + QRegion r; + register int w, y = 0; + int nrects; + + if ( QApplication::reverseLayout() ) { + + // If the caption bubble is visible and extends above the titlebar + if ( largeCaption && captionRect.width() >= 25 ) { + register int x = captionRect.left(); + w = captionRect.width(); + r += QRegion( x + 11, y++, w - 19, 1 ); + r += QRegion( x + 9, y++, w - 15, 1 ); + r += QRegion( x + 7, y++, w - 12, 1 ); + } else { + nrects = 8; + + // Do we have a large titlebar with a retracted caption bubble? + // (i.e. the style is set to use large caption bubbles, we're + // not maximized and not active) + if ( largeTitlebar ) + y = 3; + } + + w = width(); // FRAME + + // The rounded titlebar corners + r += QRegion( 9, y++, w - 17, 1 ); + r += QRegion( 7, y++, w - 13, 1 ); + r += QRegion( 5, y++, w - 9, 1 ); + r += QRegion( 4, y++, w - 7, 1 ); + r += QRegion( 3, y++, w - 5, 1 ); + r += QRegion( 2, y++, w - 4, 1 ); + r += QRegion( 1, y++, w - 2, 2 ); + } else { + + // If the caption bubble is visible and extends above the titlebar + if ( largeCaption && captionRect.width() >= 25 ) { + nrects = 11; + register int x = captionRect.left(); + w = captionRect.width(); + r += QRegion( x + 8, y++, w - 19, 1 ); + r += QRegion( x + 6, y++, w - 15, 1 ); + r += QRegion( x + 5, y++, w - 12, 1 ); + } else { + nrects = 8; + + // Do we have a large titlebar with a retracted caption bubble? + // (i.e. the style is set to use large caption bubbles, we're + // not maximized and not active) + if ( largeTitlebar ) + y = 3; + } + + w = width(); // FRAME + + // The rounded titlebar corners + r += QRegion( 8, y++, w - 17, 1 ); + r += QRegion( 6, y++, w - 13, 1 ); + r += QRegion( 4, y++, w - 9, 1 ); + r += QRegion( 3, y++, w - 7, 1 ); + r += QRegion( 2, y++, w - 5, 1 ); + r += QRegion( 2, y++, w - 4, 1 ); + r += QRegion( 1, y++, w - 2, 2 ); + } + + y++; + + // The part of the window below the titlebar + r += QRegion( 0, y, w, height() - y ); + + setMask( r, YXBanded ); + + maskDirty = false; +} + + +void KeramikClient::updateCaptionBuffer() +{ + if ( !keramik_initialized ) + return; + + bool active = isActive(); + QPixmap *icon = NULL; + + if ( captionBuffer.size() != captionRect.size() ) + captionBuffer.resize( captionRect.size() ); + + if ( captionBuffer.isNull() ) + return; + + QPainter p( &captionBuffer ); + + // Draw the caption bubble + if ( active && largeCaption ) { + p.drawPixmap( 0, 0, *clientHandler->tile( CaptionLargeLeft, true ) ); + p.drawTiledPixmap( 15, 0, captionRect.width() - 30, captionRect.height(), + *clientHandler->tile( CaptionLargeCenter, true ) ); + p.drawPixmap( captionRect.width() - 15, 0, *clientHandler->tile( CaptionLargeRight, true ) ); + } else { + p.drawPixmap( 0, 0, *clientHandler->tile( CaptionSmallLeft, active ) ); + p.drawTiledPixmap( 15, 0, captionRect.width() - 30, captionRect.height(), + *clientHandler->tile( CaptionSmallCenter, active ) ); + p.drawPixmap( captionRect.width() - 15, 0, *clientHandler->tile( CaptionSmallRight, active ) ); + } + + if ( clientHandler->showAppIcons() ) + { + if ( active ) { + if ( ! activeIcon ) + activeIcon = new QPixmap( this->icon().pixmap( QIconSet::Small, QIconSet::Normal )); // FRAME + icon = activeIcon; + } else { + if ( ! inactiveIcon ) { + QImage img = this->icon().pixmap( QIconSet::Small, QIconSet::Normal ).convertToImage(); + KIconEffect::semiTransparent( img ); + inactiveIcon = new QPixmap( img ); + } + icon = inactiveIcon; + } + } + + p.setFont( options()->font( active ) ); + int tw = p.fontMetrics().width( caption() ) + + ( clientHandler->showAppIcons() ? 16 + iconSpacing : 0 ); + + int xpos = QMAX( (captionRect.width() - tw) / 3, 8 ); + QRect tr = QStyle::visualRect( QRect(xpos, 1, captionRect.width() - xpos - 10, + captionRect.height() - 4), captionBuffer.rect() ); + + //p.setPen( Qt::red ); // debug + //p.drawRect( tr ); // debug + + // Application icon + if ( clientHandler->showAppIcons() ) + { + QRect iconRect = QStyle::visualRect( QRect(tr.x(), + 1 + (captionRect.height() - 4 - 16) / 2, 16, 16), tr ); + QRect r( icon->rect() ); + r.moveCenter( iconRect.center() ); + + if ( tr.width() > 16 ) { + p.drawPixmap( r, *icon ); + } else { + QRect sr( 0, 0, icon->width(), icon->height() ); + + if ( QApplication::reverseLayout() ) + sr.addCoords( icon->width() - tr.width(), 0, 0, 0 ); + else + sr.addCoords( 0, 0, -( icon->width() - tr.width() ), 0 ); + + p.drawPixmap( r.x() + sr.x(), r.y() + sr.y(), *icon, + sr.x(), sr.y(), sr.width(), sr.height() ); + } + + //p.drawRect( r ); // debug + + if ( QApplication::reverseLayout() ) + tr.addCoords( 0, 0, -(16 + iconSpacing), 0 ); + else + tr.addCoords( (16 + iconSpacing), 0, 0, 0 ); + } + + // Draw the titlebar text + int flags = AlignVCenter | SingleLine; + flags |= ( QApplication::reverseLayout() ? AlignRight : AlignLeft ); + + if ( clientHandler->useShadowedText() ) + { + p.translate( QApplication::reverseLayout() ? -1 : 1, 1 ); + //p.setPen( options()->color(ColorTitleBar, active).dark() ); + if (qGray(options()->color(ColorFont, active).rgb()) < 100) + p.setPen( QColor(200,200,200) ); + else + p.setPen( black ); + p.drawText( tr, flags, caption() ); + p.translate( QApplication::reverseLayout() ? 1 : -1, -1 ); + } + + p.setPen( options()->color( ColorFont, active ) ); + p.drawText( tr, flags, caption() ); + + captionBufferDirty = false; +} + + +void KeramikClient::calculateCaptionRect() +{ + QFontMetrics fm( options()->font(isActive()) ); + int cw = fm.width( caption() ) + 95; + int titleBaseY = ( largeTitlebar ? 3 : 0 ); + + if ( clientHandler->showAppIcons() ) + cw += 16 + 4; // icon width + space + + cw = QMIN( cw, titlebar->geometry().width() ); + captionRect = QStyle::visualRect( QRect(titlebar->geometry().x(), (largeCaption ? 0 : titleBaseY), + cw, clientHandler->titleBarHeight(largeCaption) ), + titlebar->geometry() ); +} + + +void KeramikClient::captionChange() +{ + QRect r( captionRect ); + calculateCaptionRect(); + + if ( r.size() != captionRect.size() ) + maskDirty = true; + + captionBufferDirty = true; + + widget()->repaint( r | captionRect, false ); +} + + +void KeramikClient::iconChange() +{ + if ( clientHandler->showAppIcons() ) { + + // Force updateCaptionBuffer() to recreate the cached icons + delete activeIcon; + + delete inactiveIcon; + + activeIcon = inactiveIcon = NULL; + + captionBufferDirty = true; + widget()->repaint( captionRect, false ); + } +} + + +void KeramikClient::activeChange() +{ + bool active = isActive(); + // Note: It's assumed that the same font will always be used for both active + // and inactive windows, since the fonts kcm hasn't supported setting + // different fonts for different window states for some time. + if ( largeTitlebar ) { + largeCaption = ( active && !maximizedVertical() ); + calculateCaptionRect(); + maskDirty = true; + } + + captionBufferDirty = true; + + widget()->repaint( false ); + + for ( int i=0; i < NumButtons; i++ ) + if ( button[i] ) button[i]->repaint( false ); +} + + +void KeramikClient::maximizeChange() +{ + if ( clientHandler->largeCaptionBubbles() ) + { + if ( maximizeMode() & MaximizeVertical ) { + // We've been maximized - shrink the titlebar by 3 pixels + topSpacer->changeSize( 10, 1, QSizePolicy::Expanding, QSizePolicy::Minimum ); + largeCaption = largeTitlebar = false; + + calculateCaptionRect(); + captionBufferDirty = maskDirty = true; + + widget()->layout()->activate(); + widget()->repaint( false ); + } else if (( maximizeMode() & MaximizeVertical ) == 0 && !largeTitlebar ) { + // We've been restored - enlarge the titlebar by 3 pixels + topSpacer->changeSize( 10, 4, QSizePolicy::Expanding, QSizePolicy::Minimum ); + largeCaption = largeTitlebar = true; + + calculateCaptionRect(); + captionBufferDirty = maskDirty = true; + + widget()->layout()->activate(); + widget()->repaint( false ); + } + } + + if ( button[ MaxButton ] ) { + QToolTip::remove( button[ MaxButton ] ); + QToolTip::add( button[ MaxButton ], maximizeMode() == MaximizeFull ? i18n("Restore") : i18n("Maximize") ); + button[ MaxButton ]->repaint(); + } +} + + +void KeramikClient::desktopChange() +{ + if ( button[ OnAllDesktopsButton ] ) + { + button[ OnAllDesktopsButton ]->repaint( true ); + QToolTip::remove( button[ OnAllDesktopsButton ] ); + QToolTip::add( button[ OnAllDesktopsButton ], isOnAllDesktops() ? i18n("Not on all desktops") : i18n("On all desktops") ); + } +} + + +void KeramikClient::shadeChange() +{ + if ( button[ ShadeButton ] ) + { + button[ ShadeButton ]->repaint( true ); + QToolTip::remove( button[ ShadeButton ] ); + QToolTip::add( button[ ShadeButton ], isSetShade() ? i18n("Unshade") : i18n("Shade") ); + } +} + + +void KeramikClient::keepAboveChange( bool ) +{ + if ( button[ AboveButton ] ) + button[ AboveButton ]->repaint( true ); +} + + +void KeramikClient::keepBelowChange( bool ) +{ + if ( button[ BelowButton ] ) + button[ BelowButton ]->repaint( true ); +} + + +void KeramikClient::menuButtonPressed() +{ + QPoint menuTop ( button[MenuButton]->rect().topLeft() ); + QPoint menuBottom ( button[MenuButton]->rect().bottomRight() ); + menuTop += QPoint(-6, -3); + menuBottom += QPoint(6, 3); + KDecorationFactory* f = factory(); + showWindowMenu( QRect( button[MenuButton]->mapToGlobal( menuTop ), + button[MenuButton]->mapToGlobal( menuBottom )) ); + if( !f->exists( this )) // 'this' was destroyed + return; + button[MenuButton]->setDown(false); +} + + +void KeramikClient::slotMaximize() +{ + maximize( button[ MaxButton ]->lastButton() ); +} + + +void KeramikClient::slotAbove() +{ + setKeepAbove( !keepAbove()); + button[ AboveButton ]->repaint( true ); +} + + +void KeramikClient::slotBelow() +{ + setKeepBelow( !keepBelow()); + button[ BelowButton ]->repaint( true ); +} + + +void KeramikClient::slotShade() +{ + setShade( !isSetShade()); + button[ ShadeButton ]->repaint( true ); +} + + +void KeramikClient::paintEvent( QPaintEvent *e ) +{ + if ( !keramik_initialized ) + return; + + QPainter p( widget()); + QRect updateRect( e->rect() ); + bool active = isActive(); + + int titleBaseY = ( largeTitlebar ? 3 : 0 ); + int titleBarHeight = clientHandler->titleBarHeight( largeTitlebar ); + int grabBarHeight = clientHandler->grabBarHeight(); + int leftBorderWidth = clientHandler->tile( BorderLeft, active )->width(); + int rightBorderWidth = clientHandler->tile( BorderRight, active )->width(); + + if ( maskDirty ) + updateMask(); + + // Titlebar + // ----------------------------------------------------------------------- + if ( updateRect.y() < titleBarHeight ) + { + int titleBarBaseHeight = titleBarHeight - titleBaseY; + + if ( captionBufferDirty ) + updateCaptionBuffer(); + + // Top left corner + if ( updateRect.x() < 15 ) + p.drawPixmap( 0, titleBaseY, + *clientHandler->tile( TitleLeft, active ) ); + + // Space between the top left corner and the caption bubble + if ( updateRect.x() < captionRect.left() && updateRect.right() >= 15 ) { + int x1 = QMAX( 15, updateRect.x() ); + int x2 = QMIN( captionRect.left(), updateRect.right() ); + + p.drawTiledPixmap( x1, titleBaseY, x2 - x1 + 1, titleBarBaseHeight, + *clientHandler->tile( TitleCenter, active ) ); + } + + // Caption bubble + if ( updateRect.x() <= captionRect.right() && updateRect.right() > 15 ) { + if ( captionRect.width() >= 25 ) + p.drawPixmap( captionRect.left(), active ? 0 : titleBaseY, captionBuffer ); + else + p.drawTiledPixmap( captionRect.x(), titleBaseY, captionRect.width(), + titleBarBaseHeight, *clientHandler->tile( TitleCenter, active ) ); + } + + // Space between the caption bubble and the top right corner + if ( updateRect.right() > captionRect.right() && updateRect.x() < width() - 15 ) { // FRAME + int x1 = QMAX( captionRect.right() + 1, updateRect.x() ); + int x2 = QMIN( width() - 15, updateRect.right() ); + + p.drawTiledPixmap( x1, titleBaseY, x2 - x1 + 1, titleBarBaseHeight, + *clientHandler->tile( TitleCenter, active ) ); + } + + // Top right corner + if ( updateRect.right() >= width() - 15 ) + p.drawPixmap( width() - 15, titleBaseY, + *clientHandler->tile( TitleRight, active ) ); + } + + // Borders + // ----------------------------------------------------------------------- + if ( updateRect.bottom() >= titleBarHeight && + updateRect.top() < height() - grabBarHeight ) + { + int top = QMAX( titleBarHeight, updateRect.top() ); + int bottom = QMIN( updateRect.bottom(), height() - grabBarHeight ); + + // Left border + if ( updateRect.x() < leftBorderWidth ) + p.drawTiledPixmap( 0, top, leftBorderWidth, bottom - top + 1, + *clientHandler->tile( BorderLeft, active ) ); + + // Right border + if ( e->rect().right() > width() - rightBorderWidth - 1 ) + p.drawTiledPixmap( width() - rightBorderWidth, top, rightBorderWidth, + bottom - top + 1, *clientHandler->tile( BorderRight, active ) ); + } + + // Bottom grab bar + // ----------------------------------------------------------------------- + if ( updateRect.bottom() >= height() - grabBarHeight ) { + // Bottom left corner + if ( updateRect.x() < 9 ) + p.drawPixmap( 0, height() - grabBarHeight, + *clientHandler->tile( GrabBarLeft, active ) ); + + // Space between the left corner and the right corner + if ( updateRect.x() < width() - 9 ) { + int x1 = QMAX( 9, updateRect.x() ); + int x2 = QMIN( width() - 9, updateRect.right() ); + + p.drawTiledPixmap( x1, height() - grabBarHeight, x2 - x1 + 1, + grabBarHeight, *clientHandler->tile( GrabBarCenter, active ) ); + } + + // Bottom right corner + if ( updateRect.right() > width() - 9 ) + p.drawPixmap( width() - 9, height() - grabBarHeight, + *clientHandler->tile( GrabBarRight, active ) ); + } + + // Extra drawline for the 1 pixel empty space QLayout leaves when a window is shaded. + p.setPen( options()->color( ColorTitleBlend, active ) ); + p.drawLine( leftBorderWidth, height() - grabBarHeight - 1, + width() - rightBorderWidth - 1, height() - grabBarHeight - 1 ); +} + + +void KeramikClient::resizeEvent( QResizeEvent *e ) +{ +// FRAME Client::resizeEvent( e ); + + QRect r( captionRect ); + calculateCaptionRect(); + + if ( r.size() != captionRect.size() ) + captionBufferDirty = true; + + maskDirty = true; + + if ( widget()->isVisible() ) + { + widget()->update( widget()->rect() ); + int dx = 0; + int dy = 0; + + if ( e->oldSize().width() != width() ) + dx = 32 + QABS( e->oldSize().width() - width() ); + + if ( e->oldSize().height() != height() ) + dy = 8 + QABS( e->oldSize().height() - height() ); + + if ( dy ) + widget()->update( 0, height() - dy + 1, width(), dy ); + + if ( dx ) + { + widget()->update( width() - dx + 1, 0, dx, height() ); + widget()->update( QRect( QPoint(4,4), titlebar->geometry().bottomLeft() - QPoint(1,0) ) ); + widget()->update( QRect( titlebar->geometry().topRight(), QPoint( width() - 4, + titlebar->geometry().bottom() ) ) ); + // Titlebar needs no paint event + QApplication::postEvent( this, new QPaintEvent( titlebar->geometry(), FALSE ) ); + } + } +} + + +void KeramikClient::mouseDoubleClickEvent( QMouseEvent *e ) +{ + if ( e->button() == LeftButton + && QRect( 0, 0, width(), clientHandler->titleBarHeight( largeTitlebar ) ).contains( e->pos() ) ) + titlebarDblClickOperation(); +} + +void KeramikClient::wheelEvent( QWheelEvent *e ) +{ + if (isSetShade() + || QRect( 0, 0, width(), clientHandler->titleBarHeight( largeTitlebar ) ).contains( e->pos() ) ) + titlebarMouseWheelOperation( e->delta()); +} + +KeramikClient::Position KeramikClient::mousePosition( const QPoint &p ) const +{ + int titleBaseY = (largeTitlebar ? 3 : 0); + + int leftBorder = clientHandler->tile( BorderLeft, true )->width(); + int rightBorder = width() - clientHandler->tile( BorderRight, true )->width() - 1; + int bottomBorder = height() - clientHandler->grabBarHeight() - 1; + int bottomCornerSize = 3*clientHandler->tile( BorderRight, true )->width()/2 + 24; + + // Test if the mouse is over the titlebar area + if ( p.y() < titleBaseY + 11 ) { + // Test for the top left corner + if ( p.x() < leftBorder + 11 ) { + if ( (p.y() < titleBaseY + 3 && p.x() < leftBorder + 11) || + (p.y() < titleBaseY + 6 && p.x() < leftBorder + 6) || + (p.y() < titleBaseY + 11 && p.x() < leftBorder + 3) ) + return PositionTopLeft; + } + + // Test for the top right corner + if ( p.x() > rightBorder - 11 ) { + if ( (p.y() < titleBaseY + 3 && p.x() > rightBorder - 11) || + (p.y() < titleBaseY + 6 && p.x() > rightBorder - 6) || + (p.y() < titleBaseY + 11 && p.x() > rightBorder - 3) ) + return PositionTopRight; + } + + // Test for the top border + if ( p.y() <= 3 || (p.y() <= titleBaseY+3 && + (p.x() < captionRect.left() || p.x() > captionRect.right()) ) ) + return PositionTop; + + // The cursor must be over the center of the titlebar. + return PositionCenter; + } + + // Test the sides + else if ( p.y() < bottomBorder ) { + // Test for the left side + if ( p.x() < leftBorder ) { + if ( p.y() < height() - bottomCornerSize ) + return PositionLeft; + else + return PositionBottomLeft; + } + + // Test for the right side + else if ( p.x() > rightBorder ) { + if ( p.y() < height() - bottomCornerSize ) + return PositionRight; + else + return PositionBottomRight; + } + + // The cursor must be over the center of the window + return PositionCenter; + } + + // Test the grab bar / bottom border + else { + // Test for the bottom left corner + if ( p.x() < bottomCornerSize ) + return PositionBottomLeft; + + // Test for the bottom right corner + else if ( p.x() > width() - bottomCornerSize - 1 ) + return PositionBottomRight; + + // The cursor must be over the bottom border + return PositionBottom; + } + + // We should never get here + return PositionCenter; +} + + +void KeramikClient::resize( const QSize& s ) +{ + widget()->resize( s ); +} + + +void KeramikClient::borders( int& left, int& right, int& top, int& bottom ) const +{ + int titleBarHeight = clientHandler->titleBarHeight( clientHandler->largeCaptionBubbles() ); + int grabBarHeight = clientHandler->grabBarHeight(); + int leftBorderWidth = clientHandler->tile( BorderLeft, isActive() )->width(); + int rightBorderWidth = clientHandler->tile( BorderRight, isActive() )->width(); + + left = leftBorderWidth; + right = rightBorderWidth; + top = titleBarHeight; + bottom = grabBarHeight; + + if ( ( maximizeMode() & MaximizeHorizontal ) && !options()->moveResizeMaximizedWindows()) + left = right = 0; + if( maximizeMode() & MaximizeVertical) + { + top = clientHandler->titleBarHeight( false ); + if( !options()->moveResizeMaximizedWindows()) + bottom = 0; + } +} + + +QSize KeramikClient::minimumSize() const +{ + return widget()->minimumSize(); +} + + +bool KeramikClient::eventFilter( QObject* o, QEvent* e ) +{ + if ( o != widget() ) + return false; + + switch ( e->type() ) + { + case QEvent::Resize: + resizeEvent( static_cast< QResizeEvent* >( e ) ); + return true; + + case QEvent::Paint: + paintEvent( static_cast< QPaintEvent* >( e ) ); + return true; + + case QEvent::MouseButtonDblClick: + mouseDoubleClickEvent( static_cast< QMouseEvent* >( e ) ); + return true; + + case QEvent::MouseButtonPress: + processMousePressEvent( static_cast< QMouseEvent* >( e ) ); + return true; + + case QEvent::Wheel: + wheelEvent( static_cast< QWheelEvent* >( e )); + return true; + + default: + return false; + } +} + +} // namespace Keramik + + + +// ------------------------------------------------------------------------------------------- + + + +extern "C" +{ + KDE_EXPORT KDecorationFactory *create_factory() + { + Keramik::clientHandler = new Keramik::KeramikHandler(); + return Keramik::clientHandler; + } +} + + + +// vim: set noet ts=4 sw=4: diff --git a/kwin/clients/keramik/keramik.desktop b/kwin/clients/keramik/keramik.desktop new file mode 100644 index 000000000..c58f8d522 --- /dev/null +++ b/kwin/clients/keramik/keramik.desktop @@ -0,0 +1,31 @@ +[Desktop Entry] +Name=Keramik +Name[ar]=قرميدي +Name[be]=Кераміка +Name[bn]=কেরামিক +Name[cs]=Keramika +Name[eo]=Ceramiko +Name[fa]=کرامیک +Name[fy]=Keramyk +Name[hi]=के-रामिक +Name[it]=Ceramica +Name[lo]=ເຄຣາມິກ - K +Name[lv]=Keramika +Name[mk]=Керамик +Name[mn]=Ваар +Name[nb]=Keramikk +Name[ne]=केरामिक +Name[nn]=Keramikk +Name[pa]=ਕੀਰਾਮਿਕ +Name[se]=Bálseduodji +Name[sr]=Керамика +Name[sr@Latn]=Keramika +Name[ta]=கெராமிக் +Name[te]=కెరామిక్ +Name[th]=เครามิก +Name[uk]=Керамік +Name[uz]=Keramika +Name[uz@cyrillic]=Керамика +Name[vi]=Gốm +X-KDE-Library=kwin3_keramik + diff --git a/kwin/clients/keramik/keramik.h b/kwin/clients/keramik/keramik.h new file mode 100644 index 000000000..40a24a058 --- /dev/null +++ b/kwin/clients/keramik/keramik.h @@ -0,0 +1,201 @@ +/* + * + * Keramik KWin client (version 0.8) + * + * Copyright (C) 2002 Fredrik Hglund <fredrik@kde.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the license, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef __KERAMIK_H +#define __KERAMIK_H + +#include <qbutton.h> +#include <kdecoration.h> +#include <kdecorationfactory.h> + +#include "tiles.h" + +class QSpacerItem; + +namespace Keramik { + + enum TilePixmap { TitleLeft=0, TitleCenter, TitleRight, + CaptionSmallLeft, CaptionSmallCenter, CaptionSmallRight, + CaptionLargeLeft, CaptionLargeCenter, CaptionLargeRight, + GrabBarLeft, GrabBarCenter, GrabBarRight, + BorderLeft, BorderRight, NumTiles }; + + enum Button { MenuButton=0, OnAllDesktopsButton, HelpButton, MinButton, + MaxButton, CloseButton, AboveButton, BelowButton, ShadeButton, + NumButtons }; + + enum ButtonDeco { Menu=0, OnAllDesktops, NotOnAllDesktops, Help, Minimize, Maximize, + Restore, Close, AboveOn, AboveOff, BelowOn, BelowOff, ShadeOn, ShadeOff, + NumButtonDecos }; + + struct SettingsCache + { + bool largeGrabBars:1; + bool smallCaptionBubbles:1; + }; + + class KeramikHandler : public KDecorationFactory + { + public: + KeramikHandler(); + ~KeramikHandler(); + + virtual QValueList< BorderSize > borderSizes() const; + virtual bool reset( unsigned long changed ); + virtual KDecoration* createDecoration( KDecorationBridge* ); + virtual bool supports( Ability ability ); + + bool showAppIcons() const { return showIcons; } + bool useShadowedText() const { return shadowedText; } + bool largeCaptionBubbles() const { return !smallCaptionBubbles; } + + int titleBarHeight( bool large ) const { + return ( large ? activeTiles[CaptionLargeCenter]->height() + : activeTiles[CaptionSmallCenter]->height() ); + } + + int grabBarHeight() const + { return activeTiles[GrabBarCenter]->height(); } + + const QPixmap *roundButton() const { return titleButtonRound; } + const QPixmap *squareButton() const { return titleButtonSquare; } + const QBitmap *buttonDeco( ButtonDeco deco ) const + { return buttonDecos[ deco ]; } + + inline const QPixmap *tile( TilePixmap tilePix, bool active ) const; + + private: + void readConfig(); + void createPixmaps(); + void destroyPixmaps(); + + void addWidth (int width, QPixmap *&pix, bool left, QPixmap *bottomPix); + void addHeight (int height, QPixmap *&pix); + void flip( QPixmap *&, QPixmap *& ); + void pretile( QPixmap *&, int, Qt::Orientation ); + QPixmap *composite( QImage *, QImage * ); + QImage *loadImage( const QString &, const QColor & ); + QPixmap *loadPixmap( const QString &, const QColor & ); + + bool showIcons:1, shadowedText:1, + smallCaptionBubbles:1, largeGrabBars:1; + SettingsCache *settings_cache; + KeramikImageDb *imageDb; + + QPixmap *activeTiles[ NumTiles ]; + QPixmap *inactiveTiles[ NumTiles ]; + QBitmap *buttonDecos[ NumButtonDecos ]; + + QPixmap *titleButtonRound, *titleButtonSquare; + + }; // class KeramikHandler + + class KeramikClient; + class KeramikButton : public QButton + { + public: + KeramikButton( KeramikClient *, const char *, Button, const QString &, const int realizeBtns = LeftButton ); + ~KeramikButton(); + + ButtonState lastButton() const { return lastbutton; } + + private: + void enterEvent( QEvent * ); + void leaveEvent( QEvent * ); + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void drawButton( QPainter * ); + + private: + KeramikClient *client; + Button button; + bool hover; + ButtonState lastbutton; + int realizeButtons; + }; // class KeramikButton + + + class KeramikClient : public KDecoration + { + Q_OBJECT + + public: + + KeramikClient( KDecorationBridge* bridge, KDecorationFactory* factory ); + ~KeramikClient(); + virtual void init(); + virtual void reset( unsigned long changed ); + virtual Position mousePosition( const QPoint& p ) const; + virtual void borders( int& left, int& right, int& top, int& bottom ) const; + virtual void resize( const QSize& s ); + virtual QSize minimumSize() const; + virtual bool eventFilter( QObject* o, QEvent* e ); + virtual void activeChange(); + virtual void captionChange(); + virtual void maximizeChange(); + virtual void desktopChange(); + virtual void shadeChange(); + + private: + void createLayout(); + void addButtons( QBoxLayout*, const QString & ); + void updateMask(); // FRAME + void updateCaptionBuffer(); + void iconChange(); + void resizeEvent( QResizeEvent *); // FRAME + void paintEvent( QPaintEvent *); // FRAME + void mouseDoubleClickEvent( QMouseEvent * ); // FRAME + void wheelEvent( QWheelEvent *); //FRAME + int width() const { return widget()->width(); } + int height() const { return widget()->height(); } + + void calculateCaptionRect(); + + inline bool maximizedVertical() const { + return ( maximizeMode() & MaximizeVertical ); + } + + private slots: + void menuButtonPressed(); + void slotMaximize(); + void slotAbove(); + void slotBelow(); + void slotShade(); + void keepAboveChange( bool ); + void keepBelowChange( bool ); + + private: + QSpacerItem *topSpacer, *titlebar; + KeramikButton *button[ NumButtons ]; + QRect captionRect; + QPixmap captionBuffer; + QPixmap *activeIcon, *inactiveIcon; + bool captionBufferDirty:1, maskDirty:1; + bool largeCaption:1, largeTitlebar:1; + }; // class KeramikClient + +} // namespace Keramik + +#endif // ___KERAMIK_H + +// vim: set noet ts=4 sw=4: diff --git a/kwin/clients/keramik/pics/border-left.png b/kwin/clients/keramik/pics/border-left.png Binary files differnew file mode 100644 index 000000000..298a0aa9a --- /dev/null +++ b/kwin/clients/keramik/pics/border-left.png diff --git a/kwin/clients/keramik/pics/border-right.png b/kwin/clients/keramik/pics/border-right.png Binary files differnew file mode 100644 index 000000000..1ca876b0d --- /dev/null +++ b/kwin/clients/keramik/pics/border-right.png diff --git a/kwin/clients/keramik/pics/bottom-center.png b/kwin/clients/keramik/pics/bottom-center.png Binary files differnew file mode 100644 index 000000000..d6d002534 --- /dev/null +++ b/kwin/clients/keramik/pics/bottom-center.png diff --git a/kwin/clients/keramik/pics/bottom-left.png b/kwin/clients/keramik/pics/bottom-left.png Binary files differnew file mode 100644 index 000000000..16a2ab982 --- /dev/null +++ b/kwin/clients/keramik/pics/bottom-left.png diff --git a/kwin/clients/keramik/pics/bottom-right.png b/kwin/clients/keramik/pics/bottom-right.png Binary files differnew file mode 100644 index 000000000..2d4045432 --- /dev/null +++ b/kwin/clients/keramik/pics/bottom-right.png diff --git a/kwin/clients/keramik/pics/caption-large-center.png b/kwin/clients/keramik/pics/caption-large-center.png Binary files differnew file mode 100644 index 000000000..786276b55 --- /dev/null +++ b/kwin/clients/keramik/pics/caption-large-center.png diff --git a/kwin/clients/keramik/pics/caption-large-left.png b/kwin/clients/keramik/pics/caption-large-left.png Binary files differnew file mode 100644 index 000000000..7d96bdcea --- /dev/null +++ b/kwin/clients/keramik/pics/caption-large-left.png diff --git a/kwin/clients/keramik/pics/caption-large-right.png b/kwin/clients/keramik/pics/caption-large-right.png Binary files differnew file mode 100644 index 000000000..3055d13a7 --- /dev/null +++ b/kwin/clients/keramik/pics/caption-large-right.png diff --git a/kwin/clients/keramik/pics/caption-small-center.png b/kwin/clients/keramik/pics/caption-small-center.png Binary files differnew file mode 100644 index 000000000..78636dfd1 --- /dev/null +++ b/kwin/clients/keramik/pics/caption-small-center.png diff --git a/kwin/clients/keramik/pics/caption-small-left.png b/kwin/clients/keramik/pics/caption-small-left.png Binary files differnew file mode 100644 index 000000000..cb7e69d73 --- /dev/null +++ b/kwin/clients/keramik/pics/caption-small-left.png diff --git a/kwin/clients/keramik/pics/caption-small-right.png b/kwin/clients/keramik/pics/caption-small-right.png Binary files differnew file mode 100644 index 000000000..9fc74640e --- /dev/null +++ b/kwin/clients/keramik/pics/caption-small-right.png diff --git a/kwin/clients/keramik/pics/grabbar-center.png b/kwin/clients/keramik/pics/grabbar-center.png Binary files differnew file mode 100644 index 000000000..b623b5df2 --- /dev/null +++ b/kwin/clients/keramik/pics/grabbar-center.png diff --git a/kwin/clients/keramik/pics/grabbar-left.png b/kwin/clients/keramik/pics/grabbar-left.png Binary files differnew file mode 100644 index 000000000..653f5ccfb --- /dev/null +++ b/kwin/clients/keramik/pics/grabbar-left.png diff --git a/kwin/clients/keramik/pics/grabbar-right.png b/kwin/clients/keramik/pics/grabbar-right.png Binary files differnew file mode 100644 index 000000000..248d55410 --- /dev/null +++ b/kwin/clients/keramik/pics/grabbar-right.png diff --git a/kwin/clients/keramik/pics/titlebar-center.png b/kwin/clients/keramik/pics/titlebar-center.png Binary files differnew file mode 100644 index 000000000..bac31dc55 --- /dev/null +++ b/kwin/clients/keramik/pics/titlebar-center.png diff --git a/kwin/clients/keramik/pics/titlebar-left.png b/kwin/clients/keramik/pics/titlebar-left.png Binary files differnew file mode 100644 index 000000000..bc8ee5ca3 --- /dev/null +++ b/kwin/clients/keramik/pics/titlebar-left.png diff --git a/kwin/clients/keramik/pics/titlebar-right.png b/kwin/clients/keramik/pics/titlebar-right.png Binary files differnew file mode 100644 index 000000000..d34a465f5 --- /dev/null +++ b/kwin/clients/keramik/pics/titlebar-right.png diff --git a/kwin/clients/keramik/pics/titlebutton-round-huge.png b/kwin/clients/keramik/pics/titlebutton-round-huge.png Binary files differnew file mode 100644 index 000000000..c5ca19342 --- /dev/null +++ b/kwin/clients/keramik/pics/titlebutton-round-huge.png diff --git a/kwin/clients/keramik/pics/titlebutton-round-large.png b/kwin/clients/keramik/pics/titlebutton-round-large.png Binary files differnew file mode 100644 index 000000000..9c3267bf9 --- /dev/null +++ b/kwin/clients/keramik/pics/titlebutton-round-large.png diff --git a/kwin/clients/keramik/pics/titlebutton-round.png b/kwin/clients/keramik/pics/titlebutton-round.png Binary files differnew file mode 100644 index 000000000..dd2369af3 --- /dev/null +++ b/kwin/clients/keramik/pics/titlebutton-round.png diff --git a/kwin/clients/keramik/pics/titlebutton-square-huge.png b/kwin/clients/keramik/pics/titlebutton-square-huge.png Binary files differnew file mode 100644 index 000000000..a908a9f27 --- /dev/null +++ b/kwin/clients/keramik/pics/titlebutton-square-huge.png diff --git a/kwin/clients/keramik/pics/titlebutton-square-large.png b/kwin/clients/keramik/pics/titlebutton-square-large.png Binary files differnew file mode 100644 index 000000000..6e3ada47b --- /dev/null +++ b/kwin/clients/keramik/pics/titlebutton-square-large.png diff --git a/kwin/clients/keramik/pics/titlebutton-square.png b/kwin/clients/keramik/pics/titlebutton-square.png Binary files differnew file mode 100644 index 000000000..871cf751a --- /dev/null +++ b/kwin/clients/keramik/pics/titlebutton-square.png diff --git a/kwin/clients/kwmtheme/Makefile.am b/kwin/clients/kwmtheme/Makefile.am new file mode 100644 index 000000000..f6c611180 --- /dev/null +++ b/kwin/clients/kwmtheme/Makefile.am @@ -0,0 +1,15 @@ +SUBDIRS=cli_installer +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = kwin3_kwmtheme.la + +kwin3_kwmtheme_la_SOURCES = kwmthemeclient.cpp +kwin3_kwmtheme_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_kwmtheme_la_LIBADD = -lkdecorations + +METASOURCES = AUTO +noinst_HEADERS = kwmthemeclient.h + +lnkdir = $(kde_datadir)/kwin +lnk_DATA = kwmtheme.desktop + diff --git a/kwin/clients/kwmtheme/cli_installer/Makefile.am b/kwin/clients/kwmtheme/cli_installer/Makefile.am new file mode 100644 index 000000000..feb31c424 --- /dev/null +++ b/kwin/clients/kwmtheme/cli_installer/Makefile.am @@ -0,0 +1,18 @@ + +# set the include path for X, qt and KDE +INCLUDES= $(all_includes) + +####### This part is very kwmtheme specific +# you can add here more. This one gets installed +bin_PROGRAMS = kwmtheme + +# Which sources should be compiled for kwmtheme. +kwmtheme_SOURCES = main.cpp + +# the library search path. +kwmtheme_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +# the libraries to link against. Be aware of the order. First the libraries, +# that depend on the following ones. +kwmtheme_LDADD = $(LIB_KDECORE) + diff --git a/kwin/clients/kwmtheme/cli_installer/main.cpp b/kwin/clients/kwmtheme/cli_installer/main.cpp new file mode 100644 index 000000000..bdc04a1de --- /dev/null +++ b/kwin/clients/kwmtheme/cli_installer/main.cpp @@ -0,0 +1,166 @@ +#include <qfile.h> +#include <qdir.h> +#include <kapplication.h> +#include <ksimpleconfig.h> +#include <kglobal.h> +#include <kdebug.h> +#include <kstandarddirs.h> +#include <kcmdlineargs.h> +#include <klocale.h> + +static const char description[] = + I18N_NOOP("Installs a KWM theme"); + +static KCmdLineOptions options[] = +{ + { "+[file]", I18N_NOOP("Path to a theme config file"), 0 }, + KCmdLineLastOption +}; + +void copy(const QString &src, const QString &dest) +{ + QFile copyInput(src); + QFile copyOutput(dest); + if(!copyInput.open(IO_ReadOnly)){ + kdWarning() << "Couldn't open " << src << endl; + return; + } + if(!copyOutput.open(IO_WriteOnly)){ + kdWarning() << "Couldn't open " << dest << endl; + copyInput.close(); + return; + } + while(!copyInput.atEnd()){ + copyOutput.putch(copyInput.getch()); + } + copyInput.close(); + copyOutput.close(); +} + +int main(int argc, char **argv) +{ + KCmdLineArgs::init(argc, argv, "kwmtheme", description, "0.1"); + KCmdLineArgs::addCmdLineOptions( options ); + KApplication app(argc, argv); + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if(!args->count()){ + kdWarning() << "You need to specify the path to a theme config file!" << endl; + return(1); + } + + QString srcStr = QString(QFile::decodeName(args->arg(0))); + QFile f(srcStr); + QString tmpStr; + + if(!f.exists()){ + kdWarning() << "Specified theme config file doesn't exist!" << endl; + return(2); + } + + QStringList appDirs = KGlobal::dirs()->findDirs("data", "kwin"); + QString localDirStr = *(appDirs.end()); + if(localDirStr.isEmpty()){ + localDirStr = KGlobal::dirs()->saveLocation("data", "kwin"); + } + localDirStr += "/pics/"; + if(!QFile::exists(localDirStr)) + QDir().mkdir(localDirStr); + + QFileInfo fi(f); + KSimpleConfig input(fi.absFilePath()); + srcStr = fi.dirPath(true) + "/"; + KConfig *output = KGlobal::config(); + input.setGroup("Window Border"); + output->setGroup("General"); + + tmpStr = input.readEntry("shapePixmapTop"); + if(!tmpStr.isEmpty()){ + copy(srcStr+tmpStr, localDirStr+tmpStr); + } + output->writeEntry("wm_top", tmpStr, true, true); + tmpStr = input.readEntry("shapePixmapBottom"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("wm_bottom", tmpStr, true, true); + tmpStr = input.readEntry("shapePixmapLeft"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("wm_left", tmpStr, true, true); + tmpStr = input.readEntry("shapePixmapRight"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("wm_right", tmpStr, true, true); + tmpStr = input.readEntry("shapePixmapTopLeft"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("wm_topleft", tmpStr, true, true); + tmpStr = input.readEntry("shapePixmapTopRight"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("wm_topright", tmpStr, true, true); + tmpStr = input.readEntry("shapePixmapBottomLeft"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("wm_bottomleft", tmpStr, true, true); + tmpStr = input.readEntry("shapePixmapBottomRight"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("wm_bottomright", tmpStr, true, true); + + + input.setGroup("Window Titlebar"); + output->writeEntry("TitleAlignment", input.readEntry("TitleAlignment"), true, true); + output->writeEntry("PixmapUnderTitleText", input.readEntry("PixmapUnderTitleText"), true, true); + output->writeEntry("TitleFrameShaded", input.readEntry("TitleFrameShaded"), true, true); + + tmpStr = input.readEntry("MenuButton"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("menu", tmpStr, true, true); + tmpStr = input.readEntry("PinUpButton"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("pinup", tmpStr, true, true); + tmpStr = input.readEntry("PinDownButton"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("pindown", tmpStr, true, true); + tmpStr = input.readEntry("CloseButton"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("close", tmpStr, true, true); + tmpStr = input.readEntry("MaximizeButton"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("maximize", tmpStr, true, true); + tmpStr = input.readEntry("MaximizeDownButton"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("maximizedown", tmpStr, true, true); + tmpStr = input.readEntry("MinimizeButton"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("iconify", tmpStr, true, true); + tmpStr = input.readEntry("TitlebarPixmapActive"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("TitlebarPixmapActive", tmpStr, true, true); + tmpStr = input.readEntry("TitlebarPixmapInactive"); + if(!tmpStr.isEmpty()) + copy(srcStr+tmpStr, localDirStr+tmpStr); + output->writeEntry("TitlebarPixmapInactive", tmpStr, true, true); + + input.setGroup("Window Button Layout"); + output->setGroup("Buttons"); + output->writeEntry("ButtonA", input.readEntry("ButtonA"), true, true); + output->writeEntry("ButtonB", input.readEntry("ButtonB"), true, true); + output->writeEntry("ButtonC", input.readEntry("ButtonC"), true, true); + output->writeEntry("ButtonD", input.readEntry("ButtonD"), true, true); + output->writeEntry("ButtonE", input.readEntry("ButtonE"), true, true); + output->writeEntry("ButtonF", input.readEntry("ButtonF"), true, true); + + output->sync(); + + return(0); +} + diff --git a/kwin/clients/kwmtheme/kwmtheme.desktop b/kwin/clients/kwmtheme/kwmtheme.desktop new file mode 100644 index 000000000..caa8a6ea8 --- /dev/null +++ b/kwin/clients/kwmtheme/kwmtheme.desktop @@ -0,0 +1,81 @@ +[Desktop Entry] +Name=KWM Theme +Name[af]=KWM Tema +Name[ar]=سمة KWM +Name[az]=KWM Örtüsü +Name[be]=Тэма KWM +Name[bn]=KWM থীম +Name[br]=Gwiskad KWM +Name[bs]=KWM Tema +Name[ca]=Tema KWM +Name[cs]=Téma KWM +Name[csb]=Témë KWM +Name[cy]=Thema KWM +Name[da]=KWM-tema +Name[de]=KWM-Design +Name[el]=Θέμα KWM +Name[eo]=KWM-etoso +Name[es]=Tema de KWM +Name[et]=KWM teema +Name[eu]=KWM gaia +Name[fa]=چهره KWM +Name[fi]=KWM-teema +Name[fr]=Thème KWM +Name[fy]=KWM-tema +Name[ga]=Téama KWM +Name[gl]=Tema do KWM +Name[hi]=केडबल्यूएम प्रसंग +Name[hr]=KWM tema +Name[hu]=KWM téma +Name[id]=Theme KWM +Name[is]=KWM þema +Name[it]=Tema KWM +Name[ja]=KWM テーマ +Name[ka]=KWM სტილი +Name[kk]=KWM нақышы +Name[km]=ស្បែក KWM +Name[ko]=KWM 테마 +Name[lo]=ແບບຕົວຈັດການຫນ້າຕ່າງ KWM +Name[lt]=KWM tema +Name[lv]=KWM Tēma +Name[mk]=KWM тема +Name[mn]=KWM-Хэлбэр +Name[ms]=Temas KWM +Name[mt]=Tema KWM +Name[nb]=KWM-tema +Name[nds]=KWM-Muster +Name[ne]=KWM विषयवस्तु +Name[nl]=KWM-thema +Name[nn]=KWM-tema +Name[nso]=Molaetsa wa KWM +Name[oc]=Tema KWM +Name[pa]=KWM ਸਰੂਪ +Name[pl]=Motyw KWM +Name[pt]=Tema KWM +Name[pt_BR]=Tema KWM +Name[ro]=Tematică KWM +Name[ru]=Стиль KWM +Name[rw]=Insanganyamatsiko KWM +Name[se]=KWM-fáddá +Name[sk]=Téma KWM +Name[sl]=Tema KWM +Name[sr]=KWM тема +Name[sr@Latn]=KWM tema +Name[ss]=Indzikimba ye KWM +Name[sv]=KWM-tema +Name[ta]=KWM தலைப்பு +Name[tg]=Услуби KWM +Name[th]=ชุดตกแต่งหน้าต่าง KWM +Name[tr]=KWM Teması +Name[tt]=KWM Tışlaw +Name[uk]=Тема KWM +Name[uz]=KWM mavzusi +Name[uz@cyrillic]=KWM мавзуси +Name[ven]=Thero ya KWM +Name[vi]=Sắc thái KWM +Name[wa]=Tinme KWM +Name[xh]=Umxholo we KWM +Name[zh_CN]=KWM 主题 +Name[zh_TW]=KWM 主題 +Name[zu]=Ingqikithi ye-KWM +X-KDE-Library=kwin3_kwmtheme diff --git a/kwin/clients/kwmtheme/kwmthemeclient.cpp b/kwin/clients/kwmtheme/kwmthemeclient.cpp new file mode 100644 index 000000000..a422f31fb --- /dev/null +++ b/kwin/clients/kwmtheme/kwmthemeclient.cpp @@ -0,0 +1,936 @@ +#include <kconfig.h> +#include "kwmthemeclient.h" +#include <kglobal.h> +#include <qlayout.h> +#include <qdrawutil.h> +#include <qpainter.h> +#include <kpixmapeffect.h> +#include <kstandarddirs.h> +#include <kdebug.h> +#include <klocale.h> +#include <qbitmap.h> +#include <qstyle.h> +#include <qlabel.h> +#include <qtooltip.h> + +namespace KWMTheme { + + +/* static QPixmap stretchPixmap(QPixmap& src, bool stretchVert){ + QPixmap dest; + QBitmap *srcMask, *destMask; + int w, h, w2, h2; + QPainter p; + + if (src.isNull()) return src; + + w = src.width(); + h = src.height(); + + if (stretchVert){ + w2 = w; + for (h2=h; h2<100; h2=h2<<1) + ; + } + else{ + h2 = h; + for (w2=w; w2<100; w2=w2<<1) + ; + } + if (w2==w && h2==h) return src; + + dest = src; + dest.resize(w2, h2); + + p.begin(&dest); + p.drawTiledPixmap(0, 0, w2, h2, src); + p.end(); + + srcMask = (QBitmap*)src.mask(); + if (srcMask){ + destMask = (QBitmap*)dest.mask(); + p.begin(destMask); + p.drawTiledPixmap(0, 0, w2, h2, *srcMask); + p.end(); + } + return dest; +} */ + + +inline const KDecorationOptions* options() { return KDecoration::options(); } + +enum FramePixmap{FrameTop=0, FrameBottom, FrameLeft, FrameRight, FrameTopLeft, + FrameTopRight, FrameBottomLeft, FrameBottomRight}; + +static QPixmap *framePixmaps[8]; +static QPixmap *menuPix, *iconifyPix, *closePix, *maxPix, *minmaxPix, + *pinupPix, *pindownPix; +static KPixmap *aTitlePix = 0; +static KPixmap *iTitlePix = 0; +static KPixmapEffect::GradientType grType; +static int maxExtent, titleAlign; +static bool titleGradient = true; +static bool pixmaps_created = false; +static bool titleSunken = false; +static bool titleTransparent; + +static void create_pixmaps() +{ + const char *keys[] = {"wm_top", "wm_bottom", "wm_left", "wm_right", + "wm_topleft", "wm_topright", "wm_bottomleft", "wm_bottomright"}; + + if(pixmaps_created) + return; + pixmaps_created = true; + + KConfig *config = KGlobal::config(); + config->setGroup("General"); + QString tmpStr; + + for(int i=0; i < 8; ++i) + { + framePixmaps[i] = new QPixmap(locate("data", + "kwin/pics/"+config->readEntry(keys[i], " "))); + if(framePixmaps[i]->isNull()) + kdWarning() << "Unable to load frame pixmap for " << keys[i] << endl; + } +/* + *framePixmaps[FrameTop] = stretchPixmap(*framePixmaps[FrameTop], false); + *framePixmaps[FrameBottom] = stretchPixmap(*framePixmaps[FrameBottom], false); + *framePixmaps[FrameLeft] = stretchPixmap(*framePixmaps[FrameLeft], true); + *framePixmaps[FrameRight] = stretchPixmap(*framePixmaps[FrameRight], true); +*/ + maxExtent = framePixmaps[FrameTop]->height(); + if(framePixmaps[FrameBottom]->height() > maxExtent) + maxExtent = framePixmaps[FrameBottom]->height(); + if(framePixmaps[FrameLeft]->width() > maxExtent) + maxExtent = framePixmaps[FrameLeft]->width(); + if(framePixmaps[FrameRight]->width() > maxExtent) + maxExtent = framePixmaps[FrameRight]->width(); + + maxExtent++; + + menuPix = new QPixmap(locate("data", + "kwin/pics/"+config->readEntry("menu", " "))); + iconifyPix = new QPixmap(locate("data", + "kwin/pics/"+config->readEntry("iconify", " "))); + maxPix = new QPixmap(locate("appdata", + "pics/"+config->readEntry("maximize", " "))); + minmaxPix = new QPixmap(locate("data", + "kwin/pics/"+config->readEntry("maximizedown", " "))); + closePix = new QPixmap(locate("data", + "kwin/pics/"+config->readEntry("close", " "))); + pinupPix = new QPixmap(locate("data", + "kwin/pics/"+config->readEntry("pinup", " "))); + pindownPix = new QPixmap(locate("data", + "kwin/pics/"+config->readEntry("pindown", " "))); + if(menuPix->isNull()) + menuPix->load(locate("data", "kwin/pics/menu.png")); + if(iconifyPix->isNull()) + iconifyPix->load(locate("data", "kwin/pics/iconify.png")); + if(maxPix->isNull()) + maxPix->load(locate("data", "kwin/pics/maximize.png")); + if(minmaxPix->isNull()) + minmaxPix->load(locate("data", "kwin/pics/maximizedown.png")); + if(closePix->isNull()) + closePix->load(locate("data", "kwin/pics/close.png")); + if(pinupPix->isNull()) + pinupPix->load(locate("data", "kwin/pics/pinup.png")); + if(pindownPix->isNull()) + pindownPix->load(locate("data", "kwin/pics/pindown.png")); + + tmpStr = config->readEntry("TitleAlignment"); + if(tmpStr == "right") + titleAlign = Qt::AlignRight | Qt::AlignVCenter; + else if(tmpStr == "middle") + titleAlign = Qt::AlignCenter; + else + titleAlign = Qt::AlignLeft | Qt::AlignVCenter; + titleSunken = config->readBoolEntry("TitleFrameShaded", true); + // titleSunken = true; // is this fixed? + titleTransparent = config->readBoolEntry("PixmapUnderTitleText", true); + + tmpStr = config->readEntry("TitlebarLook"); + if(tmpStr == "shadedVertical"){ + aTitlePix = new KPixmap; + aTitlePix->resize(32, 20); + KPixmapEffect::gradient(*aTitlePix, + options()->color(KDecorationOptions::ColorTitleBar, true), + options()->color(KDecorationOptions::ColorTitleBlend, true), + KPixmapEffect::VerticalGradient); + iTitlePix = new KPixmap; + iTitlePix->resize(32, 20); + KPixmapEffect::gradient(*iTitlePix, + options()->color(KDecorationOptions::ColorTitleBar, false), + options()->color(KDecorationOptions::ColorTitleBlend, false), + KPixmapEffect::VerticalGradient); + titleGradient = false; // we can just tile this + + } + else if(tmpStr == "shadedHorizontal") + grType = KPixmapEffect::HorizontalGradient; + else if(tmpStr == "shadedDiagonal") + grType = KPixmapEffect::DiagonalGradient; + else if(tmpStr == "shadedCrossDiagonal") + grType = KPixmapEffect::CrossDiagonalGradient; + else if(tmpStr == "shadedPyramid") + grType = KPixmapEffect::PyramidGradient; + else if(tmpStr == "shadedRectangle") + grType = KPixmapEffect::RectangleGradient; + else if(tmpStr == "shadedPipeCross") + grType = KPixmapEffect::PipeCrossGradient; + else if(tmpStr == "shadedElliptic") + grType = KPixmapEffect::EllipticGradient; + else{ + titleGradient = false; + tmpStr = config->readEntry("TitlebarPixmapActive", ""); + if(!tmpStr.isEmpty()){ + aTitlePix = new KPixmap; + aTitlePix->load(locate("data", "kwin/pics/" + tmpStr)); + } + else + aTitlePix = NULL; + tmpStr = config->readEntry("TitlebarPixmapInactive", ""); + if(!tmpStr.isEmpty()){ + iTitlePix = new KPixmap; + iTitlePix->load(locate("data", "kwin/pics/" + tmpStr)); + } + else + iTitlePix = NULL; + } +} + +static void delete_pixmaps() +{ + for(int i=0; i < 8; ++i) + delete framePixmaps[i]; + + delete menuPix; + delete iconifyPix; + delete closePix; + delete maxPix; + delete minmaxPix; + delete pinupPix; + delete pindownPix; + delete aTitlePix; + aTitlePix = 0; + delete iTitlePix; + iTitlePix = 0; + + titleGradient = true; + pixmaps_created = false; + titleSunken = false; +} + +void MyButton::drawButtonLabel(QPainter *p) +{ + if(pixmap()){ + // If we have a theme who's button covers the entire width or + // entire height, we shift down/right by 1 pixel so we have + // some visual notification of button presses. i.e. for MGBriezh + int offset = (isDown() && ((pixmap()->width() >= width()) || + (pixmap()->height() >= height()))) ? 1 : 0; + style().drawItem(p, QRect( offset, offset, width(), height() ), + AlignCenter, colorGroup(), + true, pixmap(), QString::null); + } +} + +KWMThemeClient::KWMThemeClient( KDecorationBridge* b, KDecorationFactory* f ) + : KDecoration( b, f ) +{ +} + +void KWMThemeClient::init() +{ + createMainWidget( WResizeNoErase | WStaticContents ); + widget()->installEventFilter( this ); + + stickyBtn = maxBtn = mnuBtn = 0; + layout = new QGridLayout(widget()); + layout->addColSpacing(0, maxExtent); + layout->addColSpacing(2, maxExtent); + + layout->addRowSpacing(0, maxExtent); + + layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Fixed, + QSizePolicy::Expanding)); + + if( isPreview()) + layout->addWidget( new QLabel( i18n( "<center><b>KWMTheme</b></center>" ), widget()), 2, 1); + else + layout->addItem( new QSpacerItem( 0, 0 ), 2, 1); + + // Without the next line, shading flickers + layout->addItem( new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding) ); + layout->addRowSpacing(3, maxExtent); + layout->setRowStretch(2, 10); + layout->setColStretch(1, 10); + + QBoxLayout* hb = new QBoxLayout(0, QBoxLayout::LeftToRight, 0, 0, 0); + layout->addLayout( hb, 1, 1 ); + + KConfig *config = KGlobal::config(); + config->setGroup("Buttons"); + QString val; + MyButton *btn; + int i; + static const char *defaultButtons[]={"Menu","Sticky","Off","Iconify", + "Maximize","Close"}; + static const char keyOffsets[]={"ABCDEF"}; + for(i=0; i < 6; ++i){ + if(i == 3){ + titlebar = new QSpacerItem(10, 20, QSizePolicy::Expanding, + QSizePolicy::Minimum ); + hb->addItem( titlebar ); + } + QString key("Button"); + key += QChar(keyOffsets[i]); + val = config->readEntry(key, defaultButtons[i]); + if(val == "Menu"){ + mnuBtn = new MyButton(widget(), "menu"); + QToolTip::add( mnuBtn, i18n("Menu")); + iconChange(); + hb->addWidget(mnuBtn); + mnuBtn->setFixedSize(20, 20); + connect(mnuBtn, SIGNAL(pressed()), this, + SLOT(menuButtonPressed())); + } + else if(val == "Sticky"){ + stickyBtn = new MyButton(widget(), "sticky"); + QToolTip::add( stickyBtn, i18n("Sticky")); + if (isOnAllDesktops()) + stickyBtn->setPixmap(*pindownPix); + else + stickyBtn->setPixmap(*pinupPix); + connect(stickyBtn, SIGNAL( clicked() ), this, SLOT(toggleOnAllDesktops())); + hb->addWidget(stickyBtn); + stickyBtn->setFixedSize(20, 20); + } + else if((val == "Iconify") && isMinimizable()){ + btn = new MyButton(widget(), "iconify"); + QToolTip::add( btn, i18n("Minimize")); + btn->setPixmap(*iconifyPix); + connect(btn, SIGNAL(clicked()), this, SLOT(minimize())); + hb->addWidget(btn); + btn->setFixedSize(20, 20); + } + else if((val == "Maximize") && isMaximizable()){ + maxBtn = new MyButton(widget(), "max"); + QToolTip::add( maxBtn, i18n("Maximize")); + maxBtn->setPixmap(*maxPix); + connect(maxBtn, SIGNAL(clicked()), this, SLOT(maximize())); + hb->addWidget(maxBtn); + maxBtn->setFixedSize(20, 20); + } + else if((val == "Close") && isCloseable()){ + btn = new MyButton(widget(), "close"); + QToolTip::add( btn, i18n("Close")); + btn->setPixmap(*closePix); + connect(btn, SIGNAL(clicked()), this, SLOT(closeWindow())); + hb->addWidget(btn); + btn->setFixedSize(20, 20); + } + else{ + if((val != "Off") && + ((val == "Iconify") && !isMinimizable()) && + ((val == "Maximize") && !isMaximizable())) + kdWarning() << "KWin: Unrecognized button value: " << val << endl; + + } + } + if(titleGradient){ + aGradient = new KPixmap; + iGradient = new KPixmap; + } + else{ + aGradient = 0; + iGradient = 0; + } + widget()->setBackgroundMode(NoBackground); +} + +void KWMThemeClient::drawTitle(QPainter &dest) +{ + QRect titleRect = titlebar->geometry(); + QRect r(0, 0, titleRect.width(), titleRect.height()); + QPixmap buffer; + + if(buffer.width() == r.width()) + return; + + buffer.resize(r.size()); + QPainter p; + p.begin(&buffer); + + if(titleSunken){ + qDrawShadeRect(&p, r, options()->colorGroup(KDecorationOptions::ColorFrame, isActive()), + true, 1, 0); + r.setRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2); + } + + KPixmap *fill = isActive() ? aTitlePix : iTitlePix; + if(fill) + p.drawTiledPixmap(r, *fill); + else if(titleGradient){ + fill = isActive() ? aGradient : iGradient; + if(fill->width() != r.width()){ + fill->resize(r.width(), 20); + KPixmapEffect::gradient(*fill, + options()->color(KDecorationOptions::ColorTitleBar, isActive()), + options()->color(KDecorationOptions::ColorTitleBlend, isActive()), + grType); + } + p.drawTiledPixmap(r, *fill); + } + else{ + p.fillRect(r, options()->colorGroup(KDecorationOptions::ColorTitleBar, isActive()). + brush(QColorGroup::Button)); + } + p.setFont(options()->font(isActive())); + p.setPen(options()->color(KDecorationOptions::ColorFont, isActive())); + // Add left & right margin + r.setLeft(r.left()+5); + r.setRight(r.right()-5); + p.drawText(r, titleAlign, caption()); + p.end(); + + dest.drawPixmap(titleRect.x(), titleRect.y(), buffer); +} + + +void KWMThemeClient::resizeEvent( QResizeEvent* ) +{ + doShape(); + widget()->repaint(); +} + +void KWMThemeClient::captionChange() +{ + widget()->repaint( titlebar->geometry(), false ); +} + +void KWMThemeClient::paintEvent( QPaintEvent *) +{ + QPainter p; + p.begin(widget()); + int x,y; + // first the corners + int w1 = framePixmaps[FrameTopLeft]->width(); + int h1 = framePixmaps[FrameTopLeft]->height(); + if (w1 > width()/2) w1 = width()/2; + if (h1 > height()/2) h1 = height()/2; + p.drawPixmap(0,0,*framePixmaps[FrameTopLeft], + 0,0,w1, h1); + int w2 = framePixmaps[FrameTopRight]->width(); + int h2 = framePixmaps[FrameTopRight]->height(); + if (w2 > width()/2) w2 = width()/2; + if (h2 > height()/2) h2 = height()/2; + p.drawPixmap(width()-w2,0,*framePixmaps[FrameTopRight], + framePixmaps[FrameTopRight]->width()-w2,0,w2, h2); + + int w3 = framePixmaps[FrameBottomLeft]->width(); + int h3 = framePixmaps[FrameBottomLeft]->height(); + if (w3 > width()/2) w3 = width()/2; + if (h3 > height()/2) h3 = height()/2; + p.drawPixmap(0,height()-h3,*framePixmaps[FrameBottomLeft], + 0,framePixmaps[FrameBottomLeft]->height()-h3,w3, h3); + + int w4 = framePixmaps[FrameBottomRight]->width(); + int h4 = framePixmaps[FrameBottomRight]->height(); + if (w4 > width()/2) w4 = width()/2; + if (h4 > height()/2) h4 = height()/2; + p.drawPixmap(width()-w4,height()-h4,*(framePixmaps[FrameBottomRight]), + framePixmaps[FrameBottomRight]->width()-w4, + framePixmaps[FrameBottomRight]->height()-h4, + w4, h4); + + QPixmap pm; + QWMatrix m; + int n,s,w; + //top + pm = *framePixmaps[FrameTop]; + + if (pm.width() > 0){ + s = width()-w2-w1; + n = s/pm.width(); + w = n>0?s/n:s; + m.reset(); + m.scale(w/(float)pm.width(), 1); + pm = pm.xForm(m); + + x = w1; + while (1){ + if (pm.width() < width()-w2-x){ + p.drawPixmap(x,maxExtent-pm.height()-1, + pm); + x += pm.width(); + } + else { + p.drawPixmap(x,maxExtent-pm.height()-1, + pm, + 0,0,width()-w2-x,pm.height()); + break; + } + } + } + + //bottom + pm = *framePixmaps[FrameBottom]; + + if (pm.width() > 0){ + s = width()-w4-w3; + n = s/pm.width(); + w = n>0?s/n:s; + m.reset(); + m.scale(w/(float)pm.width(), 1); + pm = pm.xForm(m); + + x = w3; + while (1){ + if (pm.width() < width()-w4-x){ + p.drawPixmap(x,height()-maxExtent+1,pm); + x += pm.width(); + } + else { + p.drawPixmap(x,height()-maxExtent+1,pm, + 0,0,width()-w4-x,pm.height()); + break; + } + } + } + + //left + pm = *framePixmaps[FrameLeft]; + + if (pm.height() > 0){ + s = height()-h3-h1; + n = s/pm.height(); + w = n>0?s/n:s; + m.reset(); + m.scale(1, w/(float)pm.height()); + pm = pm.xForm(m); + + y = h1; + while (1){ + if (pm.height() < height()-h3-y){ + p.drawPixmap(maxExtent-pm.width()-1, y, + pm); + y += pm.height(); + } + else { + p.drawPixmap(maxExtent-pm.width()-1, y, + pm, + 0,0, pm.width(), + height()-h3-y); + break; + } + } + } + + //right + pm = *framePixmaps[FrameRight]; + + if (pm.height() > 0){ + s = height()-h4-h2; + n = s/pm.height(); + w = n>0?s/n:s; + m.reset(); + m.scale(1, w/(float)pm.height()); + pm = pm.xForm(m); + + y = h2; + while (1){ + if (pm.height() < height()-h4-y){ + p.drawPixmap(width()-maxExtent+1, y, + pm); + y += pm.height(); + } + else { + p.drawPixmap(width()-maxExtent+1, y, + pm, + 0,0, pm.width(), + height()-h4-y); + break; + } + } + } + drawTitle(p); + + QColor c = widget()->colorGroup().background(); + + // KWM evidently had a 1 pixel border around the client window. We + // emulate it here, but should be removed at some point in order to + // seamlessly mesh widget themes + p.setPen(c); + p.drawRect(maxExtent-1, maxExtent-1, width()-(maxExtent-1)*2, + height()-(maxExtent-1)*2); + + // We fill the area behind the wrapped widget to ensure that + // shading animation is drawn as smoothly as possible + QRect r(layout->cellGeometry(2, 1)); + p.fillRect( r.x(), r.y(), r.width(), r.height(), c); + p.end(); +} + +void KWMThemeClient::doShape() +{ + + QBitmap shapemask(width(), height()); + shapemask.fill(color0); + QPainter p; + p.begin(&shapemask); + p.setBrush(color1); + p.setPen(color1); + int x,y; + // first the corners + int w1 = framePixmaps[FrameTopLeft]->width(); + int h1 = framePixmaps[FrameTopLeft]->height(); + if (w1 > width()/2) w1 = width()/2; + if (h1 > height()/2) h1 = height()/2; + if (framePixmaps[FrameTopLeft]->mask()) + p.drawPixmap(0,0,*framePixmaps[FrameTopLeft]->mask(), + 0,0,w1, h1); + else + p.fillRect(0,0,w1,h1,color1); + int w2 = framePixmaps[FrameTopRight]->width(); + int h2 = framePixmaps[FrameTopRight]->height(); + if (w2 > width()/2) w2 = width()/2; + if (h2 > height()/2) h2 = height()/2; + if (framePixmaps[FrameTopRight]->mask()) + p.drawPixmap(width()-w2,0,*framePixmaps[FrameTopRight]->mask(), + framePixmaps[FrameTopRight]->width()-w2,0,w2, h2); + else + p.fillRect(width()-w2,0,w2, h2,color1); + + int w3 = framePixmaps[FrameBottomLeft]->width(); + int h3 = framePixmaps[FrameBottomLeft]->height(); + if (w3 > width()/2) w3 = width()/2; + if (h3 > height()/2) h3 = height()/2; + if (framePixmaps[FrameBottomLeft]->mask()) + p.drawPixmap(0,height()-h3,*framePixmaps[FrameBottomLeft]->mask(), + 0,framePixmaps[FrameBottomLeft]->height()-h3,w3, h3); + else + p.fillRect(0,height()-h3,w3,h3,color1); + + int w4 = framePixmaps[FrameBottomRight]->width(); + int h4 = framePixmaps[FrameBottomRight]->height(); + if (w4 > width()/2) w4 = width()/2; + if (h4 > height()/2) h4 = height()/2; + if (framePixmaps[FrameBottomRight]->mask()) + p.drawPixmap(width()-w4,height()-h4,*framePixmaps[FrameBottomRight]->mask(), + framePixmaps[FrameBottomRight]->width()-w4, + framePixmaps[FrameBottomRight]->height()-h4, + w4, h4); + else + p.fillRect(width()-w4,height()-h4,w4,h4,color1); + + QPixmap pm; + QWMatrix m; + int n,s,w; + //top + if (framePixmaps[FrameTop]->mask()) + { + pm = *framePixmaps[FrameTop]->mask(); + + s = width()-w2-w1; + n = s/pm.width(); + w = n>0?s/n:s; + m.reset(); + m.scale(w/(float)pm.width(), 1); + pm = pm.xForm(m); + + x = w1; + while (1){ + if (pm.width() < width()-w2-x){ + p.drawPixmap(x,maxExtent-pm.height()-1, + pm); + x += pm.width(); + } + else { + p.drawPixmap(x,maxExtent-pm.height()-1, + pm, + 0,0,width()-w2-x,pm.height()); + break; + } + } + } + + //bottom + if (framePixmaps[FrameBottom]->mask()) + { + pm = *framePixmaps[FrameBottom]->mask(); + + s = width()-w4-w3; + n = s/pm.width(); + w = n>0?s/n:s; + m.reset(); + m.scale(w/(float)pm.width(), 1); + pm = pm.xForm(m); + + x = w3; + while (1){ + if (pm.width() < width()-w4-x){ + p.drawPixmap(x,height()-maxExtent+1,pm); + x += pm.width(); + } + else { + p.drawPixmap(x,height()-maxExtent+1,pm, + 0,0,width()-w4-x,pm.height()); + break; + } + } + } + + //left + if (framePixmaps[FrameLeft]->mask()) + { + pm = *framePixmaps[FrameLeft]->mask(); + + s = height()-h3-h1; + n = s/pm.height(); + w = n>0?s/n:s; + m.reset(); + m.scale(1, w/(float)pm.height()); + pm = pm.xForm(m); + + y = h1; + while (1){ + if (pm.height() < height()-h3-y){ + p.drawPixmap(maxExtent-pm.width()-1, y, + pm); + y += pm.height(); + } + else { + p.drawPixmap(maxExtent-pm.width()-1, y, + pm, + 0,0, pm.width(), + height()-h3-y); + break; + } + } + } + + //right + if (framePixmaps[FrameRight]->mask()) + { + pm = *framePixmaps[FrameRight]->mask(); + + s = height()-h4-h2; + n = s/pm.height(); + w = n>0?s/n:s; + m.reset(); + m.scale(1, w/(float)pm.height()); + pm = pm.xForm(m); + + y = h2; + while (1){ + if (pm.height() < height()-h4-y){ + p.drawPixmap(width()-maxExtent+1, y, + pm); + y += pm.height(); + } + else { + p.drawPixmap(width()-maxExtent+1, y, + pm, + 0,0, pm.width(), + height()-h4-y); + break; + } + } + } + p.fillRect(maxExtent-1, maxExtent-1, width()-2*maxExtent+2, height()-2*maxExtent+2, color1); + setMask(shapemask); +} + + +void KWMThemeClient::showEvent(QShowEvent *) +{ + doShape(); + widget()->repaint(false); +} + +void KWMThemeClient::mouseDoubleClickEvent( QMouseEvent * e ) +{ + if (e->button() == LeftButton && titlebar->geometry().contains( e->pos() ) ) + titlebarDblClickOperation(); +} + +void KWMThemeClient::desktopChange() +{ + if (stickyBtn) { + bool on = isOnAllDesktops(); + stickyBtn->setPixmap(on ? *pindownPix : *pinupPix); + QToolTip::remove( stickyBtn ); + QToolTip::add( stickyBtn, on ? i18n("Unsticky") : i18n("Sticky") ); + } +} + +void KWMThemeClient::maximizeChange() +{ + if (maxBtn) { + bool m = maximizeMode() == MaximizeFull; + maxBtn->setPixmap(m ? *minmaxPix : *maxPix); + QToolTip::remove( maxBtn ); + QToolTip::add( maxBtn, m ? i18n("Restore") : i18n("Maximize")); + } +} + +void KWMThemeClient::slotMaximize() +{ + maximize( maximizeMode() == MaximizeFull ? MaximizeRestore : MaximizeFull ); +} + +void KWMThemeClient::activeChange() +{ + widget()->update(); +} + +KDecoration::Position KWMThemeClient::mousePosition(const QPoint &p) const +{ + Position m = KDecoration::mousePosition(p); + // corners + if(p.y() < framePixmaps[FrameTop]->height() && + p.x() < framePixmaps[FrameLeft]->width()){ + m = PositionTopLeft; + } + else if(p.y() < framePixmaps[FrameTop]->height() && + p.x() > width()-framePixmaps[FrameRight]->width()){ + m = PositionTopRight; + } + else if(p.y() > height()-framePixmaps[FrameBottom]->height() && + p.x() < framePixmaps[FrameLeft]->width()){ + m = PositionBottomLeft; + } + else if(p.y() > height()-framePixmaps[FrameBottom]->height() && + p.x() > width()-framePixmaps[FrameRight]->width()){ + m = PositionBottomRight; + } // edges + else if(p.y() < framePixmaps[FrameTop]->height()) + m = PositionTop; + else if(p.y() > height()-framePixmaps[FrameBottom]->height()) + m = PositionBottom; + else if(p.x() < framePixmaps[FrameLeft]->width()) + m = PositionLeft; + else if(p.x() > width()-framePixmaps[FrameRight]->width()) + m = PositionRight; + return(m); +} + +void KWMThemeClient::menuButtonPressed() +{ + mnuBtn->setDown(false); // will stay down if I don't do this + QPoint pos = mnuBtn->mapToGlobal(mnuBtn->rect().bottomLeft()); + showWindowMenu( pos ); +} + +void KWMThemeClient::iconChange() +{ + if(mnuBtn){ + if( icon().pixmap( QIconSet::Small, QIconSet::Normal ).isNull()){ + mnuBtn->setPixmap(*menuPix); + } + else{ + mnuBtn->setPixmap(icon().pixmap( QIconSet::Small, QIconSet::Normal )); + } + } +} + +bool KWMThemeClient::eventFilter( QObject* o, QEvent* e ) +{ + if ( o != widget() ) + return false; + + switch ( e->type() ) + { + case QEvent::Resize: + resizeEvent( static_cast< QResizeEvent* >( e ) ); + return true; + + case QEvent::Paint: + paintEvent( static_cast< QPaintEvent* >( e ) ); + return true; + + case QEvent::MouseButtonDblClick: + mouseDoubleClickEvent( static_cast< QMouseEvent* >( e ) ); + return true; + + case QEvent::MouseButtonPress: + processMousePressEvent( static_cast< QMouseEvent* >( e ) ); + return true; + + case QEvent::Show: + showEvent( static_cast< QShowEvent* >( e ) ); + return true; + + default: + return false; + } +} + +QSize KWMThemeClient::minimumSize() const +{ + return widget()->minimumSize().expandedTo( QSize( 100, 50 )); +} + +void KWMThemeClient::resize( const QSize& s ) +{ + widget()->resize( s ); +} + +void KWMThemeClient::borders( int& left, int& right, int& top, int& bottom ) const +{ + left = + right = + top = + bottom = + +TODO +} + +KWMThemeFactory::KWMThemeFactory() +{ + create_pixmaps(); +} + +KWMThemeFactory::~KWMThemeFactory() +{ + delete_pixmaps(); +} + +KDecoration* KWMThemeFactory::createDecoration( KDecorationBridge* b ) +{ + return new KWMThemeClient( b, this ); +} + +bool KWMThemeFactory::reset( unsigned long mask ) +{ + bool needHardReset = false; + +TODO + + // doesn't obey the Border size setting + if( mask & ( SettingFont | SettingButtons )) + needHardReset = true; + + if( mask & ( SettingFont | SettingColors )) { + KWMTheme::delete_pixmaps(); + KWMTheme::create_pixmaps(); + } + + if( !needHardReset ) + resetDecorations( mask ); + return needHardReset; +} + +} + +extern "C" +{ + KDE_EXPORT KDecorationFactory *create_factory() + { + return new KWMTheme::KWMThemeFactory(); + } +} + +#include "kwmthemeclient.moc" diff --git a/kwin/clients/kwmtheme/kwmthemeclient.h b/kwin/clients/kwmtheme/kwmthemeclient.h new file mode 100644 index 000000000..b1d623965 --- /dev/null +++ b/kwin/clients/kwmtheme/kwmthemeclient.h @@ -0,0 +1,74 @@ +#ifndef __KWMTHEMECLIENT_H +#define __KWMTHEMECLIENT_H + +#include <qbutton.h> +#include <qtoolbutton.h> +#include <kpixmap.h> +#include <kdecoration.h> +#include <kdecorationfactory.h> + +class QLabel; +class QSpacerItem; +class QGridLayout; + +namespace KWMTheme { + +class MyButton : public QToolButton +{ +public: + MyButton(QWidget *parent=0, const char *name=0) + : QToolButton(parent, name){setAutoRaise(true);setCursor( arrowCursor ); } +protected: + void drawButtonLabel(QPainter *p); +}; + +class KWMThemeClient : public KDecoration +{ + Q_OBJECT +public: + KWMThemeClient( KDecorationBridge* b, KDecorationFactory* f ); + ~KWMThemeClient(){;} + void init(); + void resize( const QSize& s ); + QSize minimumSize() const; + void borders( int& left, int& right, int& top, int& bottom ) const; +protected: + void doShape(); + void drawTitle(QPainter &p); + void resizeEvent( QResizeEvent* ); + void paintEvent( QPaintEvent* ); + void showEvent( QShowEvent* ); + void mouseDoubleClickEvent( QMouseEvent * ); + bool eventFilter( QObject* o, QEvent* e ); + void captionChange(); + void desktopChange(); + void maximizeChange(); + void iconChange(); + void activeChange(); + void shadeChange() {}; + Position mousePosition(const QPoint &) const; +protected slots: + //void slotReset(); + void menuButtonPressed(); + void slotMaximize(); +private: + QPixmap buffer; + KPixmap *aGradient, *iGradient; + MyButton *maxBtn, *stickyBtn, *mnuBtn; + QSpacerItem *titlebar; + QGridLayout* layout; +}; + +class KWMThemeFactory : public KDecorationFactory +{ +public: + KWMThemeFactory(); + ~KWMThemeFactory(); + KDecoration* createDecoration( KDecorationBridge* b ); + bool reset( unsigned long mask ); +}; + +} + +#endif + diff --git a/kwin/clients/laptop/Makefile.am b/kwin/clients/laptop/Makefile.am new file mode 100644 index 000000000..eb870720a --- /dev/null +++ b/kwin/clients/laptop/Makefile.am @@ -0,0 +1,17 @@ + +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +kde_module_LTLIBRARIES = kwin3_laptop.la + +kwin3_laptop_la_SOURCES = laptopclient.cpp +kwin3_laptop_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_laptop_la_LIBADD = ../../lib/libkdecorations.la + +METASOURCES = AUTO +noinst_HEADERS = laptopclient.h + +lnkdir = $(kde_datadir)/kwin +lnk_DATA = laptop.desktop + +EXTRA_DIST = $(lnk_DATA) + diff --git a/kwin/clients/laptop/laptop.desktop b/kwin/clients/laptop/laptop.desktop new file mode 100644 index 000000000..38c5386f1 --- /dev/null +++ b/kwin/clients/laptop/laptop.desktop @@ -0,0 +1,69 @@ +[Desktop Entry] +Name=Laptop +Name[af]=Draagbare rekenaar +Name[ar]=الحاسوب النقّال +Name[az]=Dizüstü Kompüter +Name[be]=Ноўтбук +Name[bg]=Лаптоп +Name[bn]=ল্যাপটপ +Name[br]=Hezoug +Name[ca]=Portàtil +Name[cs]=Notebook +Name[cy]=Gluniadur +Name[da]=Bærbar +Name[el]=Φορητό +Name[eo]=Tekokomputilo +Name[es]=Portátil +Name[eu]=Ordenagailu eramangarria +Name[fa]=رایانۀ کیفی +Name[fi]=Kannettava +Name[fr]=Ordinateur portable +Name[fy]=Skoatkompjûter +Name[ga]=Ríomhaire Glúine +Name[gl]=Portátil +Name[he]=מחשב נייד +Name[hi]=लैपटॉप +Name[hsb]=laptop +Name[hu]=Noteszgép +Name[is]=Ferðavél +Name[it]=Portatile +Name[ja]=ラップトップ +Name[ka]=ლეპტოპი +Name[kk]=Ноутбук +Name[km]=កុំព្យូទ័រយួរដៃ +Name[ko]=랩탑 +Name[lo]=ແລບທອບ +Name[lt]=Nešiojamas kompiuteris +Name[lv]=Laptops +Name[mk]=Лаптоп +Name[mn]=Лаптоп +Name[ms]=Komputer riba +Name[nb]=Bærbar +Name[nds]=Klappreekner +Name[ne]=ल्यापटप +Name[nn]=Berbar +Name[oc]=Portatil +Name[pa]=ਲੈਪਟਾਪ +Name[pt]=Portátil +Name[ru]=Ноутбук +Name[rw]=Mudasobwa Igendanwa +Name[se]=Mátkedihtor +Name[sl]=Prenosnik +Name[sr]=Лаптоп +Name[sv]=Bärbar dator +Name[ta]=மடிக்கணினி +Name[te]=లాప్ టాప్ +Name[tg]=Ноутбук +Name[th]=แลปทอป +Name[tr]=Dizüstü +Name[tt]=Qulsanaq +Name[uk]=Мобільний комп'ютер (лептоп) +Name[uz@cyrillic]=Лаптоп +Name[ven]=Khomupwutha pfarwa +Name[vi]=Máy xách tay +Name[wa]=Poirtåve +Name[xh]=Umphezulu osongiweyo +Name[zh_CN]=笔记本电脑 +Name[zh_TW]=筆記型電腦 +Name[zu]=Ikhomputha ephathwayo eyisicaba +X-KDE-Library=kwin3_laptop diff --git a/kwin/clients/laptop/laptopclient.cpp b/kwin/clients/laptop/laptopclient.cpp new file mode 100644 index 000000000..4d959ff8c --- /dev/null +++ b/kwin/clients/laptop/laptopclient.cpp @@ -0,0 +1,761 @@ +/* + * Laptop KWin Decoration + * + * Copyright (c) 2005 Sandro Giessl <sandro@giessl.com> + * Port of this decoration to KDE 3.2, accessibility enhancement are + * Copyright (c) 2003 Luciano Montanaro <mikelima@cirulla.net> + */ + +#include <kconfig.h> // up here to avoid X11 header conflict :P +#include "laptopclient.h" +#include <qdrawutil.h> +#include <kpixmapeffect.h> +#include <kdrawutil.h> +#include <kglobal.h> +#include <klocale.h> +#include <qbitmap.h> + +namespace Laptop { + +static const unsigned char iconify_bits[] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18}; + +static const unsigned char close_bits[] = { + 0x42, 0xe7, 0x7e, 0x3c, 0x3c, 0x7e, 0xe7, 0x42}; + +static const unsigned char maximize_bits[] = { + 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0xff, 0xff }; + +static const unsigned char r_minmax_bits[] = { + 0x0c, 0x18, 0x33, 0x67, 0xcf, 0x9f, 0x3f, 0x3f}; + +static const unsigned char l_minmax_bits[] = { + 0x30, 0x18, 0xcc, 0xe6, 0xf3, 0xf9, 0xfc, 0xfc}; + +static const unsigned char question_bits[] = { + 0x3c, 0x66, 0x60, 0x30, 0x18, 0x00, 0x18, 0x18}; + +static const unsigned char unsticky_bits[] = { + 0x3c, 0x42, 0x99, 0xbd, 0xbd, 0x99, 0x42, 0x3c}; + +static const unsigned char sticky_bits[] = { + 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c}; + +static QPixmap *titlePix; +static KPixmap *aUpperGradient; +static KPixmap *iUpperGradient; +// buttons active, inactive, up, down, and 2 sizes :P +static KPixmap *btnPix1; +static KPixmap *iBtnPix1; +static KPixmap *btnDownPix1; +static KPixmap *iBtnDownPix1; +static KPixmap *btnPix2; +static KPixmap *btnDownPix2; +static KPixmap *iBtnPix2; +static KPixmap *iBtnDownPix2; +static QColor btnForeground; + +static int titleHeight = 14; +static int btnWidth1 = 17; +static int btnWidth2 = 27; + +static int handleSize = 8; // the resize handle size in pixels + +static bool pixmaps_created = false; + +// ===================================== + +extern "C" KDE_EXPORT KDecorationFactory* create_factory() +{ + return new Laptop::LaptopClientFactory(); +} + +// ===================================== + +static inline const KDecorationOptions* options() +{ + return KDecoration::options(); +} + +static void drawButtonFrame(KPixmap *pix, const QColorGroup &g, bool sunken) +{ + QPainter p; + int w = pix->width(); + int h = pix->height(); + int x2 = w-1; + int y2 = h-1; + p.begin(pix); + + if(sunken){ + qDrawShadePanel(&p, 0, 0, w, h, g, true, 2); + } + else{ + p.setPen(g.dark()); + p.drawRect(0, 0, w-1, h-1); + p.setPen(g.light()); + p.drawLine(x2, 0, x2, y2); + p.drawLine(0, y2, x2, y2); + p.drawLine(1, 1, x2-2, 1); + p.drawLine(1, 1, 1, y2-2); + p.end(); + } +} + +static void create_pixmaps() +{ + if(pixmaps_created) + return; + pixmaps_created = true; + + titleHeight = QFontMetrics(options()->font(true)).height() + 2; + if (titleHeight < handleSize) titleHeight = handleSize; + titleHeight &= ~1; // Make title height even + if (titleHeight < 14) titleHeight = 14; + + btnWidth1 = titleHeight + 3; + btnWidth2 = 3*titleHeight/2 + 6; + + // titlebar + QPainter p; + QPainter maskPainter; + int i, x, y; + titlePix = new QPixmap(33, 12); + QBitmap mask(33, 12); + mask.fill(Qt::color0); + + p.begin(titlePix); + maskPainter.begin(&mask); + maskPainter.setPen(Qt::color1); + for(i=0, y=2; i < 3; ++i, y+=4){ + for(x=1; x <= 33; x+=3){ + p.setPen(options()->color(KDecoration::ColorTitleBar, true).light(150)); + p.drawPoint(x, y); + maskPainter.drawPoint(x, y); + p.setPen(options()->color(KDecoration::ColorTitleBar, true).dark(150)); + p.drawPoint(x+1, y+1); + maskPainter.drawPoint(x+1, y+1); + } + } + p.end(); + maskPainter.end(); + titlePix->setMask(mask); + + if(QPixmap::defaultDepth() > 8){ + aUpperGradient = new KPixmap; + aUpperGradient->resize(32, titleHeight+2); + iUpperGradient = new KPixmap; + iUpperGradient->resize(32, titleHeight+2); + QColor bgColor = options()->color(KDecoration::ColorTitleBar, true); + KPixmapEffect::gradient(*aUpperGradient, + bgColor.light(120), + bgColor.dark(120), + KPixmapEffect::VerticalGradient); + bgColor = options()->color(KDecoration::ColorTitleBar, false); + KPixmapEffect::gradient(*iUpperGradient, + bgColor.light(120), + bgColor.dark(120), + KPixmapEffect::VerticalGradient); + } + // buttons (active/inactive, sunken/unsunken, 2 sizes each) + QColorGroup g = options()->colorGroup(KDecoration::ColorButtonBg, true); + QColor c = g.background(); + btnPix1 = new KPixmap; + btnPix1->resize(btnWidth1, titleHeight); + btnDownPix1 = new KPixmap; + btnDownPix1->resize(btnWidth1, titleHeight); + btnPix2 = new KPixmap; + btnPix2->resize(btnWidth2, titleHeight); + btnDownPix2 = new KPixmap; + btnDownPix2->resize(btnWidth2, titleHeight); + iBtnPix1 = new KPixmap; + iBtnPix1->resize(btnWidth1, titleHeight); + iBtnDownPix1 = new KPixmap; + iBtnDownPix1->resize(btnWidth1, titleHeight); + iBtnPix2 = new KPixmap; + iBtnPix2->resize(btnWidth2, titleHeight); + iBtnDownPix2 = new KPixmap; + iBtnDownPix2->resize(btnWidth2, titleHeight); + if(QPixmap::defaultDepth() > 8){ + KPixmapEffect::gradient(*btnPix1, c.light(120), c.dark(130), + KPixmapEffect::DiagonalGradient); + KPixmapEffect::gradient(*btnDownPix1, c.dark(130), c.light(120), + KPixmapEffect::DiagonalGradient); + KPixmapEffect::gradient(*btnPix2, c.light(120), c.dark(130), + KPixmapEffect::DiagonalGradient); + KPixmapEffect::gradient(*btnDownPix2, c.dark(130), c.light(120), + KPixmapEffect::DiagonalGradient); + g = options()->colorGroup(KDecoration::ColorButtonBg, false); + c = g.background(); + KPixmapEffect::gradient(*iBtnPix1, c.light(120), c.dark(130), + KPixmapEffect::DiagonalGradient); + KPixmapEffect::gradient(*iBtnDownPix1, c.dark(130), c.light(120), + KPixmapEffect::DiagonalGradient); + KPixmapEffect::gradient(*iBtnPix2, c.light(120), c.dark(130), + KPixmapEffect::DiagonalGradient); + KPixmapEffect::gradient(*iBtnDownPix2, c.dark(130), c.light(120), + KPixmapEffect::DiagonalGradient); + } + else{ + btnPix1->fill(c.rgb()); + btnDownPix1->fill(c.rgb()); + btnPix2->fill(c.rgb()); + btnDownPix2->fill(c.rgb()); + g = options()->colorGroup(KDecoration::ColorButtonBg, false); + c = g.background(); + iBtnPix1->fill(c.rgb()); + iBtnDownPix1->fill(c.rgb()); + iBtnPix2->fill(c.rgb()); + iBtnDownPix2->fill(c.rgb()); + } + g = options()->colorGroup(KDecoration::ColorButtonBg, true); + c = g.background(); + drawButtonFrame(btnPix1, g, false); + drawButtonFrame(btnDownPix1, g, true); + drawButtonFrame(btnPix2, g, false); + drawButtonFrame(btnDownPix2, g, true); + g = options()->colorGroup(KDecoration::ColorButtonBg, false); + c = g.background(); + drawButtonFrame(iBtnPix1, g, false); + drawButtonFrame(iBtnDownPix1, g, true); + drawButtonFrame(iBtnPix2, g, false); + drawButtonFrame(iBtnDownPix2, g, true); + + if(qGray(options()->color(KDecoration::ColorButtonBg, true).rgb()) > 128) + btnForeground = Qt::black; + else + btnForeground = Qt::white; +} + +static void delete_pixmaps() +{ + delete titlePix; + if(aUpperGradient){ + delete aUpperGradient; + delete iUpperGradient; + delete btnPix1; + delete btnDownPix1; + delete iBtnPix1; + delete iBtnDownPix1; + delete btnPix2; + delete btnDownPix2; + delete iBtnPix2; + delete iBtnDownPix2; + } + pixmaps_created = false; +} + +// ===================================== + +LaptopButton::LaptopButton(ButtonType type, LaptopClient *parent, const char *name) + : KCommonDecorationButton(type, parent, name) +{ + setBackgroundMode(QWidget::NoBackground); +} + +void LaptopButton::reset(unsigned long changed) +{ + if (changed&DecorationReset || changed&ManualReset || changed&SizeChange || changed&StateChange) { + switch (type() ) { + case CloseButton: + setBitmap(close_bits); + break; + case HelpButton: + setBitmap(question_bits); + break; + case MinButton: + setBitmap(iconify_bits); + break; + case MaxButton: + if (isOn() ) { + setBitmap(isLeft() ? l_minmax_bits : r_minmax_bits); + } else { + setBitmap(maximize_bits); + } + break; + case OnAllDesktopsButton: + setBitmap( isOn() ? unsticky_bits : sticky_bits ); + break; + default: + setBitmap(0); + break; + } + + this->update(); + } +} + +void LaptopButton::setBitmap(const unsigned char *bitmap) +{ + if (bitmap) + deco = QBitmap(8, 8, bitmap, true); + else { + deco = QBitmap(8,8); + deco.fill(Qt::color0); + } + deco.setMask(deco); + repaint(); +} + +void LaptopButton::drawButton(QPainter *p) +{ + bool smallBtn = width() == btnWidth1; + if(btnPix1){ + if(decoration()->isActive()){ + if(isDown()) + p->drawPixmap(0, 0, smallBtn ? *btnDownPix1 : *btnDownPix2); + else + p->drawPixmap(0, 0, smallBtn ? *btnPix1 : *btnPix2); + } + else{ + if(isDown()) + p->drawPixmap(0, 0, smallBtn ? *iBtnDownPix1 : *iBtnDownPix2); + else + p->drawPixmap(0, 0, smallBtn ? *iBtnPix1 : *iBtnPix2); + } + } + else{ + QColorGroup g = options()->colorGroup(KDecoration::ColorButtonBg, decoration()->isActive()); + int w = width(); + int h = height(); + p->fillRect(1, 1, w-2, h-2, isDown() ? g.mid() : g.button()); + p->setPen(isDown() ? g.dark() : g.light()); + p->drawLine(0, 0, w-1, 0); + p->drawLine(0, 0, 0, w-1); + p->setPen(isDown() ? g.light() : g.dark()); + p->drawLine(w-1, 0, w-1, h-1); + p->drawLine(0, h-1, w-1, h-1); + } + + p->setPen(btnForeground); + int xOff = (width()-8)/2; + int yOff = (height()-8)/2; + p->drawPixmap(isDown() ? xOff+1: xOff, isDown() ? yOff+1 : yOff, deco); +} + +// ===================================== + +void LaptopClient::reset(unsigned long changed) +{ + KCommonDecoration::reset(changed); +} + +LaptopClient::LaptopClient(KDecorationBridge *b, KDecorationFactory *f) + : KCommonDecoration(b, f) +{ +} + +LaptopClient::~LaptopClient() +{ +} + +QString LaptopClient::visibleName() const +{ + return i18n("Laptop"); +} + +QString LaptopClient::defaultButtonsLeft() const +{ + return "X"; +} + +QString LaptopClient::defaultButtonsRight() const +{ + return "HSIA"; +} + +bool LaptopClient::decorationBehaviour(DecorationBehaviour behaviour) const +{ + switch (behaviour) { + case DB_MenuClose: + return false; + + case DB_WindowMask: + return true; + + case DB_ButtonHide: + return true; + + default: + return KCommonDecoration::decorationBehaviour(behaviour); + } +} + +int LaptopClient::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const +{ + switch (lm) { + case LM_TitleEdgeLeft: + case LM_TitleEdgeRight: + case LM_BorderLeft: + case LM_BorderRight: + return 4; + + case LM_BorderBottom: + return mustDrawHandle() ? handleSize : 4; + + case LM_TitleEdgeTop: + return 3; + + case LM_TitleEdgeBottom: + return 1; + + case LM_TitleBorderLeft: + case LM_TitleBorderRight: + return 0; + + case LM_ButtonWidth: + { + if (btn && (btn->type()==HelpButton||btn->type()==OnAllDesktopsButton) ) { + return btnWidth1; + } else { + return btnWidth2; + } + } + + case LM_ButtonHeight: + case LM_TitleHeight: + if (isToolWindow() ) + return titleHeight-2; + else + return titleHeight; + + case LM_ButtonSpacing: + return 0; + + case LM_ExplicitButtonSpacer: + return 0; + + default: + return KCommonDecoration::layoutMetric(lm, respectWindowState, btn); + } +} + +KCommonDecorationButton *LaptopClient::createButton(ButtonType type) +{ + switch (type) { + case OnAllDesktopsButton: + return new LaptopButton(OnAllDesktopsButton, this, "on_all_desktops"); + + case HelpButton: + return new LaptopButton(HelpButton, this, "help"); + + case MinButton: + return new LaptopButton(MinButton, this, "minimize"); + + case MaxButton: + return new LaptopButton(MaxButton, this, "maximize"); + + case CloseButton: + return new LaptopButton(CloseButton, this, "close"); + + default: + return 0; + } +} + +void LaptopClient::init() +{ + bufferDirty = true; + + KCommonDecoration::init(); +} + +void LaptopClient::captionChange() +{ + bufferDirty = true; + + KCommonDecoration::captionChange(); +} + +void LaptopClient::paintEvent( QPaintEvent* ) +{ + QPainter p(widget()); + QColorGroup g = options()->colorGroup(KDecoration::ColorFrame, isActive()); + + QRect r(widget()->rect()); + p.setPen(Qt::black); + p.drawRect(r); + + // fill mid frame... + p.setPen(g.background() ); + p.drawLine(r.x()+2, r.y()+2, r.right()-2, r.y()+2); + p.drawLine(r.left()+2, r.y()+3, r.left()+2, r.bottom()-layoutMetric(LM_BorderBottom)+1 ); + p.drawLine(r.right()-2, r.y()+3, r.right()-2, r.bottom()-layoutMetric(LM_BorderBottom)+1 ); + p.drawLine(r.left()+3, r.y()+3, r.left()+3, r.y()+layoutMetric(LM_TitleEdgeTop)+layoutMetric(LM_TitleHeight)+layoutMetric(LM_TitleEdgeTop) ); + p.drawLine(r.right()-3, r.y()+3, r.right()-3, r.y()+layoutMetric(LM_TitleEdgeTop)+layoutMetric(LM_TitleHeight)+layoutMetric(LM_TitleEdgeTop) ); + if (!mustDrawHandle() ) + p.drawLine(r.left()+1, r.bottom()-2, r.right()-1, r.bottom()-2); + + // outer frame + p.setPen(g.light()); + p.drawLine(r.x()+1, r.y()+1, r.right()-1, r.y()+1); + p.drawLine(r.x()+1, r.y()+1, r.x()+1, r.bottom()-1); + p.setPen(g.dark()); + p.drawLine(r.right()-1, r.y()+1, r.right()-1, r.bottom()-1); + p.drawLine(r.x()+1, r.bottom()-1, r.right()-1, r.bottom()-1); + + int th = titleHeight; + int bb = handleSize + 2; // Bottom border + int bs = handleSize - 2; // inner size of bottom border + if (!mustDrawHandle()) { + bb = 6; + bs = 0; + } + if ( isToolWindow() ) + th -= 2; + + // inner rect + p.drawRect(r.x() + 3, r.y() + th + 3, r.width() - 6, r.height() - th - bb); + + // handles + if (mustDrawHandle()) { + if (r.width() > 3*handleSize + 20) { + int range = 8 + 3*handleSize/2; + qDrawShadePanel(&p, r.x() + 1, r.bottom() - bs, range, + handleSize - 2, g, false, 1, &g.brush(QColorGroup::Mid)); + qDrawShadePanel(&p, r.x() + range + 1, r.bottom() - bs, + r.width() - 2*range - 2, handleSize - 2, g, false, 1, + isActive() ? &g.brush(QColorGroup::Background) : + &g.brush(QColorGroup::Mid)); + qDrawShadePanel(&p, r.right() - range, r.bottom() - bs, + range, bs, g, false, 1, &g.brush(QColorGroup::Mid)); + } else { + qDrawShadePanel(&p, r.x() + 1, r.bottom() - bs, + r.width() - 2, bs, g, false, 1, + isActive() ? &g.brush(QColorGroup::Background) : + &g.brush(QColorGroup::Mid)); + } + } + + r = titleRect(); + + if(isActive()){ + updateActiveBuffer(); + p.drawPixmap(r.x(), r.y(), activeBuffer); + p.setPen(g.background()); + p.drawPoint(r.x(), r.y()); + p.drawPoint(r.right(), r.y()); + p.drawLine(r.right()+1, r.y(), r.right()+1, r.bottom()); + } + else{ + if(iUpperGradient) + p.drawTiledPixmap(r.x(), r.y(), r.width(), r.height()-1, + *iUpperGradient); + else + p.fillRect(r.x(), r.y(), r.width(), r.height()-1, + options()->color(KDecoration::ColorTitleBar, false)); + + p.setFont(options()->font(false, isToolWindow() )); + QFontMetrics fm(options()->font(false)); + g = options()->colorGroup(KDecoration::ColorTitleBar, false); + if(iUpperGradient) + p.drawTiledPixmap(r.x()+((r.width()-fm.width(caption()))/2)-4, + r.y(), fm.width(caption())+8, r.height()-1, + *iUpperGradient); + else + p.fillRect(r.x()+((r.width()-fm.width(caption()))/2)-4, r.y(), + fm.width(caption())+8, r.height()-1, + g.brush(QColorGroup::Background)); + p.setPen(g.mid()); + p.drawLine(r.x(), r.y(), r.right(), r.y()); + p.drawLine(r.x(), r.y(), r.x(), r.bottom()); + p.setPen(g.button()); + p.drawLine(r.right(), r.y(), r.right(), r.bottom()); + p.drawLine(r.x(), r.bottom(), r.right(), r.bottom()); + p.setPen(options()->color(KDecoration::ColorFont, false)); + p.drawText(r.x(), r.y(), r.width(), r.height()-1, + AlignCenter, caption() ); + g = options()->colorGroup(KDecoration::ColorFrame, true); + p.setPen(g.background()); + p.drawPoint(r.x(), r.y()); + p.drawPoint(r.right(), r.y()); + p.drawLine(r.right()+1, r.y(), r.right()+1, r.bottom()); + } +} + +QRegion LaptopClient::cornerShape(WindowCorner corner) +{ + switch (corner) { + case WC_TopLeft: + return QRect(0, 0, 1, 1); + + case WC_TopRight: + return QRect(width()-1, 0, 1, 1); + + case WC_BottomLeft: + return QRect(0, height()-1, 1, 1); + + case WC_BottomRight: + return QRect(width()-1, height()-1, 1, 1); + + default: + return QRegion(); + } + +} + +bool LaptopClient::mustDrawHandle() const +{ + bool drawSmallBorders = !options()->moveResizeMaximizedWindows(); + if (drawSmallBorders && (maximizeMode() & MaximizeVertical)) { + return false; + } else { + return isResizable(); + } +} + +void LaptopClient::updateActiveBuffer( ) +{ + QRect rTitle = titleRect(); + if( !bufferDirty && (lastBufferWidth == rTitle.width())) + return; + if ( rTitle.width() <= 0 || rTitle.height() <= 0 ) + return; + lastBufferWidth = rTitle.width(); + bufferDirty = false; + + activeBuffer.resize(rTitle.width(), + rTitle.height()); + QPainter p; + QRect r(0, 0, activeBuffer.width(), activeBuffer.height()); + p.begin(&activeBuffer); + if(aUpperGradient){ + p.drawTiledPixmap(r, *aUpperGradient); + } + else{ + p.fillRect(r, options()->color(KDecoration::ColorTitleBar, true)); + } + if(titlePix) + p.drawTiledPixmap(r, *titlePix); + + p.setFont(options()->font(true, isToolWindow() )); + QFontMetrics fm(options()->font(true)); + QColorGroup g = options()->colorGroup(KDecoration::ColorTitleBar, true); + if(aUpperGradient) + p.drawTiledPixmap(r.x()+((r.width()-fm.width(caption()))/2)-4, + r.y(), fm.width(caption())+8, r.height()-1, + *aUpperGradient); + else + p.fillRect(r.x()+((r.width()-fm.width(caption()))/2)-4, 0, + fm.width(caption())+8, r.height(), + g.brush(QColorGroup::Background)); + p.setPen(g.mid()); + p.drawLine(r.x(), r.y(), r.right(), r.y()); + p.drawLine(r.x(), r.y(), r.x(), r.bottom()); + p.setPen(g.button()); + p.drawLine(r.right(), r.y(), r.right(), r.bottom()); + p.drawLine(r.x(), r.bottom(), r.right(), r.bottom()); + p.setPen(options()->color(KDecoration::ColorFont, true)); + p.drawText(r.x(), r.y(), r.width(), r.height()-1, + AlignCenter, caption() ); + g = options()->colorGroup(KDecoration::ColorFrame, true); + p.setPen(g.background()); + p.drawPoint(r.x(), r.y()); + p.drawPoint(r.right(), r.y()); + p.drawLine(r.right()+1, r.y(), r.right()+1, r.bottom()); + p.end(); +} + +static const int SUPPORTED_WINDOW_TYPES_MASK = NET::NormalMask | + NET::DesktopMask | NET::DockMask | NET::ToolbarMask | NET::MenuMask | + NET::DialogMask | /*NET::OverrideMask |*/ NET::TopMenuMask | + NET::UtilityMask | NET::SplashMask; + +bool LaptopClient::isTransient() const +{ + NET::WindowType type = windowType(SUPPORTED_WINDOW_TYPES_MASK); + return type == NET::Dialog; +} + +// ===================================== + +LaptopClientFactory::LaptopClientFactory() +{ + create_pixmaps(); +} + +LaptopClientFactory::~LaptopClientFactory() +{ + delete_pixmaps(); +} + +KDecoration *LaptopClientFactory::createDecoration(KDecorationBridge *b) +{ + findPreferredHandleSize(); + return new Laptop::LaptopClient(b, this); +} + +bool LaptopClientFactory::reset(unsigned long changed) +{ + findPreferredHandleSize(); + + // TODO Do not recreate decorations if it is not needed. Look at + // ModernSystem for how to do that + Laptop::delete_pixmaps(); + Laptop::create_pixmaps(); + + bool needHardReset = true; + if (changed & SettingButtons) { + // handled by KCommonDecoration + needHardReset = false; + } + + if (needHardReset) { + return true; + } else { + resetDecorations(changed); + return false; + } +} + +bool LaptopClientFactory::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonOnAllDesktops: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + return true; + default: + return false; + }; +} + +QValueList< LaptopClientFactory::BorderSize > +LaptopClientFactory::borderSizes() const +{ + // the list must be sorted + return QValueList< BorderSize >() << BorderNormal << BorderLarge << + BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized; +} + +void LaptopClientFactory::findPreferredHandleSize() +{ + switch (options()->preferredBorderSize(this)) { + case KDecoration::BorderLarge: + handleSize = 11; + break; + case KDecoration::BorderVeryLarge: + handleSize = 16; + break; + case KDecoration::BorderHuge: + handleSize = 24; + break; + case KDecoration::BorderVeryHuge: + handleSize = 32; + break; + case KDecoration::BorderOversized: + handleSize = 40; + break; + case KDecoration::BorderTiny: + case KDecoration::BorderNormal: + default: + handleSize = 8; + } +} + +} // Laptop namespace + +// vim: sw=4 diff --git a/kwin/clients/laptop/laptopclient.h b/kwin/clients/laptop/laptopclient.h new file mode 100644 index 000000000..b2e8c3555 --- /dev/null +++ b/kwin/clients/laptop/laptopclient.h @@ -0,0 +1,76 @@ +/* + * Laptop KWin Client + * + * Copyright (c) 2005 Sandro Giessl <sandro@giessl.com> + * Ported to the kde3.2 API by Luciano Montanaro <mikelima@cirulla.net> + */ +#ifndef __KDECLIENT_H +#define __KDECLIENT_H + +#include <qbitmap.h> +#include <kpixmap.h> +#include <kcommondecoration.h> +#include <kdecorationfactory.h> + +namespace Laptop { + +class LaptopClient; + +class LaptopButton : public KCommonDecorationButton +{ +public: + LaptopButton(ButtonType type, LaptopClient *parent, const char *name); + void setBitmap(const unsigned char *bitmap); + virtual void reset(unsigned long changed); + +protected: + virtual void drawButton(QPainter *p); + QBitmap deco; +}; + +class LaptopClient : public KCommonDecoration +{ +public: + LaptopClient( KDecorationBridge* b, KDecorationFactory* f ); + ~LaptopClient(); + + virtual QString visibleName() const; + virtual QString defaultButtonsLeft() const; + virtual QString defaultButtonsRight() const; + virtual bool decorationBehaviour(DecorationBehaviour behaviour) const; + virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton * = 0) const; + virtual KCommonDecorationButton *createButton(ButtonType type); + + virtual QRegion cornerShape(WindowCorner corner); + + void init(); +protected: + void paintEvent( QPaintEvent* ); + void reset( unsigned long ); + void updateActiveBuffer(); + void captionChange(); +private: + bool mustDrawHandle() const; + bool isTransient() const; +private: + KPixmap activeBuffer; + int lastBufferWidth; + bool bufferDirty; +}; + +class LaptopClientFactory : public QObject, public KDecorationFactory +{ +public: + LaptopClientFactory(); + virtual ~LaptopClientFactory(); + virtual KDecoration* createDecoration( KDecorationBridge* ); + virtual bool reset( unsigned long changed ); + virtual bool supports( Ability ability ); + virtual QValueList< BorderSize > borderSizes() const; +private: + void findPreferredHandleSize(); +}; + +} + +#endif diff --git a/kwin/clients/modernsystem/Makefile.am b/kwin/clients/modernsystem/Makefile.am new file mode 100644 index 000000000..75e0249f8 --- /dev/null +++ b/kwin/clients/modernsystem/Makefile.am @@ -0,0 +1,19 @@ + +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +SUBDIRS = config + +kde_module_LTLIBRARIES = kwin3_modernsys.la + +kwin3_modernsys_la_SOURCES = modernsys.cpp +kwin3_modernsys_la_LIBADD = ../../lib/libkdecorations.la +kwin3_modernsys_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module + +METASOURCES = AUTO +noinst_HEADERS = modernsys.h + +lnkdir = $(kde_datadir)/kwin/ +lnk_DATA = modernsystem.desktop + +EXTRA_DIST = $(lnk_DATA) + diff --git a/kwin/clients/modernsystem/btnhighcolor.h b/kwin/clients/modernsystem/btnhighcolor.h new file mode 100644 index 000000000..fa323b9eb --- /dev/null +++ b/kwin/clients/modernsystem/btnhighcolor.h @@ -0,0 +1,93 @@ +/* XPM */ +static const char * btnhighcolor_xpm[] = { +"14 15 75 1", +" c None", +". c #6E6E6E", +"+ c #757575", +"@ c #878787", +"# c #7D7D7D", +"$ c #9E9E9E", +"% c #B9B9B9", +"& c #C6C6C6", +"* c #BABABA", +"= c #A5A5A5", +"- c #7F7F7F", +"; c #848484", +"> c #A7A7A7", +", c #BFBFBF", +"' c #D1D1D1", +") c #D7D7D7", +"! c #DADADA", +"~ c #CBCBCB", +"{ c #ABABAB", +"] c #B3B3B3", +"^ c #C2C2C2", +"/ c #CACACA", +"( c #C9C9C9", +"_ c #B6B6B6", +": c #9A9A9A", +"< c #999999", +"[ c #B0B0B0", +"} c #C4C4C4", +"| c #C3C3C3", +"1 c #C0C0C0", +"2 c #AEAEAE", +"3 c #969696", +"4 c #C1C1C1", +"5 c #CCCCCC", +"6 c #C5C5C5", +"7 c #BEBEBE", +"8 c #AAAAAA", +"9 c #CECECE", +"0 c #D4D4D4", +"a c #DBDBDB", +"b c #DEDEDE", +"c c #D5D5D5", +"d c #D3D3D3", +"e c #BCBCBC", +"f c #CDCDCD", +"g c #E0E0E0", +"h c #E4E4E4", +"i c #E8E8E8", +"j c #EBEBEB", +"k c #E9E9E9", +"l c #E6E6E6", +"m c #DDDDDD", +"n c #E1E1E1", +"o c #EDEDED", +"p c #F1F1F1", +"q c #F5F5F5", +"r c #F8F8F8", +"s c #F6F6F6", +"t c #F3F3F3", +"u c #EEEEEE", +"v c #E5E5E5", +"w c #DCDCDC", +"x c #B7B7B7", +"y c #E2E2E2", +"z c #FDFDFD", +"A c #FFFFFF", +"B c #FCFCFC", +"C c #F7F7F7", +"D c #B5B5B5", +"E c #F2F2F2", +"F c #FAFAFA", +"G c #9B9B9B", +"H c #FBFBFB", +"I c #A9A9A9", +"J c #747474", +" .... ", +" ..+@@+.. ", +" .#$%&&*=-. ", +" .;>,')!)~{#. ", +" .$]^///(&_:. ", +".<[*^}||11*23.", +".[4&5555~(678.", +".,90!aba)cd~e.", +".faghijklhm06.", +".'nopqrstuvw/.", +".xyprzAzBCunD.", +" .'EzAAAAFpf. ", +" .GcHAAAAF0<. ", +" ..I5kk5I.. ", +" J..... "}; diff --git a/kwin/clients/modernsystem/buttondata.h b/kwin/clients/modernsystem/buttondata.h new file mode 100644 index 000000000..1af5fb8dd --- /dev/null +++ b/kwin/clients/modernsystem/buttondata.h @@ -0,0 +1,42 @@ +/* Image bits processed by KPixmap2Bitmaps */ + +#define lowcolor_mask_width 14 +#define lowcolor_mask_height 15 +static const unsigned char lowcolor_mask_bits[] = { + 0xf0,0x03,0xf8,0x07,0xfc,0xcf,0xfe,0x1f,0xfe,0x1f,0xff,0xff,0xff,0xff,0xff, + 0x3f,0xff,0x3f,0xff,0xbf,0xfe,0xdf,0xfe,0x9f,0xfc,0x0f,0xf8,0x07,0xf0,0x03, + 0x00,0x40,0x80,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x7c,0xfe,0x87,0x40,0x00, + 0x00,0x64,0x00,0x20,0x00,0x64,0x00,0x86,0xfe,0x87,0x40,0x00,0x00,0x65,0x00 }; + +#define lowcolor_6a696a_width 14 +#define lowcolor_6a696a_height 15 +static const unsigned char lowcolor_6a696a_bits[] = { + 0xf0,0x03,0x18,0x06,0x04,0xcc,0x06,0x18,0x02,0x10,0x00,0xc0,0x00,0xc0,0x00, + 0x00,0x00,0x00,0x00,0xc0,0x00,0xc0,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x40,0x80,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03, + 0x00,0x00,0x00,0x80,0x24,0x0e,0x08,0x61,0x00,0x00,0x00,0xf0,0xd9,0x0c,0x08 }; + +#define lowcolor_949194_width 14 +#define lowcolor_949194_height 15 +static const unsigned char lowcolor_949194_bits[] = { + 0x00,0x40,0xe0,0x01,0x08,0x02,0x00,0x04,0x04,0x08,0x07,0x78,0x03,0xf0,0x01, + 0xe0,0x01,0x60,0x01,0x20,0x00,0xc0,0x02,0x90,0x04,0x08,0x08,0x04,0xf0,0x03, + 0x00,0x40,0x80,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0xc8,0x51,0x0c,0x08,0x0e, + 0x01,0x00,0x00,0x37,0x00,0x00,0x00,0x58,0x5f,0x49,0x6d,0x61,0x67,0x65,0x54 }; + +#define lowcolor_b4b6b4_width 14 +#define lowcolor_b4b6b4_height 15 +static const unsigned char lowcolor_b4b6b4_bits[] = { + 0x00,0x40,0x00,0x00,0x10,0x00,0x08,0x02,0x18,0x06,0xb8,0x47,0x0c,0xce,0x0e, + 0xd8,0x06,0x58,0x02,0x10,0x02,0xd0,0x00,0x80,0x00,0x00,0x10,0x02,0x00,0x00, + 0x00,0x40,0x80,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01, + 0x00,0x08,0x00,0x02,0x00,0x00,0x00,0x61,0x00,0x00,0x00,0x38,0x5b,0x0c,0x08 }; + +#define lowcolor_e6e6e6_width 14 +#define lowcolor_e6e6e6_height 15 +static const unsigned char lowcolor_e6e6e6_bits[] = { + 0x00,0x40,0x00,0x00,0x00,0x00,0xe0,0x01,0x00,0x00,0x00,0x40,0x00,0xc0,0x00, + 0xc0,0x00,0x40,0xe0,0xc0,0xe0,0xc1,0xe0,0x81,0xf0,0x03,0xc0,0x00,0x00,0x00, + 0x00,0x40,0x80,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x08,0x19,0x0d,0x08,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00 }; + diff --git a/kwin/clients/modernsystem/config/Makefile.am b/kwin/clients/modernsystem/config/Makefile.am new file mode 100644 index 000000000..79bf21290 --- /dev/null +++ b/kwin/clients/modernsystem/config/Makefile.am @@ -0,0 +1,14 @@ + +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = kwin_modernsys_config.la + +kwin_modernsys_config_la_SOURCES = config.cpp +kwin_modernsys_config_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin_modernsys_config_la_LIBADD = $(LIB_KDEUI) + +METASOURCES = AUTO +noinst_HEADERS = config.h + +lnkdir = $(kde_datadir)/kwin/ + diff --git a/kwin/clients/modernsystem/config/config.cpp b/kwin/clients/modernsystem/config/config.cpp new file mode 100644 index 000000000..542494d28 --- /dev/null +++ b/kwin/clients/modernsystem/config/config.cpp @@ -0,0 +1,130 @@ +// Melchior FRANZ <mfranz@kde.org> -- 2001-04-22 + +#include <kapplication.h> +#include <kconfig.h> +#include <kdialog.h> +#include <klocale.h> +#include <kglobal.h> +#include <qlayout.h> +#include <qwhatsthis.h> +#include "config.h" + + +extern "C" +{ + KDE_EXPORT QObject* allocate_config(KConfig* conf, QWidget* parent) + { + return(new ModernSysConfig(conf, parent)); + } +} + + +// 'conf' is a pointer to the kwindecoration modules open kwin config, +// and is by default set to the "Style" group. +// +// 'parent' is the parent of the QObject, which is a VBox inside the +// Configure tab in kwindecoration + +ModernSysConfig::ModernSysConfig(KConfig* conf, QWidget* parent) : QObject(parent) +{ + clientrc = new KConfig("kwinmodernsysrc"); + KGlobal::locale()->insertCatalogue("kwin_clients"); + mainw = new QWidget(parent); + vbox = new QVBoxLayout(mainw); + vbox->setSpacing(6); + vbox->setMargin(0); + + handleBox = new QWidget(mainw); + QGridLayout* layout = new QGridLayout(handleBox, 0, KDialog::spacingHint()); + + cbShowHandle = new QCheckBox(i18n("&Show window resize handle"), handleBox); + QWhatsThis::add(cbShowHandle, + i18n("When selected, all windows are drawn with a resize " + "handle at the lower right corner. This makes window resizing " + "easier, especially for trackballs and other mouse replacements " + "on laptops.")); + layout->addMultiCellWidget(cbShowHandle, 0, 0, 0, 1); + connect(cbShowHandle, SIGNAL(clicked()), this, SLOT(slotSelectionChanged())); + + sliderBox = new QVBox(handleBox); + handleSizeSlider = new QSlider(0, 4, 1, 0, QSlider::Horizontal, sliderBox); + QWhatsThis::add(handleSizeSlider, + i18n("Here you can change the size of the resize handle.")); + handleSizeSlider->setTickInterval(1); + handleSizeSlider->setTickmarks(QSlider::Below); + connect(handleSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(slotSelectionChanged())); + + hbox = new QHBox(sliderBox); + hbox->setSpacing(6); + + bool rtl = kapp->reverseLayout(); + label1 = new QLabel(i18n("Small"), hbox); + label1->setAlignment(rtl ? AlignRight : AlignLeft); + label2 = new QLabel(i18n("Medium"), hbox); + label2->setAlignment(AlignHCenter); + label3 = new QLabel(i18n("Large"), hbox); + label3->setAlignment(rtl ? AlignLeft : AlignRight); + + vbox->addWidget(handleBox); + vbox->addStretch(1); + +// layout->setColSpacing(0, 30); + layout->addItem(new QSpacerItem(30, 10, QSizePolicy::Fixed, QSizePolicy::Fixed), 1, 0); + layout->addWidget(sliderBox, 1, 1); + + load(conf); + mainw->show(); +} + + +ModernSysConfig::~ModernSysConfig() +{ + delete mainw; + delete clientrc; +} + + +void ModernSysConfig::slotSelectionChanged() +{ + bool i = cbShowHandle->isChecked(); + if (i != hbox->isEnabled()) { + hbox->setEnabled(i); + handleSizeSlider->setEnabled(i); + } + emit changed(); +} + + +void ModernSysConfig::load(KConfig* /*conf*/) +{ + clientrc->setGroup("General"); + bool i = clientrc->readBoolEntry("ShowHandle", true ); + cbShowHandle->setChecked(i); + hbox->setEnabled(i); + handleSizeSlider->setEnabled(i); + handleWidth = clientrc->readUnsignedNumEntry("HandleWidth", 6); + handleSize = clientrc->readUnsignedNumEntry("HandleSize", 30); + handleSizeSlider->setValue(QMIN((handleWidth - 6) / 2, 4)); + +} + + +void ModernSysConfig::save(KConfig* /*conf*/) +{ + clientrc->setGroup("General"); + clientrc->writeEntry("ShowHandle", cbShowHandle->isChecked()); + clientrc->writeEntry("HandleWidth", 6 + 2 * handleSizeSlider->value()); + clientrc->writeEntry("HandleSize", 30 + 4 * handleSizeSlider->value()); + clientrc->sync(); +} + + +void ModernSysConfig::defaults() +{ + cbShowHandle->setChecked(true); + hbox->setEnabled(true); + handleSizeSlider->setEnabled(true); + handleSizeSlider->setValue(0); +} + +#include "config.moc" diff --git a/kwin/clients/modernsystem/config/config.h b/kwin/clients/modernsystem/config/config.h new file mode 100644 index 000000000..4f4b97123 --- /dev/null +++ b/kwin/clients/modernsystem/config/config.h @@ -0,0 +1,50 @@ +#ifndef __KDE_MODSYSTEMCONFIG_H +#define __KDE_MODSYSTEMCONFIG_H + +#include <qcheckbox.h> +#include <qgroupbox.h> +#include <qlayout.h> +#include <qvbox.h> +#include <qslider.h> +#include <qlabel.h> + +class ModernSysConfig : public QObject +{ + Q_OBJECT + + public: + ModernSysConfig(KConfig* conf, QWidget* parent); + ~ModernSysConfig(); + + // These public signals/slots work similar to KCM modules + signals: + void changed(); + + public slots: + void load(KConfig* conf); + void save(KConfig* conf); + void defaults(); + + protected slots: + void slotSelectionChanged(); // Internal use + + private: + KConfig *clientrc; + QWidget *mainw; + QVBoxLayout *vbox; + QWidget *handleBox; + QCheckBox *cbShowHandle; + QVBox *sliderBox; + QSlider *handleSizeSlider; + QHBox *hbox; + QLabel *label1; + QLabel *label2; + QLabel *label3; + + unsigned handleWidth; + unsigned handleSize; + +}; + + +#endif diff --git a/kwin/clients/modernsystem/modernsys.cpp b/kwin/clients/modernsystem/modernsys.cpp new file mode 100644 index 000000000..d75816343 --- /dev/null +++ b/kwin/clients/modernsystem/modernsys.cpp @@ -0,0 +1,739 @@ +// Daniel M. DULEY <mosfet@kde.org> original work +// Melchior FRANZ <a8603365@unet.univie.ac.at> configuration options + +#include <kconfig.h> +#include <kglobal.h> +#include <klocale.h> +#include <qlayout.h> +#include <qdrawutil.h> +#include <kpixmapeffect.h> +#include <kdrawutil.h> +#include <qbitmap.h> +#include <qtooltip.h> +#include <qapplication.h> +#include <qlabel.h> +#include "modernsys.h" + +#include "buttondata.h" +#include "btnhighcolor.h" +#include <qimage.h> + +namespace ModernSystem { + +static unsigned char iconify_bits[] = { + 0x00, 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00}; + +static unsigned char close_bits[] = { + 0x00, 0x66, 0x7e, 0x3c, 0x3c, 0x7e, 0x66, 0x00}; + +static unsigned char maximize_bits[] = { + 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00}; + +static unsigned char r_minmax_bits[] = { + 0x0c, 0x18, 0x33, 0x67, 0xcf, 0x9f, 0x3f, 0x3f}; + +static unsigned char l_minmax_bits[] = { + 0x30, 0x18, 0xcc, 0xe6, 0xf3, 0xf9, 0xfc, 0xfc}; + +static unsigned char unsticky_bits[] = { + 0x3c, 0x42, 0x99, 0xbd, 0xbd, 0x99, 0x42, 0x3c}; + +static unsigned char sticky_bits[] = { + 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c}; + +static unsigned char question_bits[] = { + 0x3c, 0x66, 0x60, 0x30, 0x18, 0x00, 0x18, 0x18}; + +static unsigned char above_on_bits[] = { + 0x7e, 0x00, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00}; + +static unsigned char above_off_bits[] = { + 0x18, 0x3c, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00}; + +static unsigned char below_off_bits[] = { + 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x3c, 0x18}; + +static unsigned char below_on_bits[] = { + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x00, 0x7e}; + +static unsigned char shade_off_bits[] = { + 0x00, 0x7e, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static unsigned char shade_on_bits[] = { + 0x00, 0x7e, 0x7e, 0x42, 0x42, 0x42, 0x7e, 0x00}; + +static unsigned char menu_bits[] = { + 0xff, 0x81, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff}; + +static unsigned char btnhighcolor_mask_bits[] = { + 0xe0,0x41,0xf8,0x07,0xfc,0x0f,0xfe,0xdf,0xfe,0x1f,0xff,0x3f,0xff,0xff,0xff, + 0x3f,0xff,0x3f,0xff,0xff,0xff,0xff,0xfe,0x9f,0xfe,0x1f,0xfc,0x0f,0xf0,0x03, + 0x00,0x40,0x80,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x20,0x99,0x0f,0x08,0xc4, + 0x00,0x00,0x00,0x67,0x00,0x00,0x00,0x58,0x5f,0x43,0x68,0x61,0x6e,0x67,0x65 }; + +static KPixmap *aUpperGradient=0; +static KPixmap *iUpperGradient=0; +static QPixmap *buttonPix=0; +static QPixmap *buttonPixDown=0; +static QPixmap *iButtonPix=0; +static QPixmap *iButtonPixDown=0; + +static QColor *buttonFg; +static bool pixmaps_created = false; + +static QBitmap *lcDark1; +static QBitmap *lcDark2; +static QBitmap *lcDark3; +static QBitmap *lcLight1; +static QImage *btnSource; + +static bool show_handle; +static int handle_size; +static int handle_width; +static int border_width; +static int title_height; + +static inline const KDecorationOptions* options() +{ + return KDecoration::options(); +} + +static void make_button_fx(const QColorGroup &g, QPixmap *pix, bool light=false) +{ + pix->fill(g.background()); + QPainter p(pix); + + if(QPixmap::defaultDepth() > 8){ + int i, destH, destS, destV, srcH, srcS, srcV; + QColor btnColor = g.background(); + + if(btnSource->depth() < 32) + *btnSource = btnSource->convertDepth(32); + if(light) + btnColor = btnColor.light(120); + btnColor.hsv(&destH, &destS, &destV); + QImage btnDest(14, 15, 32); + + unsigned int *srcData = (unsigned int *)btnSource->bits(); + unsigned int *destData = (unsigned int *)btnDest.bits(); + QColor srcColor; + for(i=0; i < btnSource->width()*btnSource->height(); ++i){ + srcColor.setRgb(srcData[i]); + srcColor.hsv(&srcH, &srcS, &srcV); + srcColor.setHsv(destH, destS, srcV); + destData[i] = srcColor.rgb(); + } + pix->convertFromImage(btnDest); + + } + else{ + if(!lcDark1->mask()){ + lcDark1->setMask(*lcDark1); + lcDark2->setMask(*lcDark2); + lcDark3->setMask(*lcDark3); + lcLight1->setMask(*lcLight1); + } + p.setPen(g.dark()); + p.drawPixmap(0, 0, *lcDark2); + p.drawPixmap(0, 0, *lcDark1); + p.setPen(g.mid()); + p.drawPixmap(0, 0, *lcDark3); + p.setPen(g.light()); + p.drawPixmap(0, 0, *lcLight1); + } +} + + +static void create_pixmaps() +{ + if(pixmaps_created) + return; + pixmaps_created = true; + + lcDark1 = new QBitmap(14, 15, lowcolor_6a696a_bits, true); + lcDark2 = new QBitmap(14, 15, lowcolor_949194_bits, true); + lcDark3 = new QBitmap(14, 15, lowcolor_b4b6b4_bits, true); + lcLight1 = new QBitmap(14, 15, lowcolor_e6e6e6_bits, true); + btnSource = new QImage(btnhighcolor_xpm); + + if(QPixmap::defaultDepth() > 8){ + aUpperGradient = new KPixmap; + aUpperGradient->resize(32, title_height+2); + iUpperGradient = new KPixmap; + iUpperGradient->resize(32, title_height+2); + KPixmapEffect::gradient(*aUpperGradient, + options()->color(KDecoration::ColorTitleBar, true).light(130), + options()->color(KDecoration::ColorTitleBlend, true), + KPixmapEffect::VerticalGradient); + KPixmapEffect::gradient(*iUpperGradient, + options()->color(KDecoration::ColorTitleBar, false).light(130), + options()->color(KDecoration::ColorTitleBlend, false), + KPixmapEffect::VerticalGradient); + } + // buttons + QColorGroup btnColor(options()->colorGroup(KDecoration::ColorButtonBg, true)); + buttonPix = new QPixmap(14, 15); + make_button_fx(btnColor, buttonPix); + buttonPixDown = new QPixmap(14, 15); + make_button_fx(btnColor, buttonPixDown, true); + + btnColor = options()->colorGroup(KDecoration::ColorButtonBg, false); + iButtonPix = new QPixmap(14, 15); + make_button_fx(btnColor, iButtonPix); + iButtonPixDown = new QPixmap(14, 15); + make_button_fx(btnColor, iButtonPixDown, true); + + + if(qGray(btnColor.background().rgb()) < 150) + buttonFg = new QColor(Qt::white); + else + buttonFg = new QColor(Qt::black); + + delete lcDark1; + delete lcDark2; + delete lcDark3; + delete lcLight1; + delete btnSource; +} + +static void delete_pixmaps() +{ + if(aUpperGradient){ + delete aUpperGradient; + delete iUpperGradient; + } + delete buttonPix; + delete buttonPixDown; + delete iButtonPix; + delete iButtonPixDown; + + delete buttonFg; + + pixmaps_created = false; +} + +void ModernSysFactory::read_config() +{ + bool showh; + int hsize, hwidth, bwidth, theight; + + KConfig c("kwinmodernsysrc"); + c.setGroup("General"); + showh = c.readBoolEntry("ShowHandle", true); + + hwidth = c.readUnsignedNumEntry("HandleWidth", 6); + hsize = c.readUnsignedNumEntry("HandleSize", 30); + if (!(showh && hsize && hwidth)) { + showh = false; + hwidth = hsize = 0; + } + + switch(options()->preferredBorderSize( this )) { + case BorderLarge: + bwidth = 8; + hwidth = hwidth * 7/5; + hsize = hsize * 7/5; + break; + case BorderVeryLarge: + bwidth = 12; + hwidth = hwidth * 17/10 + 2; + hsize = hsize * 17/10; + break; + case BorderHuge: + bwidth = 18; + hwidth = hwidth * 2 + 6; + hsize = hsize * 2; + break; + /* + // If we allow these large sizes we need to change the + // correlation between the border width and the handle size. + case BorderVeryHuge: + bwidth = 27; + hwidth = hwidth * 5/2 + 15; + hsize = hsize * 5/2; + break; + case BorderOversized: + bwidth = 40; + hwidth = hwidth * 3 + 22; + hsize = hsize * 3; + break; + */ + case BorderNormal: + default: + bwidth = 4; + } + + theight = QFontMetrics(options()->font(true)).height() + 2; + if (theight < 16) + theight = 16; + if (theight < bwidth) + theight = bwidth; + + show_handle = showh; + handle_width = hwidth; + handle_size = hsize; + border_width = bwidth; + title_height = theight; +} + +QValueList< ModernSysFactory::BorderSize > ModernSysFactory::borderSizes() const +{ // the list must be sorted + return QValueList< BorderSize >() << BorderNormal << BorderLarge << + BorderVeryLarge << BorderHuge; + // as long as the buttons don't scale don't offer the largest two sizes. + // BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized; +} + +ModernButton::ModernButton(ButtonType type, ModernSys *parent, const char *name) + : KCommonDecorationButton(type, parent, name) +{ + setBackgroundMode( NoBackground ); + + QBitmap mask(14, 15, QPixmap::defaultDepth() > 8 ? + btnhighcolor_mask_bits : lowcolor_mask_bits, true); + resize(14, 15); + + setMask(mask); +} + +void ModernButton::reset(unsigned long changed) +{ + if (changed&DecorationReset || changed&ManualReset || changed&SizeChange || changed&StateChange) { + switch (type() ) { + case CloseButton: + setBitmap(close_bits); + break; + case HelpButton: + setBitmap(question_bits); + break; + case MinButton: + setBitmap(iconify_bits); + break; + case MaxButton: + setBitmap( isOn() ? (isLeft()?l_minmax_bits:r_minmax_bits) : maximize_bits ); + break; + case OnAllDesktopsButton: + setBitmap( isOn() ? unsticky_bits : sticky_bits ); + break; + case ShadeButton: + setBitmap( isOn() ? shade_on_bits : shade_off_bits ); + break; + case AboveButton: + setBitmap( isOn() ? above_on_bits : above_off_bits ); + break; + case BelowButton: + setBitmap( isOn() ? below_on_bits : below_off_bits ); + break; + case MenuButton: + setBitmap(menu_bits); + break; + default: + setBitmap(0); + break; + } + + this->update(); + } +} + +void ModernButton::setBitmap(const unsigned char *bitmap) +{ + if (bitmap) + deco = QBitmap(8, 8, bitmap, true); + else { + deco = QBitmap(8,8); + deco.fill(Qt::color0); + } + deco.setMask(deco); +} + +void ModernButton::drawButton(QPainter *p) +{ + if(decoration()->isActive()){ + if(buttonPix) + p->drawPixmap(0, 0, isDown() ? *buttonPixDown : *buttonPix); + } + else{ + if(iButtonPix) + p->drawPixmap(0, 0, isDown() ? *iButtonPixDown : *iButtonPix); + } + if(!deco.isNull()){ + p->setPen(*buttonFg); + p->drawPixmap(isDown() ? 4 : 3, isDown() ? 5 : 4, deco); + } +} + +void ModernSys::reset( unsigned long changed) +{ + KCommonDecoration::reset(changed); + + titleBuffer.resize(0, 0); + recalcTitleBuffer(); + resetButtons(); + widget()->update(); +} + +ModernSys::ModernSys( KDecorationBridge* b, KDecorationFactory* f ) + : KCommonDecoration( b, f ) +{ +} + +QString ModernSys::visibleName() const +{ + return i18n("Modern System"); +} + +QString ModernSys::defaultButtonsLeft() const +{ + return "X"; +} + +QString ModernSys::defaultButtonsRight() const +{ + return "HSIA"; +} + +bool ModernSys::decorationBehaviour(DecorationBehaviour behaviour) const +{ + switch (behaviour) { + case DB_MenuClose: + return false; + + case DB_WindowMask: + return true; + + case DB_ButtonHide: + return true; + + default: + return KCommonDecoration::decorationBehaviour(behaviour); + } +} + +int ModernSys::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const +{ + // bool maximized = maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows(); + + switch (lm) { + case LM_BorderLeft: + return border_width + (reverse ? handle_width : 0); + + case LM_BorderRight: + return border_width + (reverse ? 0 : handle_width); + + case LM_BorderBottom: + return border_width + handle_width; + + case LM_TitleEdgeLeft: + return layoutMetric(LM_BorderLeft,respectWindowState)+3; + case LM_TitleEdgeRight: + return layoutMetric(LM_BorderRight,respectWindowState)+3; + + case LM_TitleEdgeTop: + return 2; + + case LM_TitleEdgeBottom: + return 2; + + case LM_TitleBorderLeft: + case LM_TitleBorderRight: + return 4; + + case LM_TitleHeight: + return title_height; + + case LM_ButtonWidth: + return 14; + case LM_ButtonHeight: + return 15; + + case LM_ButtonSpacing: + return 1; + + case LM_ExplicitButtonSpacer: + return 3; + + default: + return KCommonDecoration::layoutMetric(lm, respectWindowState, btn); + } +} + +KCommonDecorationButton *ModernSys::createButton(ButtonType type) +{ + switch (type) { + case MenuButton: + return new ModernButton(MenuButton, this, "menu"); + + case OnAllDesktopsButton: + return new ModernButton(OnAllDesktopsButton, this, "on_all_desktops"); + + case HelpButton: + return new ModernButton(HelpButton, this, "help"); + + case MinButton: + return new ModernButton(MinButton, this, "minimize"); + + case MaxButton: + return new ModernButton(MaxButton, this, "maximize"); + + case CloseButton: + return new ModernButton(CloseButton, this, "close"); + + case AboveButton: + return new ModernButton(AboveButton, this, "above"); + + case BelowButton: + return new ModernButton(BelowButton, this, "below"); + + case ShadeButton: + return new ModernButton(ShadeButton, this, "shade"); + + default: + return 0; + } +} + +void ModernSys::init() +{ + reverse = QApplication::reverseLayout(); + + KCommonDecoration::init(); + + recalcTitleBuffer(); +} + +void ModernSys::recalcTitleBuffer() +{ + if(oldTitle == caption() && width() == titleBuffer.width()) + return; + + QFontMetrics fm(options()->font(true)); + titleBuffer.resize(width(), title_height+2); + QPainter p; + p.begin(&titleBuffer); + if(aUpperGradient) + p.drawTiledPixmap(0, 0, width(), title_height+2, *aUpperGradient); + else + p.fillRect(0, 0, width(), title_height+2, + options()->colorGroup(ColorTitleBar, true). + brush(QColorGroup::Button)); + + QRect t = titleRect(); // titlebar->geometry(); + t.setTop( 2 ); + t.setLeft( t.left() ); + t.setRight( t.right() - 2 ); + + QRegion r(t.x(), 0, t.width(), title_height+2); + r -= QRect(t.x()+((t.width()-fm.width(caption()))/2)-4, + 0, fm.width(caption())+8, title_height+2); + p.setClipRegion(r); + int i, ly; + ly = (title_height % 3 == 0) ? 3 : 4; + for(i=0; i < (title_height-2)/3; ++i, ly+=3){ + p.setPen(options()->color(ColorTitleBar, true).light(150)); + p.drawLine(0, ly, width()-1, ly); + p.setPen(options()->color(ColorTitleBar, true).dark(120)); + p.drawLine(0, ly+1, width()-1, ly+1); + } + p.setClipRect(t); + p.setPen(options()->color(ColorFont, true)); + p.setFont(options()->font(true)); + + p.drawText(t.x()+((t.width()-fm.width(caption()))/2)-4, + 0, fm.width(caption())+8, title_height+2, AlignCenter, caption()); + p.setClipping(false); + p.end(); + oldTitle = caption(); +} + +void ModernSys::updateCaption() +{ + widget()->update(titleRect() ); +} + +void ModernSys::drawRoundFrame(QPainter &p, int x, int y, int w, int h) +{ + kDrawRoundButton(&p, x, y, w, h, + options()->colorGroup(ColorFrame, isActive()), false); + +} + +void ModernSys::paintEvent( QPaintEvent* ) +{ + // update title buffer... + if (oldTitle != caption() || width() != titleBuffer.width() ) + recalcTitleBuffer(); + + int hs = handle_size; + int hw = handle_width; + + QPainter p( widget() ); + QRect t = titleRect(); // titlebar->geometry(); + + QBrush fillBrush(widget()->colorGroup().brush(QColorGroup::Background).pixmap() ? + widget()->colorGroup().brush(QColorGroup::Background) : + options()->colorGroup(ColorFrame, isActive()). + brush(QColorGroup::Button)); + + p.fillRect(1, title_height+3, width()-2, height()-(title_height+3), fillBrush); + p.fillRect(width()-6, 0, width()-1, height(), fillBrush); + + t.setTop( 2 ); + t.setLeft( t.left() ); + t.setRight( t.right() - 2 ); + + int w = width() - hw; // exclude handle + int h = height() - hw; + + // titlebar + QColorGroup g = options()->colorGroup(ColorTitleBar, isActive()); + if(isActive()){ + p.drawPixmap(1, 1, titleBuffer, 0, 0, w-2, title_height+2); + } + else{ + if(iUpperGradient) + p.drawTiledPixmap(1, 1, w-2, title_height+2, *iUpperGradient); + else + p.fillRect(1, 1, w-2, title_height+2, fillBrush); + p.setPen(options()->color(ColorFont, isActive())); + p.setFont(options()->font(isActive())); + p.drawText(t, AlignCenter, caption() ); + } + + // titlebar highlight + p.setPen(g.light()); + p.drawLine(1, 1, 1, title_height+3); + p.drawLine(1, 1, w-3, 1); + p.setPen(g.dark()); + p.drawLine(w-2, 1, w-2, title_height+3); + p.drawLine(0, title_height+2, w-2, title_height+2); + + // frame + g = options()->colorGroup(ColorFrame, isActive()); + p.setPen(g.light()); + p.drawLine(1, title_height+3, 1, h-2); + p.setPen(g.dark()); + p.drawLine(2, h-2, w-2, h-2); + p.drawLine(w-2, title_height+3, w-2, h-2); + //p.drawPoint(w-3, title_height+3); + //p.drawPoint(2, title_height+3); + + qDrawShadePanel(&p, border_width-1, title_height+3, w-2*border_width+2, h-title_height-border_width-2, g, true); + + if (show_handle) { + p.setPen(g.dark()); + p.drawLine(width()-3, height()-hs-1, width()-3, height()-3); + p.drawLine(width()-hs-1, height()-3, width()-3, height()-3); + + p.setPen(g.light()); + p.drawLine(width()-hw, height()-hs-1, width()-hw, height()-hw); + p.drawLine(width()-hs-1, height()-hw, width()-hw, height()-hw); + p.drawLine(width()-hw, height()-hs-1, width()-4, height()-hs-1); + p.drawLine(width()-hs-1, height()-hw, width()-hs-1, height()-4); + + p.setPen(Qt::black); + p.drawRect(0, 0, w, h); + + // handle outline + p.drawLine(width()-hw, height()-hs, width(), height()-hs); + p.drawLine(width()-2, height()-hs, width()-2, height()-2); + p.drawLine(width()-hs, height()-2, width()-2, height()-2); + p.drawLine(width()-hs, height()-hw, width()-hs, height()-2); + } else { + p.setPen(Qt::black); + p.drawRect(0, 0, w, h); + } +} + +void ModernSys::updateWindowShape() +{ + int hs = handle_size; + int hw = handle_width; + QRegion mask; + mask += QRect(0, 0, width()-hw, height()-hw); + //single points + mask -= QRect(0, 0, 1, 1); + mask -= QRect(width()-hw-1, 0, 1, 1); + mask -= QRect(0, height()-hw-1, 1, 1); + + if (show_handle) { + mask += QRect(width()-hs, height()-hs, hs-1, hs-1); + mask -= QRect(width()-2, height()-2, 1, 1); + mask -= QRect(width()-2, height()-hs, 1, 1); + mask -= QRect(width()-hs, height()-2, 1, 1); + } else + mask -= QRect(width()-1, height()-1, 1, 1); + + setMask(mask); +} + +ModernSysFactory::ModernSysFactory() +{ + read_config(); + create_pixmaps(); +} + +ModernSysFactory::~ModernSysFactory() +{ + ModernSystem::delete_pixmaps(); +} + +KDecoration* ModernSysFactory::createDecoration( KDecorationBridge* b ) +{ + return(new ModernSys(b, this)); +} + +bool ModernSysFactory::reset( unsigned long changed ) +{ + read_config(); + + bool needHardReset = true; + if( changed & (SettingColors | SettingBorder | SettingFont) ) + { + delete_pixmaps(); + create_pixmaps(); + needHardReset = false; + } else if (changed & SettingButtons) { + // handled by KCommonDecoration + needHardReset = false; + } + + if( needHardReset ) + return true; + else + { + resetDecorations( changed ); + return false; // no recreating of decorations + } +} + +bool ModernSysFactory::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonOnAllDesktops: + case AbilityButtonSpacer: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + case AbilityButtonAboveOthers: + case AbilityButtonBelowOthers: + case AbilityButtonShade: + case AbilityButtonMenu: + return true; + default: + return false; + }; +} + +} + +// KWin extended plugin interface +extern "C" KDE_EXPORT KDecorationFactory* create_factory() +{ + return new ModernSystem::ModernSysFactory(); +} + +// vim:ts=4:sw=4 diff --git a/kwin/clients/modernsystem/modernsys.h b/kwin/clients/modernsystem/modernsys.h new file mode 100644 index 000000000..d185be048 --- /dev/null +++ b/kwin/clients/modernsystem/modernsys.h @@ -0,0 +1,72 @@ +#ifndef __MODSYSTEMCLIENT_H +#define __MODSYSTEMCLIENT_H + +#include <qbitmap.h> +#include <kpixmap.h> +#include <kcommondecoration.h> +#include <kdecorationfactory.h> + +class QLabel; +class QSpacerItem; + +namespace ModernSystem { + +class ModernSys; + +class ModernButton : public KCommonDecorationButton +{ +public: + ModernButton(ButtonType type, ModernSys *parent, const char *name); + void setBitmap(const unsigned char *bitmap); + virtual void reset(unsigned long changed); +protected: + + virtual void drawButton(QPainter *p); + void drawButtonLabel(QPainter *){;} + QBitmap deco; +}; + +class ModernSys : public KCommonDecoration +{ +public: + ModernSys( KDecorationBridge* b, KDecorationFactory* f ); + ~ModernSys(){;} + + virtual QString visibleName() const; + virtual QString defaultButtonsLeft() const; + virtual QString defaultButtonsRight() const; + virtual bool decorationBehaviour(DecorationBehaviour behaviour) const; + virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton * = 0) const; + virtual KCommonDecorationButton *createButton(ButtonType type); + + virtual void updateWindowShape(); + virtual void updateCaption(); + + void init(); +protected: + void drawRoundFrame(QPainter &p, int x, int y, int w, int h); + void paintEvent( QPaintEvent* ); + void recalcTitleBuffer(); + void reset( unsigned long ); +private: + QPixmap titleBuffer; + QString oldTitle; + bool reverse; +}; + +class ModernSysFactory : public QObject, public KDecorationFactory +{ +public: + ModernSysFactory(); + virtual ~ModernSysFactory(); + virtual KDecoration* createDecoration( KDecorationBridge* ); + virtual bool reset( unsigned long changed ); + virtual bool supports( Ability ability ); + QValueList< BorderSize > borderSizes() const; +private: + void read_config(); +}; + +} + +#endif diff --git a/kwin/clients/modernsystem/modernsystem.desktop b/kwin/clients/modernsystem/modernsystem.desktop new file mode 100644 index 000000000..d6247a390 --- /dev/null +++ b/kwin/clients/modernsystem/modernsystem.desktop @@ -0,0 +1,69 @@ +[Desktop Entry] +Name=Modern System +Name[af]=Moderne Stelsel +Name[ar]=نظام معاصر +Name[az]=Modern Sistem +Name[be]=Сучасная сістэма +Name[bn]=মডার্ন সিস্টেম +Name[br]=Reizhiad Nevez +Name[ca]=Sistema modern +Name[cs]=Moderní systém +Name[csb]=Mòdernô systema +Name[cy]=Cysawd Cyfoes +Name[da]=Moderne system +Name[el]=Μοντέρνο σύστημα +Name[eo]=Moderna Sistemo +Name[es]=Sistema moderno +Name[eu]=Sistema modernoa +Name[fa]=سیستم نوین +Name[fi]=Moderni järjestelmä +Name[fr]=Système Moderne +Name[fy]=Modern systeem +Name[ga]=Córas Nua-Aimseartha +Name[gl]=Sistema Moderno +Name[hi]=आधुनिक तंत्र +Name[hr]=Suvremeni sustav +Name[is]=Nútímaleg vél +Name[it]=Sistema Moderno +Name[ja]=モダンシステム +Name[ka]=თანამედროვე სისტემა +Name[kk]=Заманауи жүйе +Name[km]=ប្រព័ន្ធទំនើប +Name[ko]=모던 시스템 +Name[lt]=Moderni sistema +Name[lv]=Moderna sistēma +Name[mk]=Модерен систем +Name[ms]=Sistem Moden +Name[mt]=Sistema Moderna +Name[nb]=Moderne System +Name[nds]=Modeern Systeem +Name[ne]=आधुनिक प्रणाली +Name[nl]=Modern systeem +Name[nn]=Moderne System +Name[pa]=ਨਵਾਂ ਸਿਸਟਮ +Name[pl]=Nowoczesny system +Name[pt]=Sistema Moderno +Name[pt_BR]=Sistema Moderno +Name[ro]=Sistem moderm +Name[ru]=Современная система +Name[rw]=Sisitemu Igezweho +Name[se]=Áigeguovdilis vuogádat +Name[sk]=Moderný systém +Name[sl]=Moderni sistem +Name[sr]=Модерни систем +Name[sr@Latn]=Moderni sistem +Name[sv]=Modernt system +Name[ta]=நவீன அமைப்பு +Name[te]=ఆధునిక వ్యవస్థ +Name[tg]=Системаи навтарин +Name[th]=แบบ Moden System +Name[tr]=Modern Sistem +Name[tt]=Zamança Sistem +Name[uk]=Сучасна система +Name[uz]=Zamonaviy tizim +Name[uz@cyrillic]=Замонавий тизим +Name[vi]=Hệ thống Hiện đại +Name[wa]=Sistinme modiene +Name[zh_CN]=现代系统 +Name[zh_TW]=現代系統 +X-KDE-Library=kwin3_modernsys diff --git a/kwin/clients/plastik/Makefile.am b/kwin/clients/plastik/Makefile.am new file mode 100644 index 000000000..78e71f763 --- /dev/null +++ b/kwin/clients/plastik/Makefile.am @@ -0,0 +1,19 @@ +AUTOMAKE_OPTIONS = foreign + +SUBDIRS = config + +KDE_CXXFLAGS = -DQT_PLUGIN + +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +kwindir = $(kde_datadir)/kwin/ +kwin_DATA = plastik.desktop + +kde_module_LTLIBRARIES = kwin3_plastik.la +kwin3_plastik_la_SOURCES = plastik.cpp plastikclient.cpp plastikbutton.cpp misc.cpp +kwin3_plastik_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_plastik_la_LIBADD = $(LIB_KDEUI) ../../lib/libkdecorations.la +kwin3_plastik_la_METASOURCES = AUTO + +DISTCLEANFILES = $(kwin3_plastik_la_METASOURCES) + diff --git a/kwin/clients/plastik/config/Makefile.am b/kwin/clients/plastik/config/Makefile.am new file mode 100644 index 000000000..78a4b0502 --- /dev/null +++ b/kwin/clients/plastik/config/Makefile.am @@ -0,0 +1,14 @@ +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = kwin_plastik_config.la + +kwin_plastik_config_la_SOURCES = config.cpp configdialog.ui +kwin_plastik_config_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin_plastik_config_la_LIBADD = $(LIB_KDEUI) + +METASOURCES = AUTO +noinst_HEADERS = config.h +DISTCLEANFILES = $(METASOURCES) + +lnkdir = $(kde_datadir)/kwin + diff --git a/kwin/clients/plastik/config/config.cpp b/kwin/clients/plastik/config/config.cpp new file mode 100644 index 000000000..91ec501d0 --- /dev/null +++ b/kwin/clients/plastik/config/config.cpp @@ -0,0 +1,123 @@ +/* Plastik KWin window decoration + Copyright (C) 2003 Sandro Giessl <ceebx@users.sourceforge.net> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#include <qbuttongroup.h> +#include <qcheckbox.h> +#include <qradiobutton.h> +#include <qslider.h> +#include <qspinbox.h> +#include <qwhatsthis.h> + +#include <kconfig.h> +#include <klocale.h> +#include <kglobal.h> + +#include "config.h" +#include "configdialog.h" + +PlastikConfig::PlastikConfig(KConfig* config, QWidget* parent) + : QObject(parent), m_config(0), m_dialog(0) +{ + // create the configuration object + m_config = new KConfig("kwinplastikrc"); + KGlobal::locale()->insertCatalogue("kwin_clients"); + + // create and show the configuration dialog + m_dialog = new ConfigDialog(parent); + m_dialog->show(); + + // load the configuration + load(config); + + // setup the connections + connect(m_dialog->titleAlign, SIGNAL(clicked(int)), + this, SIGNAL(changed())); + connect(m_dialog->animateButtons, SIGNAL(toggled(bool)), + this, SIGNAL(changed())); + connect(m_dialog->menuClose, SIGNAL(toggled(bool)), + this, SIGNAL(changed())); + connect(m_dialog->titleShadow, SIGNAL(toggled(bool)), + this, SIGNAL(changed())); + connect(m_dialog->coloredBorder, SIGNAL(toggled(bool)), + this, SIGNAL(changed())); +} + +PlastikConfig::~PlastikConfig() +{ + if (m_dialog) delete m_dialog; + if (m_config) delete m_config; +} + +void PlastikConfig::load(KConfig*) +{ + m_config->setGroup("General"); + + + QString value = m_config->readEntry("TitleAlignment", "AlignLeft"); + QRadioButton *button = (QRadioButton*)m_dialog->titleAlign->child(value.latin1()); + if (button) button->setChecked(true); + bool animateButtons = m_config->readBoolEntry("AnimateButtons", true); + m_dialog->animateButtons->setChecked(animateButtons); + bool menuClose = m_config->readBoolEntry("CloseOnMenuDoubleClick", true); + m_dialog->menuClose->setChecked(menuClose); + bool titleShadow = m_config->readBoolEntry("TitleShadow", true); + m_dialog->titleShadow->setChecked(titleShadow); + bool coloredBorder = m_config->readBoolEntry("ColoredBorder", true); + m_dialog->coloredBorder->setChecked(coloredBorder); +} + +void PlastikConfig::save(KConfig*) +{ + m_config->setGroup("General"); + + QRadioButton *button = (QRadioButton*)m_dialog->titleAlign->selected(); + if (button) m_config->writeEntry("TitleAlignment", QString(button->name())); + m_config->writeEntry("AnimateButtons", m_dialog->animateButtons->isChecked() ); + m_config->writeEntry("CloseOnMenuDoubleClick", m_dialog->menuClose->isChecked() ); + m_config->writeEntry("TitleShadow", m_dialog->titleShadow->isChecked() ); + m_config->writeEntry("ColoredBorder", m_dialog->coloredBorder->isChecked() ); + m_config->sync(); +} + +void PlastikConfig::defaults() +{ + QRadioButton *button = + (QRadioButton*)m_dialog->titleAlign->child("AlignLeft"); + if (button) button->setChecked(true); + m_dialog->animateButtons->setChecked(true); + m_dialog->menuClose->setChecked(false); + m_dialog->titleShadow->setChecked(true); + m_dialog->coloredBorder->setChecked(true); +} + +////////////////////////////////////////////////////////////////////////////// +// Plugin Stuff // +////////////////////////////////////////////////////////////////////////////// + +extern "C" +{ + KDE_EXPORT QObject* allocate_config(KConfig* config, QWidget* parent) { + return (new PlastikConfig(config, parent)); + } +} + +#include "config.moc" diff --git a/kwin/clients/plastik/config/config.h b/kwin/clients/plastik/config/config.h new file mode 100644 index 000000000..540a27cda --- /dev/null +++ b/kwin/clients/plastik/config/config.h @@ -0,0 +1,53 @@ +/* Plastik KWin window decoration + Copyright (C) 2003 Sandro Giessl <ceebx@users.sourceforge.net> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#ifndef KNIFTYCONFIG_H +#define KNIFTYCONFIG_H + +#include <qobject.h> + +class QButtonGroup; +class QGroupBox; +class KConfig; +class ConfigDialog; + +class PlastikConfig : public QObject +{ + Q_OBJECT +public: + PlastikConfig(KConfig* config, QWidget* parent); + ~PlastikConfig(); + +signals: + void changed(); + +public slots: + void load(KConfig *config); + void save(KConfig *config); + void defaults(); + +private: + KConfig *m_config; + ConfigDialog *m_dialog; +}; + +#endif // KNIFTYCONFIG_H diff --git a/kwin/clients/plastik/config/configdialog.ui b/kwin/clients/plastik/config/configdialog.ui new file mode 100644 index 000000000..642891b31 --- /dev/null +++ b/kwin/clients/plastik/config/configdialog.ui @@ -0,0 +1,119 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>ConfigDialog</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ConfigDialog</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>541</width> + <height>170</height> + </rect> + </property> + <property name="caption"> + <string>Config Dialog</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>titleAlign</cstring> + </property> + <property name="title"> + <string>Title &Alignment</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>AlignLeft</cstring> + </property> + <property name="text"> + <string>Left</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>AlignHCenter</cstring> + </property> + <property name="text"> + <string>Center</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>AlignRight</cstring> + </property> + <property name="text"> + <string>Right</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>coloredBorder</cstring> + </property> + <property name="text"> + <string>Colored window border</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if the window border should be painted in the titlebar color. Otherwise it will be painted in the background color.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>titleShadow</cstring> + </property> + <property name="text"> + <string>Use shadowed &text</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if you want the titlebar text to have a 3D look with a shadow behind it.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>animateButtons</cstring> + </property> + <property name="text"> + <string>Animate buttons</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if you want the buttons to fade in when the mouse pointer hovers over them and fade out again when it moves away.</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>menuClose</cstring> + </property> + <property name="text"> + <string>Close windows by double clicking the menu button</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Check this option if you want windows to be closed when you double click the menu button, similar to Microsoft Windows.</string> + </property> + </widget> + </vbox> +</widget> +<tabstops> + <tabstop>AlignLeft</tabstop> + <tabstop>AlignHCenter</tabstop> + <tabstop>AlignRight</tabstop> + <tabstop>animateButtons</tabstop> + <tabstop>titleShadow</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kwin/clients/plastik/misc.cpp b/kwin/clients/plastik/misc.cpp new file mode 100644 index 000000000..da491b2ba --- /dev/null +++ b/kwin/clients/plastik/misc.cpp @@ -0,0 +1,85 @@ +/* Plastik KWin window decoration + Copyright (C) 2003 Sandro Giessl <ceebx@users.sourceforge.net> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#include <kpixmap.h> +#include <kpixmapeffect.h> + +#include <qcolor.h> +#include <qimage.h> +#include <qpainter.h> + +#include "misc.h" + +QColor hsvRelative(const QColor& baseColor, int relativeH, int relativeS, int relativeV) +{ + int h, s, v; + baseColor.hsv(&h, &s, &v); + + h += relativeH; + s += relativeS; + v += relativeV; + + if(h < 0) { h = 0; } + else if(h > 359) { h = 359; } + if(s < 0) { s = 0; } + else if(s > 255) { s = 255; } + if(v < 0) { v = 0; } + else if(v > 255) { v = 255; } + + QColor c; + c.setHsv( h, s, v ); + return c; +} + +QColor alphaBlendColors(const QColor &bgColor, const QColor &fgColor, const int a) +{ + + // normal button... + QRgb rgb = bgColor.rgb(); + QRgb rgb_b = fgColor.rgb(); + int alpha = a; + if(alpha>255) alpha = 255; + if(alpha<0) alpha = 0; + int inv_alpha = 255 - alpha; + + QColor result = QColor( qRgb(qRed(rgb_b)*inv_alpha/255 + qRed(rgb)*alpha/255, + qGreen(rgb_b)*inv_alpha/255 + qGreen(rgb)*alpha/255, + qBlue(rgb_b)*inv_alpha/255 + qBlue(rgb)*alpha/255) ); + + return result; +} + +QImage recolorImage(QImage *img, QColor color) { + QImage destImg(img->width(),img->height(),32); + destImg.setAlphaBuffer(true); + for (int x = 0; x < img->width(); x++) { + for (int y = 0; y < img->height(); y++) { + if(img->pixel(x,y) == qRgb(0,0,255) ) { + destImg.setPixel(x,y,color.rgb() ); // set to the new color + } else { + destImg.setPixel(x,y,qRgba(0,0,0,0) ); // set transparent... + } + } + } + + return destImg; +} diff --git a/kwin/clients/plastik/misc.h b/kwin/clients/plastik/misc.h new file mode 100644 index 000000000..6c06b282b --- /dev/null +++ b/kwin/clients/plastik/misc.h @@ -0,0 +1,30 @@ +/* Plastik KWin window decoration + Copyright (C) 2003 Sandro Giessl <ceebx@users.sourceforge.net> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#ifndef MISC_H +#define MISC_H + +QColor hsvRelative(const QColor& baseColor, int relativeH, int relativeS = 0, int relativeV = 0); +QColor alphaBlendColors(const QColor &backgroundColor, const QColor &foregroundColor, const int alpha); +QImage recolorImage(QImage *img, QColor color); + +#endif // MISC_H diff --git a/kwin/clients/plastik/plastik.cpp b/kwin/clients/plastik/plastik.cpp new file mode 100644 index 000000000..25e6d2563 --- /dev/null +++ b/kwin/clients/plastik/plastik.cpp @@ -0,0 +1,598 @@ +/* Plastik KWin window decoration + Copyright (C) 2003-2005 Sandro Giessl <sandro@giessl.com> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#include <qbitmap.h> +#include <qpainter.h> +#include <qimage.h> + +#include <kconfig.h> +#include <kpixmap.h> +#include <kpixmapeffect.h> + +#include "misc.h" +#include "plastik.h" +#include "plastik.moc" +#include "plastikclient.h" +#include "plastikbutton.h" + +namespace KWinPlastik +{ + +PlastikHandler::PlastikHandler() +{ + memset(m_pixmaps, 0, sizeof(QPixmap*)*NumPixmaps*2*2); // set elements to 0 + memset(m_bitmaps, 0, sizeof(QBitmap*)*NumButtonIcons*2); + + reset(0); +} + +PlastikHandler::~PlastikHandler() +{ + for (int t=0; t < 2; ++t) + for (int a=0; a < 2; ++a) + for (int i=0; i < NumPixmaps; ++i) + delete m_pixmaps[t][a][i]; + for (int t=0; t < 2; ++t) + for (int i=0; i < NumButtonIcons; ++i) + delete m_bitmaps[t][i]; +} + +bool PlastikHandler::reset(unsigned long changed) +{ + // we assume the active font to be the same as the inactive font since the control + // center doesn't offer different settings anyways. + m_titleFont = KDecoration::options()->font(true, false); // not small + m_titleFontTool = KDecoration::options()->font(true, true); // small + + switch(KDecoration::options()->preferredBorderSize( this )) { + case BorderTiny: + m_borderSize = 3; + break; + case BorderLarge: + m_borderSize = 8; + break; + case BorderVeryLarge: + m_borderSize = 12; + break; + case BorderHuge: + m_borderSize = 18; + break; + case BorderVeryHuge: + m_borderSize = 27; + break; + case BorderOversized: + m_borderSize = 40; + break; + case BorderNormal: + default: + m_borderSize = 4; + } + + // check if we are in reverse layout mode + m_reverse = QApplication::reverseLayout(); + + // read in the configuration + readConfig(); + + // pixmaps probably need to be updated, so delete the cache. + for (int t=0; t < 2; ++t) { + for (int a=0; a < 2; ++a) { + for (int i=0; i < NumPixmaps; i++) { + if (m_pixmaps[t][a][i]) { + delete m_pixmaps[t][a][i]; + m_pixmaps[t][a][i] = 0; + } + } + } + } + for (int t=0; t < 2; ++t) { + for (int i=0; i < NumButtonIcons; i++) { + if (m_bitmaps[t][i]) { + delete m_bitmaps[t][i]; + m_bitmaps[t][i] = 0; + } + } + } + + // Do we need to "hit the wooden hammer" ? + bool needHardReset = true; + // TODO: besides the Color and Font settings I can maybe handle more changes + // without a hard reset. I will do this later... + if (changed & SettingColors || changed & SettingFont) + { + needHardReset = false; + } else if (changed & SettingButtons) { + // handled by KCommonDecoration + needHardReset = false; + } + + if (needHardReset) { + return true; + } else { + resetDecorations(changed); + return false; + } +} + +KDecoration* PlastikHandler::createDecoration( KDecorationBridge* bridge ) +{ + return new PlastikClient( bridge, this ); +} + +bool PlastikHandler::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonMenu: + case AbilityButtonOnAllDesktops: + case AbilityButtonSpacer: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + case AbilityButtonAboveOthers: + case AbilityButtonBelowOthers: + case AbilityButtonShade: + return true; + default: + return false; + }; +} + +void PlastikHandler::readConfig() +{ + // create a config object + KConfig config("kwinplastikrc"); + config.setGroup("General"); + + // grab settings + m_titleShadow = config.readBoolEntry("TitleShadow", true); + + QFontMetrics fm(m_titleFont); // active font = inactive font + int titleHeightMin = config.readNumEntry("MinTitleHeight", 16); + // The title should strech with bigger font sizes! + m_titleHeight = QMAX(titleHeightMin, fm.height() + 4); // 4 px for the shadow etc. + // have an even title/button size so the button icons are fully centered... + if ( m_titleHeight%2 == 0) + m_titleHeight++; + + fm = QFontMetrics(m_titleFontTool); // active font = inactive font + int titleHeightToolMin = config.readNumEntry("MinTitleHeightTool", 13); + // The title should strech with bigger font sizes! + m_titleHeightTool = QMAX(titleHeightToolMin, fm.height() ); // don't care about the shadow etc. + // have an even title/button size so the button icons are fully centered... + if ( m_titleHeightTool%2 == 0) + m_titleHeightTool++; + + QString value = config.readEntry("TitleAlignment", "AlignLeft"); + if (value == "AlignLeft") m_titleAlign = Qt::AlignLeft; + else if (value == "AlignHCenter") m_titleAlign = Qt::AlignHCenter; + else if (value == "AlignRight") m_titleAlign = Qt::AlignRight; + + m_coloredBorder = config.readBoolEntry("ColoredBorder", true); + m_animateButtons = config.readBoolEntry("AnimateButtons", true); + m_menuClose = config.readBoolEntry("CloseOnMenuDoubleClick", true); +} + +QColor PlastikHandler::getColor(KWinPlastik::ColorType type, const bool active) +{ + switch (type) { + case WindowContour: + return KDecoration::options()->color(ColorTitleBar, active).dark(200); + case TitleGradient1: + return hsvRelative(KDecoration::options()->color(ColorTitleBar, active), 0,-10,+10); + break; + case TitleGradient2: + return hsvRelative(KDecoration::options()->color(ColorTitleBar, active), 0,0,-25); + break; + case TitleGradient3: + return KDecoration::options()->color(ColorTitleBar, active); + break; + case ShadeTitleLight: + return alphaBlendColors(KDecoration::options()->color(ColorTitleBar, active), + Qt::white, active?205:215); + break; + case ShadeTitleDark: + return alphaBlendColors(KDecoration::options()->color(ColorTitleBar, active), + Qt::black, active?205:215); + break; + case Border: + return KDecoration::options()->color(ColorFrame, active); + case TitleFont: + return KDecoration::options()->color(ColorFont, active); + default: + return Qt::black; + } +} + +void PlastikHandler::pretile( QPixmap *&pix, int size, Qt::Orientation dir ) const +{ + QPixmap *newpix; + QPainter p; + + if ( dir == Qt::Horizontal ) + newpix = new QPixmap( size, pix->height() ); + else + newpix = new QPixmap( pix->width(), size ); + + p.begin( newpix ); + p.drawTiledPixmap( newpix->rect(), *pix ) ; + p.end(); + + delete pix; + pix = newpix; +} + +const QPixmap &PlastikHandler::pixmap(Pixmaps type, bool active, bool toolWindow) +{ + if (m_pixmaps[toolWindow][active][type]) + return *m_pixmaps[toolWindow][active][type]; + + QPixmap *pm = 0; + + switch (type) { + case TitleBarTileTop: + case TitleBarTile: + { + const int titleBarTileHeight = (toolWindow ? m_titleHeightTool : m_titleHeight) + 2; + // gradient used as well in TitleBarTileTop as TitleBarTile + const int gradientHeight = 2 + titleBarTileHeight-1; + QPixmap gradient(1, gradientHeight); + QPainter painter(&gradient); + KPixmap tempPixmap; + tempPixmap.resize(1, 4); + KPixmapEffect::gradient(tempPixmap, + getColor(TitleGradient1, active), + getColor(TitleGradient2, active), + KPixmapEffect::VerticalGradient); + painter.drawPixmap(0,0, tempPixmap); + tempPixmap.resize(1, gradientHeight-4); + KPixmapEffect::gradient(tempPixmap, + getColor(TitleGradient2, active), + getColor(TitleGradient3, active), + KPixmapEffect::VerticalGradient); + painter.drawPixmap(0,4, tempPixmap); + painter.end(); + + // actual titlebar tiles + if (type == TitleBarTileTop) { + pm = new QPixmap(1, 4); + painter.begin(pm); + // contour + painter.setPen(getColor(WindowContour, active) ); + painter.drawPoint(0,0); + // top highlight + painter.setPen(getColor(ShadeTitleLight, active) ); + painter.drawPoint(0,1); + // gradient + painter.drawPixmap(0, 2, gradient); + painter.end(); + } else { + pm = new QPixmap(1, titleBarTileHeight); + painter.begin(pm); + painter.drawPixmap(0, 0, gradient, 0,2); + if (m_coloredBorder) { + painter.setPen(getColor(TitleGradient3, active).dark(110) ); + } else { + painter.setPen(getColor(TitleGradient3, active) ); + } + painter.drawPoint(0,titleBarTileHeight-1); + painter.end(); + } + + pretile(pm, 64, Qt::Horizontal); + + break; + } + + case TitleBarLeft: + { + const int w = m_borderSize; + const int h = 4 + (toolWindow ? m_titleHeightTool : m_titleHeight) + 2; + + pm = new QPixmap(w, h); + QPainter painter(pm); + + painter.drawTiledPixmap(0,0, w, 4, pixmap(TitleBarTileTop, active, toolWindow) ); + painter.drawTiledPixmap(0,4, w, h-4, pixmap(TitleBarTile, active, toolWindow) ); + + painter.setPen(getColor(WindowContour, active) ); + painter.drawLine(0,0, 0,h); + painter.drawPoint(1,1); + + const QColor highlightTitleLeft = getColor(ShadeTitleLight, active); + painter.setPen(highlightTitleLeft); + painter.drawLine(1,2, 1,h); + + if (m_coloredBorder) { + painter.setPen(getColor(TitleGradient3, active) ); + painter.drawLine(2,h-1, w-1,h-1); + } + + // outside the region normally masked by doShape + painter.setPen(QColor(0,0,0) ); + painter.drawLine(0, 0, 1, 0 ); + painter.drawPoint(0, 1); + + break; + } + + case TitleBarRight: + { + const int w = m_borderSize; + const int h = 4 + (toolWindow ? m_titleHeightTool : m_titleHeight) + 2; + + pm = new QPixmap(w, h); + QPainter painter(pm); + + painter.drawTiledPixmap(0,0, w, 4, pixmap(TitleBarTileTop, active, toolWindow) ); + painter.drawTiledPixmap(0,4, w, h-4, pixmap(TitleBarTile, active, toolWindow) ); + + painter.setPen(getColor(WindowContour, active) ); + painter.drawLine(w-1,0, w-1,h); + painter.drawPoint(w-2,1); + + const QColor highlightTitleRight = getColor(ShadeTitleDark, active); + painter.setPen(highlightTitleRight); + painter.drawLine(w-2,2, w-2,h); + + if (m_coloredBorder) { + painter.setPen(getColor(TitleGradient3, active) ); + painter.drawLine(0,h-1, w-3,h-1); + } + + // outside the region normally masked by doShape + painter.setPen(QColor(0,0,0) ); + painter.drawLine(w-2, 0, w-1, 0 ); + painter.drawPoint(w-1, 1); + + break; + } + + case BorderLeftTile: + { + const int w = m_borderSize; + + pm = new QPixmap(w, 1); + QPainter painter(pm); + if (m_coloredBorder) { + painter.setPen(getColor(WindowContour, active) ); + painter.drawPoint(0, 0); + painter.setPen(getColor(ShadeTitleLight, active) ); + painter.drawPoint(1, 0); + if (w > 3) { + painter.setPen(getColor(TitleGradient3, active) ); + painter.drawLine(2,0, w-2,0); + } + painter.setPen(getColor(TitleGradient3, active).dark(110) ); + painter.drawPoint(w-1,0); + } else { + painter.setPen(getColor(WindowContour, active) ); + painter.drawPoint(0, 0); + painter.setPen( + alphaBlendColors(getColor(Border, active), + getColor(ShadeTitleLight, active), 130) ); + painter.drawPoint(1, 0); + painter.setPen(getColor(Border, active) ); + painter.drawLine(2,0, w-1,0); + } + + painter.end(); + + pretile(pm, 64, Qt::Vertical); + + break; + } + + case BorderRightTile: + { + const int w = m_borderSize; + + pm = new QPixmap(w, 1); + QPainter painter(pm); + if (m_coloredBorder) { + painter.setPen(getColor(TitleGradient3, active).dark(110) ); + painter.drawPoint(0,0); + if (w > 3) { + painter.setPen(getColor(TitleGradient3, active) ); + painter.drawLine(1,0, w-3,0); + } + painter.setPen(getColor(ShadeTitleDark, active) ); + painter.drawPoint(w-2, 0); + painter.setPen(getColor(WindowContour, active) ); + painter.drawPoint(w-1, 0); + } else { + painter.setPen(getColor(Border, active) ); + painter.drawLine(0,0, w-3,0); + painter.setPen( + alphaBlendColors(getColor(Border, active), + getColor(ShadeTitleDark, active), 130) ); + painter.drawPoint(w-2, 0); + painter.setPen(getColor(WindowContour, active) ); + painter.drawPoint(w-1, 0); + } + painter.end(); + + pretile(pm, 64, Qt::Vertical); + + break; + } + + case BorderBottomLeft: + { + const int w = m_borderSize; + const int h = m_borderSize; + + pm = new QPixmap(w, h); + QPainter painter(pm); + painter.drawTiledPixmap(0,0,w,h, pixmap(BorderBottomTile, active, toolWindow) ); + painter.setPen(getColor(WindowContour, active) ); + painter.drawLine(0,0, 0,h); + if (m_coloredBorder) { + if (h > 3) { + painter.setPen(getColor(ShadeTitleLight, active) ); + painter.drawLine(1,0, 1,h-2); + } + + painter.setPen(getColor(TitleGradient3, active) ); + painter.drawLine(2,0, w-1,0); + } else { + painter.setPen( + alphaBlendColors(getColor(Border, active), + getColor(ShadeTitleLight, active), 130) ); + painter.drawLine(1,0, 1,h-2); + } + + painter.end(); + + break; + } + + case BorderBottomRight: + { + const int w = m_borderSize; + const int h = m_borderSize; + + pm = new QPixmap(w, h); + QPainter painter(pm); + painter.drawTiledPixmap(0,0,w,h, pixmap(BorderBottomTile, active, toolWindow) ); + painter.setPen(getColor(WindowContour, active) ); + painter.drawLine(w-1,0, w-1,h); + if (m_coloredBorder) { + painter.setPen(getColor(ShadeTitleDark, active) ); + painter.drawLine(w-2,0, w-2,h-2); + + painter.setPen(getColor(TitleGradient3, active) ); + painter.drawLine(0,0, w-3,0); + } else { + painter.setPen( + alphaBlendColors(getColor(Border, active), + getColor(ShadeTitleDark, active), 130) ); + painter.drawLine(w-2,0, w-2,h-2); + } + + painter.end(); + + break; + } + + case BorderBottomTile: + default: + { + const int h = m_borderSize; + + pm = new QPixmap(1, m_borderSize); + QPainter painter(pm); + + if (m_coloredBorder) { + painter.setPen(getColor(TitleGradient3, active).dark(110) ); + painter.drawPoint(0,0); + painter.setPen(getColor(TitleGradient3, active) ); + painter.drawLine(0,1, 0,h-3); + painter.setPen(getColor(ShadeTitleDark, active) ); + painter.drawPoint(0, h-2); + } else { + painter.setPen(getColor(Border, active) ); + painter.drawLine(0,0, 0,h-3); + painter.setPen( + alphaBlendColors(getColor(Border, active), + getColor(ShadeTitleDark, active), 130) ); + painter.drawPoint(0, h-2); + } + painter.setPen(getColor(WindowContour, active) ); + painter.drawPoint(0, h-1); + painter.end(); + + pretile(pm, 64, Qt::Horizontal); + + break; + } + } + + m_pixmaps[toolWindow][active][type] = pm; + return *pm; +} + +const QBitmap &PlastikHandler::buttonBitmap(ButtonIcon type, const QSize &size, bool toolWindow) +{ + int typeIndex = type; + + // btn icon size... + int reduceW = 0, reduceH = 0; + if(size.width()>14) { + reduceW = static_cast<int>(2*(size.width()/3.5) ); + } + else + reduceW = 6; + if(size.height()>14) + reduceH = static_cast<int>(2*(size.height()/3.5) ); + else + reduceH = 6; + + int w = size.width() - reduceW; + int h = size.height() - reduceH; + + if (m_bitmaps[toolWindow][typeIndex] && m_bitmaps[toolWindow][typeIndex]->size()==QSize(w,h) ) + return *m_bitmaps[toolWindow][typeIndex]; + + // no matching pixmap found, create a new one... + + delete m_bitmaps[toolWindow][typeIndex]; + m_bitmaps[toolWindow][typeIndex] = 0; + + QBitmap bmp = IconEngine::icon(type /*icon*/, QMIN(w,h) ); + QBitmap *bitmap = new QBitmap(bmp); + m_bitmaps[toolWindow][typeIndex] = bitmap; + return *bitmap; +} + +QValueList< PlastikHandler::BorderSize > +PlastikHandler::borderSizes() const +{ + // the list must be sorted + return QValueList< BorderSize >() << BorderTiny << BorderNormal << + BorderLarge << BorderVeryLarge << BorderHuge << + BorderVeryHuge << BorderOversized; +} + +// make the handler accessible to other classes... +static PlastikHandler *handler = 0; +PlastikHandler* Handler() +{ + return handler; +} + +} // KWinPlastik + +////////////////////////////////////////////////////////////////////////////// +// Plugin Stuff // +////////////////////////////////////////////////////////////////////////////// + +extern "C" +{ + KDE_EXPORT KDecorationFactory *create_factory() + { + KWinPlastik::handler = new KWinPlastik::PlastikHandler(); + return KWinPlastik::handler; + } +} diff --git a/kwin/clients/plastik/plastik.desktop b/kwin/clients/plastik/plastik.desktop new file mode 100644 index 000000000..55800ad9b --- /dev/null +++ b/kwin/clients/plastik/plastik.desktop @@ -0,0 +1,37 @@ +[Desktop Entry] +Icon= +Name=Plastik +Name[af]=Plastiek +Name[ar]=بلاستيك +Name[be]=Пластык +Name[bn]=প্লাস্টিক +Name[eo]=Plastiko +Name[fa]=پلاستیک +Name[fy]=Plastyk +Name[hi]=प्लास्टिक +Name[hr]=Plastika +Name[is]=Plast +Name[it]=Plastica +Name[ka]=Пластик +Name[kk]=Пластик +Name[km]=ប្ល៉ាស្ទិក +Name[lt]=Plastikinis +Name[lv]=Plastika +Name[mk]=Пластик +Name[nb]=Plast +Name[ne]=प्लास्टिक +Name[nn]=Plast +Name[pa]=ਪਲਾਸਟਿਕ +Name[ro]=Plastic +Name[ru]=Пластик +Name[se]=Plastihkka +Name[sr]=Пластика +Name[sr@Latn]=Plastika +Name[ta]=திட்டம் +Name[te]=ప్లాస్టిక్ +Name[th]=พลาสติก +Name[uk]=Пластик +Name[uz@cyrillic]=Пластик +Name[vi]=Chất dẻo +Name[zh_CN]=塑料 +X-KDE-Library=kwin3_plastik diff --git a/kwin/clients/plastik/plastik.h b/kwin/clients/plastik/plastik.h new file mode 100644 index 000000000..16972c9ac --- /dev/null +++ b/kwin/clients/plastik/plastik.h @@ -0,0 +1,127 @@ +/* Plastik KWin window decoration + Copyright (C) 2003-2005 Sandro Giessl <sandro@giessl.com> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#ifndef PLASTIK_H +#define PLASTIK_H + +#include <qfont.h> + +#include <kdecoration.h> +#include <kdecorationfactory.h> + +namespace KWinPlastik { + +enum ColorType { + WindowContour=0, + TitleGradient1, // top + TitleGradient2, + TitleGradient3, // bottom + ShadeTitleLight, + ShadeTitleDark, + Border, + TitleFont +}; + +enum Pixmaps { + TitleBarTileTop=0, + TitleBarTile, + TitleBarLeft, + TitleBarRight, + BorderLeftTile, + BorderRightTile, + BorderBottomTile, + BorderBottomLeft, + BorderBottomRight, + NumPixmaps +}; + +enum ButtonIcon { + CloseIcon = 0, + MaxIcon, + MaxRestoreIcon, + MinIcon, + HelpIcon, + OnAllDesktopsIcon, + NotOnAllDesktopsIcon, + KeepAboveIcon, + NoKeepAboveIcon, + KeepBelowIcon, + NoKeepBelowIcon, + ShadeIcon, + UnShadeIcon, + NumButtonIcons +}; + +class PlastikHandler: public QObject, public KDecorationFactory +{ + Q_OBJECT +public: + PlastikHandler(); + ~PlastikHandler(); + virtual bool reset( unsigned long changed ); + + virtual KDecoration* createDecoration( KDecorationBridge* ); + virtual bool supports( Ability ability ); + + const QPixmap &pixmap(Pixmaps type, bool active, bool toolWindow); + const QBitmap &buttonBitmap(ButtonIcon type, const QSize &size, bool toolWindow); + + int titleHeight() { return m_titleHeight; } + int titleHeightTool() { return m_titleHeightTool; } + const QFont &titleFont() { return m_titleFont; } + const QFont &titleFontTool() { return m_titleFontTool; } + bool titleShadow() { return m_titleShadow; } + int borderSize() { return m_borderSize; } + bool animateButtons() { return m_animateButtons; } + bool menuClose() { return m_menuClose; } + Qt::AlignmentFlags titleAlign() { return m_titleAlign; } + bool reverseLayout() { return m_reverse; } + QColor getColor(KWinPlastik::ColorType type, const bool active = true); + + QValueList< PlastikHandler::BorderSize > borderSizes() const; +private: + void readConfig(); + + void pretile(QPixmap *&pix, int size, Qt::Orientation dir) const; + + bool m_coloredBorder; + bool m_titleShadow; + bool m_animateButtons; + bool m_menuClose; + bool m_reverse; + int m_borderSize; + int m_titleHeight; + int m_titleHeightTool; + QFont m_titleFont; + QFont m_titleFontTool; + Qt::AlignmentFlags m_titleAlign; + + // pixmap cache + QPixmap *m_pixmaps[2][2][NumPixmaps]; // button pixmaps have normal+pressed state... + QBitmap *m_bitmaps[2][NumButtonIcons]; +}; + +PlastikHandler* Handler(); + +} // KWinPlastik + +#endif // PLASTIK_H diff --git a/kwin/clients/plastik/plastikbutton.cpp b/kwin/clients/plastik/plastikbutton.cpp new file mode 100644 index 000000000..5917465ab --- /dev/null +++ b/kwin/clients/plastik/plastikbutton.cpp @@ -0,0 +1,629 @@ +/* Plastik KWin window decoration + Copyright (C) 2003-2005 Sandro Giessl <sandro@giessl.com> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +// #include <kwin/options.h> + +#include <qbitmap.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <kpixmap.h> +#include <kpixmapeffect.h> +#include <qtimer.h> + +#include "plastikbutton.h" +#include "plastikbutton.moc" +#include "plastikclient.h" +#include "misc.h" + +namespace KWinPlastik +{ + +static const uint TIMERINTERVAL = 50; // msec +static const uint ANIMATIONSTEPS = 4; + +PlastikButton::PlastikButton(ButtonType type, PlastikClient *parent, const char *name) + : KCommonDecorationButton(type, parent, name), + m_client(parent), + m_iconType(NumButtonIcons), + hover(false) +{ + setBackgroundMode(NoBackground); + + // no need to reset here as the button will be resetted on first resize. + + animTmr = new QTimer(this); + connect(animTmr, SIGNAL(timeout() ), this, SLOT(animate() ) ); + animProgress = 0; +} + +PlastikButton::~PlastikButton() +{ +} + +void PlastikButton::reset(unsigned long changed) +{ + if (changed&DecorationReset || changed&ManualReset || changed&SizeChange || changed&StateChange) { + switch (type() ) { + case CloseButton: + m_iconType = CloseIcon; + break; + case HelpButton: + m_iconType = HelpIcon; + break; + case MinButton: + m_iconType = MinIcon; + break; + case MaxButton: + if (isOn()) { + m_iconType = MaxRestoreIcon; + } else { + m_iconType = MaxIcon; + } + break; + case OnAllDesktopsButton: + if (isOn()) { + m_iconType = NotOnAllDesktopsIcon; + } else { + m_iconType = OnAllDesktopsIcon; + } + break; + case ShadeButton: + if (isOn()) { + m_iconType = UnShadeIcon; + } else { + m_iconType = ShadeIcon; + } + break; + case AboveButton: + if (isOn()) { + m_iconType = NoKeepAboveIcon; + } else { + m_iconType = KeepAboveIcon; + } + break; + case BelowButton: + if (isOn()) { + m_iconType = NoKeepBelowIcon; + } else { + m_iconType = KeepBelowIcon; + } + break; + default: + m_iconType = NumButtonIcons; // empty... + break; + } + + this->update(); + } +} + +void PlastikButton::animate() +{ + animTmr->stop(); + + if(hover) { + if(animProgress < ANIMATIONSTEPS) { + if (Handler()->animateButtons() ) { + animProgress++; + } else { + animProgress = ANIMATIONSTEPS; + } + animTmr->start(TIMERINTERVAL, true); // single-shot + } + } else { + if(animProgress > 0) { + if (Handler()->animateButtons() ) { + animProgress--; + } else { + animProgress = 0; + } + animTmr->start(TIMERINTERVAL, true); // single-shot + } + } + + repaint(false); +} + +void PlastikButton::enterEvent(QEvent *e) +{ + QButton::enterEvent(e); + + hover = true; + animate(); +} + +void PlastikButton::leaveEvent(QEvent *e) +{ + QButton::leaveEvent(e); + + hover = false; + animate(); +} + +void PlastikButton::drawButton(QPainter *painter) +{ + QRect r(0,0,width(),height()); + + bool active = m_client->isActive(); + KPixmap tempKPixmap; + + QColor highlightColor; + if(type() == CloseButton) { + highlightColor = QColor(255,64,0); + } else { + highlightColor = Qt::white; + } + + QColor contourTop = alphaBlendColors(Handler()->getColor(TitleGradient2, active), + Qt::black, 215); + QColor contourBottom = alphaBlendColors(Handler()->getColor(TitleGradient3, active), + Qt::black, 215); + QColor sourfaceTop = alphaBlendColors(Handler()->getColor(TitleGradient2, active), + Qt::white, 210); + QColor sourfaceBottom = alphaBlendColors(Handler()->getColor(TitleGradient3, active), + Qt::white, 210); + + int highlightAlpha = static_cast<int>(255-((60/static_cast<double>(ANIMATIONSTEPS))* + static_cast<double>(animProgress) ) ); + contourTop = alphaBlendColors(contourTop, highlightColor, highlightAlpha ); + contourBottom = alphaBlendColors(contourBottom, highlightColor, highlightAlpha); + sourfaceTop = alphaBlendColors(sourfaceTop, highlightColor, highlightAlpha); + sourfaceBottom = alphaBlendColors(sourfaceBottom, highlightColor, highlightAlpha); + + if (isDown() ) { + contourTop = alphaBlendColors(contourTop, Qt::black, 200); + contourBottom = alphaBlendColors(contourBottom, Qt::black, 200); + sourfaceTop = alphaBlendColors(sourfaceTop, Qt::black, 200); + sourfaceBottom = alphaBlendColors(sourfaceBottom, Qt::black, 200); + } + + QPixmap buffer; + buffer.resize(width(), height()); + QPainter bP(&buffer); + + // fake the titlebar background + bP.drawTiledPixmap(0, 0, width(), width(), m_client->getTitleBarTile(active) ); + + if (type() != MenuButton || hover || animProgress != 0) { + // contour + bP.setPen(contourTop); + bP.drawLine(r.x()+2, r.y(), r.right()-2, r.y() ); + bP.drawPoint(r.x()+1, r.y()+1); + bP.drawPoint(r.right()-1, r.y()+1); + bP.setPen(contourBottom); + bP.drawLine(r.x()+2, r.bottom(), r.right()-2, r.bottom() ); + bP.drawPoint(r.x()+1, r.bottom()-1); + bP.drawPoint(r.right()-1, r.bottom()-1); + // sides of the contour + tempKPixmap.resize(1, r.height()-2*2); + KPixmapEffect::gradient(tempKPixmap, + contourTop, + contourBottom, + KPixmapEffect::VerticalGradient); + bP.drawPixmap(r.x(), r.y()+2, tempKPixmap); + bP.drawPixmap(r.right(), r.y()+2, tempKPixmap); + // sort of anti-alias for the contour + bP.setPen(alphaBlendColors(Handler()->getColor(TitleGradient2, active), + contourTop, 150) ); + bP.drawPoint(r.x()+1, r.y()); + bP.drawPoint(r.right()-1, r.y()); + bP.drawPoint(r.x(), r.y()+1); + bP.drawPoint(r.right(), r.y()+1); + bP.setPen(alphaBlendColors(Handler()->getColor(TitleGradient3, active), + contourBottom, 150) ); + bP.drawPoint(r.x()+1, r.bottom()); + bP.drawPoint(r.right()-1, r.bottom()); + bP.drawPoint(r.x(), r.bottom()-1); + bP.drawPoint(r.right(), r.bottom()-1); + // sourface + // fill top and bottom + bP.setPen(sourfaceTop); + bP.drawLine(r.x()+2, r.y()+1, r.right()-2, r.y()+1 ); + bP.setPen(sourfaceBottom); + bP.drawLine(r.x()+2, r.bottom()-1, r.right()-2, r.bottom()-1 ); + // fill the rest! :) + tempKPixmap.resize(1, r.height()-2*2); + KPixmapEffect::gradient(tempKPixmap, + sourfaceTop, + sourfaceBottom, + KPixmapEffect::VerticalGradient); + bP.drawTiledPixmap(r.x()+1, r.y()+2, r.width()-2, r.height()-4, tempKPixmap); + } + + if (type() == MenuButton) + { + QPixmap menuIcon(m_client->icon().pixmap( QIconSet::Small, QIconSet::Normal)); + if (width() < menuIcon.width() || height() < menuIcon.height() ) { + menuIcon.convertFromImage( menuIcon.convertToImage().smoothScale(width(), height())); + } + bP.drawPixmap((width()-menuIcon.width())/2, (height()-menuIcon.height())/2, menuIcon); + } + else + { + int dX,dY; + const QBitmap &icon = Handler()->buttonBitmap(m_iconType, size(), decoration()->isToolWindow() ); + dX = r.x()+(r.width()-icon.width())/2; + dY = r.y()+(r.height()-icon.height())/2; + if (isDown() ) { + dY++; + } + + if(!isDown() && Handler()->titleShadow() ) { + QColor shadowColor; + if (qGray(Handler()->getColor(TitleFont,active).rgb()) < 100) + shadowColor = QColor(255, 255, 255); + else + shadowColor = QColor(0,0,0); + bP.setPen(alphaBlendColors(sourfaceTop, shadowColor, 180) ); + bP.drawPixmap(dX+1, dY+1, icon); + } + + bP.setPen(Handler()->getColor(TitleFont,active) ); + bP.drawPixmap(dX, dY, icon); + } + + bP.end(); + painter->drawPixmap(0, 0, buffer); +} + +QBitmap IconEngine::icon(ButtonIcon icon, int size) +{ + if (size%2 == 0) + --size; + + QBitmap bitmap(size,size); + bitmap.fill(Qt::color0); + QPainter p(&bitmap); + + p.setPen(Qt::color1); + + QRect r = bitmap.rect(); + + // line widths + int lwTitleBar = 1; + if (r.width() > 16) { + lwTitleBar = 4; + } else if (r.width() > 4) { + lwTitleBar = 2; + } + int lwArrow = 1; + if (r.width() > 16) { + lwArrow = 4; + } else if (r.width() > 7) { + lwArrow = 2; + } + + switch(icon) { + case CloseIcon: + { + int lineWidth = 1; + if (r.width() > 16) { + lineWidth = 3; + } else if (r.width() > 4) { + lineWidth = 2; + } + + drawObject(p, DiagonalLine, r.x(), r.y(), r.width(), lineWidth); + drawObject(p, CrossDiagonalLine, r.x(), r.bottom(), r.width(), lineWidth); + + break; + } + + case MaxIcon: + { + int lineWidth2 = 1; // frame + if (r.width() > 16) { + lineWidth2 = 2; + } else if (r.width() > 4) { + lineWidth2 = 1; + } + + drawObject(p, HorizontalLine, r.x(), r.top(), r.width(), lwTitleBar); + drawObject(p, HorizontalLine, r.x(), r.bottom()-(lineWidth2-1), r.width(), lineWidth2); + drawObject(p, VerticalLine, r.x(), r.top(), r.height(), lineWidth2); + drawObject(p, VerticalLine, r.right()-(lineWidth2-1), r.top(), r.height(), lineWidth2); + + break; + } + + case MaxRestoreIcon: + { + int lineWidth2 = 1; // frame + if (r.width() > 16) { + lineWidth2 = 2; + } else if (r.width() > 4) { + lineWidth2 = 1; + } + + int margin1, margin2; + margin1 = margin2 = lineWidth2*2; + if (r.width() < 8) + margin1 = 1; + + // background window + drawObject(p, HorizontalLine, r.x()+margin1, r.top(), r.width()-margin1, lineWidth2); + drawObject(p, HorizontalLine, r.right()-margin2, r.bottom()-(lineWidth2-1)-margin1, margin2, lineWidth2); + drawObject(p, VerticalLine, r.x()+margin1, r.top(), margin2, lineWidth2); + drawObject(p, VerticalLine, r.right()-(lineWidth2-1), r.top(), r.height()-margin1, lineWidth2); + + // foreground window + drawObject(p, HorizontalLine, r.x(), r.top()+margin2, r.width()-margin2, lwTitleBar); + drawObject(p, HorizontalLine, r.x(), r.bottom()-(lineWidth2-1), r.width()-margin2, lineWidth2); + drawObject(p, VerticalLine, r.x(), r.top()+margin2, r.height(), lineWidth2); + drawObject(p, VerticalLine, r.right()-(lineWidth2-1)-margin2, r.top()+margin2, r.height(), lineWidth2); + + break; + } + + case MinIcon: + { + drawObject(p, HorizontalLine, r.x(), r.bottom()-(lwTitleBar-1), r.width(), lwTitleBar); + + break; + } + + case HelpIcon: + { + int center = r.x()+r.width()/2 -1; + int side = r.width()/4; + + // paint a question mark... code is quite messy, to be cleaned up later...! :o + + if (r.width() > 16) { + int lineWidth = 3; + + // top bar + drawObject(p, HorizontalLine, center-side+3, r.y(), 2*side-3-1, lineWidth); + // top bar rounding + drawObject(p, CrossDiagonalLine, center-side-1, r.y()+5, 6, lineWidth); + drawObject(p, DiagonalLine, center+side-3, r.y(), 5, lineWidth); + // right bar + drawObject(p, VerticalLine, center+side+2-lineWidth, r.y()+3, r.height()-(2*lineWidth+side+2+1), lineWidth); + // bottom bar + drawObject(p, CrossDiagonalLine, center, r.bottom()-2*lineWidth, side+2, lineWidth); + drawObject(p, HorizontalLine, center, r.bottom()-3*lineWidth+2, lineWidth, lineWidth); + // the dot + drawObject(p, HorizontalLine, center, r.bottom()-(lineWidth-1), lineWidth, lineWidth); + } else if (r.width() > 8) { + int lineWidth = 2; + + // top bar + drawObject(p, HorizontalLine, center-(side-1), r.y(), 2*side-1, lineWidth); + // top bar rounding + if (r.width() > 9) { + drawObject(p, CrossDiagonalLine, center-side-1, r.y()+3, 3, lineWidth); + } else { + drawObject(p, CrossDiagonalLine, center-side-1, r.y()+2, 3, lineWidth); + } + drawObject(p, DiagonalLine, center+side-1, r.y(), 3, lineWidth); + // right bar + drawObject(p, VerticalLine, center+side+2-lineWidth, r.y()+2, r.height()-(2*lineWidth+side+1), lineWidth); + // bottom bar + drawObject(p, CrossDiagonalLine, center, r.bottom()-2*lineWidth+1, side+2, lineWidth); + // the dot + drawObject(p, HorizontalLine, center, r.bottom()-(lineWidth-1), lineWidth, lineWidth); + } else { + int lineWidth = 1; + + // top bar + drawObject(p, HorizontalLine, center-(side-1), r.y(), 2*side, lineWidth); + // top bar rounding + drawObject(p, CrossDiagonalLine, center-side-1, r.y()+1, 2, lineWidth); + // right bar + drawObject(p, VerticalLine, center+side+1, r.y(), r.height()-(side+2+1), lineWidth); + // bottom bar + drawObject(p, CrossDiagonalLine, center, r.bottom()-2, side+2, lineWidth); + // the dot + drawObject(p, HorizontalLine, center, r.bottom(), 1, 1); + } + + break; + } + + case NotOnAllDesktopsIcon: + { + int lwMark = r.width()-lwTitleBar*2-2; + if (lwMark < 1) + lwMark = 3; + + drawObject(p, HorizontalLine, r.x()+(r.width()-lwMark)/2, r.y()+(r.height()-lwMark)/2, lwMark, lwMark); + + // Fall through to OnAllDesktopsIcon intended! + } + case OnAllDesktopsIcon: + { + // horizontal bars + drawObject(p, HorizontalLine, r.x()+lwTitleBar, r.y(), r.width()-2*lwTitleBar, lwTitleBar); + drawObject(p, HorizontalLine, r.x()+lwTitleBar, r.bottom()-(lwTitleBar-1), r.width()-2*lwTitleBar, lwTitleBar); + // vertical bars + drawObject(p, VerticalLine, r.x(), r.y()+lwTitleBar, r.height()-2*lwTitleBar, lwTitleBar); + drawObject(p, VerticalLine, r.right()-(lwTitleBar-1), r.y()+lwTitleBar, r.height()-2*lwTitleBar, lwTitleBar); + + + break; + } + + case NoKeepAboveIcon: + { + int center = r.x()+r.width()/2; + + // arrow + drawObject(p, CrossDiagonalLine, r.x(), center+2*lwArrow, center-r.x(), lwArrow); + drawObject(p, DiagonalLine, r.x()+center, r.y()+1+2*lwArrow, center-r.x(), lwArrow); + if (lwArrow>1) + drawObject(p, HorizontalLine, center-(lwArrow-2), r.y()+2*lwArrow, (lwArrow-2)*2, lwArrow); + + // Fall through to KeepAboveIcon intended! + } + case KeepAboveIcon: + { + int center = r.x()+r.width()/2; + + // arrow + drawObject(p, CrossDiagonalLine, r.x(), center, center-r.x(), lwArrow); + drawObject(p, DiagonalLine, r.x()+center, r.y()+1, center-r.x(), lwArrow); + if (lwArrow>1) + drawObject(p, HorizontalLine, center-(lwArrow-2), r.y(), (lwArrow-2)*2, lwArrow); + + break; + } + + case NoKeepBelowIcon: + { + int center = r.x()+r.width()/2; + + // arrow + drawObject(p, DiagonalLine, r.x(), center-2*lwArrow, center-r.x(), lwArrow); + drawObject(p, CrossDiagonalLine, r.x()+center, r.bottom()-1-2*lwArrow, center-r.x(), lwArrow); + if (lwArrow>1) + drawObject(p, HorizontalLine, center-(lwArrow-2), r.bottom()-(lwArrow-1)-2*lwArrow, (lwArrow-2)*2, lwArrow); + + // Fall through to KeepBelowIcon intended! + } + case KeepBelowIcon: + { + int center = r.x()+r.width()/2; + + // arrow + drawObject(p, DiagonalLine, r.x(), center, center-r.x(), lwArrow); + drawObject(p, CrossDiagonalLine, r.x()+center, r.bottom()-1, center-r.x(), lwArrow); + if (lwArrow>1) + drawObject(p, HorizontalLine, center-(lwArrow-2), r.bottom()-(lwArrow-1), (lwArrow-2)*2, lwArrow); + + break; + } + + case ShadeIcon: + { + drawObject(p, HorizontalLine, r.x(), r.y(), r.width(), lwTitleBar); + + break; + } + + case UnShadeIcon: + { + int lw1 = 1; + int lw2 = 1; + if (r.width() > 16) { + lw1 = 4; + lw2 = 2; + } else if (r.width() > 7) { + lw1 = 2; + lw2 = 1; + } + + int h = QMAX( (r.width()/2), (lw1+2*lw2) ); + + // horizontal bars + drawObject(p, HorizontalLine, r.x(), r.y(), r.width(), lw1); + drawObject(p, HorizontalLine, r.x(), r.x()+h-(lw2-1), r.width(), lw2); + // vertical bars + drawObject(p, VerticalLine, r.x(), r.y(), h, lw2); + drawObject(p, VerticalLine, r.right()-(lw2-1), r.y(), h, lw2); + + break; + } + + default: + break; + } + + p.end(); + + bitmap.setMask(bitmap); + + return bitmap; +} + +void IconEngine::drawObject(QPainter &p, Object object, int x, int y, int length, int lineWidth) +{ + switch(object) { + case DiagonalLine: + if (lineWidth <= 1) { + for (int i = 0; i < length; ++i) { + p.drawPoint(x+i,y+i); + } + } else if (lineWidth <= 2) { + for (int i = 0; i < length; ++i) { + p.drawPoint(x+i,y+i); + } + for (int i = 0; i < (length-1); ++i) { + p.drawPoint(x+1+i,y+i); + p.drawPoint(x+i,y+1+i); + } + } else { + for (int i = 1; i < (length-1); ++i) { + p.drawPoint(x+i,y+i); + } + for (int i = 0; i < (length-1); ++i) { + p.drawPoint(x+1+i,y+i); + p.drawPoint(x+i,y+1+i); + } + for (int i = 0; i < (length-2); ++i) { + p.drawPoint(x+2+i,y+i); + p.drawPoint(x+i,y+2+i); + } + } + break; + case CrossDiagonalLine: + if (lineWidth <= 1) { + for (int i = 0; i < length; ++i) { + p.drawPoint(x+i,y-i); + } + } else if (lineWidth <= 2) { + for (int i = 0; i < length; ++i) { + p.drawPoint(x+i,y-i); + } + for (int i = 0; i < (length-1); ++i) { + p.drawPoint(x+1+i,y-i); + p.drawPoint(x+i,y-1-i); + } + } else { + for (int i = 1; i < (length-1); ++i) { + p.drawPoint(x+i,y-i); + } + for (int i = 0; i < (length-1); ++i) { + p.drawPoint(x+1+i,y-i); + p.drawPoint(x+i,y-1-i); + } + for (int i = 0; i < (length-2); ++i) { + p.drawPoint(x+2+i,y-i); + p.drawPoint(x+i,y-2-i); + } + } + break; + case HorizontalLine: + for (int i = 0; i < lineWidth; ++i) { + p.drawLine(x,y+i, x+length-1, y+i); + } + break; + case VerticalLine: + for (int i = 0; i < lineWidth; ++i) { + p.drawLine(x+i,y, x+i, y+length-1); + } + break; + default: + break; + } +} + +} // KWinPlastik diff --git a/kwin/clients/plastik/plastikbutton.h b/kwin/clients/plastik/plastikbutton.h new file mode 100644 index 000000000..0be8dddea --- /dev/null +++ b/kwin/clients/plastik/plastikbutton.h @@ -0,0 +1,90 @@ +/* Plastik KWin window decoration + Copyright (C) 2003-2005 Sandro Giessl <sandro@giessl.com> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#ifndef PLASTIKBUTTON_H +#define PLASTIKBUTTON_H + +#include <qbutton.h> +#include <qimage.h> +#include "plastik.h" + +#include <kcommondecoration.h> + +class QTimer; + +namespace KWinPlastik { + +class PlastikClient; + +class PlastikButton : public KCommonDecorationButton +{ + Q_OBJECT +public: + PlastikButton(ButtonType type, PlastikClient *parent, const char *name); + ~PlastikButton(); + + void reset(unsigned long changed); + PlastikClient * client() { return m_client; } + +protected slots: + void animate(); + +private: + void enterEvent(QEvent *e); + void leaveEvent(QEvent *e); + void drawButton(QPainter *painter); + +private: + PlastikClient *m_client; + ButtonIcon m_iconType; + bool hover; + + QTimer *animTmr; + uint animProgress; +}; + +/** + * This class creates bitmaps which can be used as icons on buttons. The icons + * are "hardcoded". + * Over the previous "Gimp->xpm->QImage->recolor->SmoothScale->QPixmap" solution + * it has the important advantage that icons are more scalable and at the same + * time sharp and not blurred. + */ +class IconEngine +{ + public: + static QBitmap icon(ButtonIcon icon, int size); + + private: + enum Object { + HorizontalLine, + VerticalLine, + DiagonalLine, + CrossDiagonalLine + }; + + static void drawObject(QPainter &p, Object object, int x, int y, int length, int lineWidth); +}; + +} // namespace KWinPlastik + +#endif // PLASTIKBUTTON_H diff --git a/kwin/clients/plastik/plastikclient.cpp b/kwin/clients/plastik/plastikclient.cpp new file mode 100644 index 000000000..722761a5f --- /dev/null +++ b/kwin/clients/plastik/plastikclient.cpp @@ -0,0 +1,529 @@ +/* Plastik KWin window decoration + Copyright (C) 2003-2005 Sandro Giessl <sandro@giessl.com> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#include <klocale.h> + +#include <qbitmap.h> +#include <qdatetime.h> +#include <qfontmetrics.h> +#include <qimage.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <qdesktopwidget.h> + +#include "plastikclient.h" +#include "plastikbutton.h" +#include "misc.h" + +namespace KWinPlastik +{ + +PlastikClient::PlastikClient(KDecorationBridge* bridge, KDecorationFactory* factory) + : KCommonDecoration (bridge, factory), + s_titleFont(QFont() ) +{ + memset(m_captionPixmaps, 0, sizeof(QPixmap*)*2); +} + +PlastikClient::~PlastikClient() +{ + clearCaptionPixmaps(); +} + +QString PlastikClient::visibleName() const +{ + return i18n("Plastik"); +} + +QString PlastikClient::defaultButtonsLeft() const +{ + return "M"; +} + +QString PlastikClient::defaultButtonsRight() const +{ + return "HIAX"; +} + +bool PlastikClient::decorationBehaviour(DecorationBehaviour behaviour) const +{ + switch (behaviour) { + case DB_MenuClose: + return Handler()->menuClose(); + + case DB_WindowMask: + return true; + + default: + return KCommonDecoration::decorationBehaviour(behaviour); + } +} + +int PlastikClient::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const +{ + bool maximized = maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows(); + + switch (lm) { + case LM_BorderLeft: + case LM_BorderRight: + case LM_BorderBottom: + { + if (respectWindowState && maximized) { + return 0; + } else { + return Handler()->borderSize(); + } + } + + case LM_TitleEdgeTop: + { + if (respectWindowState && maximized) { + return 0; + } else { + return 4; + } + } + + case LM_TitleEdgeBottom: + { +// if (respectWindowState && maximized) { +// return 1; +// } else { + return 2; +// } + } + + case LM_TitleEdgeLeft: + case LM_TitleEdgeRight: + { + if (respectWindowState && maximized) { + return 0; + } else { + return 6; + } + } + + case LM_TitleBorderLeft: + case LM_TitleBorderRight: + return 5; + + case LM_ButtonWidth: + case LM_ButtonHeight: + case LM_TitleHeight: + { + if (respectWindowState && isToolWindow()) { + return Handler()->titleHeightTool(); + } else { + return Handler()->titleHeight(); + } + } + + case LM_ButtonSpacing: + return 1; + + case LM_ButtonMarginTop: + return 0; + + case LM_ExplicitButtonSpacer: + return 3; + + default: + return KCommonDecoration::layoutMetric(lm, respectWindowState, btn); + } +} + +KCommonDecorationButton *PlastikClient::createButton(ButtonType type) +{ + switch (type) { + case MenuButton: + return new PlastikButton(MenuButton, this, "menu"); + + case OnAllDesktopsButton: + return new PlastikButton(OnAllDesktopsButton, this, "on_all_desktops"); + + case HelpButton: + return new PlastikButton(HelpButton, this, "help"); + + case MinButton: + return new PlastikButton(MinButton, this, "minimize"); + + case MaxButton: + return new PlastikButton(MaxButton, this, "maximize"); + + case CloseButton: + return new PlastikButton(CloseButton, this, "close"); + + case AboveButton: + return new PlastikButton(AboveButton, this, "above"); + + case BelowButton: + return new PlastikButton(BelowButton, this, "below"); + + case ShadeButton: + return new PlastikButton(ShadeButton, this, "shade"); + + default: + return 0; + } +} + +void PlastikClient::init() +{ + s_titleFont = isToolWindow() ? Handler()->titleFontTool() : Handler()->titleFont(); + + clearCaptionPixmaps(); + + KCommonDecoration::init(); +} + +QRegion PlastikClient::cornerShape(WindowCorner corner) +{ + int w = widget()->width(); + int h = widget()->height(); + + switch (corner) { + case WC_TopLeft: + if (layoutMetric(LM_TitleEdgeLeft) > 0) + return QRegion(0, 0, 1, 2) + QRegion(1, 0, 1, 1); + else + return QRegion(); + + case WC_TopRight: + if (layoutMetric(LM_TitleEdgeRight) > 0) + return QRegion(w-1, 0, 1, 2) + QRegion(w-2, 0, 1, 1); + else + return QRegion(); + + case WC_BottomLeft: + if (layoutMetric(LM_BorderBottom) > 0) + return QRegion(0, h-1, 1, 1); + else + return QRegion(); + + case WC_BottomRight: + if (layoutMetric(LM_BorderBottom) > 0) + return QRegion(w-1, h-1, 1, 1); + else + return QRegion(); + + default: + return QRegion(); + } + +} + +void PlastikClient::paintEvent(QPaintEvent *e) +{ + QRegion region = e->region(); + + PlastikHandler *handler = Handler(); + + if (oldCaption != caption() ) + clearCaptionPixmaps(); + + bool active = isActive(); + bool toolWindow = isToolWindow(); + + QPainter painter(widget() ); + + // often needed coordinates + QRect r = widget()->rect(); + + int r_w = r.width(); +// int r_h = r.height(); + int r_x, r_y, r_x2, r_y2; + r.coords(&r_x, &r_y, &r_x2, &r_y2); + const int borderLeft = layoutMetric(LM_BorderLeft); + const int borderRight = layoutMetric(LM_BorderRight); + const int borderBottom = layoutMetric(LM_BorderBottom); + const int titleHeight = layoutMetric(LM_TitleHeight); + const int titleEdgeTop = layoutMetric(LM_TitleEdgeTop); + const int titleEdgeBottom = layoutMetric(LM_TitleEdgeBottom); + const int titleEdgeLeft = layoutMetric(LM_TitleEdgeLeft); + const int titleEdgeRight = layoutMetric(LM_TitleEdgeRight); + + const int borderBottomTop = r_y2-borderBottom+1; + const int borderLeftRight = r_x+borderLeft-1; + const int borderRightLeft = r_x2-borderRight+1; + const int titleEdgeBottomBottom = r_y+titleEdgeTop+titleHeight+titleEdgeBottom-1; + + const int sideHeight = borderBottomTop-titleEdgeBottomBottom-1; + + QRect Rtitle = QRect(r_x+titleEdgeLeft+buttonsLeftWidth(), r_y+titleEdgeTop, + r_x2-titleEdgeRight-buttonsRightWidth()-(r_x+titleEdgeLeft+buttonsLeftWidth()), + titleEdgeBottomBottom-(r_y+titleEdgeTop) ); + + QRect tempRect; + + // topSpacer + if(titleEdgeTop > 0) + { + tempRect.setRect(r_x+2, r_y, r_w-2*2, titleEdgeTop ); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(TitleBarTileTop, active, toolWindow) ); + } + } + + // leftTitleSpacer + int titleMarginLeft = 0; + int titleMarginRight = 0; + if(titleEdgeLeft > 0) + { + tempRect.setRect(r_x, r_y, borderLeft, titleEdgeTop+titleHeight+titleEdgeBottom); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(TitleBarLeft, active, toolWindow) ); + titleMarginLeft = borderLeft; + } + } + + // rightTitleSpacer + if(titleEdgeRight > 0) + { + tempRect.setRect(borderRightLeft, r_y, borderRight, titleEdgeTop+titleHeight+titleEdgeBottom); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(TitleBarRight, active, toolWindow) ); + titleMarginRight = borderRight; + } + } + + // titleSpacer + const QPixmap &caption = captionPixmap(); + if(Rtitle.width() > 0) + { + m_captionRect = captionRect(); // also update m_captionRect! + if (m_captionRect.isValid() && region.contains(m_captionRect) ) + { + painter.drawTiledPixmap(m_captionRect, caption); + } + + // left to the title + tempRect.setRect(r_x+titleMarginLeft, m_captionRect.top(), + m_captionRect.left() - (r_x+titleMarginLeft), m_captionRect.height() ); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(TitleBarTile, active, toolWindow) ); + } + + // right to the title + tempRect.setRect(m_captionRect.right()+1, m_captionRect.top(), + (r_x2-titleMarginRight) - m_captionRect.right(), m_captionRect.height() ); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(TitleBarTile, active, toolWindow) ); + } + + } + + // leftSpacer + if(borderLeft > 0 && sideHeight > 0) + { + tempRect.setCoords(r_x, titleEdgeBottomBottom+1, borderLeftRight, borderBottomTop-1); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(BorderLeftTile, active, toolWindow) ); + } + } + + // rightSpacer + if(borderRight > 0 && sideHeight > 0) + { + tempRect.setCoords(borderRightLeft, titleEdgeBottomBottom+1, r_x2, borderBottomTop-1); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(BorderRightTile, active, toolWindow) ); + } + } + + // bottomSpacer + if(borderBottom > 0) + { + int l = r_x; + int r = r_x2; + + tempRect.setRect(r_x, borderBottomTop, borderLeft, borderBottom); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(BorderBottomLeft, active, toolWindow) ); + l = tempRect.right()+1; + } + + tempRect.setRect(borderRightLeft, borderBottomTop, borderLeft, borderBottom); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(BorderBottomRight, active, toolWindow) ); + r = tempRect.left()-1; + } + + tempRect.setCoords(l, borderBottomTop, r, r_y2); + if (tempRect.isValid() && region.contains(tempRect) ) { + painter.drawTiledPixmap(tempRect, handler->pixmap(BorderBottomTile, active, toolWindow) ); + } + } +} + +QRect PlastikClient::captionRect() const +{ + const QPixmap &caption = captionPixmap(); + QRect r = widget()->rect(); + + const int titleHeight = layoutMetric(LM_TitleHeight); + const int titleEdgeBottom = layoutMetric(LM_TitleEdgeBottom); + const int titleEdgeTop = layoutMetric(LM_TitleEdgeTop); + const int titleEdgeLeft = layoutMetric(LM_TitleEdgeLeft); + const int marginLeft = layoutMetric(LM_TitleBorderLeft); + const int marginRight = layoutMetric(LM_TitleBorderRight); + + const int titleLeft = r.left() + titleEdgeLeft + buttonsLeftWidth() + marginLeft; + const int titleWidth = r.width() - + titleEdgeLeft - layoutMetric(LM_TitleEdgeRight) - + buttonsLeftWidth() - buttonsRightWidth() - + marginLeft - marginRight; + + Qt::AlignmentFlags a = Handler()->titleAlign(); + + int tX, tW; // position/width of the title buffer + if (caption.width() > titleWidth) { + tW = titleWidth; + } else { + tW = caption.width(); + } + if (a == Qt::AlignLeft || (caption.width() > titleWidth) ) { + // Align left + tX = titleLeft; + } else if (a == Qt::AlignHCenter) { + // Align center + tX = titleLeft+(titleWidth- caption.width() )/2; + } else { + // Align right + tX = titleLeft+titleWidth-caption.width(); + } + + return QRect(tX, r.top()+titleEdgeTop, tW, titleHeight+titleEdgeBottom); +} + +void PlastikClient::updateCaption() +{ + QRect oldCaptionRect = m_captionRect; + + if (oldCaption != caption() ) + clearCaptionPixmaps(); + + m_captionRect = PlastikClient::captionRect(); + + if (oldCaptionRect.isValid() && m_captionRect.isValid() ) + widget()->update(oldCaptionRect|m_captionRect); + else + widget()->update(); +} + +void PlastikClient::reset( unsigned long changed ) +{ + if (changed & SettingColors) + { + // repaint the whole thing + clearCaptionPixmaps(); + widget()->update(); + updateButtons(); + } else if (changed & SettingFont) { + // font has changed -- update title height and font + s_titleFont = isToolWindow() ? Handler()->titleFontTool() : Handler()->titleFont(); + + updateLayout(); + + // then repaint + clearCaptionPixmaps(); + widget()->update(); + } + + KCommonDecoration::reset(changed); +} + +const QPixmap &PlastikClient::getTitleBarTile(bool active) const +{ + return Handler()->pixmap(TitleBarTile, active, isToolWindow() ); +} + +const QPixmap &PlastikClient::captionPixmap() const +{ + bool active = isActive(); + + if (m_captionPixmaps[active]) { + return *m_captionPixmaps[active]; + } + + // not found, create new pixmap... + + const uint maxCaptionLength = 300; // truncate captions longer than this! + QString c(caption() ); + if (c.length() > maxCaptionLength) { + c.truncate(maxCaptionLength); + c.append(" [...]"); + } + + QFontMetrics fm(s_titleFont); + int captionWidth = fm.width(c); + int captionHeight = fm.height(); + + const int th = layoutMetric(LM_TitleHeight, false) + layoutMetric(LM_TitleEdgeBottom, false); + + QPainter painter; + + const int thickness = 2; + + QPixmap *captionPixmap = new QPixmap(captionWidth+2*thickness, th); + + painter.begin(captionPixmap); + painter.drawTiledPixmap(captionPixmap->rect(), + Handler()->pixmap(TitleBarTile, active, isToolWindow()) ); + + painter.setFont(s_titleFont); + QPoint tp(1, captionHeight-1); + if(Handler()->titleShadow()) + { + QColor shadowColor; + if (qGray(Handler()->getColor(TitleFont,active).rgb()) < 100) + shadowColor = QColor(255, 255, 255); + else + shadowColor = QColor(0,0,0); + + painter.setPen(alphaBlendColors(options()->color(ColorTitleBar, active), shadowColor, 205) ); + painter.drawText(tp+QPoint(1,2), c); + painter.setPen(alphaBlendColors(options()->color(ColorTitleBar, active), shadowColor, 225) ); + painter.drawText(tp+QPoint(2,2), c); + painter.setPen(alphaBlendColors(options()->color(ColorTitleBar, active), shadowColor, 165) ); + painter.drawText(tp+QPoint(1,1), c); + } + painter.setPen(Handler()->getColor(TitleFont,active) ); + painter.drawText(tp, c ); + painter.end(); + + m_captionPixmaps[active] = captionPixmap; + return *captionPixmap; +} + +void PlastikClient::clearCaptionPixmaps() +{ + for (int i = 0; i < 2; ++i) { + delete m_captionPixmaps[i]; + m_captionPixmaps[i] = 0; + } + + oldCaption = caption(); +} + +} // KWinPlastik diff --git a/kwin/clients/plastik/plastikclient.h b/kwin/clients/plastik/plastikclient.h new file mode 100644 index 000000000..28b611b8e --- /dev/null +++ b/kwin/clients/plastik/plastikclient.h @@ -0,0 +1,73 @@ +/* Plastik KWin window decoration + Copyright (C) 2003-2005 Sandro Giessl <sandro@giessl.com> + + based on the window decoration "Web": + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + +#ifndef PLASTIKCLIENT_H +#define PLASTIKCLIENT_H + +#include <kcommondecoration.h> + +#include "plastik.h" + +namespace KWinPlastik { + +class PlastikButton; + +class PlastikClient : public KCommonDecoration +{ +public: + PlastikClient(KDecorationBridge* bridge, KDecorationFactory* factory); + ~PlastikClient(); + + virtual QString visibleName() const; + virtual QString defaultButtonsLeft() const; + virtual QString defaultButtonsRight() const; + virtual bool decorationBehaviour(DecorationBehaviour behaviour) const; + virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton * = 0) const; + virtual QRegion cornerShape(WindowCorner corner); + virtual KCommonDecorationButton *createButton(ButtonType type); + + virtual void init(); + virtual void reset( unsigned long changed ); + + virtual void paintEvent(QPaintEvent *e); + virtual void updateCaption(); + + const QPixmap &getTitleBarTile(bool active) const; + +private: + QRect captionRect() const; + + const QPixmap &captionPixmap() const; + void clearCaptionPixmaps(); + + mutable QPixmap *m_captionPixmaps[2]; + + QRect m_captionRect; + QString oldCaption; + + // settings... + QFont s_titleFont; +}; + +} // KWinPlastik + +#endif // PLASTIKCLIENT_H diff --git a/kwin/clients/quartz/Makefile.am b/kwin/clients/quartz/Makefile.am new file mode 100644 index 000000000..d66643ba5 --- /dev/null +++ b/kwin/clients/quartz/Makefile.am @@ -0,0 +1,23 @@ + +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +SUBDIRS = . config + +kde_module_LTLIBRARIES = kwin3_quartz.la + +kwin3_quartz_la_SOURCES = quartz.cpp +kwin3_quartz_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_quartz_la_LIBADD = $(LIB_KDEUI) ../../lib/libkdecorations.la + +METASOURCES = AUTO +noinst_HEADERS = quartz.h + +lnkdir = $(kde_datadir)/kwin/ +lnk_DATA = quartz.desktop + +EXTRA_DIST = $(lnk_DATA) + + +###KMAKE-start (don't edit or delete this block) + +###KMAKE-end diff --git a/kwin/clients/quartz/config/Makefile.am b/kwin/clients/quartz/config/Makefile.am new file mode 100644 index 000000000..eb64880ee --- /dev/null +++ b/kwin/clients/quartz/config/Makefile.am @@ -0,0 +1,16 @@ +INCLUDES = $(all_includes) + +kde_module_LTLIBRARIES = kwin_quartz_config.la + +kwin_quartz_config_la_SOURCES = config.cpp +kwin_quartz_config_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin_quartz_config_la_LIBADD = $(LIB_KDEUI) + +METASOURCES = AUTO +noinst_HEADERS = config.h + +lnkdir = $(kde_datadir)/kwin/ + +###KMAKE-start (don't edit or delete this block) + +###KMAKE-end diff --git a/kwin/clients/quartz/config/config.cpp b/kwin/clients/quartz/config/config.cpp new file mode 100644 index 000000000..8c7070b9d --- /dev/null +++ b/kwin/clients/quartz/config/config.cpp @@ -0,0 +1,104 @@ +/* + * + * This file contains the quartz configuration widget + * + * Copyright (c) 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + */ + +#include "config.h" +#include <kglobal.h> +#include <qwhatsthis.h> +#include <klocale.h> + + +extern "C" +{ + KDE_EXPORT QObject* allocate_config( KConfig* conf, QWidget* parent ) + { + return(new QuartzConfig(conf, parent)); + } +} + + +/* NOTE: + * 'conf' is a pointer to the kwindecoration modules open kwin config, + * and is by default set to the "Style" group. + * + * 'parent' is the parent of the QObject, which is a VBox inside the + * Configure tab in kwindecoration + */ + +QuartzConfig::QuartzConfig( KConfig* conf, QWidget* parent ) + : QObject( parent ) +{ + quartzConfig = new KConfig("kwinquartzrc"); + KGlobal::locale()->insertCatalogue("kwin_clients"); + gb = new QVBox( parent ); + cbColorBorder = new QCheckBox( + i18n("Draw window frames using &titlebar colors"), gb ); + QWhatsThis::add( cbColorBorder, + i18n("When selected, the window decoration borders " + "are drawn using the titlebar colors; otherwise, they are " + "drawn using normal border colors instead.") ); + cbExtraSmall = new QCheckBox( i18n("Quartz &extra slim"), gb ); + QWhatsThis::add( cbExtraSmall, + i18n("Quartz window decorations with extra-small title bar.") ); + // Load configuration options + load( conf ); + + // Ensure we track user changes properly + connect( cbColorBorder, SIGNAL(clicked()), this, SLOT(slotSelectionChanged()) ); + connect( cbExtraSmall, SIGNAL(clicked()), this, SLOT(slotSelectionChanged()) ); + + // Make the widgets visible in kwindecoration + gb->show(); +} + + +QuartzConfig::~QuartzConfig() +{ + delete gb; + delete quartzConfig; +} + + +void QuartzConfig::slotSelectionChanged() +{ + emit changed(); +} + + +// Loads the configurable options from the kwinrc config file +// It is passed the open config from kwindecoration to improve efficiency +void QuartzConfig::load( KConfig* /*conf*/ ) +{ + quartzConfig->setGroup("General"); + bool override = quartzConfig->readBoolEntry( "UseTitleBarBorderColors", true ); + cbColorBorder->setChecked( override ); + override = quartzConfig->readBoolEntry( "UseQuartzExtraSlim", false ); + cbExtraSmall->setChecked( override ); +} + + +// Saves the configurable options to the kwinrc config file +void QuartzConfig::save( KConfig* /*conf*/ ) +{ + quartzConfig->setGroup("General"); + quartzConfig->writeEntry( "UseTitleBarBorderColors", cbColorBorder->isChecked() ); + quartzConfig->writeEntry( "UseQuartzExtraSlim", cbExtraSmall->isChecked() ); + // Ensure others trying to read this config get updated + quartzConfig->sync(); +} + + +// Sets UI widget defaults which must correspond to style defaults +void QuartzConfig::defaults() +{ + cbColorBorder->setChecked( true ); + cbExtraSmall->setChecked( false ); +} + +#include "config.moc" +// vim: ts=4 diff --git a/kwin/clients/quartz/config/config.h b/kwin/clients/quartz/config/config.h new file mode 100644 index 000000000..815cc9644 --- /dev/null +++ b/kwin/clients/quartz/config/config.h @@ -0,0 +1,47 @@ +/* + * + * This file contains the quartz configuration widget + * + * Copyright (c) 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + */ + +#ifndef __KDE_QUARTZCONFIG_H +#define __KDE_QUARTZCONFIG_H + +#include <qcheckbox.h> +#include <qvbox.h> +#include <kconfig.h> + +class QuartzConfig: public QObject +{ + Q_OBJECT + + public: + QuartzConfig( KConfig* conf, QWidget* parent ); + ~QuartzConfig(); + + // These public signals/slots work similar to KCM modules + signals: + void changed(); + + public slots: + void load( KConfig* conf ); + void save( KConfig* conf ); + void defaults(); + + protected slots: + void slotSelectionChanged(); // Internal use + + private: + KConfig* quartzConfig; + QCheckBox* cbColorBorder; + QCheckBox* cbExtraSmall; + QVBox* gb; +}; + + +#endif + +// vim: ts=4 diff --git a/kwin/clients/quartz/quartz.cpp b/kwin/clients/quartz/quartz.cpp new file mode 100644 index 000000000..857971780 --- /dev/null +++ b/kwin/clients/quartz/quartz.cpp @@ -0,0 +1,797 @@ +/* + * + * Gallium-Quartz KWin client + * + * Copyright (C) 2005 Sandro Giessl <sandro@giessl.com> + * Copyright 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + * + * Based on the KDE default client. + * + * Includes mini titlebars for ToolWindow Support. + * Button positions are now customizable. + * + */ + +#include <kconfig.h> +#include <kdrawutil.h> +#include <kglobal.h> +#include <klocale.h> +#include <kpixmapeffect.h> +#include <qbitmap.h> +#include <qdrawutil.h> +#include <qimage.h> +#include <qapplication.h> + +#include "quartz.h" + + +namespace Quartz { + +static const unsigned char iconify_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char close_bits[] = { + 0x00, 0x00, 0x86, 0x01, 0xcc, 0x00, 0x78, 0x00, 0x30, 0x00, 0x78, 0x00, + 0xcc, 0x00, 0x86, 0x01, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char maximize_bits[] = { + 0xff, 0x01, 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0xff, 0x01, 0x00, 0x00}; + +static const unsigned char minmax_bits[] = { + 0xfc, 0x00, 0xfc, 0x00, 0x84, 0x00, 0xbf, 0x00, 0xbf, 0x00, 0xe1, 0x00, + 0x21, 0x00, 0x21, 0x00, 0x3f, 0x00, 0x00, 0x00}; + +static const unsigned char question_bits[] = { + 0x00, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_white_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x1f, 0xa0, 0x03, + 0xb0, 0x01, 0x30, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_gray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, + 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pindown_dgray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x10, 0x70, 0x20, 0x50, 0x20, + 0x48, 0x30, 0xc8, 0x38, 0x08, 0x1f, 0x08, 0x18, 0x10, 0x1c, 0x10, 0x0e, + 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_white_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x11, + 0x3f, 0x15, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_gray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x0a, 0xbf, 0x0a, 0x80, 0x15, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char pinup_dgray_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x20, 0x40, 0x31, 0x40, 0x2e, + 0x40, 0x20, 0x40, 0x20, 0x7f, 0x2a, 0x40, 0x3f, 0xc0, 0x31, 0xc0, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char above_on_bits[] = { + 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0x30, 0x00, 0xfc, 0x00, 0x78, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char above_off_bits[] = { + 0x30, 0x00, 0x78, 0x00, 0xfc, 0x00, 0x30, 0x00, 0xfe, 0x01, 0xfe, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static const unsigned char below_on_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x78, 0x00, 0xfc, 0x00, + 0x30, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0x00, 0x00}; + +static const unsigned char below_off_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, + 0x30, 0x00, 0xfc, 0x00, 0x78, 0x00, 0x30, 0x00}; + +static const unsigned char shade_on_bits[] = { + 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0x02, 0x01, 0x02, 0x01, + 0x02, 0x01, 0x02, 0x01, 0xfe, 0x01, 0x00, 0x00}; + +static const unsigned char shade_off_bits[] = { + 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + +/////////////////////////////////////////////////////////////////////////// + +// Titlebar button positions +bool onAllDesktopsButtonOnLeft = true; +bool coloredFrame = true; +bool extraSlim = false; + +KPixmap* titleBlocks = NULL; +KPixmap* ititleBlocks = NULL; +KPixmap* pinDownPix = NULL; +KPixmap* pinUpPix = NULL; +KPixmap* ipinDownPix = NULL; +KPixmap* ipinUpPix = NULL; +static int normalTitleHeight; +static int toolTitleHeight; +static int borderWidth; + +bool quartz_initialized = false; +QuartzHandler* clientHandler; + +/////////////////////////////////////////////////////////////////////////// + + +QuartzHandler::QuartzHandler() +{ + quartz_initialized = false; + readConfig(); + createPixmaps(); + quartz_initialized = true; +} + + +QuartzHandler::~QuartzHandler() +{ + quartz_initialized = false; + freePixmaps(); +} + + +KDecoration* QuartzHandler::createDecoration( KDecorationBridge* bridge ) +{ + return new QuartzClient( bridge, this ); +} + + +bool QuartzHandler::reset(unsigned long changed) +{ + quartz_initialized = false; + freePixmaps(); + readConfig(); + createPixmaps(); + quartz_initialized = true; + + // Do we need to "hit the wooden hammer" ? + bool needHardReset = true; + if (changed & SettingColors) + { + needHardReset = false; + } else if (changed & SettingButtons) { + // handled by KCommonDecoration + needHardReset = false; + } + + if (needHardReset) { + return true; + } else { + resetDecorations(changed); + return false; + } + return true; +} + + +bool QuartzHandler::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonMenu: + case AbilityButtonOnAllDesktops: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + case AbilityButtonAboveOthers: + case AbilityButtonBelowOthers: + case AbilityButtonShade: + case AbilityButtonSpacer: + return true; + default: + return false; + }; +} + + +void QuartzHandler::readConfig() +{ + KConfig conf("kwinquartzrc"); + conf.setGroup("General"); + coloredFrame = conf.readBoolEntry( "UseTitleBarBorderColors", true ); + extraSlim = conf.readBoolEntry( "UseQuartzExtraSlim", false ); + + // A small hack to make the on all desktops button look nicer + onAllDesktopsButtonOnLeft = KDecoration::options()->titleButtonsLeft().contains( 'S' ); + if ( QApplication::reverseLayout() ) + onAllDesktopsButtonOnLeft = !onAllDesktopsButtonOnLeft; + switch(options()->preferredBorderSize(this)) { + case BorderLarge: + borderWidth = 8; + break; + case BorderVeryLarge: + borderWidth = 12; + break; + case BorderHuge: + borderWidth = 18; + break; + case BorderVeryHuge: + borderWidth = 27; + break; + case BorderOversized: + borderWidth = 40; + break; + case BorderTiny: + case BorderNormal: + default: + borderWidth = extraSlim?2:4; + } + + normalTitleHeight = QFontMetrics(options()->font(true)).height(); + int nTH_limit=extraSlim?14:18; + normalTitleHeight = QFontMetrics(options()->font(true)).height()-(extraSlim?1:0); + if (normalTitleHeight < nTH_limit) normalTitleHeight = nTH_limit; + if (normalTitleHeight < borderWidth) normalTitleHeight = borderWidth; + + toolTitleHeight = QFontMetrics(options()->font(true, true)).height(); + if (toolTitleHeight < 12) toolTitleHeight = 12; + if (toolTitleHeight < borderWidth) toolTitleHeight = borderWidth; +} + + +// This does the colour transition magic. (You say "Oh, is that it?") +// This may be made configurable at a later stage +void QuartzHandler::drawBlocks( KPixmap *pi, KPixmap &p, const QColor &c1, const QColor &c2 ) +{ + QPainter px; + + px.begin( pi ); + + // Draw a background gradient first + KPixmapEffect::gradient(p, c1, c2, KPixmapEffect::HorizontalGradient); + + int factor = (pi->height()-2)/4; + int square = factor - (factor+2)/4; + + int x = pi->width() - 5*factor - square; + int y = (pi->height() - 4*factor)/2; + + px.fillRect( x, y, square, square, c1.light(120) ); + px.fillRect( x, y+factor, square, square, c1 ); + px.fillRect( x, y+2*factor, square, square, c1.light(110) ); + px.fillRect( x, y+3*factor, square, square, c1 ); + + px.fillRect( x+factor, y, square, square, c1.light(110) ); + px.fillRect( x+factor, y+factor, square, square, c2.light(110) ); + px.fillRect( x+factor, y+2*factor, square, square, c1.light(120) ); + px.fillRect( x+factor, y+3*factor, square, square, c2.light(130) ); + + px.fillRect( x+2*factor, y+factor, square, square, c1.light(110) ); + px.fillRect( x+2*factor, y+2*factor, square, square, c2.light(120) ); + px.fillRect( x+2*factor, y+3*factor, square, square, c2.light(150) ); + + px.fillRect( x+3*factor, y, square, square, c1.dark(110) ); + px.fillRect( x+3*factor, y+2*factor, square, square, c2.light(120) ); + px.fillRect( x+3*factor, y+3*factor, square, square, c1.dark(120) ); + + px.fillRect( x+4*factor, y+factor, square, square, c1.light(110) ); + px.fillRect( x+4*factor, y+3*factor, square, square, c1.dark(110) ); + + px.fillRect( x+5*factor, y+2*factor, square, square, c2.light(120)); + px.fillRect( x+5*factor, y+3*factor, square, square, c2.light(110) ); +} + + +// This paints the button pixmaps upon loading the style. +void QuartzHandler::createPixmaps() +{ + // Obtain titlebar blend colours, and create the block stuff on pixmaps. + QColorGroup g2 = options()->colorGroup(ColorTitleBlend, true); + QColor c2 = g2.background(); + g2 = options()->colorGroup(ColorTitleBar, true ); + QColor c = g2.background().light(130); + + titleBlocks = new KPixmap(); + titleBlocks->resize( normalTitleHeight*25/18, normalTitleHeight ); + drawBlocks( titleBlocks, *titleBlocks, c, c2 ); + + g2 = options()->colorGroup(ColorTitleBlend, false); + c2 = g2.background(); + g2 = options()->colorGroup(ColorTitleBar, false ); + c = g2.background().light(130); + + ititleBlocks = new KPixmap(); + ititleBlocks->resize( normalTitleHeight*25/18, normalTitleHeight ); + drawBlocks( ititleBlocks, *ititleBlocks, c, c2 ); + + // Set the on all desktops pin pixmaps; + QColorGroup g; + QPainter p; + + g = options()->colorGroup( onAllDesktopsButtonOnLeft ? ColorTitleBar : ColorTitleBlend, true ); + c = onAllDesktopsButtonOnLeft ? g.background().light(130) : g.background(); + g2 = options()->colorGroup( ColorButtonBg, true ); + + pinUpPix = new KPixmap(); + pinUpPix->resize(16, 16); + p.begin( pinUpPix ); + p.fillRect( 0, 0, 16, 16, c); + kColorBitmaps( &p, g2, 0, 1, 16, 16, true, pinup_white_bits, + pinup_gray_bits, NULL, NULL, pinup_dgray_bits, NULL ); + p.end(); + + pinDownPix = new KPixmap(); + pinDownPix->resize(16, 16); + p.begin( pinDownPix ); + p.fillRect( 0, 0, 16, 16, c); + kColorBitmaps( &p, g2, 0, 1, 16, 16, true, pindown_white_bits, + pindown_gray_bits, NULL, NULL, pindown_dgray_bits, NULL ); + p.end(); + + + // Inactive pins + g = options()->colorGroup( onAllDesktopsButtonOnLeft ? ColorTitleBar : ColorTitleBlend, false ); + c = onAllDesktopsButtonOnLeft ? g.background().light(130) : g.background(); + g2 = options()->colorGroup( ColorButtonBg, false ); + + ipinUpPix = new KPixmap(); + ipinUpPix->resize(16, 16); + p.begin( ipinUpPix ); + p.fillRect( 0, 0, 16, 16, c); + kColorBitmaps( &p, g2, 0, 1, 16, 16, true, pinup_white_bits, + pinup_gray_bits, NULL, NULL, pinup_dgray_bits, NULL ); + p.end(); + + ipinDownPix = new KPixmap(); + ipinDownPix->resize(16, 16); + p.begin( ipinDownPix ); + p.fillRect( 0, 0, 16, 16, c); + kColorBitmaps( &p, g2, 0, 1, 16, 16, true, pindown_white_bits, + pindown_gray_bits, NULL, NULL, pindown_dgray_bits, NULL ); + p.end(); +} + + +void QuartzHandler::freePixmaps() +{ + delete titleBlocks; + delete ititleBlocks; + + // On all desktops pin images + delete pinUpPix; + delete ipinUpPix; + delete pinDownPix; + delete ipinDownPix; +} + + +QValueList< QuartzHandler::BorderSize > QuartzHandler::borderSizes() const +{ // the list must be sorted + return QValueList< BorderSize >() << BorderNormal << BorderLarge << + BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized; +} + + +QuartzButton::QuartzButton(ButtonType type, QuartzClient *parent, const char *name) + : KCommonDecorationButton(type, parent, name) +{ + // Eliminate any possible background flicker + setBackgroundMode( QWidget::NoBackground ); + + deco = 0; +} + + +QuartzButton::~QuartzButton() +{ + delete deco; +} + +void QuartzButton::reset(unsigned long changed) +{ + if (changed&DecorationReset || changed&ManualReset || changed&SizeChange || changed&StateChange) { + switch (type() ) { + case CloseButton: + setBitmap(close_bits); + break; + case HelpButton: + setBitmap(question_bits); + break; + case MinButton: + setBitmap(iconify_bits); + break; + case MaxButton: + setBitmap( isOn() ? minmax_bits : maximize_bits ); + break; + case OnAllDesktopsButton: + setBitmap(0); + break; + case ShadeButton: + setBitmap( isOn() ? shade_on_bits : shade_off_bits ); + break; + case AboveButton: + setBitmap( isOn() ? above_on_bits : above_off_bits ); + break; + case BelowButton: + setBitmap( isOn() ? below_on_bits : below_off_bits ); + break; + default: + setBitmap(0); + break; + } + + this->update(); + } +} + +void QuartzButton::setBitmap(const unsigned char *bitmap) +{ + delete deco; + deco = 0; + + if (bitmap) { + deco = new QBitmap(10, 10, bitmap, true); + deco->setMask( *deco ); + repaint( false ); + } +} + +void QuartzButton::drawButton(QPainter *p) +{ + // Never paint if the pixmaps have not been created + if (!quartz_initialized) + return; + + QColor c; + + if (isLeft() ) + c = KDecoration::options()->color(KDecoration::ColorTitleBar, decoration()->isActive()).light(130); + else + c = KDecoration::options()->color(KDecoration::ColorTitleBlend, decoration()->isActive()); + + // Fill the button background with an appropriate color + p->fillRect(0, 0, width(), height(), c ); + + // If we have a decoration bitmap, then draw that + // otherwise we paint a menu button (with mini icon), or a onAllDesktops button. + if( deco ) + { + int xOff = (width()-10)/2; + int yOff = (height()-10)/2; + p->setPen( Qt::black ); + p->drawPixmap(isDown() ? xOff+2: xOff+1, isDown() ? yOff+2 : yOff+1, *deco); + p->setPen( KDecoration::options()->color(KDecoration::ColorButtonBg, decoration()->isActive()).light(150) ); + p->drawPixmap(isDown() ? xOff+1: xOff, isDown() ? yOff+1 : yOff, *deco); + } else + { + QPixmap btnpix; + int Offset = 0; + + if (type() == OnAllDesktopsButton) + { + if (isDown()) + Offset = 1; + + // Select the right onAllDesktops button to paint + if (decoration()->isActive()) + btnpix = isOn() ? *pinDownPix : *pinUpPix; + else + btnpix = isOn() ? *ipinDownPix : *ipinUpPix; + + } else + btnpix = decoration()->icon().pixmap( QIconSet::Small, QIconSet::Normal); + + // Shrink the miniIcon for tiny titlebars. + if ( height() < 16) + { + QPixmap tmpPix; + + // Smooth scale the image + tmpPix.convertFromImage( btnpix.convertToImage().smoothScale(height(), height())); + p->drawPixmap( 0, 0, tmpPix ); + } else { + Offset += (height() - 16)/2; + p->drawPixmap( Offset, Offset, btnpix ); + } + } +} + +/////////////////////////////////////////////////////////////////////////// + +QuartzClient::QuartzClient(KDecorationBridge* bridge, KDecorationFactory* factory) + : KCommonDecoration (bridge, factory) +{ +} + +QString QuartzClient::visibleName() const +{ + return i18n("Quartz"); +} + +QString QuartzClient::defaultButtonsLeft() const +{ + return "M"; +} + +QString QuartzClient::defaultButtonsRight() const +{ + return "HIAX"; +} + +bool QuartzClient::decorationBehaviour(DecorationBehaviour behaviour) const +{ + switch (behaviour) { + case DB_MenuClose: + return false; + + case DB_WindowMask: + return false; + + case DB_ButtonHide: + return true; + + default: + return KCommonDecoration::decorationBehaviour(behaviour); + } +} + +int QuartzClient::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const +{ + bool maximized = maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows(); + + switch (lm) { + case LM_BorderLeft: + case LM_BorderRight: + case LM_BorderBottom: + case LM_TitleEdgeLeft: + case LM_TitleEdgeRight: + { + if (respectWindowState && maximized) { + return 0; + } else { + return borderSize; + } + } + + case LM_TitleEdgeTop: + return borderSize-1; + + case LM_TitleEdgeBottom: + return 1; + + case LM_TitleBorderLeft: + case LM_TitleBorderRight: + return 5; + + case LM_TitleHeight: + return titleHeight; + + case LM_ButtonWidth: + case LM_ButtonHeight: + return titleHeight-2; + + case LM_ButtonSpacing: + return 1; + + case LM_ExplicitButtonSpacer: + return 3; + + default: + return KCommonDecoration::layoutMetric(lm, respectWindowState, btn); + } +} + +KCommonDecorationButton *QuartzClient::createButton(ButtonType type) +{ + switch (type) { + case MenuButton: + return new QuartzButton(MenuButton, this, "menu"); + + case OnAllDesktopsButton: + return new QuartzButton(OnAllDesktopsButton, this, "on_all_desktops"); + + case HelpButton: + return new QuartzButton(HelpButton, this, "help"); + + case MinButton: + return new QuartzButton(MinButton, this, "minimize"); + + case MaxButton: + return new QuartzButton(MaxButton, this, "maximize"); + + case CloseButton: + return new QuartzButton(CloseButton, this, "close"); + + case AboveButton: + return new QuartzButton(AboveButton, this, "above"); + + case BelowButton: + return new QuartzButton(BelowButton, this, "below"); + + case ShadeButton: + return new QuartzButton(ShadeButton, this, "shade"); + + default: + return 0; + } +} + + +void QuartzClient::init() +{ + // Finally, toolWindows look small + if ( isToolWindow() ) { + titleHeight = toolTitleHeight; + largeButtons = false; + } + else { + titleHeight = normalTitleHeight; + largeButtons = true; + } + + borderSize = borderWidth; + + KCommonDecoration::init(); +} + +void QuartzClient::reset( unsigned long changed ) +{ + if (changed & SettingColors || changed & SettingFont) + { + // repaint the whole thing + widget()->repaint(false); + } + + KCommonDecoration::reset(changed); +} + + +// Quartz Paint magic goes here. +void QuartzClient::paintEvent( QPaintEvent* ) +{ + // Never paint if the pixmaps have not been created + if (!quartz_initialized) + return; + + const bool maxFull = (maximizeMode()==MaximizeFull) && !options()->moveResizeMaximizedWindows(); + + QColorGroup g; + QPainter p(widget()); + + // Obtain widget bounds. + QRect r(widget()->rect()); + int x = r.x(); + int y = r.y(); + int x2 = r.width() - 1; + int y2 = r.height() - 1; + int w = r.width(); + int h = r.height(); + + // Draw part of the frame that is the title color + + if( coloredFrame ) + g = options()->colorGroup(ColorTitleBar, isActive()); + else + g = options()->colorGroup(ColorFrame, isActive()); + + // Draw outer highlights and lowlights + p.setPen( g.light().light(120) ); + p.drawLine( x, y, x2-1, y ); + p.drawLine( x, y+1, x, y2-1 ); + p.setPen( g.dark().light(120) ); + p.drawLine( x2, y, x2, y2 ); + p.drawLine( x, y2, x2, y2 ); + + // Fill out the border edges + QColor frameColor; + if ( coloredFrame) + frameColor = g.background().light(130); + else + frameColor = g.background(); + if (borderSize > 2) { + p.fillRect(x+1, y+1, w-2, borderSize-2, frameColor); // top + if (!maxFull) { + p.fillRect(x+1, y+h-(borderSize-1), w-2, borderSize-2, frameColor); // bottom + p.fillRect(x+1, y+borderSize-1, borderSize-1, h-2*(borderSize-1), frameColor); // left + p.fillRect(x+w-(borderSize), y+borderSize-1, borderSize-1, h-2*(borderSize-1), frameColor); // right + } + } + + // Draw a frame around the wrapped widget. + p.setPen( g.background() ); + if (maxFull) { + p.drawLine(x+1, y+titleHeight+(borderSize-1), w-2, y+titleHeight+(borderSize-1)); + } else { + p.drawRect( x+(borderSize-1), y+titleHeight+(borderSize-1), w-2*(borderSize-1), h-titleHeight-2*(borderSize-1) ); + } + + // Drawing this extra line removes non-drawn areas when shaded + p.drawLine( x+borderSize, y2-borderSize, x2-borderSize, y2-borderSize); + + // Highlight top corner + p.setPen( g.light().light(160) ); + p.drawPoint( x, y ); + p.setPen( g.light().light(140) ); + p.drawPoint( x+1, y ); + p.drawPoint( x, y+1 ); + + // Draw the title bar. + // =================== + int r_x, r_y, r_x2, r_y2; + widget()->rect().coords(&r_x, &r_y, &r_x2, &r_y2); + const int titleEdgeLeft = layoutMetric(LM_TitleEdgeLeft); + const int titleEdgeTop = layoutMetric(LM_TitleEdgeTop); + const int titleEdgeRight = layoutMetric(LM_TitleEdgeRight); + const int titleEdgeBottom = layoutMetric(LM_TitleEdgeBottom); + const int ttlHeight = layoutMetric(LM_TitleHeight); + const int titleEdgeBottomBottom = r_y+titleEdgeTop+ttlHeight+titleEdgeBottom-1; + r = QRect(r_x+titleEdgeLeft+buttonsLeftWidth(), r_y+titleEdgeTop, + r_x2-titleEdgeRight-buttonsRightWidth()-(r_x+titleEdgeLeft+buttonsLeftWidth()), + titleEdgeBottomBottom-(r_y+titleEdgeTop) ); + + // Obtain titlebar blend colours + QColor c1 = options()->color(ColorTitleBar, isActive() ).light(130); + QColor c2 = options()->color(ColorTitleBlend, isActive() ); + + // Create a disposable pixmap buffer for the titlebar + KPixmap* titleBuffer = new KPixmap; + titleBuffer->resize( maxFull?w-2:(w-2*(borderSize-1)), titleHeight ); + + QPainter p2( titleBuffer, this ); + + // subtract titleBlocks pixmap width and some + int rightoffset = r.x()+r.width()-titleBlocks->width()-borderSize; + + p2.fillRect( 0, 0, w, r.height(), c1 ); + p2.fillRect( rightoffset, 0, maxFull?w-rightoffset:w-rightoffset-2*(borderSize-1), r.height(), c2 ); + + // 8 bit displays will be a bit dithered, but they still look ok. + if ( isActive() ) + p2.drawPixmap( rightoffset, 0, *titleBlocks ); + else + p2.drawPixmap( rightoffset, 0, *ititleBlocks ); + + // Draw the title text on the pixmap, and with a smaller font + // for toolwindows than the default. + QFont fnt; + if ( largeButtons ) { + fnt = options()->font(true, false); // font not small + } else { + fnt = options()->font(true, true); // font small + fnt.setWeight( QFont::Normal ); // and disable bold + } + p2.setFont( fnt ); + + p2.setPen( options()->color(ColorFont, isActive() )); + p2.drawText(r.x()+4-borderSize, 0, r.width()-3, r.height(), + AlignLeft | AlignVCenter, caption() ); + p2.end(); + + p.drawPixmap( maxFull?1:borderSize-1, borderSize-1, *titleBuffer ); + + delete titleBuffer; +} + +} + +// Extended KWin plugin interface +///////////////////////////////// +extern "C" +{ + KDE_EXPORT KDecorationFactory *create_factory() + { + Quartz::clientHandler = new Quartz::QuartzHandler(); + return Quartz::clientHandler; + } +} + + + +#include "quartz.moc" +// vim: ts=4 +// kate: space-indent off; tab-width 4; diff --git a/kwin/clients/quartz/quartz.desktop b/kwin/clients/quartz/quartz.desktop new file mode 100644 index 000000000..30a177973 --- /dev/null +++ b/kwin/clients/quartz/quartz.desktop @@ -0,0 +1,39 @@ +[Desktop Entry] +Name=Quartz +Name[az]=Kvarts +Name[be]=Кварц +Name[bn]=কোয়ার্ট্জ +Name[csb]=Kwarc +Name[cy]=Cwarts +Name[eo]=Kvarco +Name[es]=Cuarzo +Name[eu]=Kuartzoa +Name[fa]=کوارتز +Name[ga]=Grianchloch +Name[hi]=क्वार्ट्ज +Name[hr]=Kvarc +Name[ka]=კვარცი +Name[kk]=Кварц +Name[ko]=수정 +Name[lo]=ແບບຄວອທ +Name[lv]=Kvarcs +Name[mk]=Кварц +Name[mt]=Kwartz +Name[ne]=क्वार्ज +Name[pl]=Kwarc +Name[pt_BR]=Quartzo +Name[ro]=Cuarț +Name[ru]=Кварц +Name[rw]=Ibuye +Name[ta]=குவார்ட்ஸ் +Name[te]=క్వార్జ్ +Name[tg]=Квартс +Name[th]=แบบควอทซ์ +Name[tr]=Kuartz +Name[uk]=Кварц +Name[uz]=Chaqmoqtosh +Name[uz@cyrillic]=Чақмоқтош +Name[ven]=Musuku +Name[vi]=Thạch anh +Name[zh_TW]=石英 +X-KDE-Library=kwin3_quartz diff --git a/kwin/clients/quartz/quartz.h b/kwin/clients/quartz/quartz.h new file mode 100644 index 000000000..37f3dff6e --- /dev/null +++ b/kwin/clients/quartz/quartz.h @@ -0,0 +1,95 @@ +/* + * Gallium-Quartz KWin client + * + * Copyright (C) 2005 Sandro Giessl <sandro@giessl.com> + * Copyright 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + * + * Based on the KDE default client. + * + * Includes mini titlebars for ToolWindow Support. + * Button positions are now customizable. + * + */ + +#ifndef __KDEGALLIUM_QUARTZ_H +#define __KDEGALLIUM_QUARTZ_H + +#include <qbitmap.h> +#include <kpixmap.h> +#include "../../lib/kcommondecoration.h" +#include "../../lib/kdecorationfactory.h" + +class QSpacerItem; +class QBoxLayout; + +namespace Quartz { + +class QuartzClient; + +class QuartzHandler: public QObject, public KDecorationFactory +{ + Q_OBJECT + public: + QuartzHandler(); + ~QuartzHandler(); + + virtual KDecoration* createDecoration( KDecorationBridge* ); + virtual bool reset(unsigned long changed); + virtual bool supports( Ability ability ); + virtual QValueList< BorderSize > borderSizes() const; + + private: + void readConfig(); + void createPixmaps(); + void freePixmaps(); + void drawBlocks(KPixmap* pi, KPixmap &p, const QColor &c1, const QColor &c2); +}; + + +class QuartzButton : public KCommonDecorationButton +{ + public: + QuartzButton(ButtonType type, QuartzClient *parent, const char *name); + ~QuartzButton(); + void setBitmap(const unsigned char *bitmap); + + void reset(unsigned long changed); + + protected: + void drawButton(QPainter *p); + + QBitmap* deco; +}; + + +class QuartzClient : public KCommonDecoration +{ + public: + QuartzClient(KDecorationBridge* bridge, KDecorationFactory* factory); + ~QuartzClient() {;} + + virtual QString visibleName() const; + virtual QString defaultButtonsLeft() const; + virtual QString defaultButtonsRight() const; + virtual bool decorationBehaviour(DecorationBehaviour behaviour) const; + virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton * = 0) const; + virtual KCommonDecorationButton *createButton(ButtonType type); + + virtual void init(); + + protected: + virtual void reset( unsigned long changed ); + void paintEvent( QPaintEvent* ); + + private: + int titleHeight, borderSize; + bool largeButtons; +}; + +} + +#endif +// vim: ts=4 +// kate: space-indent off; tab-width 4; diff --git a/kwin/clients/redmond/Makefile.am b/kwin/clients/redmond/Makefile.am new file mode 100644 index 000000000..85d0236d2 --- /dev/null +++ b/kwin/clients/redmond/Makefile.am @@ -0,0 +1,17 @@ + +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +kde_module_LTLIBRARIES = kwin3_redmond.la + +kwin3_redmond_la_SOURCES = redmond.cpp +kwin3_redmond_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_redmond_la_LIBADD = $(LIB_KDECORE) ../../lib/libkdecorations.la + +METASOURCES = AUTO +noinst_HEADERS = redmond.h + +lnkdir = $(kde_datadir)/kwin +lnk_DATA = redmond.desktop + +EXTRA_DIST = $(lnk_DATA) + diff --git a/kwin/clients/redmond/redmond.cpp b/kwin/clients/redmond/redmond.cpp new file mode 100644 index 000000000..e41eb6353 --- /dev/null +++ b/kwin/clients/redmond/redmond.cpp @@ -0,0 +1,689 @@ +/* + * + * Redmond KWin client + * + * Copyright 2001 + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + * + * Based on the default KWin client. + * + * Updated to support toolwindows 3/2001 (KS) + * + */ + +#include "redmond.h" + +#include <qdrawutil.h> +#include <qdatetime.h> +#include <kpixmapeffect.h> +#include <kimageeffect.h> +#include <kdrawutil.h> +#include <klocale.h> + +#include <qbitmap.h> +#include <qimage.h> +#include <qapplication.h> + +namespace Redmond { + +static const char *kdelogo[] = { +/* columns rows colors chars-per-pixel */ +"16 16 8 1", +" c None", +". c #000000", +"+ c #A0A0A4", +"@ c #FFFFFF", +"# c #585858", +"$ c #C0C0C0", +"% c #808080", +"& c #DCDCDC", +" ", +" .. .. ", +" .+@. .@#. ", +" .@@@. .@@@# ", +" .@@@..$@@$. ", +" .@@@.@@@$. ", +" .@@@%@@$. ", +" .@@@&@@. ", +" .@@@@@@. ", +" .@@@$@@&. ", +" .@@@.@@@. ", +" .@@@.+@@@. ", +" .@@@..$@@&. ", +" .@@%. .@@@. ", +" .... ... ", +" "}; + +static unsigned char iconify_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00}; + +static unsigned char close_bits[] = { + 0x00, 0x00, 0x86, 0x01, 0xcc, 0x00, 0x78, 0x00, 0x30, 0x00, 0x78, 0x00, + 0xcc, 0x00, 0x86, 0x01, 0x00, 0x00, 0x00, 0x00}; + +static unsigned char maximize_bits[] = { + 0xff, 0x01, 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0xff, 0x01, 0x00, 0x00}; + +static unsigned char minmax_bits[] = { + 0xfc, 0x00, 0xfc, 0x00, 0x84, 0x00, 0xbf, 0x00, 0xbf, 0x00, 0xe1, 0x00, + 0x21, 0x00, 0x21, 0x00, 0x3f, 0x00, 0x00, 0x00}; + +static unsigned char question_bits[] = { + 0x00, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00}; + + +// Up / Down titlebar button images +static KPixmap *btnPix1; +static KPixmap *iBtnPix1; +static KPixmap *btnDownPix1; +static KPixmap *iBtnDownPix1; + +static KPixmap *miniBtnPix1; +static KPixmap *iMiniBtnPix1; +static KPixmap *miniBtnDownPix1; +static KPixmap *iMiniBtnDownPix1; + +static QPixmap *defaultMenuPix; +static QColor *btnForeground; +static bool pixmaps_created = false; + +static int toolTitleHeight; +static int normalTitleHeight; +static int borderWidth; + +static inline const KDecorationOptions *options() +{ + return KDecoration::options(); +} + +static void drawButtonFrame( KPixmap *pix, const QColorGroup &g, bool sunken ) +{ + QPainter p; + int x2 = pix->width() - 1; + int y2 = pix->height() - 1; + p.begin(pix); + + // titlebar button frame + p.setPen( sunken ? g.dark().dark(155) : g.light()); + p.drawLine(0, 0, x2-1, 0); + p.drawLine(0, 0, 0, y2-1); + + if (sunken) + { + p.setPen( g.mid().dark(135) ); + p.drawLine(1, 1, x2-2, 1); + p.drawLine(1, 1, 1, y2-2); + } + + p.setPen( sunken ? g.light() : g.mid().dark(135)); + p.drawLine(1, y2-1, x2-1, y2-1); + p.drawLine(x2-1, 1, x2-1, y2-1); + + p.setPen( sunken ? g.light() : g.dark().dark(155)); + p.drawLine(0, y2, x2, y2); + p.drawLine(x2, 0, x2, y2); +} + + +static void create_pixmaps () +{ + if (pixmaps_created) + return; + + pixmaps_created = true; + + bool highcolor = QPixmap::defaultDepth() > 8; + + btnPix1 = new KPixmap; + btnDownPix1 = new KPixmap; + iBtnPix1 = new KPixmap; + iBtnDownPix1 = new KPixmap; + miniBtnPix1 = new KPixmap; + miniBtnDownPix1 = new KPixmap; + iMiniBtnPix1 = new KPixmap; + iMiniBtnDownPix1 = new KPixmap; + defaultMenuPix = new QPixmap(kdelogo); + + // buttons (active/inactive, sunken/unsunken) + QColorGroup g = options()->colorGroup(KDecoration::ColorButtonBg, true); + QColor c = g.background(); + btnPix1->resize(normalTitleHeight, normalTitleHeight-2); + btnDownPix1->resize(normalTitleHeight, normalTitleHeight-2); + iBtnPix1->resize(normalTitleHeight, normalTitleHeight-2); + iBtnDownPix1->resize(normalTitleHeight, normalTitleHeight-2); + + miniBtnPix1->resize(toolTitleHeight, toolTitleHeight); + miniBtnDownPix1->resize(toolTitleHeight, toolTitleHeight); + iMiniBtnPix1->resize(toolTitleHeight, toolTitleHeight); + iMiniBtnDownPix1->resize(toolTitleHeight, toolTitleHeight); + + if (highcolor && false) { + KPixmapEffect::gradient(*btnPix1, c.light(130), c.dark(130), + KPixmapEffect::VerticalGradient); + KPixmapEffect::gradient(*btnDownPix1, c.dark(130), c.light(130), + KPixmapEffect::VerticalGradient); + + KPixmapEffect::gradient(*miniBtnPix1, c.light(130), c.dark(130), + KPixmapEffect::VerticalGradient); + KPixmapEffect::gradient(*miniBtnDownPix1, c.dark(130), c.light(130), + KPixmapEffect::VerticalGradient); + + g = options()->colorGroup(KDecoration::ColorButtonBg, false); + c = g.background(); + KPixmapEffect::gradient(*iBtnPix1, c.light(130), c.dark(130), + KPixmapEffect::VerticalGradient); + KPixmapEffect::gradient(*iBtnDownPix1, c.dark(130), c.light(130), + KPixmapEffect::VerticalGradient); + KPixmapEffect::gradient(*iMiniBtnPix1, c.light(130), c.dark(130), + KPixmapEffect::VerticalGradient); + KPixmapEffect::gradient(*iMiniBtnDownPix1, c.dark(130), c.light(130), + KPixmapEffect::VerticalGradient); + } else { + btnPix1->fill(c.rgb()); + btnDownPix1->fill(c.rgb()); + miniBtnPix1->fill(c.rgb()); + miniBtnDownPix1->fill(c.rgb()); + + g = options()->colorGroup(KDecoration::ColorButtonBg, false); + c = g.background(); + iBtnPix1->fill(c.rgb()); + iBtnDownPix1->fill(c.rgb()); + iMiniBtnPix1->fill(c.rgb()); + iMiniBtnDownPix1->fill(c.rgb()); + } + + g = options()->colorGroup(KDecoration::ColorButtonBg, true); + drawButtonFrame(btnPix1, g, false); + drawButtonFrame(btnDownPix1, g, true); + drawButtonFrame(miniBtnPix1, g, false); + drawButtonFrame(miniBtnDownPix1, g, true); + + g = options()->colorGroup(KDecoration::ColorButtonBg, false); + drawButtonFrame(iBtnPix1, g, false); + drawButtonFrame(iBtnDownPix1, g, true); + drawButtonFrame(iMiniBtnPix1, g, false); + drawButtonFrame(iMiniBtnDownPix1, g, true); + + // Make sure button pixmaps contrast with the current colour scheme. + if (qGray(options()->color(KDecoration::ColorButtonBg, true).rgb()) > 127) + btnForeground = new QColor(Qt::black); + else + btnForeground = new QColor(Qt::white); +} + +void delete_pixmaps() +{ + delete btnPix1; + delete btnDownPix1; + delete iBtnPix1; + delete iBtnDownPix1; + delete miniBtnPix1; + delete miniBtnDownPix1; + delete iMiniBtnPix1; + delete iMiniBtnDownPix1; + delete defaultMenuPix; + delete btnForeground; + pixmaps_created = false; +} + +RedmondButton::RedmondButton(ButtonType type, RedmondDeco *parent, const char *name) + : KCommonDecorationButton(type, parent, name) +{ + // Eliminate background flicker + setBackgroundMode( NoBackground ); + + miniBtn = decoration()->isToolWindow(); +} + +void RedmondButton::reset(unsigned long changed) +{ + if (changed&DecorationReset || changed&ManualReset || changed&SizeChange || changed&StateChange) { + switch (type() ) { + case CloseButton: + setBitmap(close_bits); + break; + case HelpButton: + setBitmap(question_bits); + break; + case MinButton: + setBitmap(iconify_bits); + break; + case MaxButton: + setBitmap( isOn() ? minmax_bits : maximize_bits ); + break; + case MenuButton: + { + QPixmap miniIcon = decoration()->icon().pixmap(QIconSet::Small, QIconSet::Normal); + if (!miniIcon.isNull()) { + setPixmap(miniIcon); + } else { + setPixmap(*defaultMenuPix); + } + break; + } + default: + setBitmap(0); + break; + } + + this->update(); + } +} + + +void RedmondButton::setBitmap(const unsigned char *bitmap) +{ + pix.resize(0, 0); + + if (bitmap) + deco = QBitmap(10, 10, bitmap, true); + else { + deco = QBitmap(10,10); + deco.fill(Qt::color0); + } + deco.setMask(deco); +} + + +void RedmondButton::setPixmap( const QPixmap &p ) +{ + deco.resize(0, 0); + pix = p; + + repaint(false); +} + + +void RedmondButton::drawButton(QPainter *p) +{ + if ( pix.isNull() ) { + if ( decoration()->isActive() ) { + if ( isDown() ) + p->drawPixmap(0, 0, miniBtn ? *miniBtnDownPix1 : *btnDownPix1); + else + p->drawPixmap(0, 0, miniBtn ? *miniBtnPix1 : *btnPix1); + } else { + if ( isDown() ) + p->drawPixmap(0, 0, miniBtn ? *iMiniBtnDownPix1 : *iBtnDownPix1); + else + p->drawPixmap(0, 0, miniBtn ? *iMiniBtnPix1 : *iBtnPix1); + } + + p->setPen( *btnForeground ); + int xOff = (width()-10)/2; + int yOff = (height()-10)/2; + p->drawPixmap(isDown() ? xOff+1: xOff, isDown() ? yOff+1 : yOff, deco); + } else { + if (isLeft() ) { + p->fillRect(0, 0, width(), height(), + options()->color(KDecoration::ColorTitleBar, decoration()->isActive())); + } else { + p->fillRect(0, 0, width(), height(), + options()->color(KDecoration::ColorTitleBlend, decoration()->isActive())); + } + + if ( type()==MenuButton && height() < 16) { + QPixmap tmpPix; + + // Smooth scale the menu button pixmap + tmpPix.convertFromImage( + pix.convertToImage().smoothScale(height(), height() )); + + p->drawPixmap( 0, 0, tmpPix ); + } else { + int xOff = (width() -pix.width() )/2; + int yOff = (height()-pix.height())/2; + p->drawPixmap(xOff, yOff, pix ); + } + } +} + + +RedmondDeco::RedmondDeco(KDecorationBridge *b, KDecorationFactory *f) + : KCommonDecoration(b, f) +{ +} + +QString RedmondDeco::visibleName() const +{ + return i18n("Redmond"); +} + +QString RedmondDeco::defaultButtonsLeft() const +{ + return "M"; +} + +QString RedmondDeco::defaultButtonsRight() const +{ + return "HIA_X"; +} + +bool RedmondDeco::decorationBehaviour(DecorationBehaviour behaviour) const +{ + switch (behaviour) { + case DB_MenuClose: + return true; + + case DB_WindowMask: + return false; + + case DB_ButtonHide: + return true; + + default: + return KCommonDecoration::decorationBehaviour(behaviour); + } +} + +int RedmondDeco::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const +{ + bool border = !(maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows()); + + switch (lm) { + case LM_BorderLeft: + case LM_BorderRight: + case LM_BorderBottom: + return border ? borderWidth : 0; + + case LM_TitleEdgeLeft: + case LM_TitleEdgeRight: + return border ? borderWidth+2 : 2; + + case LM_TitleEdgeTop: + return border ? borderWidth+2 : 2; + + case LM_TitleEdgeBottom: + return border ? 1 : 0; + + case LM_TitleBorderLeft: + case LM_TitleBorderRight: + return border ? 1 : 0; + + case LM_TitleHeight: + return titleHeight-2; + + case LM_ButtonWidth: + return titleHeight-2; + case LM_ButtonHeight: + if (isToolWindow() || (btn && btn->type()==MenuButton) ) { + return titleHeight-2; + } else { + return titleHeight-2-2; + } + + case LM_ButtonSpacing: + return 0; + + case LM_ExplicitButtonSpacer: + return 2; + + default: + return KCommonDecoration::layoutMetric(lm, respectWindowState, btn); + } +} + +KCommonDecorationButton *RedmondDeco::createButton(ButtonType type) +{ + switch (type) { + case MenuButton: + return new RedmondButton(MenuButton, this, "menu"); + case HelpButton: + return new RedmondButton(HelpButton, this, "help"); + case MinButton: + return new RedmondButton(MinButton, this, "minimize"); + case MaxButton: + return new RedmondButton(MaxButton, this, "maximize"); + case CloseButton: + return new RedmondButton(CloseButton, this, "close"); + + default: + return 0; + } +} + +void RedmondDeco::init() +{ +// Finally, toolwindows look small + if ( isToolWindow() ) { + titleHeight = toolTitleHeight+2; + } else { + titleHeight = normalTitleHeight+2; + } + + KCommonDecoration::init(); +} + +void RedmondDeco::reset( unsigned long changed ) +{ + KCommonDecoration::reset(changed); +} + +void RedmondDeco::paintEvent( QPaintEvent* ) +{ + bool hicolor = QPixmap::defaultDepth() > 8; + int fontoffset = 1; + + // Modify borderWith used by titlebar to 0, when maximized and not move or resize able + bool border = !(maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows()); + int modBorderWidth = border ? borderWidth : 0; + + QPainter p(widget()); + + // Obtain widget bounds. + QRect r(widget()->rect()); + int x = r.x(); + int y = r.y(); + int x2 = r.width()-1; + int y2 = r.height()-1; + int w = r.width(); + int h = r.height(); + + // Draw part of the frame that is the frame color + // ============================================== + QColorGroup g = options()->colorGroup(KDecoration::ColorFrame, isActive()); + p.setPen( g.background() ); + p.drawLine( x, y, x2-1, y ); + p.drawLine( x, y, x, y2-1 ); + + // Draw line under title bar + p.drawLine( x+borderWidth, y+titleHeight+borderWidth, x2-borderWidth, y+titleHeight+borderWidth ); + // Draw a hidden line that appears during shading + p.drawLine( x+borderWidth, y2-borderWidth, x2-borderWidth, y2-borderWidth ); + + // Fill out the border edges + for (int i = 1; i < borderWidth; i++) + p.drawRect( x+i, y+i, w-2*i, h-2*i ); + + // Draw highlights and lowlights + p.setPen(g.light()); + for (int i = 1; i <= borderWidth/3; i++) { + p.drawLine( x+i, y+i, x2-i-1, y+i); + p.drawLine( x+i, y+i, x+i, y2-i-1); + } + + p.setPen(g.mid().dark(135)); + for (int i = 1; i <= borderWidth/3; i++) { + p.drawLine( x2-i, y+i+1, x2-i, y2-i); + p.drawLine( x+i+1, y2-i, x2-i, y2-i); + } + + // Draw black edges + p.setPen( g.dark().dark(155) ); + p.drawLine(x2, y, x2, y2); + p.drawLine(x, y2, x2, y2); + + // Draw the title bar. + // =================== + r = titleRect(); +// QFontMetrics fm(options()->font(true)); + + // Obtain blend colours. + QColor c1 = options()->color(KDecoration::ColorTitleBar, isActive() ); + QColor c2 = options()->color(KDecoration::ColorTitleBlend, isActive() ); + + QFont fnt = options()->font(true, isToolWindow() ); + if (isToolWindow() ) { + fnt.setWeight( QFont::Normal ); + fontoffset = 0; + } + + // Paint without a buffer if the colours are the same to + // improve performance, and only draw gradients on hicolor displays. + if ((c1 != c2) && hicolor) { + // KS - Add gradient caching if needed at a later stage. + + // Create a disposable pixmap buffer for the title blend + KPixmap* titleBuffer = new KPixmap; + titleBuffer->resize(w-2*modBorderWidth, titleHeight); + + if (titleBuffer->depth() > 16) { + KPixmapEffect::gradient(*titleBuffer, c1, c2, + KPixmapEffect::HorizontalGradient); + } else { + // This enables dithering on 15 and 16bit displays, preventing + // some pretty horrible banding effects + QImage image = KImageEffect::gradient(titleBuffer->size(), c1, c2, + KImageEffect::HorizontalGradient); + + titleBuffer->convertFromImage(image, Qt::OrderedDither); + } + + QPainter p2( titleBuffer, this ); + + // Since drawing the gradient is (relatively) slow, it is best + // to draw the title text on the pixmap. + + p2.setFont( fnt ); + p2.setPen( options()->color(KDecoration::ColorFont, isActive() )); + p2.drawText( r.x(), fontoffset, r.width()-3, r.height()-1, + AlignLeft | AlignVCenter, caption() ); + p2.end(); + + p.drawPixmap( modBorderWidth, modBorderWidth, *titleBuffer ); + + delete titleBuffer; + + } else { + // Assume lower ended hardware, so don't use buffers. + // Don't draw a gradient either. + p.fillRect( modBorderWidth, modBorderWidth, w-2*modBorderWidth, titleHeight, c1 ); + + // Draw the title text. + p.setFont( fnt ); + p.setPen(options()->color(KDecoration::ColorFont, isActive() )); + p.drawText(r.x()+4, r.y()+fontoffset-2, r.width()-3, r.height()-1, + AlignLeft | AlignVCenter, caption() ); + } + +} + +void RedmondDecoFactory::readConfig() { + normalTitleHeight = QFontMetrics(options()->font(true)).height(); + QFont toolFont = options()->font(true, true); + toolFont.setWeight(QFont::Normal); + toolTitleHeight = QFontMetrics(toolFont).height(); + switch(options()->preferredBorderSize(this)) { + case BorderLarge: + borderWidth = 8; + if (normalTitleHeight < 20) normalTitleHeight = 20; + if (toolTitleHeight < 20) toolTitleHeight = 20; + break; + case BorderVeryLarge: + borderWidth = 12; + if (normalTitleHeight < 24) normalTitleHeight = 24; + if (toolTitleHeight < 24) toolTitleHeight = 24; + break; + case BorderHuge: + borderWidth = 18; + if (normalTitleHeight < 28) normalTitleHeight = 28; + if (toolTitleHeight < 28) toolTitleHeight = 28; + break; + case BorderVeryHuge: + borderWidth = 27; + if (normalTitleHeight < 33) normalTitleHeight = 33; + if (toolTitleHeight < 33) toolTitleHeight = 33; + break; + case BorderOversized: + borderWidth = 40; + if (normalTitleHeight < 40) normalTitleHeight = 40; + if (toolTitleHeight < 40) toolTitleHeight = 40; + break; + case BorderTiny: + case BorderNormal: + default: + borderWidth = 4; + if (normalTitleHeight < 16) normalTitleHeight = 16; + if (toolTitleHeight < 16) toolTitleHeight = 16; + } +} + +RedmondDecoFactory::RedmondDecoFactory() +{ + readConfig(); + create_pixmaps(); +} + +RedmondDecoFactory::~RedmondDecoFactory() +{ + Redmond::delete_pixmaps(); +} + +KDecoration *RedmondDecoFactory::createDecoration( KDecorationBridge *b ) +{ + return new RedmondDeco(b, this); +} + +bool RedmondDecoFactory::reset( unsigned long changed ) +{ + // SettingButtons is handled by KCommonDecoration + if ( changed & ( SettingFont | SettingBorder | SettingColors | SettingButtons ) ) { + delete_pixmaps(); + readConfig(); + create_pixmaps(); + resetDecorations(changed); + return true; + } else { + resetDecorations(changed); + return false; + } +} + +bool RedmondDecoFactory::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonMenu: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + case AbilityButtonSpacer: + return true; + default: + return false; + } +} + +QValueList< RedmondDecoFactory::BorderSize > RedmondDecoFactory::borderSizes() const +{ // the list must be sorted + return QValueList< BorderSize >() << BorderNormal << BorderLarge << + BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized; +} + +} + +extern "C" KDE_EXPORT KDecorationFactory *create_factory() +{ + return new Redmond::RedmondDecoFactory(); +} + + +#include "redmond.moc" +// vim: ts=4 +// kate: space-indent off; tab-width 4; diff --git a/kwin/clients/redmond/redmond.desktop b/kwin/clients/redmond/redmond.desktop new file mode 100644 index 000000000..ae7628876 --- /dev/null +++ b/kwin/clients/redmond/redmond.desktop @@ -0,0 +1,19 @@ +[Desktop Entry] +Name=Redmond +Name[bn]=রেডমন্ড +Name[eo]=Redmondo +Name[fa]=ردموند +Name[hi]=रेडमण्ड +Name[ka]=რედმონდი +Name[ko]=레드몬드 +Name[lo]=ເລດມອນ +Name[lv]=Redmonda +Name[mk]=Редмонд +Name[ne]=रेडमोन्ड +Name[pa]=ਰੀਡਮੋਂਡ +Name[ta]=ரெட்மான்ட் +Name[te]=రెడ్ మండ్ +Name[tg]=Райдмонд +Name[th]=แบบเรดมอนด์ +Name[uz@cyrillic]=Редмонд +X-KDE-Library=kwin3_redmond diff --git a/kwin/clients/redmond/redmond.h b/kwin/clients/redmond/redmond.h new file mode 100644 index 000000000..340fdd1b0 --- /dev/null +++ b/kwin/clients/redmond/redmond.h @@ -0,0 +1,91 @@ +/* + * + * Redmond KWin client + * + * Copyright 2001-2003 + * Ported to kwin_iii by Chris Lee <clee@kde.org> + * Karol Szwed <gallium@kde.org> + * http://gallium.n3.net/ + * + * Based on the default KWin client. + * + * Updated to support the new API 9/2003 (CL) + * Updated to emulate More Accurately 9/2003 (CL) + * Updated to support toolwindows 3/2001 (KS) + * + */ + +#ifndef __KDE_REDMOND_H +#define __KDE_REDMOND_H + +#include <qbitmap.h> +#include <kpixmap.h> +#include <kcommondecoration.h> +#include <kdecorationfactory.h> + +namespace Redmond { + +class RedmondDeco; + +class RedmondButton : public KCommonDecorationButton +{ + Q_OBJECT +public: + RedmondButton(ButtonType type, RedmondDeco *parent, const char *name); + void setBitmap(const unsigned char *bitmap); + void setPixmap(const QPixmap &p); + void reset(unsigned long changed); + +protected: + virtual void drawButton(QPainter *p); + void drawButtonLabel(QPainter *){;} + + QBitmap deco; + QPixmap pix; + bool miniBtn; +}; + + +class RedmondDeco : public KCommonDecoration +{ +public: + RedmondDeco(KDecorationBridge *, KDecorationFactory *); + ~RedmondDeco() {;} + + virtual QString visibleName() const; + virtual QString defaultButtonsLeft() const; + virtual QString defaultButtonsRight() const; + virtual bool decorationBehaviour(DecorationBehaviour behaviour) const; + virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton * = 0) const; + virtual KCommonDecorationButton *createButton(ButtonType type); + + void init(); + +protected: + virtual void reset( unsigned long changed ); + + void paintEvent(QPaintEvent*); + +private: + int titleHeight; +}; + +class RedmondDecoFactory : public QObject, public KDecorationFactory +{ + Q_OBJECT +public: + RedmondDecoFactory(); + virtual ~RedmondDecoFactory(); + virtual KDecoration *createDecoration(KDecorationBridge *); + virtual bool reset(unsigned long); + virtual bool supports( Ability ability ); + virtual QValueList< BorderSize > borderSizes() const; +private: + void readConfig(); +}; + +} + +#endif +// vim: ts=4 +// kate: space-indent off; tab-width 4; diff --git a/kwin/clients/test/Makefile.am b/kwin/clients/test/Makefile.am new file mode 100644 index 000000000..a5a3fcf67 --- /dev/null +++ b/kwin/clients/test/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES = -I$(srcdir)/../../lib $(all_includes) + +kde_module_LTLIBRARIES = kwin3_test.la + +kwin3_test_la_SOURCES = test.cpp +kwin3_test_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_test_la_LIBADD = $(LIB_KDEUI) ../../lib/libkdecorations.la +#kwin_keramik_la_LDFLAGS = $(all_libraries) -avoid-version -module $(KDE_RPATH) $(KDE_MT_LDFLAGS) + +METASOURCES = AUTO + +kwin_test_lnkdir = $(kde_datadir)/kwin +kwin_test_lnk_DATA = test.desktop + +EXTRA_DIST = $(kwin_test_lnk_DATA) diff --git a/kwin/clients/test/test.cpp b/kwin/clients/test/test.cpp new file mode 100644 index 000000000..abac77e64 --- /dev/null +++ b/kwin/clients/test/test.cpp @@ -0,0 +1,343 @@ +#include "test.h" + +#include <qtooltip.h> +#include <kglobal.h> +#include <kdebug.h> + +namespace KWinTest +{ + +Decoration::Decoration( KDecorationBridge* bridge, KDecorationFactory* factory ) + : KDecoration( bridge, factory ), + button( NULL ) + { + } + +void Decoration::init() + { + createMainWidget(); + widget()->setEraseColor( red ); + widget()->installEventFilter( this ); + if( isCloseable()) + { + button = new QPushButton( widget()); + button->show(); + button->setCursor( arrowCursor ); + button->move( 0, 0 ); + connect( button, SIGNAL( clicked()), SLOT( closeWindow())); + QToolTip::add( button, "Zelva Mana" ); + } + } + +Decoration::MousePosition Decoration::mousePosition( const QPoint& p ) const + { + const int range = 16; + const int border = 4; + + MousePosition m = Nowhere; + + int width = widget()->width(); + int height = widget()->height(); + if ( ( p.x() > border && p.x() < width - border ) + && ( p.y() > border && p.y() < height - border ) ) + return Center; + + if ( p.y() <= range && p.x() <= range) + m = TopLeft2; + else if ( p.y() >= height-range && p.x() >= width-range) + m = BottomRight2; + else if ( p.y() >= height-range && p.x() <= range) + m = BottomLeft2; + else if ( p.y() <= range && p.x() >= width-range) + m = TopRight2; + else if ( p.y() <= border ) + m = Top; + else if ( p.y() >= height-border ) + m = Bottom; + else if ( p.x() <= border ) + m = Left; + else if ( p.x() >= width-border ) + m = Right; + else + m = Center; + return m; + } + +void Decoration::borders( int& left, int& right, int& top, int& bottom ) const + { + if( options()->preferredBorderSize( factory()) == BorderTiny ) + { + left = right = bottom = 1; + top = 5; + } + else + { + left = right = options()->preferredBorderSize( factory()) * 5; + top = options()->preferredBorderSize( factory()) * 10; + bottom = options()->preferredBorderSize( factory()) * 2; + } + if( isShade()) + bottom = 0; + if( ( maximizeMode() & MaximizeHorizontal ) && !options()->moveResizeMaximizedWindows()) + left = right = 0; + if( ( maximizeMode() & MaximizeVertical ) && !options()->moveResizeMaximizedWindows()) + bottom = 0; + } + +void Decoration::reset( unsigned long ) + { + } + +void Decoration::resize( const QSize& s ) + { + widget()->resize( s ); + } + +QSize Decoration::minimumSize() const + { + return QSize( 100, 50 ); + } + +bool Decoration::eventFilter( QObject* o, QEvent* e ) + { + if( o == widget()) + { + switch( e->type()) + { + case QEvent::MouseButtonPress: + { // FRAME + processMousePressEvent( static_cast< QMouseEvent* >( e )); + return true; + } + case QEvent::Show: + break; + case QEvent::Hide: + break; + default: + break; + } + } + return false; + } + +} +#include <qapplication.h> +#include <qpainter.h> +#include <X11/Xlib.h> +#include <math.h> +#include <unistd.h> +namespace KWinTest +{ + +// taken from riscos +bool Decoration::animateMinimize(bool iconify) +{ + int style = 1; + switch (style) { + + case 1: + { + // Double twisting double back, with pike ;) + + if (!iconify) // No animation for restore. + return true; + + // Go away quick. + helperShowHide( false ); + qApp->syncX(); + + QRect r = iconGeometry(); + + if (!r.isValid()) + return true; + + // Algorithm taken from Window Maker (http://www.windowmaker.org) + + int sx = geometry().x(); + int sy = geometry().y(); + int sw = width(); + int sh = height(); + int dx = r.x(); + int dy = r.y(); + int dw = r.width(); + int dh = r.height(); + + double steps = 12; + + double xstep = double((dx-sx)/steps); + double ystep = double((dy-sy)/steps); + double wstep = double((dw-sw)/steps); + double hstep = double((dh-sh)/steps); + + double cx = sx; + double cy = sy; + double cw = sw; + double ch = sh; + + double finalAngle = 3.14159265358979323846; + + double delta = finalAngle / steps; + + QPainter p( workspaceWidget()); + p.setRasterOp(Qt::NotROP); + + for (double angle = 0; ; angle += delta) { + + if (angle > finalAngle) + angle = finalAngle; + + double dx = (cw / 10) - ((cw / 5) * sin(angle)); + double dch = (ch / 2) * cos(angle); + double midy = cy + (ch / 2); + + QPoint p1(int(cx + dx), int(midy - dch)); + QPoint p2(int(cx + cw - dx), p1.y()); + QPoint p3(int(cx + dw + dx), int(midy + dch)); + QPoint p4(int(cx - dx), p3.y()); + + grabXServer(); + + p.drawLine(p1, p2); + p.drawLine(p2, p3); + p.drawLine(p3, p4); + p.drawLine(p4, p1); + + p.flush(); + + usleep(500); + + p.drawLine(p1, p2); + p.drawLine(p2, p3); + p.drawLine(p3, p4); + p.drawLine(p4, p1); + + ungrabXServer(); + +// FRAME qApp->processEvents(); // FRAME ??? + + cx += xstep; + cy += ystep; + cw += wstep; + ch += hstep; + + if (angle >= finalAngle) + break; + } + } + break; + + case 2: + { + // KVirc style ? Maybe. For qwertz. + + if (!iconify) // No animation for restore. + return true; + + // Go away quick. + helperShowHide( false ); + + qApp->syncX(); + + int stepCount = 12; + + QRect r(geometry()); + + int dx = r.width() / (stepCount * 2); + int dy = r.height() / (stepCount * 2); + + QPainter p( workspaceWidget()); + p.setRasterOp(Qt::NotROP); + + for (int step = 0; step < stepCount; step++) { + + r.moveBy(dx, dy); + r.setWidth(r.width() - 2 * dx); + r.setHeight(r.height() - 2 * dy); + + grabXServer(); + + p.drawRect(r); + p.flush(); + usleep(200); + p.drawRect(r); + + ungrabXServer(); + +// FRAME qApp->processEvents(); + } + } + break; + + + default: + { + QRect icongeom = iconGeometry(); + + if (!icongeom.isValid()) + return true; + + QRect wingeom = geometry(); + + QPainter p( workspaceWidget()); + + p.setRasterOp(Qt::NotROP); + +#if 0 + if (iconify) + p.setClipRegion( + QRegion( workspaceWidget()->rect()) - wingeom + ); +#endif + + grabXServer(); + + p.drawLine(wingeom.bottomRight(), icongeom.bottomRight()); + p.drawLine(wingeom.bottomLeft(), icongeom.bottomLeft()); + p.drawLine(wingeom.topLeft(), icongeom.topLeft()); + p.drawLine(wingeom.topRight(), icongeom.topRight()); + + p.flush(); + + qApp->syncX(); + + usleep(30000); + + p.drawLine(wingeom.bottomRight(), icongeom.bottomRight()); + p.drawLine(wingeom.bottomLeft(), icongeom.bottomLeft()); + p.drawLine(wingeom.topLeft(), icongeom.topLeft()); + p.drawLine(wingeom.topRight(), icongeom.topRight()); + + ungrabXServer(); + } + break; + } + return true; +} + +KDecoration* Factory::createDecoration( KDecorationBridge* bridge ) + { + NET::WindowType type = windowType( SUPPORTED_WINDOW_TYPES_MASK, bridge ); + if( type == NET::Dialog ) + ; + return new Decoration( bridge, this ); + } + +bool Factory::reset( unsigned long changed ) + { + resetDecorations( changed ); + return false; + } + +} // namespace + +extern "C" +{ + +KDE_EXPORT KDecorationFactory *create_factory() + { + return new KWinTest::Factory(); + } + +} + +#include "test.moc" diff --git a/kwin/clients/test/test.desktop b/kwin/clients/test/test.desktop new file mode 100644 index 000000000..ed340f573 --- /dev/null +++ b/kwin/clients/test/test.desktop @@ -0,0 +1,67 @@ +[Desktop Entry] +Name=KWin test +Name[af]=KWin toets +Name[ar]=اختبار KWin +Name[az]=KWin sınağı +Name[be]=Тэст KWin +Name[bn]=Kwin পরীক্ষা +Name[ca]=Test de KWin +Name[csb]=Test KWin +Name[cy]=arbrawf KWin +Name[da]=KWin-test +Name[de]=KWin-Test +Name[el]=KWin τεστ +Name[eo]=Testo de KDE-fenestroadministrilo +Name[es]=Prueba de KWin +Name[eu]=KWin froga +Name[fa]=آزمون KWin +Name[fi]=KWin-testi +Name[fr]=Test de KWin +Name[ga]=Tástáil KWim +Name[gl]=Proba de KWin +Name[he]=ניסיון KWin +Name[hi]=के-विन जांच +Name[hr]=KWina proba +Name[hu]=KWin-teszt +Name[is]=KWin prófun +Name[it]=Prova KWin +Name[ja]=KWin テスト +Name[ka]=KWin შემოწმება +Name[kk]=KWin сынауы +Name[km]=សាកល្បង KWin +Name[ko]=KWin 테스트 +Name[lt]=KWin patikrinimas +Name[lv]=KWin tests +Name[mk]=Тест за KWin +Name[mn]=KWin шалгалт +Name[ms]=Ujian KWin +Name[nds]=KWin-Test +Name[ne]=के विन परीक्षण +Name[nn]=KWin-test +Name[pa]=KWin ਜਾਂਚ +Name[pl]=Test KWin +Name[pt]=Teste do KWin +Name[pt_BR]=Teste do KWin +Name[ro]=Test KWin +Name[ru]=Проверка KWin +Name[rw]= Isuzuma rya KWin +Name[se]=KWin-geahččaleapmi +Name[sk]=Test KWin +Name[sl]=Preizkus KWin +Name[sr]=Проба KWin-а +Name[sr@Latn]=Proba KWin-a +Name[sv]=Kwin-test +Name[ta]=KWin சோதனை +Name[te]=కెవిన్ పరిక్ష +Name[tg]=Тафтиши KWin +Name[th]=ทดสอบ KWin +Name[tr]=KWin Testi +Name[tt]=KWin sınaw +Name[uk]=Тест KWin +Name[uz]=KWin sinash +Name[uz@cyrillic]=KWin синаш +Name[vi]=Thử KWin +Name[wa]=Saye KPurnea +Name[zh_CN]=KWin 测试 +Name[zh_TW]=KWin 測試 +X-KDE-Library=kwin3_test diff --git a/kwin/clients/test/test.h b/kwin/clients/test/test.h new file mode 100644 index 000000000..56e21dbc0 --- /dev/null +++ b/kwin/clients/test/test.h @@ -0,0 +1,49 @@ +#ifndef KWIN_TEST +#define KWIN_TEST + +#include <kdecoration.h> +#include <kdecorationfactory.h> +#include <qpushbutton.h> + +namespace KWinTest +{ + +const int SUPPORTED_WINDOW_TYPES_MASK = NET::NormalMask | NET::DesktopMask | NET::DockMask + | NET::ToolbarMask | NET::MenuMask | NET::DialogMask /*| NET::OverrideMask*/ | NET::TopMenuMask + | NET::UtilityMask | NET::SplashMask; + +class Decoration + : public KDecoration + { + Q_OBJECT + public: + Decoration( KDecorationBridge* bridge, KDecorationFactory* factory ); + virtual void init(); + virtual MousePosition mousePosition( const QPoint& p ) const; + virtual void borders( int& left, int& right, int& top, int& bottom ) const; + virtual void resize( const QSize& s ); + virtual QSize minimumSize() const; + virtual void activeChange() {}; + virtual void captionChange() {}; + virtual void maximizeChange() {}; + virtual void desktopChange() {}; + virtual void shadeChange() {}; + virtual void iconChange() {}; + virtual bool eventFilter( QObject* o, QEvent* e ); + virtual void reset( unsigned long changed ); + virtual bool animateMinimize( bool minimize ); + private: + QPushButton* button; + }; + +class Factory + : public KDecorationFactory + { + public: + virtual KDecoration* createDecoration( KDecorationBridge* ); + virtual bool reset( unsigned long changed ); + }; + +} // namespace + +#endif diff --git a/kwin/clients/web/Makefile.am b/kwin/clients/web/Makefile.am new file mode 100644 index 000000000..3619301f4 --- /dev/null +++ b/kwin/clients/web/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES = -I$(top_srcdir) $(all_includes) +kde_module_LTLIBRARIES = kwin3_web.la + +kwin3_web_la_SOURCES = Web.cpp WebButton.cpp + +noinst_HEADERS = Web.h WebButton.h + +kwin3_web_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module +kwin3_web_la_LIBADD = $(LIB_KDEUI) ../../lib/libkdecorations.la +METASOURCES = AUTO + +linkdir = $(kde_datadir)/kwin/ +link_DATA = web.desktop +EXTRA_DIST = $(link_DATA) + diff --git a/kwin/clients/web/Web.cpp b/kwin/clients/web/Web.cpp new file mode 100644 index 000000000..7d63b7d72 --- /dev/null +++ b/kwin/clients/web/Web.cpp @@ -0,0 +1,385 @@ +/* + 'Web' kwin client + + Copyright (C) 2005 Sandro Giessl <sandro@giessl.com> + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qpainter.h> + +#include <kconfig.h> + +#include "Web.h" +#include "WebButton.h" + +extern "C" +{ + KDE_EXPORT KDecorationFactory *create_factory() + { + return new Web::WebFactory(); + } +} + +namespace Web { + +WebClient::WebClient(KDecorationBridge* bridge, KDecorationFactory* factory) + : KCommonDecoration(bridge, factory) +{ + // Empty. +} + +WebClient::~WebClient() +{ + // Empty. +} + +QString WebClient::visibleName() const +{ + return i18n("Web"); +} + +QString WebClient::defaultButtonsLeft() const +{ + return "S"; +} + +QString WebClient::defaultButtonsRight() const +{ + return "HIAX"; +} + +bool WebClient::decorationBehaviour(DecorationBehaviour behaviour) const +{ + switch (behaviour) { + case DB_MenuClose: + return false; + + case DB_WindowMask: + return true; + + case DB_ButtonHide: + return true; + + default: + return KCommonDecoration::decorationBehaviour(behaviour); + } +} + +int WebClient::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const +{ +// bool maximized = maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows(); + + switch (lm) { + case LM_BorderLeft: + case LM_BorderRight: + case LM_BorderBottom: + return borderSize_; + + case LM_TitleEdgeLeft: + case LM_TitleEdgeRight: + case LM_TitleEdgeTop: + case LM_TitleEdgeBottom: + return 0; + + case LM_TitleBorderLeft: + case LM_TitleBorderRight: + return 0; + + case LM_TitleHeight: + case LM_ButtonWidth: + case LM_ButtonHeight: + return titleHeight_; + + case LM_ButtonSpacing: + return 0; + + case LM_ExplicitButtonSpacer: + return 0; + + default: + return KCommonDecoration::layoutMetric(lm, respectWindowState, btn); + } +} + +KCommonDecorationButton *WebClient::createButton(ButtonType type) +{ + switch (type) { + case MenuButton: + return new WebButton(MenuButton, this, "menu", shape_); + + case OnAllDesktopsButton: + return new WebButton(OnAllDesktopsButton, this, "on_all_desktops", shape_); + + case HelpButton: + return new WebButton(HelpButton, this, "help", shape_); + + case MinButton: + return new WebButton(MinButton, this, "minimize", shape_); + + case MaxButton: + return new WebButton(MaxButton, this, "maximize", shape_); + + case CloseButton: + return new WebButton(CloseButton, this, "close", shape_); + + case AboveButton: + return new WebButton(AboveButton, this, "above", shape_); + + case BelowButton: + return new WebButton(BelowButton, this, "below", shape_); + + case ShadeButton: + return new WebButton(ShadeButton, this, "shade", shape_); + + default: + return 0; + } +} + + void +WebClient::init() +{ + // title height + const int textVMargin = 2; + QFontMetrics fm(options()->font(isActive(), isToolWindow())); + + // border size + switch(options()->preferredBorderSize( factory())) { + case BorderLarge: + borderSize_ = 8; + break; + case BorderVeryLarge: + borderSize_ = 12; + break; + case BorderHuge: + borderSize_ = 18; + break; + case BorderVeryHuge: + borderSize_ = 27; + break; + case BorderOversized: + borderSize_ = 40; + break; + case BorderNormal: + default: + borderSize_ = 4; + } + titleHeight_ = QMAX(QMAX(14, fm.height() + textVMargin * 2), borderSize_); + if (0 != titleHeight_ % 2) + titleHeight_ += 1; + + KConfig c("kwinwebrc"); + c.setGroup("General"); + shape_ = c.readBoolEntry("Shape", true); + + KCommonDecoration::init(); +} + + void +WebClient::reset( unsigned long changed ) +{ + if (changed & SettingColors) + { + // repaint the whole thing + widget()->repaint(false); + } else if (changed & SettingFont) { + // font has changed -- update title height + // title height + const int textVMargin = 2; + QFontMetrics fm(options()->font(isActive(), isToolWindow())); + titleHeight_ = QMAX(QMAX(14, fm.height() + textVMargin * 2), borderSize_); + if (0 != titleHeight_ % 2) + titleHeight_ += 1; + + widget()->repaint(false); + } + + KCommonDecoration::reset(changed); +} + + void +WebClient::paintEvent(QPaintEvent * pe) +{ + int r_x, r_y, r_x2, r_y2; + widget()->rect().coords(&r_x, &r_y, &r_x2, &r_y2); + const int titleEdgeLeft = layoutMetric(LM_TitleEdgeLeft); + const int titleEdgeTop = layoutMetric(LM_TitleEdgeTop); + const int titleEdgeRight = layoutMetric(LM_TitleEdgeRight); + const int titleEdgeBottom = layoutMetric(LM_TitleEdgeBottom); + const int ttlHeight = layoutMetric(LM_TitleHeight); + const int titleEdgeBottomBottom = r_y+titleEdgeTop+ttlHeight+titleEdgeBottom-1; + QRect titleRect = QRect(r_x+titleEdgeLeft+buttonsLeftWidth(), r_y+titleEdgeTop, + r_x2-titleEdgeRight-buttonsRightWidth()-(r_x+titleEdgeLeft+buttonsLeftWidth()), + titleEdgeBottomBottom-(r_y+titleEdgeTop) ); + titleRect.setTop(1); + + QPainter p(widget()); + + p.setPen(Qt::black); + p.setBrush(options()->colorGroup(ColorFrame, isActive()).background()); + + p.setClipRegion(pe->region() - titleRect); + + p.drawRect(widget()->rect()); + + p.setClipRegion(pe->region()); + + p.fillRect(titleRect, options()->color(ColorTitleBar, isActive())); + + if (shape_) + { + int r(width()); + int b(height()); + + // Draw edge of top-left corner inside the area removed by the mask. + + p.drawPoint(3, 1); + p.drawPoint(4, 1); + p.drawPoint(2, 2); + p.drawPoint(1, 3); + p.drawPoint(1, 4); + + // Draw edge of top-right corner inside the area removed by the mask. + + p.drawPoint(r - 5, 1); + p.drawPoint(r - 4, 1); + p.drawPoint(r - 3, 2); + p.drawPoint(r - 2, 3); + p.drawPoint(r - 2, 4); + + // Draw edge of bottom-left corner inside the area removed by the mask. + + p.drawPoint(1, b - 5); + p.drawPoint(1, b - 4); + p.drawPoint(2, b - 3); + p.drawPoint(3, b - 2); + p.drawPoint(4, b - 2); + + // Draw edge of bottom-right corner inside the area removed by the mask. + + p.drawPoint(r - 2, b - 5); + p.drawPoint(r - 2, b - 4); + p.drawPoint(r - 3, b - 3); + p.drawPoint(r - 4, b - 2); + p.drawPoint(r - 5, b - 2); + } + + p.setFont(options()->font(isActive(), isToolWindow())); + + p.setPen(options()->color(ColorFont, isActive())); + + p.drawText(titleRect, AlignCenter, caption()); +} + +void WebClient::updateWindowShape() +{ + if (!shape_) + return; + + QRegion mask(0, 0, width(), height()); + + int r(width()); + int b(height()); + + // Remove top-left corner. + + mask -= QRegion(0, 0, 5, 1); + mask -= QRegion(0, 1, 3, 1); + mask -= QRegion(0, 2, 2, 1); + mask -= QRegion(0, 3, 1, 2); + + // Remove top-right corner. + + mask -= QRegion(r - 5, 0, 5, 1); + mask -= QRegion(r - 3, 1, 3, 1); + mask -= QRegion(r - 2, 2, 2, 1); + mask -= QRegion(r - 1, 3, 1, 2); + + // Remove bottom-left corner. + + mask -= QRegion(0, b - 5, 1, 3); + mask -= QRegion(0, b - 3, 2, 1); + mask -= QRegion(0, b - 2, 3, 1); + mask -= QRegion(0, b - 1, 5, 1); + + // Remove bottom-right corner. + + mask -= QRegion(r - 5, b - 1, 5, 1); + mask -= QRegion(r - 3, b - 2, 3, 1); + mask -= QRegion(r - 2, b - 3, 2, 1); + mask -= QRegion(r - 1, b - 5, 1, 2); + + setMask(mask); +} + +KDecoration* WebFactory::createDecoration( KDecorationBridge* b ) +{ + return(new WebClient(b, this)); +} + +bool WebFactory::reset(unsigned long changed) +{ + // Do we need to "hit the wooden hammer" ? + bool needHardReset = true; + if (changed & SettingColors || changed & SettingFont) + { + needHardReset = false; + } else if (changed & SettingButtons) { + // handled by KCommonDecoration + needHardReset = false; + } + + if (needHardReset) { + return true; + } else { + resetDecorations(changed); + return false; + } +} + +bool WebFactory::supports( Ability ability ) +{ + switch( ability ) + { + case AbilityAnnounceButtons: + case AbilityButtonOnAllDesktops: + case AbilityButtonHelp: + case AbilityButtonMinimize: + case AbilityButtonMaximize: + case AbilityButtonClose: + case AbilityButtonMenu: + case AbilityButtonAboveOthers: + case AbilityButtonBelowOthers: + case AbilityButtonShade: + return true; + default: + return false; + }; +} + +QValueList< WebFactory::BorderSize > WebFactory::borderSizes() const +{ // the list must be sorted + return QValueList< BorderSize >() << BorderNormal << BorderLarge << + BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized; +} + +} + +#include "Web.moc" +// vim:ts=2:sw=2:tw=78:set et: +// kate: indent-width 2; replace-tabs on; tab-width 2; space-indent on; diff --git a/kwin/clients/web/Web.h b/kwin/clients/web/Web.h new file mode 100644 index 000000000..183829e8d --- /dev/null +++ b/kwin/clients/web/Web.h @@ -0,0 +1,87 @@ +/* + 'Web' kwin client + + Copyright (C) 2005 Sandro Giessl <sandro@giessl.com> + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KWIN_WEB_H +#define KWIN_WEB_H + +#include "../../lib/kcommondecoration.h" +#include "../../lib/kdecorationfactory.h" + +class QLabel; +class QSpacerItem; +class QBoxLayout; + +namespace Web +{ + + class WebButton; + + class WebClient : public KCommonDecoration + { + public: + + WebClient(KDecorationBridge* bridge, KDecorationFactory* factory); + ~WebClient(); + + virtual QString visibleName() const; + virtual QString defaultButtonsLeft() const; + virtual QString defaultButtonsRight() const; + virtual bool decorationBehaviour(DecorationBehaviour behaviour) const; + virtual int layoutMetric(LayoutMetric lm, bool respectWindowState = true, const KCommonDecorationButton * = 0) const; + virtual KCommonDecorationButton *createButton(ButtonType type); + + virtual void updateWindowShape(); + + virtual void init(); + + protected: + virtual void reset( unsigned long changed ); + + virtual void paintEvent(QPaintEvent *); + + private: + + int titleHeight_, borderSize_; + + bool shape_; + + QBitmap _buttonBitmap(ButtonType t) const; + }; + + class WebFactory : public QObject, public KDecorationFactory + { + Q_OBJECT + + public: + + WebFactory() {}; + virtual ~WebFactory() {}; + virtual KDecoration* createDecoration( KDecorationBridge* ); + virtual bool reset( unsigned long changed ); + virtual bool supports( Ability ability ); + virtual QValueList< BorderSize > borderSizes() const; + }; +} + +#endif +// vim:ts=2:sw=2:tw=78:set et: +// kate: indent-width 2; replace-tabs on; tab-width 2; space-indent on; diff --git a/kwin/clients/web/WebButton.cpp b/kwin/clients/web/WebButton.cpp new file mode 100644 index 000000000..66bedc19f --- /dev/null +++ b/kwin/clients/web/WebButton.cpp @@ -0,0 +1,287 @@ +/* + 'Web' kwin client + + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qpainter.h> + +#include "WebButton.h" +#include "Web.h" + +namespace Web { + + static unsigned char close_bits[] = { + 0x42, 0xe7, 0x7e, 0x3c, 0x3c, 0x7e, 0xe7, 0x42 + }; + static unsigned char iconify_bits[] = { + 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x3c, 0x18, 0x00 + }; + static unsigned char maximize_bits[] = { + 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00 + }; + static unsigned char unmaximize_bits[] = { + 0x00, 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f + }; + static unsigned char sticky_bits[] = { + 0x20, 0x70, 0xfa, 0x7e, 0x3c, 0x1c, 0x32, 0x01 + }; + static unsigned char unsticky_bits[] = { + 0x1c, 0x1c, 0x1c, 0x3e, 0x7f, 0x08, 0x08, 0x08 + }; + static unsigned char help_bits[] = { + 0x18, 0x18, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x3c + }; + static unsigned char shade_on_bits[] = { + 0xff, 0xff, 0x81, 0x81, 0x99, 0xbd, 0x81, 0xff + }; + static unsigned char shade_off_bits[] = { + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + static unsigned char above_on_bits[] = { + 0xff, 0x7e, 0x3c, 0x18, 0x00, 0xff, 0xff, 0x00 + }; + static unsigned char above_off_bits[] = { + 0x18, 0x3c, 0x7e, 0xff, 0x00, 0xff, 0xff, 0x00 + }; + static unsigned char below_on_bits[] = { + 0x00, 0xff, 0xff, 0x00, 0x18, 0x3c, 0x7e, 0xff + }; + static unsigned char below_off_bits[] = { + 0x00, 0xff, 0xff, 0x00, 0xff, 0x7e, 0x3c, 0x18 + }; + static unsigned char menu_bits[] = { + 0xff, 0x81, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff + }; + +WebButton::WebButton(ButtonType type, WebClient *parent, const char *name, bool shape) + : KCommonDecorationButton (type, parent, name), + mouseOver_ (false), + shape_ (shape), + deco_ (parent) +{ + setBackgroundMode(NoBackground); +} + +WebButton::~WebButton() +{ + // Empty. +} + +void WebButton::reset(unsigned long changed) +{ + if (changed&DecorationReset || changed&ManualReset || changed&SizeChange || changed&StateChange) { + switch (type() ) { + case CloseButton: + setBitmap(close_bits); + break; + case HelpButton: + setBitmap(help_bits); + break; + case MinButton: + setBitmap(iconify_bits); + break; + case MaxButton: + setBitmap( isOn() ? unmaximize_bits : maximize_bits ); + break; + case OnAllDesktopsButton: + setBitmap( isOn() ? unsticky_bits : sticky_bits ); + break; + case ShadeButton: + setBitmap( isOn() ? shade_on_bits : shade_off_bits ); + break; + case AboveButton: + setBitmap( isOn() ? above_on_bits : above_off_bits ); + break; + case BelowButton: + setBitmap( isOn() ? below_on_bits : below_off_bits ); + break; + case MenuButton: + setBitmap(menu_bits); + break; + default: + setBitmap(0); + break; + } + + this->update(); + } +} + + void +WebButton::enterEvent(QEvent * e) +{ + mouseOver_ = true; + repaint(); + QButton::enterEvent(e); +} + + void +WebButton::leaveEvent(QEvent * e) +{ + mouseOver_ = false; + repaint(); + QButton::leaveEvent(e); +} + + void +WebButton::drawButton(QPainter *p) +{ + QPen highlightPen; + + if (isDown() ) + highlightPen = QPen(colorGroup().light()); + + else + { + if (mouseOver_) + highlightPen = QPen(colorGroup().highlight()); + else + highlightPen = QPen(NoPen); + } + + p->fillRect(rect(), colorGroup().background()); + + Position position_; + if (0 == mapToParent(rect().topLeft() ).x() ) + position_ = Left; + else if (deco_->width()-1 == mapToParent(rect().topRight() ).x() ) + position_ = Right; + else + position_ = Mid; + switch ( position_ ) + { + case Left: + { + // Draw edge. + + p->setPen(Qt::black); + + p->drawLine(0, 0, width(), 0); + p->drawLine(0, 1, 0, height() - 1); + if (shape_) + { + p->drawPoint(3, 1); + p->drawPoint(4, 1); + p->drawPoint(2, 2); + p->drawPoint(1, 3); + p->drawPoint(1, 4); + } + // Draw highlight. + + p->setBrush(NoBrush); + p->setPen(highlightPen); + + if (shape_) + p->setClipRegion(QRegion(rect()) - QRect(0, 0, 6, 6)); + + p->drawRect(2, 2, width() - 4, height() - 4); + if (shape_) + { + p->setClipRect(rect()); + p->drawPoint(4, 3); + p->drawPoint(5, 3); + p->drawPoint(3, 4); + p->drawPoint(3, 5); + } + } + + break; + + case Right: + { + // Draw edge. + + p->setPen(Qt::black); + p->drawLine(0, 0, width(), 0); + p->drawLine(width() - 1, 1, width() - 1, height() - 1); + if (shape_) + { + p->drawPoint(width() - 5, 1); + p->drawPoint(width() - 4, 1); + p->drawPoint(width() - 3, 2); + p->drawPoint(width() - 2, 3); + p->drawPoint(width() - 2, 4); + } + // Draw highlight. + + p->setBrush(NoBrush); + p->setPen(highlightPen); + + if (shape_) + p->setClipRegion(QRegion(rect()) - QRect(width() - 6, 0, 6, 6)); + + p->drawRect(2, 2, width() - 4, height() - 4); + if (shape_) + { + p->setClipRect(rect()); + p->drawPoint(width() - 5, 3); + p->drawPoint(width() - 6, 3); + p->drawPoint(width() - 4, 4); + p->drawPoint(width() - 4, 5); + } + } + + break; + + case Mid: + default: + { + // Draw edge. + + p->setPen(Qt::black); + p->drawLine(0, 0, width(), 0); + + // Draw highlight. + + p->setBrush(NoBrush); + p->setPen(highlightPen); + + p->drawRect(2, 2, width() - 4, height() - 4); + } + + break; + } + + // Draw icon. + + QPoint center(rect().center()); + + int bwby2(bitmap_.width() / 2); // Bitmap Width BY 2 + int bhby2(bitmap_.height() / 2); // Bitmap Height BY 2 + + p->setBrush(NoBrush); + p->setPen(Qt::black); + + p->drawPixmap(center.x() - bwby2 + 1, center.y() - bhby2 + 1, bitmap_); +} + + void +WebButton::setBitmap(const unsigned char *bitmap) +{ + if (bitmap) + bitmap_ = QBitmap(8,8, bitmap, true); + else + bitmap_ = QBitmap(8,8); + bitmap_.setMask(bitmap_); +} + +} + +// vim:ts=2:sw=2:tw=78:set et: +// kate: indent-width 2; replace-tabs on; tab-width 2; space-indent on; diff --git a/kwin/clients/web/WebButton.h b/kwin/clients/web/WebButton.h new file mode 100644 index 000000000..6c1530f42 --- /dev/null +++ b/kwin/clients/web/WebButton.h @@ -0,0 +1,70 @@ +/* + 'Web' kwin client + + Copyright (C) 2001 Rik Hemsley (rikkus) <rik@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KWIN_WEB_BUTTON_H +#define KWIN_WEB_BUTTON_H + +#include <qwidget.h> +#include <qbitmap.h> +#include <klocale.h> + +#include "../../lib/kcommondecoration.h" + +namespace Web +{ + class WebClient; + + class WebButton : public KCommonDecorationButton + { + public: + + enum Position + { + Left, Mid, Right + }; + + WebButton(ButtonType type, WebClient *parent, const char *name, bool shape); + + virtual ~WebButton(); + + virtual void reset(unsigned long changed); + + protected: + void setBitmap(const unsigned char *bitmap); + + void enterEvent(QEvent *); + void leaveEvent(QEvent *); + void drawButton(QPainter *p); + + private: + QBitmap bitmap_; + + bool mouseOver_; + + bool shape_; + WebClient* deco_; + }; +} + +#endif + +// vim:ts=2:sw=2:tw=78:set et: +// kate: indent-width 2; replace-tabs on; tab-width 2; space-indent on; diff --git a/kwin/clients/web/web.desktop b/kwin/clients/web/web.desktop new file mode 100644 index 000000000..12ced34f4 --- /dev/null +++ b/kwin/clients/web/web.desktop @@ -0,0 +1,47 @@ +[Desktop Entry] +Name=Web +Name[ar]=الشبكة +Name[az]=Veb +Name[be]=Сеціва +Name[bn]=ওয়েব +Name[br]=Gwiad +Name[csb]=Séc +Name[cy]=Gwe +Name[da]=Net +Name[el]=Ιστός +Name[eo]=TTT +Name[et]=Veeb +Name[fa]=وب +Name[ga]=Gréasán +Name[hi]=वेब +Name[hr]=Internet +Name[is]=Vefur +Name[km]=បណ្ដាញ +Name[lo]=ແບບເວ໊ບ +Name[lv]=Tīmekļa +Name[mk]=Веб +Name[mn]=Веб +Name[nb]=Nett +Name[ne]=वेब +Name[nn]=Vev +Name[pa]=ਵੈੱਬ +Name[pl]=Sieć +Name[rw]=Urubugamakuru +Name[se]=Fierpmádat +Name[sl]=Splet +Name[sr]=Веб +Name[sr@Latn]=Veb +Name[sv]=Webb +Name[ta]=வலை +Name[te]=వెబ్ +Name[tg]=Вэб +Name[th]=แบบเว็บ +Name[uk]=Тенета +Name[uz]=Veb +Name[uz@cyrillic]=Веб +Name[ven]=Webu +Name[vi]=Mạng +Name[wa]=Waibe +Name[zh_TW]=網頁 +Name[zu]=I-Web +X-KDE-Library=kwin3_web |