diff options
Diffstat (limited to 'kcontrol/input/xcursor/previewwidget.cpp')
-rw-r--r-- | kcontrol/input/xcursor/previewwidget.cpp | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/kcontrol/input/xcursor/previewwidget.cpp b/kcontrol/input/xcursor/previewwidget.cpp new file mode 100644 index 000000000..36108ef36 --- /dev/null +++ b/kcontrol/input/xcursor/previewwidget.cpp @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2003 Fredrik Höglund <fredrik@kde.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <kglobal.h> + +#include <qwidget.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <qstring.h> +#include <qcursor.h> + +#include <kglobal.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/extensions/Xrender.h> +#include <X11/Xcursor/Xcursor.h> + +#include "previewwidget.h" + + +extern bool qt_has_xft; +extern bool qt_use_xrender; + + +namespace { + + // Preview cursors + const char *cursor_names[] = + { + "left_ptr", + "left_ptr_watch", + "watch", + "hand2", + "question_arrow", + "sb_h_double_arrow", + "sb_v_double_arrow", + "bottom_left_corner", + "bottom_right_corner", + "fleur", + "pirate", + "cross", + "X_cursor", + "right_ptr", + "right_side", + "right_tee", + "sb_right_arrow", + "sb_right_tee", + "base_arrow_down", + "base_arrow_up", + "bottom_side", + "bottom_tee", + "center_ptr", + "circle", + "dot", + "dot_box_mask", + "dot_box_mask", + "double_arrow", + "draped_box", + "left_side", + "left_tee", + "ll_angle", + "top_side", + "top_tee", + }; + + const int numCursors = 6; // The number of cursors in the above list to be previewed + const int previewSize = 24; // The cursor size to be used in the preview widget + const int cursorSpacing = 20; +} + + +class PreviewCursor +{ + public: + PreviewCursor(); + ~PreviewCursor(); + + void load( const QString &, const QString & ); + const Picture picture() const { return m_pict; } + const Cursor handle() const { return m_handle; } + const int width() const { return m_width; } + const int height() const { return m_height; } + + private: + Picture createPicture( const XcursorImage* ) const; + void cropCursorImage( XcursorImage*& ) const; + Picture m_pict; + Cursor m_handle; + int m_width; + int m_height; +}; // class PreviewCursor + + +PreviewCursor::PreviewCursor() : + m_pict( 0 ), m_handle( 0 ), m_width( 0 ), m_height( 0 ) +{ +} + + +void PreviewCursor::load( const QString &name, const QString &theme ) +{ + Display *dpy = QPaintDevice::x11AppDisplay(); + + if ( m_pict ) XRenderFreePicture( dpy, m_pict ); + if ( m_handle ) XFreeCursor( dpy, m_handle ); + m_pict = 0; + m_handle = 0; + m_width = m_height = 0; + + // Load the preview cursor image + XcursorImage *image = + XcursorLibraryLoadImage( name.latin1(), theme.latin1(), previewSize ); + + // If the theme doesn't have this cursor, load the default cursor for now + if ( !image ) + image = XcursorLibraryLoadImage( "left_ptr", theme.latin1(), previewSize ); + + // TODO The old classic X cursors + if ( !image ) + return; + + // Auto-crop the image (some cursor themes use a fixed image size + // for all cursors, and doing this results in correctly centered images) + cropCursorImage( image ); + + m_pict = createPicture( image ); + m_width = image->width; + m_height = image->height; + + // Scale the image if its height is greater than 2x the requested size + if ( m_height > previewSize * 2.0 ) { + double factor = double( previewSize * 2.0 / m_height ); + XTransform xform = { + {{ XDoubleToFixed(1.0), XDoubleToFixed(0), XDoubleToFixed(0) }, + { XDoubleToFixed(0), XDoubleToFixed(1.0), XDoubleToFixed(0) }, + { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(factor) }} + }; + XRenderSetPictureTransform( dpy, m_pict, &xform ); + m_width = int( m_width * factor ); + m_height = int( m_height * factor ); + } + + // We don't need this image anymore + XcursorImageDestroy( image ); + + // Load the actual cursor we will use + int size = XcursorGetDefaultSize( dpy ); + XcursorImages *images = XcursorLibraryLoadImages( name.latin1(), theme.latin1(), size ); + + if ( images ) { + m_handle = XcursorImagesLoadCursor( dpy, images ); + XcursorImagesDestroy( images ); + } else { + images = XcursorLibraryLoadImages( "left_ptr", theme.latin1(), size ); + m_handle = XcursorImagesLoadCursor( dpy, images ); + XcursorImagesDestroy( images ); + } +} + + +PreviewCursor::~PreviewCursor() +{ + if ( m_handle ) XFreeCursor( QPaintDevice::x11AppDisplay(), m_handle ); + if ( m_pict ) XRenderFreePicture( QPaintDevice::x11AppDisplay(), m_pict ); +} + + +Picture PreviewCursor::createPicture( const XcursorImage* image ) const +{ + Display *dpy = QPaintDevice::x11AppDisplay(); + + XImage ximage; + ximage.width = image->width; + ximage.height = image->height; + ximage.xoffset = 0; + ximage.format = ZPixmap; + ximage.data = reinterpret_cast<char*>( image->pixels ); + ximage.byte_order = ImageByteOrder( dpy ); + ximage.bitmap_unit = 32; + ximage.bitmap_bit_order = ximage.byte_order; + ximage.bitmap_pad = 32; + ximage.depth = 32; + ximage.bits_per_pixel = 32; + ximage.bytes_per_line = image->width * 4; + ximage.red_mask = 0x00ff0000; + ximage.green_mask = 0x0000ff00; + ximage.blue_mask = 0x000000ff; + ximage.obdata = 0; + + XInitImage( &ximage ); + + Pixmap pix = XCreatePixmap( dpy, DefaultRootWindow(dpy), ximage.width, ximage.height, 32 ); + GC gc = XCreateGC( dpy, pix, 0, NULL ); + XPutImage( dpy, pix, gc, &ximage, 0, 0, 0, 0, ximage.width, ximage.height ); + XFreeGC( dpy, gc ); + + XRenderPictFormat *fmt = XRenderFindStandardFormat( dpy, PictStandardARGB32 ); + Picture pict = XRenderCreatePicture( dpy, pix, fmt, 0, NULL ); + XFreePixmap( dpy, pix ); + + return pict; +} + + +void PreviewCursor::cropCursorImage( XcursorImage *&image ) const +{ + // Calculate the auto-crop rectangle + QRect r( QPoint( image->width, image->height ), QPoint() ); + XcursorPixel *pixels = image->pixels; + for ( int y = 0; y < int(image->height); y++ ) { + for ( int x = 0; x < int(image->width); x++ ) { + if ( *(pixels++) >> 24 ) { + if ( x < r.left() ) r.setLeft( x ); + if ( x > r.right() ) r.setRight( x ); + if ( y < r.top() ) r.setTop( y ); + if ( y > r.bottom() ) r.setBottom( y ); + } + } + } + + // Normalize the rectangle + r = r.normalize(); + + // Don't crop the image if the size isn't going to change + if ( r.width() == int( image->width ) && r.height() == int( image->height ) ) + return; + + // Create the new image + XcursorImage *cropped = XcursorImageCreate( r.width(), r.height() ); + XcursorPixel *src = image->pixels + r.top() * image->width + r.left(); + XcursorPixel *dst = cropped->pixels; + for ( int y = 0; y < r.height(); y++, src += (image->width - r.width()) ) { + for ( int x = 0; x < r.width(); x++ ) { + *(dst++) = *(src++); + } + } + + // Destroy the original + XcursorImageDestroy( image ); + image = cropped; +} + + + +// ------------------------------------------------------------------------------------------------ + + + +PreviewWidget::PreviewWidget( QWidget *parent, const char *name ) + : QWidget( parent, name ) +{ + cursors = new PreviewCursor* [ numCursors ]; + for ( int i = 0; i < numCursors; i++ ) + cursors[i] = new PreviewCursor; + + current = -1; + setMouseTracking( true ); + setFixedHeight( previewSize + 20 ); +} + + +PreviewWidget::~PreviewWidget() +{ + for ( int i = 0; i < numCursors; i++ ) + delete cursors[i]; + + delete [] cursors; +} + + +void PreviewWidget::setTheme( const QString &theme ) +{ + setUpdatesEnabled( false ); + + int minHeight = previewSize + 20; // Minimum height of the preview widget + int maxHeight = height(); // Tallest cursor height + int maxWidth = previewSize; // Widest cursor width + + for ( int i = 0; i < numCursors; i++ ) { + cursors[i]->load( cursor_names[i], theme.latin1() ); + if ( cursors[i]->width() > maxWidth ) + maxWidth = cursors[i]->width(); + if ( cursors[i]->height() > maxHeight ) + maxHeight = cursors[i]->height(); + } + + current = -1; + setFixedSize( ( maxWidth + cursorSpacing ) * numCursors, kMax( maxHeight, minHeight ) ); + setUpdatesEnabled( true ); + repaint( false ); +} + + +void PreviewWidget::paintEvent( QPaintEvent * ) +{ + QPixmap buffer( size() ); + QPainter p( &buffer ); + p.fillRect( rect(), colorGroup().brush( QColorGroup::Background ) ); + Picture dest; + + if ( !qt_has_xft || !qt_use_xrender ) { + XRenderPictFormat *fmt = XRenderFindVisualFormat( x11Display(), (Visual*)buffer.x11Visual() ); + dest = XRenderCreatePicture( x11Display(), buffer.handle(), fmt, 0, NULL ); + } else + dest = buffer.x11RenderHandle(); + + int rwidth = width() / numCursors; + + for ( int i = 0; i < numCursors; i++ ) { + if ( cursors[i]->picture() ) { + XRenderComposite( x11Display(), PictOpOver, + cursors[i]->picture(), 0, dest, 0, 0, 0, 0, + rwidth * i + (rwidth - cursors[i]->width()) / 2, + (height() - cursors[i]->height()) / 2, + cursors[i]->width(), cursors[i]->height() ); + } + } + + bitBlt( this, 0, 0, &buffer ); + + if ( !qt_has_xft || !qt_use_xrender ) + XRenderFreePicture( x11Display(), dest ); +} + + +void PreviewWidget::mouseMoveEvent( QMouseEvent *e ) +{ + int pos = e->x() / ( width() / numCursors ); + + if ( pos != current && pos < numCursors ) { + XDefineCursor( x11Display(), winId(), cursors[pos]->handle() ); + current = pos; + } +} + + +// vim: set noet ts=4 sw=4: |