* Copyright © 2005-2007 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 <KLocalizedString>
#include <KConfig>
#include <KConfigGroup>
#include <QStringList>
#include <QDir>
#include <QX11Info>
#include "thememodel.h"
#include "thememodel.moc"
#include "xcursortheme.h"
#include <X11/Xlib.h>
#include <X11/Xcursor/Xcursor.h>
#if !defined(XCURSOR_LIB_MAJOR) && defined(XCURSOR_MAJOR)
# define XCURSOR_LIB_MAJOR XCURSOR_MAJOR
# define XCURSOR_LIB_MINOR XCURSOR_MINOR
#endif
CursorThemeModel::CursorThemeModel(QObject *parent)
: QAbstractTableModel(parent)
{
insertThemes();
}
CursorThemeModel::~CursorThemeModel()
{
qDeleteAll(list);
list.clear();
}
void CursorThemeModel::refreshList()
{
beginResetModel();
qDeleteAll(list);
list.clear();
endResetModel();
insertThemes();
}
QVariant CursorThemeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal)
{
switch (section)
{
case NameColumn:
return i18n("Name");
case DescColumn:
return i18n("Description");
default: return QVariant();
}
}
return QString(section);
}
QVariant CursorThemeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= list.count())
return QVariant();
const CursorTheme *theme = list.at(index.row());
if (role == Qt::DisplayRole)
{
switch (index.column())
{
case NameColumn:
return theme->title();
case DescColumn:
return theme->description();
default: return QVariant();
}
}
if (role == CursorTheme::DisplayDetailRole && index.column() == NameColumn)
return theme->description();
if (role == Qt::DecorationRole && index.column() == NameColumn)
return theme->icon();
return QVariant();
}
void CursorThemeModel::sort(int column, Qt::SortOrder order)
{
Q_UNUSED(column);
Q_UNUSED(order);
}
const CursorTheme *CursorThemeModel::theme(const QModelIndex &index)
{
if (!index.isValid())
return NULL;
if (index.row() < 0 || index.row() >= list.count())
return NULL;
return list.at(index.row());
}
QModelIndex CursorThemeModel::findIndex(const QString &name)
{
uint hash = qHash(name);
for (int i = 0; i < list.count(); i++)
{
const CursorTheme *theme = list.at(i);
if (theme->hash() == hash)
return index(i, 0);
}
return QModelIndex();
}
QModelIndex CursorThemeModel::defaultIndex()
{
return findIndex(defaultName);
}
const QStringList CursorThemeModel::searchPaths()
{
if (!baseDirs.isEmpty())
return baseDirs;
#if XCURSOR_LIB_MAJOR == 1 && XCURSOR_LIB_MINOR < 1
QString path("~/.icons:/usr/share/icons:/usr/share/pixmaps:/usr/X11R6/lib/X11/icons");
char *xcursorPath = std::getenv("XCURSOR_PATH");
if (xcursorPath)
path = xcursorPath;
#else
QString path = XcursorLibraryPath();
#endif
baseDirs = path.split(':', QString::SkipEmptyParts);
QMutableStringListIterator i(baseDirs);
while (i.hasNext())
{
const QString path = i.next();
QMutableStringListIterator j(i);
while (j.hasNext())
if (j.next() == path)
j.remove();
}
baseDirs.replaceInStrings(QRegExp("^~\\/"), QDir::home().path() + '/');
return baseDirs;
}
bool CursorThemeModel::hasTheme(const QString &name) const
{
const uint hash = qHash(name);
foreach (const CursorTheme *theme, list)
if (theme->hash() == hash)
return true;
return false;
}
bool CursorThemeModel::isCursorTheme(const QString &theme, const int depth)
{
if (depth > 10)
return false;
foreach (const QString &baseDir, searchPaths())
{
QDir dir(baseDir);
if (!dir.exists() || !dir.cd(theme))
continue;
if (dir.exists("cursors"))
return true;
if (!dir.exists("index.theme"))
continue;
KConfig config(dir.path() + "/index.theme", KConfig::NoGlobals);
KConfigGroup cg(&config, "Icon Theme");
QStringList inherits = cg.readEntry("Inherits", QStringList());
foreach (const QString &inherit, inherits)
{
if (inherit == theme)
continue;
if (isCursorTheme(inherit, depth + 1))
return true;
}
}
return false;
}
bool CursorThemeModel::handleDefault(const QDir &themeDir)
{
QFileInfo info(themeDir.path());
if (info.isSymLink())
{
QFileInfo target(info.symLinkTarget());
if (target.exists() && (target.isDir() || target.isSymLink()))
defaultName = target.fileName();
return true;
}
if (!themeDir.exists("cursors") || QDir(themeDir.path() + "/cursors")
.entryList(QDir::Files | QDir::NoDotAndDotDot ).isEmpty())
{
if (themeDir.exists("index.theme"))
{
XCursorTheme theme(themeDir);
if (!theme.inherits().isEmpty())
defaultName = theme.inherits().at(0);
}
return true;
}
defaultName = QLatin1String("default");
return false;
}
void CursorThemeModel::processThemeDir(const QDir &themeDir)
{
bool haveCursors = themeDir.exists("cursors");
if (defaultName.isNull() && themeDir.dirName() == "default")
{
if (handleDefault(themeDir))
return;
}
if (!themeDir.exists("index.theme") && !haveCursors)
return;
static bool isX11 = QX11Info::isPlatformX11();
if (!isX11) {
return;
}
XCursorTheme *theme = new XCursorTheme(themeDir);
if (theme->isHidden()) {
delete theme;
return;
}
if (!haveCursors)
{
bool foundCursorTheme = false;
foreach (const QString &name, theme->inherits())
if ((foundCursorTheme = isCursorTheme(name)))
break;
if (!foundCursorTheme) {
delete theme;
return;
}
}
beginInsertRows(QModelIndex(), list.size(), list.size());
list.append(theme);
endInsertRows();
}
void CursorThemeModel::insertThemes()
{
foreach (const QString &baseDir, searchPaths())
{
QDir dir(baseDir);
if (!dir.exists())
continue;
foreach (const QString &name, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
{
if (hasTheme(name) || !dir.cd(name))
continue;
processThemeDir(dir);
dir.cdUp();
}
}
if (defaultName.isNull() || !hasTheme(defaultName))
defaultName = QLatin1String("KDE_Classic");
}
bool CursorThemeModel::addTheme(const QDir &dir)
{
XCursorTheme *theme = new XCursorTheme(dir);
if (theme->isHidden()) {
delete theme;
return false;
}
for (int i = 0; i < list.count(); i++)
{
if (list.at(i)->hash() == theme->hash()) {
removeTheme(index(i, 0));
break;
}
}
beginInsertRows(QModelIndex(), rowCount(), rowCount());
list.append(theme);
endInsertRows();
return true;
}
void CursorThemeModel::removeTheme(const QModelIndex &index)
{
if (!index.isValid())
return;
beginRemoveRows(QModelIndex(), index.row(), index.row());
delete list.takeAt(index.row());
endRemoveRows();
}