2 * Copyright © 2006-2007 Fredrik Höglund <fredrik@kde.org>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License version 2 or at your option version 3 as published
7 * by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; see the file COPYING. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
22 #include <KConfigGroup>
30 #include <X11/Xcursor/Xcursor.h>
32 #include "xcursortheme.h"
35 // Static variable holding alternative names for some cursors
36 QHash
<QString
, QString
> XCursorTheme::alternatives
;
38 XCursorTheme::XCursorTheme(const QDir
&themeDir
)
39 : LegacyTheme(themeDir
.dirName(), i18n("No description available"))
41 // Directory information
42 setName(themeDir
.dirName());
43 setPath(themeDir
.path());
44 setIsWritable(QFileInfo(themeDir
.path()).isWritable()); // ### perhaps this shouldn't be cached
46 if (themeDir
.exists("index.theme"))
51 void XCursorTheme::parseIndexFile()
53 KConfig
config(path() + "/index.theme", KConfig::NoGlobals
);
54 KConfigGroup
cg(&config
, "Icon Theme");
56 m_title
= cg
.readEntry("Name", m_title
);
57 m_description
= cg
.readEntry("Comment", m_description
);
58 m_sample
= cg
.readEntry("Example", m_sample
);
59 m_hidden
= cg
.readEntry("Hidden", false);
60 m_inherits
= cg
.readEntry("Inherits", QStringList());
64 QString
XCursorTheme::findAlternative(const QString
&name
) const
66 if (alternatives
.isEmpty())
68 alternatives
.reserve(18);
70 // Qt uses non-standard names for some core cursors.
71 // If Xcursor fails to load the cursor, Qt creates it with the correct name using the
72 // core protcol instead (which in turn calls Xcursor). We emulate that process here.
73 // Note that there's a core cursor called cross, but it's not the one Qt expects.
74 alternatives
.insert("cross", "crosshair");
75 alternatives
.insert("up_arrow", "center_ptr");
76 alternatives
.insert("wait", "watch");
77 alternatives
.insert("ibeam", "xterm");
78 alternatives
.insert("size_all", "fleur");
79 alternatives
.insert("pointing_hand", "hand2");
81 // Precomputed MD5 hashes for the hardcoded bitmap cursors in Qt and KDE.
82 // Note that the MD5 hash for left_ptr_watch is for the KDE version of that cursor.
83 alternatives
.insert("size_ver", "00008160000006810000408080010102");
84 alternatives
.insert("size_hor", "028006030e0e7ebffc7f7070c0600140");
85 alternatives
.insert("size_bdiag", "c7088f0f3e6c8088236ef8e1e3e70000");
86 alternatives
.insert("size_fdiag", "fcf1c3c7cd4491d801f1e1c78f100000");
87 alternatives
.insert("whats_this", "d9ce0ab605698f320427677b458ad60b");
88 alternatives
.insert("split_h", "14fef782d02440884392942c11205230");
89 alternatives
.insert("split_v", "2870a09082c103050810ffdffffe0204");
90 alternatives
.insert("forbidden", "03b6e0fcb3499374a867c041f52298f0");
91 alternatives
.insert("left_ptr_watch", "3ecb610c1bf2410f44200f48c40d3599");
92 alternatives
.insert("hand2", "e29285e634086352946a0e7090d73106");
93 alternatives
.insert("openhand", "9141b49c8149039304290b508d208c40");
94 alternatives
.insert("closedhand", "05e88622050804100c20044008402080");
97 return alternatives
.value(name
, QString());
101 XcursorImage
*XCursorTheme::xcLoadImage(const QString
&image
, int size
) const
103 QByteArray cursorName
= QFile::encodeName(image
);
104 QByteArray themeName
= QFile::encodeName(name());
106 return XcursorLibraryLoadImage(cursorName
, themeName
, size
);
110 XcursorImages
*XCursorTheme::xcLoadImages(const QString
&image
, int size
) const
112 QByteArray cursorName
= QFile::encodeName(image
);
113 QByteArray themeName
= QFile::encodeName(name());
115 return XcursorLibraryLoadImages(cursorName
, themeName
, size
);
119 QCursor
XCursorTheme::loadCursor(const QString
&name
, int size
) const
122 size
= XcursorGetDefaultSize(QX11Info::display());
124 // Load the cursor images
125 XcursorImages
*images
= xcLoadImages(name
, size
);
128 images
= xcLoadImages(findAlternative(name
), size
);
130 // Fall back to a legacy cursor
132 return LegacyTheme::loadCursor(name
);
135 Cursor handle
= XcursorImagesLoadCursor(QX11Info::display(), images
);
136 QCursor cursor
= QCursor(Qt::HANDLE(handle
)); // QCursor takes ownership of the handle
137 XcursorImagesDestroy(images
);
139 setCursorName(cursor
, name
);
144 QImage
XCursorTheme::loadImage(const QString
&name
, int size
) const
147 size
= XcursorGetDefaultSize(QX11Info::display());
150 XcursorImage
*xcimage
= xcLoadImage(name
, size
);
153 xcimage
= xcLoadImage(findAlternative(name
), size
);
155 // Fall back to a legacy cursor
157 return LegacyTheme::loadImage(name
);
159 // Convert the XcursorImage to a QImage, and auto-crop it
160 QImage
image((uchar
*)xcimage
->pixels
, xcimage
->width
, xcimage
->height
,
161 QImage::Format_ARGB32_Premultiplied
);
163 image
= autoCropImage(image
);
164 XcursorImageDestroy(xcimage
);