You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
222 lines
6.5 KiB
222 lines
6.5 KiB
10 years ago
|
/* Copyright © 2003-2007 Fredrik Höglund <fredrik@kde.org>
|
||
|
* (c)GPL2 (c)GPL3
|
||
|
*
|
||
|
* 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.
|
||
|
*/
|
||
|
/*
|
||
|
* additional code: Ketmar // Vampire Avalon (psyc://ketmar.no-ip.org/~Ketmar)
|
||
|
*/
|
||
|
#include <QDebug>
|
||
|
|
||
|
#include <QPainter>
|
||
|
#include <QMouseEvent>
|
||
|
|
||
|
#include "previewwidget.h"
|
||
|
|
||
|
#include "crtheme.h"
|
||
|
|
||
|
#include <X11/Xlib.h>
|
||
|
#include <X11/Xcursor/Xcursor.h>
|
||
|
|
||
|
#include <QX11Info>
|
||
|
#include <QWindow>
|
||
|
|
||
|
namespace {
|
||
|
// Preview cursors
|
||
|
const char *const cursorNames[] = {
|
||
|
"left_ptr",
|
||
|
"left_ptr_watch",
|
||
|
"wait",
|
||
|
"pointing_hand",
|
||
|
"whats_this",
|
||
|
"ibeam",
|
||
|
"size_all",
|
||
|
"size_fdiag",
|
||
|
"cross",
|
||
|
"split_h",
|
||
|
"size_ver",
|
||
|
"size_hor",
|
||
|
"size_bdiag",
|
||
|
"split_v",
|
||
|
};
|
||
|
|
||
|
const int numCursors = 9; // The number of cursors from the above list to be previewed
|
||
|
const int previewSize = 24; // The nominal cursor size to be used in the preview widget
|
||
|
const int cursorSpacing = 20; // Spacing between preview cursors
|
||
|
const int widgetMinWidth = 10; // The minimum width of the preview widget
|
||
|
const int widgetMinHeight = 48; // The minimum height of the preview widget
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
class PreviewCursor
|
||
|
{
|
||
|
public:
|
||
|
PreviewCursor (const XCursorThemeData &theme, const QString &name);
|
||
|
~PreviewCursor () {}
|
||
|
|
||
|
const QPixmap &pixmap () const { return mPixmap; }
|
||
|
int width () const { return mPixmap.width(); }
|
||
|
int height () const { return mPixmap.height(); }
|
||
|
inline QRect rect () const;
|
||
|
void setPosition (const QPoint &p) { mPos = p; }
|
||
|
void setPosition (int x, int y) { mPos = QPoint(x, y); }
|
||
|
QPoint position () const { return mPos; }
|
||
|
operator const xcb_cursor_t& () const { return mCursorHandle; }
|
||
|
operator const QPixmap& () const { return pixmap(); }
|
||
|
|
||
|
private:
|
||
|
QPixmap mPixmap;
|
||
|
xcb_cursor_t mCursorHandle;
|
||
|
QPoint mPos;
|
||
|
};
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
PreviewCursor::PreviewCursor(const XCursorThemeData &theme, const QString &name)
|
||
|
{
|
||
|
// Create the preview pixmap
|
||
|
QImage image = theme.loadImage(name, previewSize);
|
||
|
if (image.isNull()) return;
|
||
|
int maxSize = previewSize*2;
|
||
|
if (image.height() > maxSize || image.width() > maxSize)
|
||
|
image = image.scaled(maxSize, maxSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||
|
mPixmap = QPixmap::fromImage(image);
|
||
|
// load the cursor
|
||
|
mCursorHandle = theme.loadCursorHandle(name, previewSize);
|
||
|
}
|
||
|
|
||
|
QRect PreviewCursor::rect() const
|
||
|
{
|
||
|
return QRect(mPos, mPixmap.size()).adjusted(-(cursorSpacing/2), -(cursorSpacing/2), cursorSpacing/2, cursorSpacing/2);
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
PreviewWidget::PreviewWidget(QWidget *parent) : QWidget(parent)
|
||
|
{
|
||
|
setMouseTracking(true);
|
||
|
mCurrent = NULL;
|
||
|
}
|
||
|
|
||
|
PreviewWidget::~PreviewWidget()
|
||
|
{
|
||
|
qDeleteAll(mList);
|
||
|
mList.clear();
|
||
|
}
|
||
|
|
||
|
|
||
|
// Since Qt5, wrapping a Cursor handle with QCursor is no longer supported.
|
||
|
// So we have to do it ourselves. I really hate Qt 5!
|
||
|
void PreviewWidget::setCursorHandle(xcb_cursor_t cursorHandle)
|
||
|
{
|
||
|
WId wid = nativeParentWidget()->windowHandle()->winId();
|
||
|
xcb_change_window_attributes(QX11Info::connection(), wid, XCB_CW_CURSOR, &cursorHandle);
|
||
|
xcb_flush(QX11Info::connection());
|
||
|
}
|
||
|
|
||
|
|
||
|
QSize PreviewWidget::sizeHint() const
|
||
|
{
|
||
|
int totalWidth = 0, maxHeight = 0;
|
||
|
foreach (const PreviewCursor *c, mList)
|
||
|
{
|
||
|
totalWidth += c->width();
|
||
|
maxHeight = qMax(c->height(), maxHeight);
|
||
|
}
|
||
|
totalWidth += (mList.count()-1)*cursorSpacing;
|
||
|
maxHeight = qMax(maxHeight, widgetMinHeight);
|
||
|
return QSize(qMax(totalWidth, widgetMinWidth), qMax(height(), maxHeight));
|
||
|
}
|
||
|
|
||
|
void PreviewWidget::layoutItems()
|
||
|
{
|
||
|
if (!mList.isEmpty())
|
||
|
{
|
||
|
QSize size = sizeHint();
|
||
|
int cursorWidth = size.width()/mList.count();
|
||
|
int nextX = (width()-size.width())/2;
|
||
|
foreach (PreviewCursor *c, mList)
|
||
|
{
|
||
|
c->setPosition(nextX+(cursorWidth-c->width())/2, (height()-c->height())/2);
|
||
|
nextX += cursorWidth;
|
||
|
}
|
||
|
}
|
||
|
mNeedLayout = false;
|
||
|
}
|
||
|
|
||
|
void PreviewWidget::setTheme(const XCursorThemeData &theme)
|
||
|
{
|
||
|
qDeleteAll(mList);
|
||
|
mList.clear();
|
||
|
for (int i = 0; i < numCursors; ++i) mList << new PreviewCursor(theme, cursorNames[i]);
|
||
|
mNeedLayout = true;
|
||
|
updateGeometry();
|
||
|
mCurrent = NULL;
|
||
|
update();
|
||
|
}
|
||
|
|
||
|
|
||
|
void PreviewWidget::clearTheme()
|
||
|
{
|
||
|
qDeleteAll(mList);
|
||
|
mList.clear();
|
||
|
mCurrent = NULL;
|
||
|
update();
|
||
|
}
|
||
|
|
||
|
void PreviewWidget::paintEvent(QPaintEvent *)
|
||
|
{
|
||
|
QPainter p(this);
|
||
|
if (mNeedLayout) layoutItems();
|
||
|
foreach (const PreviewCursor *c, mList)
|
||
|
{
|
||
|
if (c->pixmap().isNull()) continue;
|
||
|
p.drawPixmap(c->position(), *c);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PreviewWidget::mouseMoveEvent(QMouseEvent *e)
|
||
|
{
|
||
|
if (mNeedLayout) layoutItems();
|
||
|
foreach (const PreviewCursor *c, mList)
|
||
|
{
|
||
|
if (c->rect().contains(e->pos()))
|
||
|
{
|
||
|
if (c != mCurrent)
|
||
|
{
|
||
|
// NOTE: we have to set the Qt cursor value to something other than Qt::ArrowCursor
|
||
|
// Otherwise, though we set the xcursor handle to the underlying window, Qt still
|
||
|
// thinks that the current cursor is Qt::ArrowCursor and will not restore the cursor
|
||
|
// later when we call setCursor(Qt::ArrowCursor). So, we set it to BlankCursor to
|
||
|
// cheat Qt so it knows that the current cursor is not Qt::ArrowCursor.
|
||
|
// This is a dirty hack, but without this, Qt cannot restore Qt::ArrowCursor later.
|
||
|
setCursor(Qt::BlankCursor);
|
||
|
setCursorHandle(*c);
|
||
|
mCurrent = c;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
setCursor(Qt::ArrowCursor);
|
||
|
mCurrent = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
void PreviewWidget::resizeEvent(QResizeEvent *)
|
||
|
{
|
||
|
if (!mList.isEmpty()) mNeedLayout = true;
|
||
|
}
|