File indexing completed on 2024-04-28 16:55:25
0001 /* 0002 * kwin_smaragd.cpp - Emerald window decoration for KDE 0003 * 0004 * Copyright (c) 2010 Christoph Feck <christoph@maxiom.de> 0005 * Copyright (c) 2006 Novell, Inc. 0006 * 0007 * This program is free software; you can redistribute it and/or modify 0008 * it under the terms of the GNU General Public License as published by 0009 * the Free Software Foundation; either version 2 of the License, or 0010 * (at your option) any later version. 0011 * 0012 * This program is distributed in the hope that it will be useful, 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0015 * GNU General Public License for more details. 0016 * 0017 * You should have received a copy of the GNU General Public License 0018 * along with this program; if not, write to the Free Software 0019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 0020 * 0021 */ 0022 0023 #include "kwin_smaragd.h" 0024 0025 #include <KDecoration2/DecoratedClient> 0026 #include <KDecoration2/DecorationButtonGroup> 0027 #include <KDecoration2/DecorationSettings> 0028 #include <KDecoration2/DecorationShadow> 0029 0030 #include <KConfig> 0031 #include <KConfigGroup> 0032 #include <KPluginFactory> 0033 0034 #include <QDebug> 0035 #include <QPaintEngine> 0036 0037 #include <QBitmap> 0038 #include <QPainter> 0039 #include <QPropertyAnimation> 0040 0041 #include <cairo.h> 0042 0043 extern "C" 0044 { 0045 0046 #include <engine.h> 0047 0048 void draw_button_with_glow_alpha_bstate(gint b_t, decor_t * d, cairo_t * cr, 0049 gint y1, gdouble button_alpha, 0050 gdouble glow_alpha, int b_state); 0051 0052 void pango_layout_get_pixel_size(PangoLayout *layout, int *pwidth, int *pheight) 0053 { 0054 if (pwidth) { 0055 *pwidth = layout->bounding_width; 0056 } 0057 if (pheight) { 0058 *pheight = layout->bounding_height; 0059 } 0060 } 0061 0062 void gdk_color_parse(gchar *s, GdkColor *c) 0063 { 0064 QString string = QString::fromLocal8Bit(s); 0065 QColor color(string); 0066 c->red = qRound(color.redF() * 65535); 0067 c->green = qRound(color.greenF() * 65535); 0068 c->blue = qRound(color.blueF() * 65535); 0069 } 0070 0071 void gdk_drawable_get_size(GdkPixmap *pixmap, int *width, int *height) 0072 { 0073 *width = pixmap->width; 0074 *height = pixmap->height; 0075 } 0076 0077 extern window_settings *create_settings(); 0078 extern void update_settings(window_settings *ws); 0079 0080 extern void legacy_load_engine_settings(GKeyFile *f, window_settings *ws); 0081 extern void line_load_engine_settings(GKeyFile *f, window_settings *ws); 0082 extern void oxygen_load_engine_settings(GKeyFile *f, window_settings *ws); 0083 extern void pixmap_load_engine_settings(GKeyFile *f, window_settings *ws); 0084 extern void truglass_load_engine_settings(GKeyFile *f, window_settings *ws); 0085 extern void vrunner_load_engine_settings(GKeyFile *f, window_settings *ws); 0086 extern void zootreeves_load_engine_settings(GKeyFile *f, window_settings *ws); 0087 0088 extern void legacy_init_engine(window_settings *ws); 0089 extern void line_init_engine(window_settings *ws); 0090 extern void oxygen_init_engine(window_settings *ws); 0091 extern void pixmap_init_engine(window_settings *ws); 0092 extern void truglass_init_engine(window_settings *ws); 0093 extern void vrunner_init_engine(window_settings *ws); 0094 extern void zootreeves_init_engine(window_settings *ws); 0095 0096 extern void legacy_engine_draw_frame(decor_t * d, cairo_t * cr); 0097 extern void line_engine_draw_frame(decor_t * d, cairo_t * cr); 0098 extern void oxygen_engine_draw_frame(decor_t * d, cairo_t * cr); 0099 extern void pixmap_engine_draw_frame(decor_t * d, cairo_t * cr); 0100 extern void truglass_engine_draw_frame(decor_t * d, cairo_t * cr); 0101 extern void vrunner_engine_draw_frame(decor_t * d, cairo_t * cr); 0102 extern void zootreeves_engine_draw_frame(decor_t * d, cairo_t * cr); 0103 0104 static init_engine_proc init_engine; 0105 static draw_frame_proc draw_frame; 0106 static load_settings_proc load_settings; 0107 0108 gboolean load_engine(gchar *engine, window_settings *ws) 0109 { 0110 if (!engine || !strcmp(engine, "legacy")) { 0111 init_engine = legacy_init_engine; 0112 draw_frame = legacy_engine_draw_frame; 0113 load_settings = legacy_load_engine_settings; 0114 } else if (!strcmp(engine, "line")) { 0115 init_engine = line_init_engine; 0116 draw_frame = line_engine_draw_frame; 0117 load_settings = line_load_engine_settings; 0118 } else if (!strcmp(engine, "oxygen")) { 0119 init_engine = oxygen_init_engine; 0120 draw_frame = oxygen_engine_draw_frame; 0121 load_settings = oxygen_load_engine_settings; 0122 } else if (!strcmp(engine, "pixmap")) { 0123 init_engine = pixmap_init_engine; 0124 draw_frame = pixmap_engine_draw_frame; 0125 load_settings = pixmap_load_engine_settings; 0126 } else if (!strcmp(engine, "truglass")) { 0127 init_engine = truglass_init_engine; 0128 draw_frame = truglass_engine_draw_frame; 0129 load_settings = truglass_load_engine_settings; 0130 } else if (!strcmp(engine, "vrunner")) { 0131 init_engine = vrunner_init_engine; 0132 draw_frame = vrunner_engine_draw_frame; 0133 load_settings = vrunner_load_engine_settings; 0134 } else if (!strcmp(engine, "zootreeves")) { 0135 init_engine = zootreeves_init_engine; 0136 draw_frame = zootreeves_engine_draw_frame; 0137 load_settings = zootreeves_load_engine_settings; 0138 } else { 0139 return false; 0140 } 0141 init_engine(ws); 0142 return true; 0143 } 0144 0145 0146 void load_engine_settings(GKeyFile *f, window_settings *ws) 0147 { 0148 load_settings(f, ws); 0149 } 0150 0151 #define CORNER_REDUCTION 3 0152 0153 int update_shadow(frame_settings * fs) 0154 { 0155 window_settings *ws = fs->ws; 0156 0157 int size = ws->shadow_radius * 2 + 2; 0158 0159 ws->shadow_offset_x = ws->shadow_offset_y = size = 0; 0160 0161 if (ws->shadow_radius <= 0.0 && ws->shadow_offset_x == 0 && 0162 ws->shadow_offset_y == 0) 0163 size = 0; 0164 0165 size = size / 2; 0166 0167 ws->left_space = ws->win_extents.left + size - ws->shadow_offset_x; 0168 ws->right_space = ws->win_extents.right + size + ws->shadow_offset_x; 0169 ws->top_space = ws->win_extents.top + size - ws->shadow_offset_y; 0170 ws->bottom_space = ws->win_extents.bottom + size + ws->shadow_offset_y; 0171 0172 0173 ws->left_space = MAX(ws->win_extents.left, ws->left_space); 0174 ws->right_space = MAX(ws->win_extents.right, ws->right_space); 0175 ws->top_space = MAX(ws->win_extents.top, ws->top_space); 0176 ws->bottom_space = MAX(ws->win_extents.bottom, ws->bottom_space); 0177 0178 ws->shadow_left_space = MAX(0, size - ws->shadow_offset_x); 0179 ws->shadow_right_space = MAX(0, size + ws->shadow_offset_x); 0180 ws->shadow_top_space = MAX(0, size - ws->shadow_offset_y); 0181 ws->shadow_bottom_space = MAX(0, size + ws->shadow_offset_y); 0182 0183 ws->shadow_left_corner_space = MAX(0, size + ws->shadow_offset_x); 0184 ws->shadow_right_corner_space = MAX(0, size - ws->shadow_offset_x); 0185 ws->shadow_top_corner_space = MAX(0, size + ws->shadow_offset_y); 0186 ws->shadow_bottom_corner_space = MAX(0, size - ws->shadow_offset_y); 0187 0188 ws->left_corner_space = 0189 MAX(0, ws->shadow_left_corner_space - CORNER_REDUCTION); 0190 ws->right_corner_space = 0191 MAX(0, ws->shadow_right_corner_space - CORNER_REDUCTION); 0192 ws->top_corner_space = 0193 MAX(0, ws->shadow_top_corner_space - CORNER_REDUCTION); 0194 ws->bottom_corner_space = 0195 MAX(0, ws->shadow_bottom_corner_space - CORNER_REDUCTION); 0196 0197 ws->normal_top_corner_space = 0198 MAX(0, ws->top_corner_space - ws->titlebar_height); 0199 /* 0200 d.width = 0201 ws->left_space + ws->left_corner_space + 1 + 0202 ws->right_corner_space + ws->right_space; 0203 d.height = 0204 ws->top_space + ws->titlebar_height + 0205 ws->normal_top_corner_space + 2 + ws->bottom_corner_space + 0206 ws->bottom_space; 0207 */ 0208 return 1; 0209 } 0210 0211 struct _GdkPixbuf 0212 { 0213 QImage image; 0214 }; 0215 0216 void g_object_unref(void *x) 0217 { 0218 delete (_GdkPixbuf *) x; 0219 } 0220 0221 int gdk_pixbuf_get_width(GdkPixbuf *pixbuf) 0222 { 0223 return pixbuf->image.width(); 0224 } 0225 0226 int gdk_pixbuf_get_height(GdkPixbuf *pixbuf) 0227 { 0228 return pixbuf->image.height(); 0229 } 0230 0231 GdkPixbuf *gdk_pixbuf_new(GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample, int w, int h) 0232 { 0233 Q_ASSERT(colorspace == GDK_COLORSPACE_RGB); 0234 Q_ASSERT(bits_per_sample == 8); 0235 0236 _GdkPixbuf *pixbuf = new _GdkPixbuf; 0237 pixbuf->image = QImage(w, h, has_alpha ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32); 0238 pixbuf->image.fill(qRgba(0, 0, 0, 0)); 0239 return pixbuf; 0240 } 0241 0242 GdkPixbuf *gdk_pixbuf_new_from_file(gchar *file, GError **/*error*/) 0243 { 0244 QImage image = QImage(QString::fromLocal8Bit(file)); 0245 0246 if (image.isNull()) { 0247 return 0; 0248 } 0249 _GdkPixbuf *pixbuf = new _GdkPixbuf; 0250 pixbuf->image = image; 0251 return pixbuf; 0252 } 0253 0254 GdkPixbuf *gdk_pixbuf_new_subpixbuf(GdkPixbuf *source, int x, int y, int w, int h) 0255 { 0256 _GdkPixbuf *pixbuf = new _GdkPixbuf; 0257 pixbuf->image = source->image.copy(x, y, w, h); 0258 return pixbuf; 0259 } 0260 0261 void gdk_pixbuf_scale(GdkPixbuf *source, GdkPixbuf *dest, int x, int y, int w, int h, double source_x, double source_y, double scale_x, double scale_y, int interp) 0262 { 0263 QPainter p(&dest->image); 0264 if (interp == GDK_INTERP_BILINEAR) { 0265 p.setRenderHint(QPainter::SmoothPixmapTransform, true); 0266 } 0267 p.setCompositionMode(QPainter::CompositionMode_Source); 0268 p.drawImage(QRect(x, y, w, h), source->image, QRect(qRound(source_x), qRound(source_y), qRound(w / scale_x), qRound(h / scale_y))); 0269 p.end(); 0270 } 0271 0272 int gdk_pixbuf_get_colorspace(GdkPixbuf */*pixbuf*/) 0273 { 0274 return GDK_COLORSPACE_RGB; 0275 } 0276 0277 int gdk_pixbuf_get_bits_per_sample(GdkPixbuf */*pixbuf*/) 0278 { 0279 return 8; 0280 } 0281 0282 } 0283 0284 K_PLUGIN_FACTORY_WITH_JSON(SmaragdDecorationFactory, 0285 "smaragd.json", 0286 registerPlugin<Smaragd::Decoration>(); 0287 ) 0288 0289 namespace Smaragd 0290 { 0291 0292 static QRegion findCornerShape(const QImage &image, int corner, const QSize &maxSize) 0293 { 0294 QSize cornerSize = maxSize.boundedTo(image.size()); 0295 QImage cornerImage(cornerSize, QImage::Format_MonoLSB); 0296 cornerImage.fill(1); 0297 0298 int xd = 1, yd = 1; // scanning direction 0299 int sx = 0, sy = 0; 0300 int cx = 0, cy = 0; 0301 if (corner & 1) { 0302 xd = -1; 0303 sx = image.width() - 1; 0304 cx = cornerImage.width() - 1; 0305 } 0306 if (corner & 2) { 0307 yd = -1; 0308 sy = image.height() - 1; 0309 cy = cornerImage.height() - 1; 0310 } 0311 0312 int threshold = qAlpha(QRgb(image.pixel(sx + (cornerSize.width() - 1) * xd, sy))) >> 1; 0313 for (int y = 0, ys = sy, yc = cy; y < cornerSize.height(); ++y, ys += yd, yc += yd) { 0314 for (int x = 0, xs = sx, xc = cx; x < cornerSize.width(); ++x, xs += xd, xc += xd) { 0315 QRgb pixel = QRgb(image.pixel(xs, ys)); 0316 if (qAlpha(pixel) >= threshold) { 0317 break; 0318 } 0319 cornerImage.setPixel(xc, yc, 0); 0320 } 0321 } 0322 return QRegion(QBitmap::fromImage(cornerImage)); 0323 } 0324 0325 0326 DecorationFactory::DecorationFactory() 0327 { 0328 ws = create_settings(); 0329 } 0330 0331 DecorationFactory::~DecorationFactory() 0332 { 0333 free(ws); 0334 } 0335 0336 void DecorationFactory::setFontHeight(int fontHeight) 0337 { 0338 ws->text_height = fontHeight; 0339 update_settings(ws); 0340 0341 QImage decoImage = decorationImage(QSize(96, 64), true, 0, QRect(32, 8, 32, 8)); 0342 QPainter p(&decoImage); 0343 QRect rect(0, 0, 96, 64); 0344 rect.adjust(ws->left_space + ws->left_corner_space, ws->top_space + ws->normal_top_corner_space + ws->titlebar_height, 0345 -(ws->right_space + ws->right_corner_space), -(ws->bottom_space + ws->bottom_corner_space)); 0346 p.fillRect(rect, Qt::black); 0347 p.end(); 0348 for (int corner = 0; corner < 4; ++corner) { 0349 cornerRegion[corner] = findCornerShape(decoImage, corner, QSize(32, 32)); 0350 } 0351 0352 KConfig configFile(QLatin1String("kwinsmaragdrc")); 0353 KConfigGroup configGroup(&configFile, "General"); 0354 0355 m_config.useKWinTextColors = configGroup.readEntry("UseKWinTextColors", false); 0356 m_config.useKWinShadows = configGroup.readEntry("UseKWinShadows", false); 0357 m_config.hoverDuration = configGroup.readEntry("HoverDuration", 200); 0358 0359 if (!m_config.useKWinShadows) { 0360 m_config.shadowSettings.radius = configGroup.readEntry("ShadowRadius", 5); 0361 m_config.shadowSettings.color = configGroup.readEntry("ShadowColor", QColor(0, 0, 0)); 0362 m_config.shadowSettings.color.setAlpha(configGroup.readEntry("ShadowAlpha", 180)); 0363 m_config.shadowSettings.offsetX = configGroup.readEntry("ShadowOffsetX", 0); 0364 m_config.shadowSettings.offsetY = configGroup.readEntry("ShadowOffsetY", 0); 0365 m_config.shadowSettings.size = configGroup.readEntry("ShadowSize", -3); 0366 m_config.shadowSettings.linearDecay = configGroup.readEntry("ShadowLinearDecay", 1.0); 0367 m_config.shadowSettings.exponentialDecay = configGroup.readEntry("ShadowExponentialDecay", 6.0); 0368 0369 m_config.shadowImage = createShadowImage(m_config.shadowSettings); 0370 } 0371 } 0372 0373 QRegion DecorationFactory::cornerShape(int corner) const 0374 { 0375 return cornerRegion[corner]; 0376 } 0377 0378 Decoration::Decoration(QObject *parent, const QVariantList &args) 0379 : KDecoration2::Decoration(parent, args) 0380 , m_titleLeft(0) 0381 , m_titleRight(0) 0382 { 0383 } 0384 0385 Decoration::~Decoration() 0386 { 0387 } 0388 0389 void Decoration::init() 0390 { 0391 connect(client().data(), &KDecoration2::DecoratedClient::widthChanged, this, &Decoration::updateLayout); 0392 connect(client().data(), &KDecoration2::DecoratedClient::heightChanged, this, &Decoration::updateLayout); 0393 connect(client().data(), &KDecoration2::DecoratedClient::maximizedChanged, this, &Decoration::updateLayout); 0394 connect(client().data(), &KDecoration2::DecoratedClient::shadedChanged, this, &Decoration::updateLayout); 0395 0396 connect(client().data(), &KDecoration2::DecoratedClient::paletteChanged, this, [this]() { update(); }); 0397 connect(client().data(), &KDecoration2::DecoratedClient::iconChanged, this, [this]() { update(); }); 0398 connect(client().data(), &KDecoration2::DecoratedClient::captionChanged, this, [this]() { update(); }); 0399 connect(client().data(), &KDecoration2::DecoratedClient::activeChanged, this, [this]() { update(); }); 0400 0401 window_settings *ws = factory()->windowSettings(); 0402 factory()->setFontHeight(settings()->fontMetrics().height()); 0403 parseButtonLayout(ws->tobj_layout ? ws->tobj_layout : (char *) "I:T:NXC"); 0404 0405 QVector<QPointer<KDecoration2::DecorationButton>> buttons; 0406 buttons = m_buttonGroup[0]->buttons(); 0407 if (!buttons.isEmpty()) { 0408 KDecoration2::DecorationButton *button = buttons.at(0); 0409 if (button->type() == KDecoration2::DecorationButtonType::Custom && button->geometry().width() < 0) { 0410 m_titleLeft = button->geometry().width(); 0411 m_buttonGroup[0]->removeButton(button); 0412 } 0413 } 0414 buttons = m_buttonGroup[2]->buttons(); 0415 if (!buttons.isEmpty()) { 0416 KDecoration2::DecorationButton *button = buttons.at(buttons.count() - 1); 0417 if (button->type() == KDecoration2::DecorationButtonType::Custom && button->geometry().width() < 0) { 0418 m_titleRight = button->geometry().width(); 0419 m_buttonGroup[2]->removeButton(button); 0420 } 0421 } 0422 0423 KDecoration2::DecorationShadow *shadow = new KDecoration2::DecorationShadow(); 0424 const Config *config = factory()->config(); 0425 QImage image = config->shadowImage; 0426 shadow->setShadow(image); 0427 shadow->setInnerShadowRect(QRect(image.width() / 2, image.height() / 2, 1, 1)); 0428 int p = 32 + config->shadowSettings.size; 0429 shadow->setPadding(QMargins(p, p, p, p)); 0430 setShadow(QSharedPointer<KDecoration2::DecorationShadow>(shadow)); 0431 0432 updateLayout(); 0433 } 0434 0435 void Decoration::updateLayout() 0436 { 0437 window_settings *ws = factory()->windowSettings(); 0438 bool horizontalBorders = !client().data()->isMaximizedHorizontally(); 0439 bool verticalBorders = !client().data()->isMaximizedVertically(); 0440 factory()->setFontHeight(settings()->fontMetrics().height()); 0441 setBorders(QMargins( 0442 horizontalBorders ? ws->left_space + ws->left_corner_space : 0, 0443 ws->top_space + ws->normal_top_corner_space + ws->titlebar_height, 0444 horizontalBorders ? ws->right_space + ws->right_corner_space : 0, 0445 verticalBorders ? ws->bottom_space + ws->bottom_corner_space : 0 0446 )); 0447 setTitleBar(QRect(2, 4, size().width() - 2 * 2, borderTop() - 4)); 0448 int titleEdgeLeft = horizontalBorders ? ws->left_space + ws->button_hoffset + m_titleLeft : 0; 0449 int titleEdgeRight = horizontalBorders ? ws->right_space + ws->button_hoffset + m_titleRight : 0; 0450 0451 m_buttonGroup[0]->setPos(QPointF(titleEdgeLeft, 0)); 0452 m_buttonGroup[2]->setPos(QPointF(size().width() - qRound(m_buttonGroup[2]->geometry().width()) - titleEdgeRight, 0)); 0453 } 0454 0455 int Decoration::buttonGlyph(KDecoration2::DecorationButtonType type) const 0456 { 0457 switch (type) { 0458 case KDecoration2::DecorationButtonType::ContextHelp: 0459 return B_HELP; 0460 case KDecoration2::DecorationButtonType::Maximize: 0461 return client().data()->isMaximized() ? B_RESTORE : B_MAXIMIZE; 0462 case KDecoration2::DecorationButtonType::Minimize: 0463 return B_MINIMIZE; 0464 case KDecoration2::DecorationButtonType::Close: 0465 return B_CLOSE; 0466 case KDecoration2::DecorationButtonType::Menu: 0467 case KDecoration2::DecorationButtonType::ApplicationMenu: 0468 return B_MENU; 0469 case KDecoration2::DecorationButtonType::OnAllDesktops: 0470 return client().data()->isOnAllDesktops() ? B_UNSTICK : B_STICK; 0471 case KDecoration2::DecorationButtonType::KeepAbove: 0472 return client().data()->isKeepAbove() ? B_UNABOVE : B_ABOVE; 0473 case KDecoration2::DecorationButtonType::KeepBelow: 0474 return client().data()->isKeepBelow() ? B_UNABOVE : B_ABOVE; 0475 case KDecoration2::DecorationButtonType::Shade: 0476 return client().data()->isShaded() ? B_UNSHADE : B_SHADE; 0477 case KDecoration2::DecorationButtonType::Custom: 0478 break; 0479 } 0480 return -1; // spacer 0481 } 0482 0483 static inline KDecoration2::DecorationButtonType parseButtonCode(char c) 0484 { 0485 switch (c) { 0486 case 'H': // B_HELP 0487 return KDecoration2::DecorationButtonType::ContextHelp; 0488 case 'M': // B_MENU 0489 return KDecoration2::DecorationButtonType::ApplicationMenu; 0490 case 'I': 0491 return KDecoration2::DecorationButtonType::Menu; 0492 case 'N': // B_MINIMIZE 0493 return KDecoration2::DecorationButtonType::Minimize; 0494 case 'R': 0495 case 'X': // B_MAXIMIZE 0496 return KDecoration2::DecorationButtonType::Maximize; 0497 case 'C': // B_CLOSE 0498 return KDecoration2::DecorationButtonType::Close; 0499 case 'U': 0500 case 'A': // B_ABOVE 0501 return KDecoration2::DecorationButtonType::KeepAbove; 0502 case 'D': // B_BELOW 0503 return KDecoration2::DecorationButtonType::KeepBelow; 0504 case 'S': // B_SHADE 0505 return KDecoration2::DecorationButtonType::Shade; 0506 case 'Y': // B_STICK 0507 return KDecoration2::DecorationButtonType::OnAllDesktops; 0508 default: 0509 return KDecoration2::DecorationButtonType::Custom; 0510 } 0511 } 0512 0513 static Qt::Alignment parseTitleAlignment(char *p) 0514 { 0515 char c; 0516 0517 while ((c = *p++) && c != ':') { 0518 if (c == 'T') { 0519 return Qt::AlignLeft; 0520 } 0521 } 0522 while ((c = *p++) && c != ':') { 0523 if (c == 'T') { 0524 return Qt::AlignHCenter; 0525 } 0526 } 0527 return Qt::AlignRight; 0528 } 0529 0530 void Decoration::parseButtonLayout(char *p) 0531 { 0532 for (int group = 0; group < 3; ++group) { 0533 m_buttonGroup[group] = new KDecoration2::DecorationButtonGroup(this); 0534 } 0535 0536 KDecoration2::DecorationButtonType type; 0537 bool negative; 0538 int s; 0539 char c; 0540 0541 int group = 0; 0542 0543 while ((c = *p++)) { 0544 switch (c) { 0545 case ':': 0546 ++group; 0547 if (!(group < 3)) { 0548 return; 0549 } 0550 break; 0551 case '(': 0552 negative = false; 0553 s = 0; 0554 if (*p == '-') { 0555 negative = true; 0556 ++p; 0557 } 0558 while (c = *p, c >= '0' && c <= '9') { 0559 s = s * 10 + c - '0'; 0560 ++p; 0561 } 0562 if (c == ')') { 0563 ++p; 0564 } 0565 if (s > 99) { 0566 s = 99; 0567 } 0568 if (negative) { 0569 s = -s; 0570 } 0571 if (s != 0) { 0572 DecorationButton *button = new DecorationButton(KDecoration2::DecorationButtonType::Custom, this); 0573 button->setAcceptedButtons(Qt::NoButton); 0574 button->setGeometry(QRect(0, 0, s, 0)); 0575 m_buttonGroup[group]->addButton(button); 0576 } 0577 break; 0578 default: 0579 type = parseButtonCode(c); 0580 if (type != KDecoration2::DecorationButtonType::Custom) { 0581 DecorationButton *button = new DecorationButton(type, this); 0582 window_settings *ws = factory()->windowSettings(); 0583 int width; 0584 int height; 0585 if (type == KDecoration2::DecorationButtonType::Menu || !ws->use_pixmap_buttons) { 0586 width = 16; 0587 height = ws->top_space + ws->normal_top_corner_space + ws->titlebar_height; 0588 } else { 0589 GdkPixbuf *pixbuf = ws->ButtonPix[buttonGlyph(type) * S_COUNT]; 0590 if (pixbuf) { 0591 width = gdk_pixbuf_get_width(pixbuf); 0592 height = gdk_pixbuf_get_height(pixbuf) + ws->button_offset; 0593 } 0594 } 0595 button->setGeometry(QRect(0, 0, width, height)); 0596 m_buttonGroup[group]->addButton(button); 0597 } 0598 break; 0599 } 0600 } 0601 } 0602 0603 QImage DecorationFactory::buttonImage(const QSize &size, bool active, int button, int state) const 0604 { 0605 decor_t deco, *d = &deco; 0606 bzero(d, sizeof(decor_t)); 0607 0608 d->decorated = true; 0609 d->active = active; 0610 0611 d->fs = active ? ws->fs_act : ws->fs_inact; 0612 0613 QSize allocSize(cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, size.width()) / 4, size.height()); 0614 0615 QImage image(allocSize, QImage::Format_ARGB32_Premultiplied); 0616 image.fill(0); 0617 0618 cairo_surface_t *surface; 0619 cairo_t *cr; 0620 0621 surface = cairo_image_surface_create_for_data(image.bits(), CAIRO_FORMAT_ARGB32, size.width(), image.height(), image.bytesPerLine()); 0622 cr = cairo_create(surface); 0623 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); 0624 draw_button_with_glow_alpha_bstate(button, d, cr, 0, 1.0, 1.0, state); 0625 cairo_destroy(cr); 0626 cairo_surface_destroy(surface); 0627 0628 return image; 0629 } 0630 0631 QImage DecorationFactory::decorationImage(const QSize &size, bool active, int state, const QRect &titleRect) const 0632 { 0633 decor_t deco, *d = &deco; 0634 bzero(d, sizeof(decor_t)); 0635 0636 // ### Title objects position and sizes 0637 d->tobj_pos[0] = 0; // left 0638 d->tobj_pos[1] = 0; // mid 0639 d->tobj_pos[2] = 0; // right 0640 d->tobj_size[0] = 0; 0641 d->tobj_size[1] = 0; 0642 d->tobj_size[2] = 0; 0643 0644 // ### Buttons 0645 for (int i = 0; i < 11; ++i) { 0646 d->tobj_item_pos[i] = 0; 0647 d->tobj_item_width[i] = 0; 0648 d->tobj_item_state[i] = 3; 0649 } 0650 0651 d->width = size.width(); 0652 d->height = size.height(); 0653 0654 const int left = ws->left_space + ws->left_corner_space; 0655 const int top = ws->top_space + ws->titlebar_height + ws->normal_top_corner_space; 0656 const int right = ws->right_corner_space + ws->right_space; 0657 const int bottom = ws->bottom_corner_space + ws->bottom_space; 0658 0659 d->client_width = d->width - (left + right); 0660 d->client_height = d->height - (top + bottom); 0661 0662 d->tobj_item_state[TBT_TITLE] = 0; 0663 d->tobj_item_pos[TBT_TITLE] = titleRect.left() - ws->left_space; 0664 PangoLayout pangoLayout; 0665 pangoLayout.bounding_width = titleRect.width(); 0666 pangoLayout.bounding_height = titleRect.height(); 0667 d->layout = &pangoLayout; 0668 0669 d->state = WnckWindowState(state); 0670 0671 d->decorated = true; 0672 d->active = active; 0673 0674 d->fs = active ? ws->fs_act : ws->fs_inact; 0675 0676 QSize allocSize(cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, size.width()) / 4, size.height()); 0677 0678 QImage image(allocSize, QImage::Format_ARGB32_Premultiplied); 0679 QPainter painter(&image); 0680 painter.setCompositionMode(QPainter::CompositionMode_Source); 0681 painter.fillRect(QRect(0, 0, d->width, top), Qt::transparent); 0682 painter.fillRect(QRect(0, top, left, d->client_height), Qt::transparent); 0683 painter.fillRect(QRect(d->width - right, top, right, d->client_height), Qt::transparent); 0684 painter.fillRect(QRect(0, d->height - bottom, d->width, bottom), Qt::transparent); 0685 painter.end(); 0686 0687 cairo_surface_t *surface; 0688 cairo_t *cr; 0689 0690 surface = cairo_image_surface_create_for_data(image.bits(), CAIRO_FORMAT_ARGB32, size.width(), image.height(), image.bytesPerLine()); 0691 cr = cairo_create(surface); 0692 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); 0693 cairo_set_line_width(cr, 1.0); 0694 draw_frame(d, cr); 0695 cairo_destroy(cr); 0696 cairo_surface_destroy(surface); 0697 0698 return image; 0699 } 0700 0701 static QImage hoverImage(const QImage &image, const QImage &hoverImage, qreal hoverProgress) 0702 { 0703 if (hoverProgress <= 0.5 / 256) { 0704 return image; 0705 } 0706 if (hoverProgress >= 1.0 - 0.5 / 256) { 0707 return hoverImage; 0708 } 0709 QImage result = image; 0710 QImage over = hoverImage; 0711 QColor alpha = Qt::black; 0712 alpha.setAlphaF(hoverProgress); 0713 QPainter p; 0714 p.begin(&over); 0715 p.setCompositionMode(QPainter::CompositionMode_DestinationIn); 0716 p.fillRect(image.rect(), alpha); 0717 p.end(); 0718 p.begin(&result); 0719 p.setCompositionMode(QPainter::CompositionMode_DestinationOut); 0720 p.fillRect(image.rect(), alpha); 0721 p.setCompositionMode(QPainter::CompositionMode_Plus); 0722 p.drawImage(0, 0, over); 0723 p.end(); 0724 return result; 0725 } 0726 0727 void Decoration::paint(QPainter *painter, const QRect &repaintArea) 0728 { 0729 painter->save(); 0730 painter->setClipRect(repaintArea, Qt::IntersectClip); 0731 0732 const bool horizontalBorders = !client().data()->isMaximizedHorizontally(); 0733 const bool verticalBorders = !client().data()->isMaximizedVertically(); 0734 const bool active = client().data()->isActive(); 0735 QSize decoSize = size(); 0736 0737 QRect captionRect(m_buttonGroup[0]->geometry().right() + 2, 0, m_buttonGroup[2]->geometry().left() - m_buttonGroup[0]->geometry().right() - 4, borderTop()); 0738 QImage decoImage = factory()->decorationImage(size(), active, 0, captionRect); 0739 window_settings *ws = factory()->windowSettings(); 0740 const Config *config = factory()->config(); 0741 0742 painter->drawImage(0, 0, decoImage); 0743 0744 frame_settings *fs = active ? ws->fs_act : ws->fs_inact; 0745 0746 QColor shadowColor = QColor(0, 0, 0, 255); 0747 QColor textColor = client().data()->color(active ? KDecoration2::ColorGroup::Active : KDecoration2::ColorGroup::Inactive, KDecoration2::ColorRole::Foreground); 0748 int textHaloXOffset = 1; 0749 int textHaloYOffset = 1; 0750 int textHaloSize = 2; 0751 if (!config->useKWinTextColors) { 0752 alpha_color &c = fs->text_halo; 0753 shadowColor = QColor::fromRgbF(c.color.r, c.color.g, c.color.b, c.alpha); 0754 c = fs->text; 0755 textColor = QColor::fromRgbF(c.color.r, c.color.g, c.color.b, c.alpha); 0756 } 0757 QString caption = settings()->fontMetrics().elidedText(client().data()->caption(), Qt::ElideMiddle, captionRect.width()); 0758 captionRect.setHeight(captionRect.height() & -2); 0759 painter->setFont(settings()->font()); 0760 painter->setPen(shadowColor); 0761 // painter->drawText(captionRect.adjusted(1, 1, 1, 1), Qt::AlignVCenter, caption); 0762 painter->setPen(textColor); 0763 Qt::Alignment alignment = Qt::AlignHCenter; 0764 if (ws->tobj_layout) { 0765 alignment = parseTitleAlignment(ws->tobj_layout); 0766 } 0767 painter->drawText(captionRect, alignment | Qt::AlignVCenter | Qt::TextSingleLine, caption); 0768 0769 m_buttonGroup[0]->paint(painter, repaintArea); 0770 m_buttonGroup[2]->paint(painter, repaintArea); 0771 0772 foreach (QPointer<KDecoration2::DecorationButton> button, m_buttonGroup[0]->buttons()) { 0773 static_cast<DecorationButton *>(button.data())->paintGlow(painter, repaintArea); 0774 } 0775 foreach (QPointer<KDecoration2::DecorationButton> button, m_buttonGroup[2]->buttons()) { 0776 static_cast<DecorationButton *>(button.data())->paintGlow(painter, repaintArea); 0777 } 0778 painter->restore(); 0779 } 0780 0781 #if 0 0782 void Decoration::paintEvent(QPaintEvent */*event */) 0783 { 0784 DecorationFactory *decorationFactory = static_cast<DecorationFactory *>(factory()); 0785 window_settings *ws = decorationFactory->windowSettings(); 0786 const Config *config = decorationFactory->config(); 0787 bool border = !(maximizeMode() == MaximizeFull && !options()->moveResizeMaximizedWindows()); 0788 bool active = isActive(); 0789 QPainter painter(widget()); 0790 0791 QSize size(widget()->size()); 0792 #if KDE_IS_VERSION(4,3,0) 0793 size -= QSize(layoutMetric(LM_OuterPaddingLeft, true) + layoutMetric(LM_OuterPaddingRight, true), 0794 layoutMetric(LM_OuterPaddingTop, true) + layoutMetric(LM_OuterPaddingBottom, true)); 0795 #endif 0796 0797 painter.setFont(options()->font(active)); 0798 Qt::Alignment alignment = Qt::AlignHCenter; 0799 if (ws->tobj_layout) { 0800 alignment = parseTitleAlignment(ws->tobj_layout); 0801 } 0802 QRect labelRect = titleRect().adjusted(0, 0, 1, 1); 0803 0804 QString text = painter.fontMetrics().elidedText(caption(), Qt::ElideMiddle, labelRect.width()); 0805 int state = 0; 0806 #if 0 0807 if (maximizeMode() & MaximizeHorizontal) state |= WNCK_WINDOW_STATE_MAXIMIZED_HORIZONTALLY; 0808 if (maximizeMode() & MaximizeVertical) state |= WNCK_WINDOW_STATE_MAXIMIZED_VERTICALLY; 0809 #else 0810 if (!border) state |= WNCK_WINDOW_STATE_MAXIMIZED_HORIZONTALLY | WNCK_WINDOW_STATE_MAXIMIZED_VERTICALLY; 0811 #endif 0812 if (isShade()) state |= WNCK_WINDOW_STATE_SHADED; 0813 if (isOnAllDesktops()) state |= WNCK_WINDOW_STATE_STICKY; 0814 if (keepAbove()) state |= WNCK_WINDOW_STATE_ABOVE; 0815 if (keepBelow()) state |= WNCK_WINDOW_STATE_BELOW; 0816 0817 if (!border) { 0818 size += QSize(layoutMetric(LM_TitleEdgeLeft, false) + layoutMetric(LM_TitleEdgeRight, false), 0); 0819 } 0820 QRect titleRect = painter.boundingRect(labelRect, alignment | Qt::AlignVCenter | Qt::TextSingleLine, text); 0821 #if KDE_IS_VERSION(4,3,0) 0822 titleRect.adjust(-layoutMetric(LM_OuterPaddingLeft, true), 0, -layoutMetric(LM_OuterPaddingLeft, true), 0); 0823 #endif 0824 if (!border) { 0825 titleRect.adjust(layoutMetric(LM_TitleEdgeLeft, false), 0, layoutMetric(LM_TitleEdgeLeft, false), 0); 0826 } 0827 QImage decoImage = static_cast<DecorationFactory *>(factory())->decorationImage(size, active, state, titleRect); 0828 0829 #if KDE_IS_VERSION(4,3,0) 0830 const int paddingLeft = layoutMetric(LM_OuterPaddingLeft, true); 0831 const int paddingTop = layoutMetric(LM_OuterPaddingTop, true); 0832 #else 0833 const int paddingLeft = 0; 0834 const int paddingTop = 0; 0835 #endif 0836 QRect outerRect(paddingLeft, paddingTop, size.width(), size.height()); 0837 QRect innerRect = outerRect.adjusted(layoutMetric(LM_BorderLeft, true), layoutMetric(LM_TitleHeight, true), 0838 -layoutMetric(LM_BorderRight, true), -layoutMetric(LM_BorderBottom, true)); 0839 0840 #if KDE_IS_VERSION(4,3,0) 0841 if (border && !config->useKWinShadows) { 0842 paintShadow(&painter, outerRect, config->shadowSettings, config->shadowImage); 0843 } 0844 #endif 0845 0846 if (border) { 0847 painter.drawImage(outerRect.x(), outerRect.y(), decoImage, 0848 0, 0, outerRect.width(), innerRect.y() - outerRect.y()); 0849 painter.drawImage(outerRect.x(), innerRect.y() + innerRect.height(), decoImage, 0850 0, outerRect.height() - (outerRect.bottom() - innerRect.bottom()), 0851 outerRect.width(), outerRect.bottom() - innerRect.bottom()); 0852 painter.drawImage(outerRect.x(), innerRect.y(), decoImage, 0853 0, innerRect.y() - outerRect.y(), 0854 innerRect.x() - outerRect.x(), innerRect.height()); 0855 painter.drawImage(innerRect.x() + innerRect.width(), innerRect.y(), decoImage, 0856 outerRect.width() - (outerRect.right() - innerRect.right()), innerRect.y() - outerRect.y(), 0857 outerRect.right() - innerRect.right(), innerRect.height()); 0858 } else { 0859 painter.drawImage(outerRect.x(), outerRect.y(), decoImage, 0860 layoutMetric(LM_TitleEdgeLeft, false), 0, outerRect.width(), innerRect.y() - outerRect.y()); 0861 } 0862 0863 frame_settings *fs = active ? ws->fs_act : ws->fs_inact; 0864 0865 QColor shadowColor = QColor(0, 0, 0, 255); 0866 QColor textColor = options()->color(ColorFont, active); 0867 int textHaloXOffset = 1; 0868 int textHaloYOffset = 1; 0869 int textHaloSize = 2; 0870 if (!config->useKWinTextColors) { 0871 alpha_color &c = fs->text_halo; 0872 shadowColor = QColor::fromRgbF(c.color.r, c.color.g, c.color.b, c.alpha); 0873 c = fs->text; 0874 textColor = QColor::fromRgbF(c.color.r, c.color.g, c.color.b, c.alpha); 0875 } 0876 QPixmap shadowText = Plasma::PaintUtils::shadowText(text, painter.font(), textColor, shadowColor, QPoint(0, 0), 2); 0877 // widget()->style()->drawItemPixmap(&painter, labelRect.adjusted(-2, -2, 2, 2), alignment | Qt::AlignVCenter, shadowText); 0878 } 0879 #endif 0880 0881 DecorationButton::DecorationButton(KDecoration2::DecorationButtonType type, Decoration *parent) 0882 : KDecoration2::DecorationButton(type, parent) 0883 , m_hoverProgress(0.0) 0884 { 0885 /* */ 0886 } 0887 0888 DecorationButton::~DecorationButton() 0889 { 0890 /* */ 0891 } 0892 0893 void DecorationButton::paint(QPainter *painter, const QRect &repaintArea) 0894 { 0895 Decoration *decoration = static_cast<Decoration *>(KDecoration2::DecorationButton::decoration().data()); 0896 KDecoration2::DecoratedClient *client = decoration->client().data(); 0897 DecorationFactory *decorationFactory =decoration->factory(); 0898 window_settings *ws = decorationFactory->windowSettings(); 0899 const bool active = client->isActive(); 0900 const bool down = isPressed(); 0901 QRect rect = geometry().toRect(); 0902 0903 int state = 0; 0904 if (down) { 0905 state = 2; 0906 } 0907 if (!active) { 0908 state += 3; 0909 } 0910 0911 if (type() == KDecoration2::DecorationButtonType::Menu) { 0912 client->icon().paint(painter, rect); 0913 } else { 0914 int glyph = decoration->buttonGlyph(type()); 0915 if (glyph == -1) { 0916 return; 0917 } 0918 if (ws->use_pixmap_buttons) { 0919 QImage image = ws->ButtonPix[state + glyph * S_COUNT]->image; 0920 if (!down) { 0921 image = hoverImage(image, ws->ButtonPix[state + 1 + glyph * S_COUNT]->image, m_hoverProgress); 0922 } 0923 painter->drawImage(rect.x(), rect.y() + ws->button_offset, image); 0924 } else { 0925 state = 0; 0926 if (down) state |= PRESSED_EVENT_WINDOW; 0927 if (isHovered()) state |= IN_EVENT_WINDOW; 0928 QImage buttonImage = decorationFactory->buttonImage(QSize(16, 16), active, glyph, state); 0929 0930 painter->drawImage(rect.x(), rect.y() + ws->button_offset, buttonImage); 0931 } 0932 } 0933 } 0934 0935 void DecorationButton::paintGlow(QPainter *painter, const QRect &repaintArea) 0936 { 0937 if (m_hoverProgress > 0.0 && isVisible() && type() != KDecoration2::DecorationButtonType::Menu) { 0938 Decoration *decoration = static_cast<Decoration *>(KDecoration2::DecorationButton::decoration().data()); 0939 KDecoration2::DecoratedClient *client = decoration->client().data(); 0940 DecorationFactory *decorationFactory =decoration->factory(); 0941 window_settings *ws = decorationFactory->windowSettings(); 0942 const bool active = client->isActive(); 0943 0944 QRect rect = geometry().toRect(); 0945 int glyph = decoration->buttonGlyph(type()); 0946 if (glyph == -1) { 0947 return; 0948 } 0949 QImage image; 0950 0951 if (active && ws->use_button_glow) { 0952 image = ws->ButtonGlowPix[glyph]->image; 0953 } else if (!active && ws->use_button_inactive_glow) { 0954 image = ws->ButtonInactiveGlowPix[glyph]->image; 0955 } 0956 if (!image.isNull() && ws->use_pixmap_buttons) { 0957 QImage buttonImage = ws->ButtonPix[glyph * S_COUNT]->image; 0958 painter->setOpacity(m_hoverProgress); 0959 const int xp = rect.x() + (buttonImage.width() - ws->c_glow_size.w) / 2; 0960 const int yp = rect.y() + (buttonImage.height() - ws->c_glow_size.h) / 2; 0961 painter->drawImage(xp, yp + ws->button_offset, image); 0962 } 0963 } 0964 } 0965 0966 void DecorationButton::hoverEnterEvent(QHoverEvent *event) 0967 { 0968 KDecoration2::DecorationButton::hoverEnterEvent(event); 0969 if (isHovered()) { 0970 startHoverAnimation(1.0); 0971 } 0972 } 0973 0974 void DecorationButton::hoverLeaveEvent(QHoverEvent *event) 0975 { 0976 KDecoration2::DecorationButton::hoverLeaveEvent(event); 0977 if (!isHovered()) { 0978 startHoverAnimation(0.0); 0979 } 0980 } 0981 0982 qreal DecorationButton::hoverProgress() const 0983 { 0984 return m_hoverProgress; 0985 } 0986 0987 void DecorationButton::setHoverProgress(qreal hoverProgress) 0988 { 0989 if (m_hoverProgress != hoverProgress) { 0990 m_hoverProgress = hoverProgress; 0991 if (static_cast<Decoration *>(decoration().data())) { 0992 update(geometry().adjusted(-32, -32, 32, 32)); 0993 } 0994 } 0995 } 0996 0997 void DecorationButton::startHoverAnimation(qreal endValue) 0998 { 0999 DecorationFactory *decorationFactory = static_cast<Decoration *>(decoration().data())->factory(); 1000 const Config *config = decorationFactory->config(); 1001 QPropertyAnimation *hoverAnimation = m_hoverAnimation.data(); 1002 1003 if (hoverAnimation) { 1004 if (hoverAnimation->endValue() == endValue) { 1005 return; 1006 } 1007 hoverAnimation->stop(); 1008 } else if (m_hoverProgress != endValue) { 1009 if (config->hoverDuration < 10) { 1010 setHoverProgress(endValue); 1011 return; 1012 } 1013 hoverAnimation = new QPropertyAnimation(this, "hoverProgress"); 1014 m_hoverAnimation = hoverAnimation; 1015 } else { 1016 return; 1017 } 1018 hoverAnimation->setEasingCurve(QEasingCurve::OutQuad); 1019 hoverAnimation->setStartValue(m_hoverProgress); 1020 hoverAnimation->setEndValue(endValue); 1021 hoverAnimation->setDuration(1 + qRound(config->hoverDuration * qAbs(m_hoverProgress - endValue))); 1022 hoverAnimation->start(); 1023 } 1024 1025 }; // namespace Smaragd 1026 1027 #include "kwin_smaragd.moc"