--- client.cpp.orig 2003-05-07 07:44:18.000000000 -0400 +++ client.cpp.orig 2003-06-10 04:57:04.000000000 -0400 @@ -4,6 +4,7 @@ Copyright (C) 1999, 2000 Matthias Ettrich ******************************************************************/ //#define QT_CLEAN_NAMESPACE +#include #include #include #include @@ -42,8 +43,23 @@ #define IconicState XIconicState #endif +// wait 200 ms before drawing shadow after move/resize +static const int SHADOW_DELAY = 200; + namespace KWinInternal { +/* TODO: Remove this once X has real translucency. + * + * A list of the regions covered by all shadows and the Clients to which they + * belong. Used to redraw shadows when a window overlapping or underlying a + * shadow is moved, resized, or hidden. + */ +struct ShadowRegion { + QRegion region; + Client *client; +}; +static QValueList shadowRegions; + // NET WM Protocol handler class class WinInfo : public NETWinInfo { @@ -97,6 +113,12 @@ public: ClientPrivate() {}; QCString windowRole; + QWidget *shadowWidget; + QMemArray activeOpacityCache; + QMemArray inactiveOpacityCache; + QMemArray *opacityCache; + QRegion shapeBoundingRegion; + QTimer *shadowDelayTimer; }; }; @@ -500,6 +522,12 @@ NET::WMIconGeometry ; + d->shadowDelayTimer = new QTimer(this); + d->opacityCache = &d->activeOpacityCache; + d->shadowWidget = NULL; + connect(d->shadowDelayTimer, SIGNAL(timeout()), this, SLOT(slotDrawShadow())); + connect(wspace, SIGNAL(reconfigured()), SLOT(slotUpdateOpacityCache())); + info = new WinInfo( this, qt_xdisplay(), win, qt_xrootwin(), properties ); wwrap = new WindowWrapper( w, this ); @@ -601,6 +629,7 @@ { if (moveResizeMode) stopMoveResize(); + slotRemoveShadow(); releaseWindow(); delete info; delete d; @@ -608,6 +637,7 @@ void Client::startMoveResize() { + slotRemoveShadow(); moveResizeMode = true; workspace()->setClientIsMoving(this); grabMouse( cursor() ); @@ -628,6 +658,10 @@ workspace()->setClientIsMoving(0); moveResizeMode = false; update(); + if (options->shadowEnabled(active)) { + drawIntersectingShadows(); + slotDrawDelayedShadow(); + } } /*! @@ -794,7 +828,7 @@ // initial desktop placement - note we don't clobber desk if it is // set to some value, in case the initial desktop code in the // constructor has already set a value for us - + if ( session ) { desk = session->desktop; if ( desk <= 0 ) @@ -829,7 +863,7 @@ info->setDesktop( NETWinInfo::OnAllDesktops ); } else info->setDesktop( desk ); - + if (isInitial) { setMappingState( init_state ); @@ -879,7 +913,7 @@ if( isTopMenu() && workspace()->activeClient() != mainClient()) doNotShow = true; - + bool showMe = (state == NormalState) && isOnDesktop( workspace()->currentDesktop() ); workspace()->clientReady( this ); // will call Workspace::propagateClients() @@ -1147,6 +1181,7 @@ { if ( e.event != windowWrapper()->winId() && !e.send_event ) return TRUE; + drawIntersectingShadows(); switch ( mappingState() ) { case IconicState: @@ -1206,7 +1241,7 @@ } /*! - Handles configure requests of the client window + Handles configure requests of the client window */ bool Client::configureRequest( XConfigureRequestEvent& e ) { @@ -1639,12 +1674,24 @@ } } +void Client::moveEvent(QMoveEvent *e) +{ + QWidget::moveEvent(e); -/*! - */ -void Client::resizeEvent( QResizeEvent * e) + if (!moveResizeMode) { + // If the user is manually resizing, let Client::stopMoveResize() + // decide when to redraw the shadow + slotRemoveShadow(); + drawIntersectingShadows(); + if (options->shadowEnabled(isActive())) + slotDrawDelayedShadow(); + } +} + +void Client::resizeEvent(QResizeEvent *e) { QWidget::resizeEvent( e ); + slotUpdateOpacityCache(); } @@ -1833,6 +1880,9 @@ setMappingState( NormalState ); QWidget::show(); windowWrapper()->map(); + + if (options->shadowEnabled(active)) + slotDrawDelayedShadow(); } /*! @@ -1841,6 +1891,9 @@ */ void Client::hide() { + d->shadowDelayTimer->stop(); + slotRemoveShadow(); + drawIntersectingShadows(); QWidget::hide(); workspace()->clientHidden( this ); windowWrapper()->unmap(); @@ -1918,6 +1971,9 @@ */ void Client::shadeChange( bool ) { + slotRemoveShadow(); + if (options->shadowEnabled(active)) + slotDrawDelayedShadow(); } @@ -2161,17 +2217,216 @@ */ bool Client::eventFilter( QObject *o, QEvent * e) { - if ( o != wwrap ) - return FALSE; - switch ( e->type() ) { - case QEvent::Show: - windowWrapperShowEvent( (QShowEvent*)e ); - break; - case QEvent::Hide: - windowWrapperHideEvent( (QHideEvent*)e ); - break; - default: - break; + if ( o == wwrap ) { + switch ( e->type() ) { + case QEvent::Show: + windowWrapperShowEvent( (QShowEvent*)e ); + break; + case QEvent::Hide: + windowWrapperHideEvent( (QHideEvent*)e ); + break; + default: + break; + } + } + else if (o == d->shadowWidget) { + if (e->type() == QEvent::MouseButtonRelease) { + int buttonMask, buttonPressed, x, y, x_root, y_root; + unsigned int mask; + QMouseEvent *qe = (QMouseEvent *)e; + Window inner_window, parent_window, pointer_window, root_window; + XButtonEvent xe; + + slotRemoveShadow(); + switch (qe->button()) { + case Qt::MidButton: + buttonMask = Button2Mask; + buttonPressed = Button2; + break; + case Qt::RightButton: + buttonMask = Button3Mask; + buttonPressed = Button3; + break; + default: + buttonMask = Button1Mask; + buttonPressed = Button1; + break; + } + + // find the window under the cursor that should receive the + // simulated events + root_window = qt_xrootwin(); + XQueryPointer(qt_xdisplay(), root_window, &root_window, + &pointer_window, &x_root, &y_root, &x, &y, &mask); + + if (pointer_window != None) { + // Save the child window immediately under the window + // decoration, if any. This is so that we can send an event to + // the immediate descendant of a window's window decoration, + // which causes KWin to refocus windows properly + parent_window = pointer_window; + XQueryPointer(qt_xdisplay(), parent_window, &root_window, + &pointer_window, &x_root, &y_root, &x, &y, &mask); + inner_window = pointer_window; + + while (pointer_window != None) { + // Recursively query for the child window under the pointer, + // using the returned child window as the parent window for + // the subsequent query. When no child window is left, we've + // found the child that will receive the simulated event + parent_window = pointer_window; + XQueryPointer(qt_xdisplay(), parent_window, &root_window, + &pointer_window, &x_root, &y_root, &x, &y, &mask); + } + pointer_window = parent_window; + } + else + inner_window = None; + + // simulate a mouse button press + xe.type = ButtonPress; + xe.display = qt_xdisplay(); + xe.root = qt_xrootwin(); + xe.subwindow = None; + xe.time = CurrentTime; + xe.x = x; + xe.y = y; + xe.x_root = x_root; + xe.y_root = y_root; + xe.state = 0; + xe.button = buttonPressed; + xe.same_screen = True; + if (inner_window != None && inner_window != pointer_window) { + xe.window = inner_window; + XSendEvent(qt_xdisplay(), inner_window, True, ButtonPressMask, + (XEvent *)&xe); + } + xe.window = pointer_window; + XSendEvent(qt_xdisplay(), pointer_window, True, ButtonPressMask, + (XEvent *)&xe); + + // simulate a mouse button release + xe.type = ButtonRelease; + xe.display = qt_xdisplay(); + xe.root = qt_xrootwin(); + xe.subwindow = None; + xe.time = CurrentTime; + xe.x = x; + xe.y = y; + xe.x_root = x_root; + xe.y_root = y_root; + xe.state = buttonMask; + xe.button = buttonPressed; + xe.same_screen = True; + if (inner_window != None && inner_window != pointer_window) { + xe.window = inner_window; + XSendEvent(qt_xdisplay(), inner_window, True, ButtonReleaseMask, + (XEvent *)&xe); + } + xe.window = pointer_window; + XSendEvent(qt_xdisplay(), pointer_window, True, ButtonReleaseMask, + (XEvent *)&xe); + + slotDrawDelayedShadow(); + + return TRUE; + } + else if (e->type() == QEvent::Wheel) { + int x, y, x_root, y_root; + unsigned int buttonMask, buttonPressed, mask; + QWheelEvent *wheelEvent = (QWheelEvent *)e; + Window inner_window, parent_window, pointer_window, + root_window; + XButtonEvent xe; + + slotRemoveShadow(); + + // state and button parameters passed to XSendEvent depend on the + // direction in which the mouse wheel was rolled + buttonMask = wheelEvent->delta() > 0 ? Button4Mask : Button5Mask; + buttonPressed = wheelEvent->delta() > 0 ? Button4 : Button5; + + // find the window under the cursor that should receive the + // simulated events + root_window = qt_xrootwin(); + XQueryPointer(qt_xdisplay(), root_window, &root_window, + &pointer_window, &x_root, &y_root, &x, &y, &mask); + + if (pointer_window != None) { + // Save the child window immediately under the window + // decoration, if any. This is so that we can send an event to + // the immediate descendant of a window's window decoration, + // which causes KWin to refocus windows properly + parent_window = pointer_window; + XQueryPointer(qt_xdisplay(), parent_window, &root_window, + &pointer_window, &x_root, &y_root, &x, &y, &mask); + inner_window = pointer_window; + + while (pointer_window != None) { + // Recursively query for the child window under the pointer, + // using the returned child window as the parent window for + // the subsequent query. When no child window is left, we've + // found the child that will receive the simulated event + parent_window = pointer_window; + XQueryPointer(qt_xdisplay(), parent_window, &root_window, + &pointer_window, &x_root, &y_root, &x, &y, &mask); + } + pointer_window = parent_window; + } + else + inner_window = None; + + // simulate a mouse button press + xe.type = ButtonPress; + xe.display = qt_xdisplay(); + xe.root = qt_xrootwin(); + xe.subwindow = None; + xe.time = CurrentTime; + xe.x = x; + xe.y = y; + xe.x_root = x_root; + xe.y_root = y_root; + xe.state = 0; + xe.same_screen = True; + if (inner_window != None && inner_window != pointer_window) { + xe.button = buttonPressed; + xe.window = inner_window; + XSendEvent(qt_xdisplay(), inner_window, True, ButtonPressMask, + (XEvent *)&xe); + } + xe.button = buttonPressed; + xe.window = pointer_window; + XSendEvent(qt_xdisplay(), pointer_window, True, ButtonPressMask, + (XEvent *)&xe); + + // simulate a mouse button release + xe.type = ButtonRelease; + xe.display = qt_xdisplay(); + xe.root = qt_xrootwin(); + xe.subwindow = None; + xe.time = CurrentTime; + xe.x = x; + xe.y = y; + xe.x_root = x_root; + xe.y_root = y_root; + xe.same_screen = True; + if (inner_window != None && inner_window != pointer_window) { + xe.window = inner_window; + xe.state = buttonMask; + xe.button = buttonPressed; + XSendEvent(qt_xdisplay(), inner_window, True, ButtonReleaseMask, + (XEvent *)&xe); + } + xe.state = buttonMask; + xe.button = buttonPressed; + xe.window = pointer_window; + XSendEvent(qt_xdisplay(), pointer_window, True, ButtonReleaseMask, + (XEvent *)&xe); + + slotDrawDelayedShadow(); + + return TRUE; + } } return FALSE; @@ -2485,8 +2740,26 @@ if ( active == act ) return; active = act; - if ( active ) + if ( active ) { + if (options->shadowEnabled(true)) { + if (options->shadowEnabled(false)) { + // Wait for inactive shadow to expose occluded windows and give + // them a chance to redraw before painting the active shadow + slotRemoveShadow(); + slotDrawDelayedShadow(); + } + else + slotDrawShadow(); + } + Events::raise( Events::Activate ); + } + else { + slotRemoveShadow(); + + if (options->shadowEnabled(false)) + slotDrawDelayedShadow(); + } if ( !active && autoRaiseTimer ) { delete autoRaiseTimer; @@ -2496,6 +2769,488 @@ activeChange( active ); } +void Client::slotUpdateOpacityCache() +{ + if (!options->shadowEnabled(isActive())) + return; + + if (!d->activeOpacityCache.isNull()) + d->activeOpacityCache.resize(0); + if (!d->inactiveOpacityCache.isNull()) + d->inactiveOpacityCache.resize(0); + + if (!moveResizeMode) { + // If the user is manually resizing, let Client::stopMoveResize() + // decide when to redraw the shadow + slotRemoveShadow(); + drawIntersectingShadows(); + if (options->shadowEnabled(isActive())) + slotDrawDelayedShadow(); + } +} + +/*! + Redraw shadows that were previously occluding or occluded by this window, + to avoid visual glitches. + */ +void Client::drawIntersectingShadows() { + QRegion region; + QValueList::Iterator it; + QPtrList reshadowClients; + + if (!options->shadowEnabled(false)) + // No point in redrawing overlapping/overpalled shadows if only the + // active window has a shadow. + return; + + region = d->shapeBoundingRegion; + + // Generate list of Clients whose shadows that need to be redrawn. That is, + // those that are currently overlapping or overlapped by other windows or + // shadows. + for (it = shadowRegions.begin(); it != shadowRegions.end(); ++it) + if ((*it).client->isOnDesktop(desktop()) && + !(*it).region.intersect(region).isEmpty()) + reshadowClients.append((*it).client); + + // Redraw shadows for each of the Clients in the list generated above + QPtrListIterator it2(reshadowClients); + while (it2.current() != 0) { + it2.current()->slotRemoveShadow(); + it2.current()->slotDrawDelayedShadow(); + ++it2; + } +} + +/*! + Draw shadow after some time has elapsed, to give recently exposed windows a + chance to repaint before a shadow gradient is drawn over them. + */ +void Client::slotDrawDelayedShadow() { + d->shadowDelayTimer->start(SHADOW_DELAY, true); +} + +/*! + Draw a shadow under this window and XShape the shadow accordingly. + */ +void Client::slotDrawShadow() +{ + XRectangle *shapes; + int i, count, ordering; + + /* Store this window's ShapeBoundingRegion even if shadows aren't drawn for + * this type of window. Otherwise, drawIntersectingShadows() won't update + * properly when this window is moved/resized/hidden/closed. + */ + shapes = XShapeGetRectangles(qt_xdisplay(), winId(), ShapeBounding, + &count, &ordering); + if (!shapes) + // XShape extension not supported + d->shapeBoundingRegion = QRegion(x(), y(), width(), height()); + else { + d->shapeBoundingRegion = QRegion(); + for (i = 0; i < count; i++) { + // Translate XShaped window into a QRegion + QRegion shapeRectangle(shapes[i].x, shapes[i].y, shapes[i].width, + shapes[i].height); + d->shapeBoundingRegion += shapeRectangle; + } + d->shapeBoundingRegion.translate(x(), y()); + } + + if (isHidden() || isMaximized() || + !options->shadowWindowType(windowType())) { + XFree(shapes); + return; + } + + slotRemoveShadow(); + + QMemArray pixelData; + QPixmap shadowPixmap; + QRect shadow; + QRegion exposedRegion; + ShadowRegion shadowRegion; + int thickness, xOffset, yOffset; + + thickness = options->shadowThickness(isActive()); + xOffset = options->shadowXOffset(isActive()); + yOffset = options->shadowYOffset(isActive()); + d->opacityCache = active? &d->activeOpacityCache : &d->inactiveOpacityCache; + + shadow.setRect(x() - thickness + xOffset, y() - thickness + yOffset, + width() + thickness * 2, height() + thickness * 2); + shadowPixmap.resize(shadow.size()); + + // Create a fake drop-down shadow effect via blended Xwindows + d->shadowWidget = new QWidget(0, 0, WStyle_Customize | WX11BypassWM); + d->shadowWidget->setGeometry(shadow); + XSelectInput(qt_xdisplay(), d->shadowWidget->winId(), + ButtonPressMask | ButtonReleaseMask | StructureNotifyMask); + d->shadowWidget->installEventFilter(this); + + shapes = XShapeGetRectangles(qt_xdisplay(), winId(), ShapeBounding, + &count, &ordering); + + if (!shapes) { + // XShape extension not supported + exposedRegion = getExposedRegion(d->shapeBoundingRegion, shadow.x(), + shadow.y(), shadow.width(), shadow.height(), thickness, + xOffset, yOffset); + shadowRegion.region = exposedRegion; + shadowRegion.client = this; + shadowRegions.append(shadowRegion); + + if (d->opacityCache->isNull()) + imposeRegionShadow(shadowPixmap, d->shapeBoundingRegion, + exposedRegion, thickness, + options->shadowOpacity(isActive())); + else + imposeCachedShadow(shadowPixmap, exposedRegion); + } + else { + QMemArray exposedRects; + QMemArray::Iterator it, itEnd; + XRectangle *shadowShapes; + + exposedRegion = getExposedRegion(d->shapeBoundingRegion, shadow.x(), + shadow.y(), shadow.width(), shadow.height(), thickness, + xOffset, yOffset); + shadowRegion.region = exposedRegion; + shadowRegion.client = this; + shadowRegions.append(shadowRegion); + + // XShape the shadow + exposedRects = exposedRegion.rects(); + i = 0; + itEnd = exposedRects.end(); + shadowShapes = new XRectangle[exposedRects.count()]; + for (it = exposedRects.begin(); it != itEnd; ++it) { + shadowShapes[i].x = (*it).x(); + shadowShapes[i].y = (*it).y(); + shadowShapes[i].width = (*it).width(); + shadowShapes[i].height = (*it).height(); + i++; + } + XShapeCombineRectangles(qt_xdisplay(), d->shadowWidget->winId(), + ShapeBounding, -x() + thickness - xOffset, + -y() + thickness - yOffset, shadowShapes, i, ShapeSet, + Unsorted); + delete [] shadowShapes; + + if (d->opacityCache->isNull()) + imposeRegionShadow(shadowPixmap, d->shapeBoundingRegion, + exposedRegion, thickness, + options->shadowOpacity(isActive())); + else + imposeCachedShadow(shadowPixmap, exposedRegion); + } + + XFree(shapes); + + // Set the background pixmap + //shadowPixmap.convertFromImage(shadowImage); + d->shadowWidget->setErasePixmap(shadowPixmap); + + // Restack shadows under this window so that shadows drawn for a newly + // focused (but not raised) window don't overlap any windows above it. + restackShadows(); + + // Don't use QWidget::show() so we don't confuse QEffects, thus causing + // broken focus. + XMapWindow(qt_xdisplay(), d->shadowWidget->winId()); +} + +/*! + Remove shadow under this window. + */ +void Client::slotRemoveShadow() +{ + QValueList::Iterator it; + + d->shadowDelayTimer->stop(); + + if (d->shadowWidget != NULL) { + for (it = shadowRegions.begin(); it != shadowRegions.end(); ++it) + if ((*it).client == this) { + shadowRegions.remove(it); + break; + } + delete d->shadowWidget; + d->shadowWidget = NULL; + } +} + +/*! + Calculate regions in which the shadow will be visible given the window's + origin, height and width and the shadow's thickness, and X- and Y-offsets. + */ +QRegion Client::getExposedRegion(QRegion occludedRegion, int x, int y, int w, + int h, int thickness, int xOffset, int yOffset) +{ + QRegion exposedRegion; + + exposedRegion = QRegion(x, y, w, h); + exposedRegion -= occludedRegion; + + if (thickness > 0) { + // Limit exposedRegion to include only where a shadow of the specified + // thickness will be drawn + QMemArray occludedRects; + QMemArray::Iterator it, itEnd; + QRegion shadowRegion; + + occludedRects = occludedRegion.rects(); + itEnd = occludedRects.end(); + for (it = occludedRects.begin(); it != itEnd; ++it) { + // Expand each of the occluded region's shape rectangles to contain + // where a shadow of the specified thickness will be drawn. Create + // a new QRegion that contains the expanded occluded region + it->setTop(it->top() - thickness + yOffset); + it->setLeft(it->left() - thickness + xOffset); + it->setRight(it->right() + thickness + xOffset); + it->setBottom(it->bottom() + thickness + yOffset); + shadowRegion += QRegion(*it); + } + exposedRegion -= exposedRegion - shadowRegion; + } + + return exposedRegion; +} + +/*! + Draw shadow gradient around this window using cached opacity values. + */ +void Client::imposeCachedShadow(QPixmap &pixmap, QRegion exposed) +{ + QPainter painter; + QRgb pixel; + double opacity; + int red, green, blue, pixelRed, pixelGreen, pixelBlue; + int subW, subH, w, h, x, y, zeroX, zeroY; + QImage image; + QMemArray::Iterator it, itEnd; + QMemArray rectangles; + QPixmap subPixmap; + Window rootWindow; + int thickness, windowX, windowY, xOffset, yOffset; + + rectangles = exposed.rects(); + rootWindow = qt_xrootwin(); + thickness = options->shadowThickness(isActive()); + windowX = this->x(); + windowY = this->y(); + xOffset = options->shadowXOffset(isActive()); + yOffset = options->shadowYOffset(isActive()); + options->shadowColour(isActive()).rgb(&red, &green, &blue); + w = pixmap.width(); + h = pixmap.height(); + + painter.begin(&pixmap); + + itEnd = rectangles.end(); + for (it = rectangles.begin(); it != itEnd; ++it) { + subW = (*it).width(); + subH = (*it).height(); + subPixmap = QPixmap::grabWindow(rootWindow, (*it).x(), (*it).y(), + subW, subH); + zeroX = (*it).x() - windowX + thickness - xOffset; + zeroY = (*it).y() - windowY + thickness - yOffset; + image = subPixmap.convertToImage(); + + for (x = 0; x < subW; x++) { + for (y = 0; y < subH; y++) { + opacity = (*(d->opacityCache))[(zeroY + y) * w + zeroX + x]; + pixel = image.pixel(x, y); + pixelRed = qRed(pixel); + pixelGreen = qGreen(pixel); + pixelBlue = qBlue(pixel); + image.setPixel(x, y, + qRgb((int)(pixelRed + (red - pixelRed) * opacity), + (int)(pixelGreen + (green - pixelGreen) * opacity), + (int)(pixelBlue + (blue - pixelBlue) * opacity))); + } + } + + subPixmap.convertFromImage(image); + painter.drawPixmap(zeroX, zeroY, subPixmap); + } + + painter.end(); +} + +/*! + Draw shadow around this window using calculated opacity values. + */ +void Client::imposeRegionShadow(QPixmap &pixmap, QRegion occluded, + QRegion exposed, int thickness, double maxOpacity) +{ + register int distance, intersectCount, i, j, x, y; + QPainter painter; + QRgb pixel; + double decay, factor, opacity; + int red, green, blue, pixelRed, pixelGreen, pixelBlue; + int halfMaxIntersects, lineIntersects, maxIntersects, maxY; + int irBottom, irLeft, irRight, irTop, yIncrement; + int subW, subH, w, h, zeroX, zeroY; + QImage image; + QMemArray::Iterator it, itEnd; + QMemArray rectangles; + QPixmap subPixmap; + Window rootWindow; + int windowX, windowY, xOffset, yOffset; + + rectangles = exposed.rects(); + rootWindow = qt_xrootwin(); + windowX = this->x(); + windowY = this->y(); + xOffset = options->shadowXOffset(isActive()); + yOffset = options->shadowYOffset(isActive()); + options->shadowColour(isActive()).rgb(&red, &green, &blue); + maxIntersects = thickness * thickness * 4 + (thickness * 4) + 1; + halfMaxIntersects = maxIntersects / 2; + lineIntersects = thickness * 2 + 1; + factor = maxIntersects / maxOpacity; + decay = (lineIntersects / 0.01 - factor) / pow((double)maxIntersects, 3.5); + w = pixmap.width(); + h = pixmap.height(); + xOffset = options->shadowXOffset(isActive()); + yOffset = options->shadowYOffset(isActive()); + + d->opacityCache->resize(0); + d->opacityCache->resize(w * h); + occluded.translate(-windowX + thickness, -windowY + thickness); + + painter.begin(&pixmap); + + itEnd = rectangles.end(); + for (it = rectangles.begin(); it != itEnd; ++it) { + subW = (*it).width(); + subH = (*it).height(); + subPixmap = QPixmap::grabWindow(rootWindow, (*it).x(), (*it).y(), + subW, subH); + maxY = subH; + zeroX = (*it).x() - windowX + thickness - xOffset; + zeroY = (*it).y() - windowY + thickness - yOffset; + image = subPixmap.convertToImage(); + + intersectCount = 0; + opacity = -1; + y = 0; + yIncrement = 1; + for (x = 0; x < subW; x++) { + irLeft = zeroX + x - thickness; + irRight = zeroX + x + thickness; + + while (y != maxY) { + // horizontal row about to leave the intersect region, not + // necessarily the top row + irTop = zeroY + y - thickness * yIncrement; + // horizontal row that just came into the intersect region, + // not necessarily the bottom row + irBottom = zeroY + y + thickness * yIncrement; + + if (opacity == -1) { + // If occluded pixels caused an intersect count to be + // skipped, recount it + intersectCount = 0; + + for (j = irTop; j != irBottom; j += yIncrement) { + // irTop is not necessarily larger than irBottom and + // yIncrement isn't necessarily positive + for (i = irLeft; i <= irRight; i++) { + if (occluded.contains(QPoint(i, j))) + intersectCount++; + } + } + } + else { + if (intersectCount < 0) + intersectCount = 0; + + for (i = irLeft; i <= irRight; i++) { + if (occluded.contains(QPoint(i, irBottom))) + intersectCount++; + } + } + + distance = maxIntersects - intersectCount; + opacity = intersectCount / (factor + pow((double)distance, 3.5) * decay); + + (*(d->opacityCache))[(zeroY + y) * w + zeroX + x] = opacity; + pixel = image.pixel(x, y); + pixelRed = qRed(pixel); + pixelGreen = qGreen(pixel); + pixelBlue = qBlue(pixel); + image.setPixel(x, y, + qRgb((int)(pixelRed + (red - pixelRed) * opacity), + (int)(pixelGreen + (green - pixelGreen) * opacity), + (int)(pixelBlue + (blue - pixelBlue) * opacity))); + + for (i = irLeft; i <= irRight; i++) { + if (occluded.contains(QPoint(i, irTop))) + intersectCount--; + } + + y += yIncrement; + } + y -= yIncrement; + + irTop += yIncrement; + for (j = irTop; j != irBottom; j += yIncrement) { + if (occluded.contains(QPoint(irLeft, j))) + intersectCount--; + } + irRight++; + for (j = irTop; j != irBottom; j += yIncrement) { + if (occluded.contains(QPoint(irRight, j))) + intersectCount++; + } + + yIncrement *= -1; + if (yIncrement < 0) + // Scan Y-axis bottom-up for next X-coordinate iteration + maxY = -1; + else + // Scan Y-axis top-down for next X-coordinate iteration + maxY = subH; + } + + subPixmap.convertFromImage(image); + painter.drawPixmap(zeroX, zeroY, subPixmap); + } + + painter.end(); +} + +/*! + Returns X window ID of the shadow widget. + */ +Window Client::shadowId() const +{ + return d->shadowWidget != NULL ? d->shadowWidget->winId() : None; +} + +/* + * Restack shadow windows so that they are immediately under the window they + * belong to. + */ +void Client::restackShadows() +{ + Window shadows[2]; + + if (isDock()) { + shadows[0] = workspace()->stackingOrder().first()->winId(); + shadows[1] = d->shadowWidget->winId(); + } + else { + shadows[0] = winId(); + if (d->shadowWidget != NULL) + shadows[1] = d->shadowWidget->winId(); + } + + XRestackWindows(qt_xdisplay(), shadows, 2); +} /*! Sets the window's sticky property to b --- client.h.orig 2003-05-07 07:44:18.000000000 -0400 +++ client.h.orig 2003-06-10 04:45:50.000000000 -0400 @@ -222,9 +222,13 @@ void updateUserTime(); const QPoint gravitate( bool invert ) const; - + void NETMoveResize( int x_root, int y_root, NET::Direction direction ); + void restackShadows(); + Window shadowId() const; + void drawIntersectingShadows(); + public slots: void iconify(); void closeWindow(); @@ -242,6 +246,7 @@ void mousePressEvent( QMouseEvent * ); void mouseReleaseEvent( QMouseEvent * ); void mouseMoveEvent( QMouseEvent * ); + void moveEvent( QMoveEvent * ); void resizeEvent( QResizeEvent * ); virtual void windowWrapperShowEvent( QShowEvent* ){} virtual void windowWrapperHideEvent( QHideEvent* ){} @@ -275,6 +280,11 @@ virtual QPixmap animationPixmap( int w ); + QRegion getExposedRegion(QRegion occludedRegion, int x, int y, + int w, int h, int thickness, int xOffset, int yOffset); + void imposeRegionShadow(QPixmap &pixmap, QRegion occluded, + QRegion exposed, int thickness, double maxOpacity = 0.75); + // handlers for X11 events bool mapRequest( XMapRequestEvent& e ); bool unmapNotify( XUnmapEvent& e ); @@ -284,8 +294,14 @@ NETWinInfo * netWinInfo(); +protected slots: + void slotDrawShadow(); + void slotDrawDelayedShadow(); + void slotRemoveShadow(); + private slots: void destroyClient(); + void slotUpdateOpacityCache(); private: QSize sizeForWindowSize( const QSize&, bool ignore_height = FALSE ) const; @@ -358,6 +374,7 @@ QString cap; WId wmClientLeaderWin; void getWmClientLeader(); + void imposeCachedShadow(QPixmap &pixmap, QRegion exposed); public: static QCString staticWindowRole(WId); --- options.cpp.orig 2003-06-10 01:54:58.000000000 -0400 +++ options.cpp.orig 2003-06-10 04:45:50.000000000 -0400 @@ -10,6 +10,7 @@ #include #include #include +#include using namespace KWinInternal; @@ -31,6 +32,21 @@ bool animate_tooltips; int electric_borders; int electric_border_delay; + QColor shadow_colour; + QColor shadow_inactive_colour; + bool shadow_docks; + bool shadow_overrides; + bool shadow_topMenus; + bool shadow_inactive_enabled; + bool shadow_enabled; + double shadow_inactive_opacity; + double shadow_opacity; + int shadow_inactive_thickness; + int shadow_thickness; + int shadow_inactive_x_offset; + int shadow_x_offset; + int shadow_inactive_y_offset; + int shadow_y_offset; }; }; @@ -264,6 +280,23 @@ d->fade_tooltips = globalConfig.readBoolEntry("EffectFadeTooltip", false); d->animate_tooltips = globalConfig.readBoolEntry("EffectAnimateTooltip", false); + // window drop shadows + d->shadow_colour = config->readColorEntry("ShadowColour", &Qt::black); + d->shadow_docks = config->readBoolEntry("ShadowDocks", false); + d->shadow_overrides = config->readBoolEntry("ShadowOverrides", false); + d->shadow_topMenus = config->readBoolEntry("ShadowTopMenus", false); + d->shadow_inactive_colour = config->readColorEntry("InactiveShadowColour", &Qt::black); + d->shadow_inactive_enabled = config->readBoolEntry("InactiveShadowEnabled", false); + d->shadow_inactive_opacity = config->readDoubleNumEntry("InactiveShadowOpacity", 0.75); + d->shadow_inactive_thickness = config->readNumEntry("InactiveShadowThickness", 5); + d->shadow_inactive_x_offset = config->readNumEntry("InactiveShadowXOffset", 0); + d->shadow_inactive_y_offset = config->readNumEntry("InactiveShadowYOffset", 5); + d->shadow_enabled = config->readBoolEntry("ShadowEnabled", false); + d->shadow_opacity = config->readDoubleNumEntry("ShadowOpacity", 0.75); + d->shadow_thickness = config->readNumEntry("ShadowThickness", 10); + d->shadow_x_offset = config->readNumEntry("ShadowXOffset", 0); + d->shadow_y_offset = config->readNumEntry("ShadowYOffset", 10); + emit resetClients(); } @@ -313,6 +346,67 @@ return MouseNothing; } +QColor &Options::shadowColour(bool active) +{ + return active ? d->shadow_colour : d->shadow_inactive_colour; +} + +bool Options::shadowWindowType(NET::WindowType t) +{ + bool retval; + + switch (t) { + case NET::Dialog: + case NET::Normal: + retval = true; + break; + case NET::Desktop: + case NET::Menu: + case NET::Toolbar: + retval = false; + break; + case NET::Dock: + retval = d->shadow_docks; + break; + case NET::Override: + retval = d->shadow_overrides; + break; + case NET::TopMenu: + retval = d->shadow_topMenus; + break; + default: + retval = false; + break; + } + + return retval; +} + +bool Options::shadowEnabled(bool active) +{ + return active ? d->shadow_enabled : d->shadow_inactive_enabled; +} + +double Options::shadowOpacity(bool active) +{ + return active ? d->shadow_opacity : d->shadow_inactive_opacity; +} + +int Options::shadowThickness(bool active) +{ + return active ? d->shadow_thickness : d->shadow_inactive_thickness; +} + +int Options::shadowXOffset(bool active) +{ + return active ? d->shadow_x_offset : d->shadow_inactive_x_offset; +} + +int Options::shadowYOffset(bool active) +{ + return active ? d->shadow_y_offset : d->shadow_inactive_y_offset; +} + QString Options::titleButtonsLeft() { return d->title_buttons_left; --- options.h.orig 2003-06-10 01:54:49.000000000 -0400 +++ options.h.orig 2003-06-10 04:45:50.000000000 -0400 @@ -10,6 +10,7 @@ #include #include #include +#include // increment this when you add a color type (mosfet) #define KWINCOLORS 6 @@ -254,6 +255,45 @@ static MouseCommand mouseCommand(const QString &name); /** + * @returns A QColor representing the colour that window drop shadows should + * be. + */ + QColor &shadowColour(bool active=true); + + /** + * @returns true if shadows should be drawn around windows of the + * specified type + */ + bool shadowWindowType(NET::WindowType t); + + /** + * @returns true if window shadows should be drawn + */ + bool shadowEnabled(bool active=true); + + /** + * @returns Window shadow's opacity between 0.01 and 1.00. + */ + double shadowOpacity(bool active=true); + + /** + * @returns How thick a shadow should be to either side of of a window. + */ + int shadowThickness(bool active=true); + + /** + * @returns Number of pixels along the X-axis by which to offset window + * shadows. + */ + int shadowXOffset(bool active=true); + + /** + * @returns Number of pixels along the Y-axis by which to offset window + * shadows. + */ + int shadowYOffset(bool active=true); + + /** * @returns true if the style should use custom button positions * @see #titleButtonsLeft * @see #titleButtonsRight --- workspace.cpp.orig 2003-05-07 07:44:18.000000000 -0400 +++ workspace.cpp.orig 2003-06-10 04:45:50.000000000 -0400 @@ -1265,12 +1265,36 @@ Client* first = desktops.first(); desktops.remove( first ); desktops.append( first ); - Window* new_stack = new Window[ desktops.count() + 1 ]; - int i = 0; - for ( ClientList::ConstIterator it = desktops.fromLast(); it != desktops.end(); --it) - new_stack[i++] = (*it)->winId(); - XRestackWindows(qt_xdisplay(), new_stack, i); - delete [] new_stack; + int i, j, numDocks; + Window shadow; + Window *dock_shadow_stack, *window_stack; + + dock_shadow_stack = new Window[stacking_order.count() * 2]; + window_stack = new Window[stacking_order.count() * 2]; + i = 0; + j = 0; + for ( ClientList::ConstIterator it = stacking_order.fromLast(); + it != stacking_order.end(); --it) { + window_stack[i++] = (*it)->winId(); + if ((*it)->isDock()) { + if ((shadow = (*it)->shadowId()) != None) + dock_shadow_stack[j++] = shadow; + } + else { + if ((shadow = (*it)->shadowId()) != None) + // If the current window also has a shadow, place it + // immediately under the current window + window_stack[i++] = shadow; + } + } + numDocks = j; + for (j = 0; j < numDocks; j++) + // Shadows for dock windows go at the bottom of the stack + window_stack[i++] = dock_shadow_stack[j++]; + + XRestackWindows(qt_xdisplay(), window_stack, i); + delete [] dock_shadow_stack; + delete [] window_stack; } void Workspace::addClient( Client* c ) @@ -1874,6 +1898,7 @@ else destroyBorderWindows(); + emit reconfigured(); } /*! @@ -1939,12 +1964,36 @@ stacking_order.prepend(c); stacking_order = constrainedStackingOrder( stacking_order ); - Window* new_stack = new Window[ stacking_order.count() + 1 ]; - int i = 0; - for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) - new_stack[i++] = (*it)->winId(); - XRestackWindows(qt_xdisplay(), new_stack, i); - delete [] new_stack; + int i, j, numDocks; + Window shadow; + Window *dock_shadow_stack, *window_stack; + + dock_shadow_stack = new Window[stacking_order.count() * 2]; + window_stack = new Window[stacking_order.count() * 2]; + i = 0; + j = 0; + for ( ClientList::ConstIterator it = stacking_order.fromLast(); + it != stacking_order.end(); --it) { + window_stack[i++] = (*it)->winId(); + if ((*it)->isDock()) { + if ((shadow = (*it)->shadowId()) != None) + dock_shadow_stack[j++] = shadow; + } + else { + if ((shadow = (*it)->shadowId()) != None) + // If the current window also has a shadow, place it + // immediately under the current window + window_stack[i++] = shadow; + } + } + numDocks = j; + for (j = 0; j < numDocks; j++) + // Shadows for dock windows go at the bottom of the stack + window_stack[i++] = dock_shadow_stack[j++]; + + XRestackWindows(qt_xdisplay(), window_stack, i); + delete [] dock_shadow_stack; + delete [] window_stack; propagateClients( TRUE ); @@ -2024,13 +2073,36 @@ } /* end workaround */ - Window* new_stack = new Window[ stacking_order.count() + 1 ]; - int i = 0; - for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) - new_stack[i++] = (*it)->winId(); - XRestackWindows(qt_xdisplay(), new_stack, i); - delete [] new_stack; + int i, j, numDocks; + Window shadow; + Window *dock_shadow_stack, *window_stack; + dock_shadow_stack = new Window[stacking_order.count() * 2]; + window_stack = new Window[stacking_order.count() * 2]; + i = 0; + j = 0; + for ( ClientList::ConstIterator it = stacking_order.fromLast(); + it != stacking_order.end(); --it) { + window_stack[i++] = (*it)->winId(); + if ((*it)->isDock()) { + if ((shadow = (*it)->shadowId()) != None) + dock_shadow_stack[j++] = shadow; + } + else { + if ((shadow = (*it)->shadowId()) != None) + // If the current window also has a shadow, place it + // immediately under the current window + window_stack[i++] = shadow; + } + } + numDocks = j; + for (j = 0; j < numDocks; j++) + // Shadows for dock windows go at the bottom of the stack + window_stack[i++] = dock_shadow_stack[j++]; + + XRestackWindows(qt_xdisplay(), window_stack, i); + delete [] dock_shadow_stack; + delete [] window_stack; propagateClients( TRUE ); @@ -2054,13 +2126,36 @@ stacking_order.remove( c ); stacking_order.insert( it, c ); stacking_order = constrainedStackingOrder( stacking_order ); - Window* new_stack = new Window[ stacking_order.count() + 1 ]; - int i = 0; - for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) { - new_stack[i++] = (*it)->winId(); + int i, j, numDocks; + Window shadow; + Window *dock_shadow_stack, *window_stack; + + dock_shadow_stack = new Window[stacking_order.count() * 2]; + window_stack = new Window[stacking_order.count() * 2]; + i = 0; + j = 0; + for ( ClientList::ConstIterator it = stacking_order.fromLast(); + it != stacking_order.end(); --it) { + window_stack[i++] = (*it)->winId(); + if ((*it)->isDock()) { + if ((shadow = (*it)->shadowId()) != None) + dock_shadow_stack[j++] = shadow; + } + else { + if ((shadow = (*it)->shadowId()) != None) + // If the current window also has a shadow, place it + // immediately under the current window + window_stack[i++] = shadow; + } } - XRestackWindows(qt_xdisplay(), new_stack, i); - delete [] new_stack; + numDocks = j; + for (j = 0; j < numDocks; j++) + // Shadows for dock windows go at the bottom of the stack + window_stack[i++] = dock_shadow_stack[j++]; + + XRestackWindows(qt_xdisplay(), window_stack, i); + delete [] dock_shadow_stack; + delete [] window_stack; propagateClients( TRUE ); } --- workspace.h.orig 2003-06-10 01:54:42.000000000 -0400 +++ workspace.h.orig 2003-06-10 04:45:50.000000000 -0400 @@ -268,6 +268,9 @@ bool isNotManaged( const QString& title ); +signals: + void reconfigured(); + public slots: void refresh(); // keybindings --- kcmkwin/kwindecoration/kwindecoration.cpp.orig 2003-06-10 01:55:12.000000000 -0400 +++ kcmkwin/kwindecoration/kwindecoration.cpp.orig 2003-06-10 04:45:50.000000000 -0400 @@ -10,7 +10,7 @@ Supports new kwin configuration plugins, and titlebar button position modification via dnd interface. - Based on original "kwintheme" (Window Borders) + Based on original "kwintheme" (Window Borders) Copyright (C) 2001 Rik Hemsley (rikkus) */ @@ -25,8 +25,11 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -61,14 +64,14 @@ page1->setMargin( KDialog::marginHint() ); QGroupBox* btnGroup = new QGroupBox( 1, Qt::Horizontal, i18n("Window Decoration"), page1 ); - QWhatsThis::add( btnGroup, + QWhatsThis::add( btnGroup, i18n("Select the window decoration. This is the look and feel of both " "the window borders and the window handle.") ); decorationListBox = new QListBox( btnGroup ); - QGroupBox* checkGroup = new QGroupBox( 1, Qt::Horizontal, + QGroupBox* checkGroup = new QGroupBox( 1, Qt::Horizontal, i18n("General Options (if available)"), page1 ); - + cbUseCustomButtonPositions = new QCheckBox( i18n("Use custom titlebar button &positions"), checkGroup ); @@ -76,12 +79,18 @@ i18n( "The appropriate settings can be found in the \"Buttons\" Tab. " "Please note that this option is not available on all styles yet!" ) ); - cbShowToolTips = new QCheckBox( + cbShowToolTips = new QCheckBox( i18n("&Show window button tooltips"), checkGroup ); - QWhatsThis::add( cbShowToolTips, + QWhatsThis::add( cbShowToolTips, i18n( "Enabling this checkbox will show window button tooltips. " "If this checkbox is off, no window button tooltips will be shown.")); + cbWindowShadow = new QCheckBox( + i18n("&Draw a drop shadow under windows"), checkGroup); + QWhatsThis::add(cbWindowShadow, + i18n("Enabling this checkbox will allow you to choose a kind of " + "drop shadow to draw under each window.")); + // Save this for later... // cbUseMiniWindows = new QCheckBox( i18n( "Render mini &titlebars for all windows"), checkGroup ); // QWhatsThis::add( cbUseMiniWindows, i18n( "Note that this option is not available on all styles yet!" ) ); @@ -91,10 +100,10 @@ buttonPage->setSpacing( KDialog::spacingHint() ); buttonPage->setMargin( KDialog::marginHint() ); - QGroupBox* buttonBox = new QGroupBox( 1, Qt::Horizontal, + QGroupBox* buttonBox = new QGroupBox( 1, Qt::Horizontal, i18n("Titlebar Button Position"), buttonPage ); - // Add nifty dnd button modification widgets + // Add nifty dnd button modification widgets QLabel* label = new QLabel( buttonBox ); dropSite = new ButtonDropSite( buttonBox ); label->setText( i18n( "To add or remove titlebar buttons, simply drag items " @@ -102,7 +111,161 @@ "drag items within the titlebar preview to re-position them.") ); buttonSource = new ButtonSource( buttonBox ); - // Page 3 (Configure decoration via client plugin page) + // Page 3 (Window Shadows) + QGroupBox *activeShadowSettings, *inactiveShadowSettings; + QGroupBox *whichShadowSettings; + QHBox *inactiveShadowColourHBox, *shadowColourHBox; + QHBox *inactiveShadowOpacityHBox, *shadowOpacityHBox; + QHBox *inactiveShadowXOffsetHBox, *shadowXOffsetHBox; + QHBox *inactiveShadowYOffsetHBox, *shadowYOffsetHBox; + QHBox *inactiveShadowThicknessHBox, *shadowThicknessHBox; + QLabel *inactiveShadowColourLabel, *shadowColourLabel; + QLabel *inactiveShadowOpacityLabel, *shadowOpacityLabel; + QLabel *inactiveShadowXOffsetLabel, *shadowXOffsetLabel; + QLabel *inactiveShadowYOffsetLabel, *shadowYOffsetLabel; + QLabel *inactiveShadowThicknessLabel, *shadowThicknessLabel; + + shadowPage = new QVBox(tabWidget); + shadowPage->setSpacing(KDialog::spacingHint()); + shadowPage->setMargin(KDialog::marginHint()); + + activeShadowSettings = new QGroupBox(1, Qt::Horizontal, + i18n("Active Window Shadow"), shadowPage); + inactiveShadowSettings = new QGroupBox(1, Qt::Horizontal, + i18n("Inactive Window Shadows"), shadowPage); + whichShadowSettings = new QGroupBox(3, Qt::Horizontal, + i18n("Draw Shadow Under Normal Windows And..."), shadowPage); + + cbShadowDocks = new QCheckBox(i18n("Docks and &panels"), + whichShadowSettings); + connect(cbShadowDocks, SIGNAL(toggled(bool)), + SLOT(slotSelectionChanged())); + cbShadowOverrides = new QCheckBox(i18n("O&verride windows"), + whichShadowSettings); + connect(cbShadowOverrides, SIGNAL(toggled(bool)), + SLOT(slotSelectionChanged())); + cbShadowTopMenus = new QCheckBox(i18n("&Top menu"), + whichShadowSettings); + connect(cbShadowTopMenus, SIGNAL(toggled(bool)), + SLOT(slotSelectionChanged())); + cbInactiveShadow = new QCheckBox( + i18n("Draw shadow under &inactive windows"), inactiveShadowSettings); + connect(cbInactiveShadow, SIGNAL(toggled(bool)), + SLOT(slotSelectionChanged())); + + shadowColourHBox = new QHBox(activeShadowSettings); + shadowColourHBox->setSpacing(KDialog::spacingHint()); + shadowColourLabel = new QLabel(i18n("Colour:"), shadowColourHBox); + shadowColourButton = new KColorButton(shadowColourHBox); + connect(shadowColourButton, SIGNAL(changed(const QColor &)), SLOT(slotSelectionChanged())); + + inactiveShadowColourHBox = new QHBox(inactiveShadowSettings); + inactiveShadowColourHBox->setSpacing(KDialog::spacingHint()); + inactiveShadowColourLabel = new QLabel(i18n("Colour:"), inactiveShadowColourHBox); + inactiveShadowColourButton = new KColorButton(inactiveShadowColourHBox); + connect(inactiveShadowColourButton, SIGNAL(changed(const QColor &)), SLOT(slotSelectionChanged())); + + shadowOpacityHBox = new QHBox(activeShadowSettings); + shadowOpacityHBox->setSpacing(KDialog::spacingHint()); + shadowOpacityLabel = new QLabel(i18n("Maximum opacity:"), shadowOpacityHBox); + shadowOpacitySlider = new QSlider(1, 100, 10, 50, Qt::Horizontal, + shadowOpacityHBox); + shadowOpacitySlider->setTickmarks(QSlider::Below); + shadowOpacitySlider->setTickInterval(10); + shadowOpacitySpinBox = new QSpinBox(1, 100, 1, shadowOpacityHBox); + shadowOpacitySpinBox->setSuffix(" %"); + connect(shadowOpacitySlider, SIGNAL(valueChanged(int)), shadowOpacitySpinBox, + SLOT(setValue(int))); + connect(shadowOpacitySpinBox, SIGNAL(valueChanged(int)), shadowOpacitySlider, + SLOT(setValue(int))); + connect(shadowOpacitySlider, SIGNAL(valueChanged(int)), + SLOT(slotSelectionChanged())); + + inactiveShadowOpacityHBox = new QHBox(inactiveShadowSettings); + inactiveShadowOpacityHBox->setSpacing(KDialog::spacingHint()); + inactiveShadowOpacityLabel = new QLabel(i18n("Maximum opacity:"), + inactiveShadowOpacityHBox); + inactiveShadowOpacitySlider = new QSlider(1, 100, 10, 50, Qt::Horizontal, + inactiveShadowOpacityHBox); + inactiveShadowOpacitySlider->setTickmarks(QSlider::Below); + inactiveShadowOpacitySlider->setTickInterval(10); + inactiveShadowOpacitySpinBox = new QSpinBox(1, 100, 1, + inactiveShadowOpacityHBox); + inactiveShadowOpacitySpinBox->setSuffix(" %"); + connect(inactiveShadowOpacitySlider, SIGNAL(valueChanged(int)), + inactiveShadowOpacitySpinBox, + SLOT(setValue(int))); + connect(inactiveShadowOpacitySpinBox, SIGNAL(valueChanged(int)), + inactiveShadowOpacitySlider, + SLOT(setValue(int))); + connect(inactiveShadowOpacitySlider, SIGNAL(valueChanged(int)), + SLOT(slotSelectionChanged())); + + shadowXOffsetHBox = new QHBox(activeShadowSettings); + shadowXOffsetHBox->setSpacing(KDialog::spacingHint()); + shadowXOffsetLabel = new QLabel( + i18n("Offset rightward (may be negative):"), + shadowXOffsetHBox); + shadowXOffsetSpinBox = new QSpinBox(-1024, 1024, 1, shadowXOffsetHBox); + shadowXOffsetSpinBox->setSuffix(i18n(" pixels")); + connect(shadowXOffsetSpinBox, SIGNAL(valueChanged(int)), + SLOT(slotSelectionChanged())); + + inactiveShadowXOffsetHBox = new QHBox(inactiveShadowSettings); + inactiveShadowXOffsetHBox->setSpacing(KDialog::spacingHint()); + inactiveShadowXOffsetLabel = new QLabel( + i18n("Offset rightward (may be negative):"), + inactiveShadowXOffsetHBox); + inactiveShadowXOffsetSpinBox = new QSpinBox(-1024, 1024, 1, + inactiveShadowXOffsetHBox); + inactiveShadowXOffsetSpinBox->setSuffix(i18n(" pixels")); + connect(inactiveShadowXOffsetSpinBox, SIGNAL(valueChanged(int)), + SLOT(slotSelectionChanged())); + + shadowYOffsetHBox = new QHBox(activeShadowSettings); + shadowYOffsetHBox->setSpacing(KDialog::spacingHint()); + shadowYOffsetLabel = new QLabel( + i18n("Offset downward (may be negative):"), + shadowYOffsetHBox); + shadowYOffsetSpinBox = new QSpinBox(-1024, 1024, 1, shadowYOffsetHBox); + shadowYOffsetSpinBox->setSuffix(i18n(" pixels")); + connect(shadowYOffsetSpinBox, SIGNAL(valueChanged(int)), + SLOT(slotSelectionChanged())); + + inactiveShadowYOffsetHBox = new QHBox(inactiveShadowSettings); + inactiveShadowYOffsetHBox->setSpacing(KDialog::spacingHint()); + inactiveShadowYOffsetLabel = new QLabel( + i18n("Offset downward (may be negative):"), + inactiveShadowYOffsetHBox); + inactiveShadowYOffsetSpinBox = new QSpinBox(-1024, 1024, 1, + inactiveShadowYOffsetHBox); + inactiveShadowYOffsetSpinBox->setSuffix(i18n(" pixels")); + connect(inactiveShadowYOffsetSpinBox, SIGNAL(valueChanged(int)), + SLOT(slotSelectionChanged())); + + shadowThicknessHBox = new QHBox(activeShadowSettings); + shadowThicknessHBox->setSpacing(KDialog::spacingHint()); + shadowThicknessLabel = new QLabel( + i18n("Thickness to either side of window:"), + shadowThicknessHBox); + shadowThicknessSpinBox = new QSpinBox(1, 100, 1, + shadowThicknessHBox); + shadowThicknessSpinBox->setSuffix(i18n(" pixels")); + connect(shadowThicknessSpinBox, SIGNAL(valueChanged(int)), + SLOT(slotSelectionChanged())); + + inactiveShadowThicknessHBox = new QHBox(inactiveShadowSettings); + inactiveShadowThicknessHBox->setSpacing(KDialog::spacingHint()); + inactiveShadowThicknessLabel = new QLabel( + i18n("Thickness to either side of window:"), + inactiveShadowThicknessHBox); + inactiveShadowThicknessSpinBox = new QSpinBox(1, 100, 1, + inactiveShadowThicknessHBox); + inactiveShadowThicknessSpinBox->setSuffix(i18n(" pixels")); + connect(inactiveShadowThicknessSpinBox, SIGNAL(valueChanged(int)), + SLOT(slotSelectionChanged())); + + // Page 4 (Configure decoration via client plugin page) pluginPage = new QVBox( tabWidget ); pluginPage->setSpacing( KDialog::spacingHint() ); pluginPage->setMargin( KDialog::marginHint() ); @@ -117,10 +280,12 @@ tabWidget->insertTab( page1, i18n("&General") ); tabWidget->insertTab( buttonPage, i18n("&Buttons") ); - tabWidget->insertTab( pluginPage, i18n("&Configure [") + + tabWidget->insertTab( shadowPage, i18n("S&hadows") ); + tabWidget->insertTab( pluginPage, i18n("&Configure [") + decorationListBox->currentText() + i18n("]") ); tabWidget->setTabEnabled( buttonPage, cbUseCustomButtonPositions->isChecked() ); + tabWidget->setTabEnabled(shadowPage, cbWindowShadow->isChecked()); tabWidget->setTabEnabled( pluginPage, pluginObject ? true : false ); connect( dropSite, SIGNAL(buttonAdded(char)), buttonSource, SLOT(hideButton(char)) ); @@ -129,11 +294,13 @@ connect( dropSite, SIGNAL(changed()), this, SLOT(slotSelectionChanged()) ); connect( buttonSource, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()) ); connect( decorationListBox, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged()) ); - connect( decorationListBox, SIGNAL(highlighted(const QString&)), + connect( decorationListBox, SIGNAL(highlighted(const QString&)), SLOT(slotDecorationHighlighted(const QString&)) ); connect( cbUseCustomButtonPositions, SIGNAL(clicked()), SLOT(slotSelectionChanged()) ); connect( cbUseCustomButtonPositions, SIGNAL(toggled(bool)), SLOT(slotEnableButtonTab(bool)) ); connect( cbShowToolTips, SIGNAL(clicked()), SLOT(slotSelectionChanged()) ); + connect( cbWindowShadow, SIGNAL(clicked()), SLOT(slotSelectionChanged()) ); + connect( cbWindowShadow, SIGNAL(toggled(bool)), SLOT(slotEnableShadowTab(bool)) ); // connect( cbUseMiniWindows, SIGNAL(clicked()), SLOT(slotSelectionChanged()) ); // Allow kwin dcop signal to update our selection list @@ -184,7 +351,7 @@ QValueList::ConstIterator it; // Sync with kwin hardcoded KDE2 style which has no desktop item - decorationListBox->insertItem( i18n("KDE 2") ); + decorationListBox->insertItem( i18n("KDE 2") ); for (it = decorations.begin(); it != decorations.end(); ++it) { @@ -221,6 +388,10 @@ tabWidget->setTabEnabled( buttonPage, on ); } +void KWinDecorationModule::slotEnableShadowTab(bool on) +{ + tabWidget->setTabEnabled(shadowPage, on); +} QString KWinDecorationModule::decorationName( QString& libName ) { @@ -253,7 +424,7 @@ } if (libName.isEmpty()) - libName = "kwin_default"; // KDE 2 + libName = "kwin_default"; // KDE 2 return libName; } @@ -290,22 +461,22 @@ KLibrary* library = loader->library( QFile::encodeName(currentName) ); if (library != NULL) - { + { void* alloc_ptr = library->symbol("allocate_config"); if (alloc_ptr != NULL) { allocatePlugin = (QObject* (*)(KConfig* conf, QWidget* parent))alloc_ptr; pluginObject = allocatePlugin( conf, pluginPage ); - + // connect required signals and slots together... - connect( pluginObject, SIGNAL(changed()), this, SLOT(slotSelectionChanged()) ); + connect( pluginObject, SIGNAL(changed()), this, SLOT(slotSelectionChanged()) ); connect( this, SIGNAL(pluginLoad(KConfig*)), pluginObject, SLOT(load(KConfig*)) ); connect( this, SIGNAL(pluginSave(KConfig*)), pluginObject, SLOT(save(KConfig*)) ); connect( this, SIGNAL(pluginDefaults()), pluginObject, SLOT(defaults()) ); return; - } + } } pluginObject = NULL; @@ -322,13 +493,14 @@ cbUseCustomButtonPositions->setChecked( conf->readBoolEntry("CustomButtonPositions", false)); tabWidget->setTabEnabled( buttonPage, cbUseCustomButtonPositions->isChecked() ); cbShowToolTips->setChecked( conf->readBoolEntry("ShowToolTips", true )); + cbWindowShadow->setChecked( conf->readBoolEntry("ShadowEnabled", false) ); // cbUseMiniWindows->setChecked( conf->readBoolEntry("MiniWindowBorders", false)); // Find the corresponding decoration name to that of // the current plugin library name oldLibraryName = currentLibraryName; - currentLibraryName = conf->readEntry("PluginLib", + currentLibraryName = conf->readEntry("PluginLib", ((QPixmap::defaultDepth() > 8) ? "kwin_keramik" : "kwin_quartz")); QString decoName = decorationName( currentLibraryName ); @@ -349,6 +521,35 @@ buttonSource->showAllButtons(); + // Shadows tab + // =========== + shadowColourButton->setColor( + conf->readColorEntry("ShadowColour", &Qt::black)); + shadowOpacitySlider->setValue( + (int)(conf->readDoubleNumEntry("ShadowOpacity", 0.75) * 100)); + shadowXOffsetSpinBox->setValue(conf->readNumEntry("ShadowXOffset", 0)); + shadowYOffsetSpinBox->setValue(conf->readNumEntry("ShadowYOffset", 8)); + cbShadowDocks->setChecked( + conf->readBoolEntry("ShadowDocks", false)); + cbShadowOverrides->setChecked( + conf->readBoolEntry("ShadowOverrides", false)); + cbShadowTopMenus->setChecked( + conf->readBoolEntry("ShadowTopMenus", false)); + shadowThicknessSpinBox->setValue( + conf->readNumEntry("ShadowThickness", 8)); + cbInactiveShadow->setChecked( + conf->readBoolEntry("InactiveShadowEnabled", false)); + inactiveShadowColourButton->setColor( + conf->readColorEntry("InactiveShadowColour", &Qt::black)); + inactiveShadowOpacitySlider->setValue( + (int)(conf->readDoubleNumEntry("InactiveShadowOpacity", 0.75)*100)); + inactiveShadowXOffsetSpinBox->setValue( + conf->readNumEntry("InactiveShadowXOffset", 0)); + inactiveShadowYOffsetSpinBox->setValue( + conf->readNumEntry("InactiveShadowYOffset", 4)); + inactiveShadowThicknessSpinBox->setValue( + conf->readNumEntry("InactiveShadowThickness", 4)); + // Step through the button lists, and hide the dnd button source items unsigned int i; for(i = 0; i < dropSite->buttonsLeft.length(); i++) @@ -368,17 +569,38 @@ KConfig kwinConfig("kwinrc"); kwinConfig.setGroup("Style"); - + // General settings conf->writeEntry("PluginLib", libName); conf->writeEntry("CustomButtonPositions", cbUseCustomButtonPositions->isChecked()); conf->writeEntry("ShowToolTips", cbShowToolTips->isChecked()); + conf->writeEntry("ShadowEnabled", cbWindowShadow->isChecked()); // conf->writeEntry("MiniWindowBorders", cbUseMiniWindows->isChecked()); // Button settings conf->writeEntry("ButtonsOnLeft", dropSite->buttonsLeft ); conf->writeEntry("ButtonsOnRight", dropSite->buttonsRight ); + // Shadow settings + conf->writeEntry("ShadowColour", shadowColourButton->color()); + conf->writeEntry("ShadowOpacity", shadowOpacitySlider->value() / 100.0); + conf->writeEntry("ShadowXOffset", shadowXOffsetSpinBox->value()); + conf->writeEntry("ShadowYOffset", shadowYOffsetSpinBox->value()); + conf->writeEntry("ShadowThickness", shadowThicknessSpinBox->value()); + conf->writeEntry("ShadowDocks", cbShadowDocks->isChecked()); + conf->writeEntry("ShadowOverrides", cbShadowOverrides->isChecked()); + conf->writeEntry("ShadowTopMenus", cbShadowTopMenus->isChecked()); + conf->writeEntry("InactiveShadowEnabled", cbInactiveShadow->isChecked()); + conf->writeEntry("InactiveShadowColour", inactiveShadowColourButton->color()); + conf->writeEntry("InactiveShadowOpacity", + inactiveShadowOpacitySlider->value() / 100.0); + conf->writeEntry("InactiveShadowXOffset", + inactiveShadowXOffsetSpinBox->value()); + conf->writeEntry("InactiveShadowYOffset", + inactiveShadowYOffsetSpinBox->value()); + conf->writeEntry("InactiveShadowThickness", + inactiveShadowThicknessSpinBox->value()); + oldLibraryName = currentLibraryName; currentLibraryName = libName; @@ -405,7 +627,7 @@ KConfig kwinConfig("kwinrc"); kwinConfig.setGroup("Style"); - // Reset by re-reading the config + // Reset by re-reading the config // The plugin doesn't need changing, as we have not saved readConfig( &kwinConfig ); emit pluginLoad( &kwinConfig ); @@ -431,9 +653,10 @@ // Set the KDE defaults cbUseCustomButtonPositions->setChecked( false ); cbShowToolTips->setChecked( true ); + cbWindowShadow->setChecked( false ); // cbUseMiniWindows->setChecked( false); // Don't set default for now -// decorationListBox->setSelected( +// decorationListBox->setSelected( // decorationListBox->findItem( i18n("KDE 2") ), true ); // KDE classic client dropSite->buttonsLeft = "MS"; @@ -448,6 +671,21 @@ buttonSource->hideButton('A'); buttonSource->hideButton('X'); + shadowColourButton->setColor(Qt::black); + shadowOpacitySlider->setValue(75); + shadowXOffsetSpinBox->setValue(0); + shadowYOffsetSpinBox->setValue(10); + shadowThicknessSpinBox->setValue(10); + cbShadowDocks->setChecked(false); + cbShadowOverrides->setChecked(false); + cbShadowTopMenus->setChecked(false); + cbInactiveShadow->setChecked(false); + inactiveShadowColourButton->setColor(Qt::black); + inactiveShadowOpacitySlider->setValue(75); + inactiveShadowXOffsetSpinBox->setValue(0); + inactiveShadowYOffsetSpinBox->setValue(5); + inactiveShadowThicknessSpinBox->setValue(5); + // Set plugin defaults emit pluginDefaults(); } @@ -463,7 +701,7 @@ const KAboutData* KWinDecorationModule::aboutData() const { - KAboutData* about = + KAboutData* about = new KAboutData(I18N_NOOP("kcmkwindecoration"), I18N_NOOP("Window Decoration Control Module"), 0, 0, KAboutData::License_GPL, @@ -477,7 +715,7 @@ { bool ok = kapp->dcopClient()->send("kwin", "KWinInterface", "reconfigure()", QByteArray()); - if (!ok) + if (!ok) kdDebug() << "kcmkwindecoration: Could not reconfigure kwin" << endl; } --- kcmkwin/kwindecoration/kwindecoration.h.orig 2003-06-10 01:55:17.000000000 -0400 +++ kcmkwin/kwindecoration/kwindecoration.h.orig 2003-06-10 04:45:50.000000000 -0400 @@ -10,7 +10,7 @@ Supports new kwin configuration plugins, and titlebar button position modification via dnd interface. - Based on original "kwintheme" (Window Borders) + Based on original "kwintheme" (Window Borders) Copyright (C) 2001 Rik Hemsley (rikkus) */ @@ -25,8 +25,12 @@ #include "kwindecorationIface.h" -class QListBox; +class KColorButton; class QCheckBox; +class QHBox; +class QListBox; +class QSlider; +class QSpinBox; class QTabWidget; class QVBox; @@ -64,6 +68,7 @@ // Allows us to turn "save" on void slotSelectionChanged(); void slotEnableButtonTab(bool on); + void slotEnableShadowTab(bool on); void slotDecorationHighlighted( const QString& s ); private: @@ -84,13 +89,25 @@ QValueList decorations; QCheckBox* cbUseCustomButtonPositions; // QCheckBox* cbUseMiniWindows; - QCheckBox* cbShowToolTips; + QCheckBox *cbShowToolTips; + QCheckBox *cbWindowShadow; // Page 2 ButtonDropSite* dropSite; ButtonSource* buttonSource; // Page 3 + QVBox *shadowPage; + KColorButton *inactiveShadowColourButton, *shadowColourButton; + QCheckBox *cbShadowDocks, *cbShadowOverrides, *cbShadowTopMenus; + QCheckBox *cbInactiveShadow; + QSlider *inactiveShadowOpacitySlider, *shadowOpacitySlider; + QSpinBox *inactiveShadowOpacitySpinBox, *shadowOpacitySpinBox; + QSpinBox *inactiveShadowXOffsetSpinBox, *shadowXOffsetSpinBox; + QSpinBox *inactiveShadowYOffsetSpinBox, *shadowYOffsetSpinBox; + QSpinBox *inactiveShadowThicknessSpinBox, *shadowThicknessSpinBox; + + // Page 4 QObject* pluginObject; QString currentLibraryName; QString oldLibraryName;