summaryrefslogtreecommitdiffstats
path: root/kcontrol/input/xcursor/previewwidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kcontrol/input/xcursor/previewwidget.cpp')
-rw-r--r--kcontrol/input/xcursor/previewwidget.cpp353
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 <[email protected]>
+ *
+ * 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: