18
18
#include "desktopfilereader.h"
19
#include "gscopedpointer.h"
19
20
#include "logging.h"
27
#include <gio/gdesktopappinfo.h>
27
DesktopFileReader::Factory::Factory()
31
DesktopFileReader::Factory::~Factory()
35
33
DesktopFileReader* DesktopFileReader::Factory::createInstance(const QString &appId, const QFileInfo& fi)
37
35
return new DesktopFileReader(appId, fi);
40
// Retrieves the size of an array at compile time.
41
#define ARRAY_SIZE(a) \
42
((sizeof(a) / sizeof(*(a))) / static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
38
typedef GObjectScopedPointer<GAppInfo> GAppInfoPointer;
40
struct DesktopFileReaderPrivate
42
DesktopFileReaderPrivate(DesktopFileReader *parent):
46
QString getKey(const char *key) const
48
if (!loaded()) return QString();
50
return QString::fromUtf8(g_desktop_app_info_get_string((GDesktopAppInfo*)appInfo.data(), key));
55
return !appInfo.isNull();
58
DesktopFileReader * const q_ptr;
59
Q_DECLARE_PUBLIC(DesktopFileReader)
63
GAppInfoPointer appInfo; // GAppInfo is actually implemented by GDesktopAppInfo
44
67
DesktopFileReader::DesktopFileReader(const QString &appId, const QFileInfo &desktopFile)
46
, entries_(DesktopFileReader::kNumberOfEntries, "")
68
: d_ptr(new DesktopFileReaderPrivate(this))
48
qCDebug(QTMIR_APPLICATIONS) << "DesktopFileReader::DesktopFileReader - this=" << this << "appId=" << appId;
50
file_ = desktopFile.absoluteFilePath();
51
loaded_ = loadDesktopFile(file_);
70
Q_D(DesktopFileReader);
73
d->file = desktopFile.absoluteFilePath();
74
d->appInfo.reset((GAppInfo*) g_desktop_app_info_new_from_filename(d->file.toUtf8().constData()));
77
if (!desktopFile.exists()) {
78
qCWarning(QTMIR_APPLICATIONS) << "Desktop file for appId:" << appId << "at:" << d->file
81
qCWarning(QTMIR_APPLICATIONS) << "Desktop file for appId:" << appId << "at:" << d->file
82
<< "is not valid - check its syntax, and that the binary specified"
83
<< "by the Exec line is installed!";
54
88
DesktopFileReader::~DesktopFileReader()
56
qCDebug(QTMIR_APPLICATIONS) << "DesktopFileReader::~DesktopFileReader";
60
bool DesktopFileReader::loadDesktopFile(QString desktopFile)
62
qCDebug(QTMIR_APPLICATIONS) << "DesktopFileReader::loadDesktopFile - this=" << this << "desktopFile=" << desktopFile;
64
if (this->file().isNull() || this->file().isEmpty()) {
65
qCritical() << "No desktop file found for appId:" << appId_;
69
Q_ASSERT(desktopFile != NULL);
70
const struct { const char* const name; int size; unsigned int flag; } kEntryNames[] = {
71
{ "Name=", sizeof("Name=") - 1, 1 << DesktopFileReader::kNameIndex },
72
{ "Comment=", sizeof("Comment=") - 1, 1 << DesktopFileReader::kCommentIndex },
73
{ "Icon=", sizeof("Icon=") - 1, 1 << DesktopFileReader::kIconIndex },
74
{ "Exec=", sizeof("Exec=") - 1, 1 << DesktopFileReader::kExecIndex },
75
{ "Path=", sizeof("Path=") - 1, 1 << DesktopFileReader::kPathIndex },
76
{ "X-Ubuntu-StageHint=", sizeof("X-Ubuntu-StageHint=") - 1, 1 << DesktopFileReader::kStageHintIndex },
77
{ "X-Ubuntu-Splash-Title=", sizeof("X-Ubuntu-Splash-Title=") - 1, 1 << DesktopFileReader::kSplashTitleIndex },
78
{ "X-Ubuntu-Splash-Image=", sizeof("X-Ubuntu-Splash-Image=") - 1, 1 << DesktopFileReader::kSplashImageIndex },
79
{ "X-Ubuntu-Splash-Show-Header=", sizeof("X-Ubuntu-Splash-Show-Header=") - 1, 1 << DesktopFileReader::kSplashShowHeaderIndex },
80
{ "X-Ubuntu-Splash-Color=", sizeof("X-Ubuntu-Splash-Color=") - 1, 1 << DesktopFileReader::kSplashColorIndex },
81
{ "X-Ubuntu-Splash-Color-Header=", sizeof("X-Ubuntu-Splash-Color-Header=") - 1, 1 << DesktopFileReader::kSplashColorHeaderIndex },
82
{ "X-Ubuntu-Splash-Color-Footer=", sizeof("X-Ubuntu-Splash-Color-Footer=") - 1, 1 << DesktopFileReader::kSplashColorFooterIndex }
84
const unsigned int kAllEntriesMask =
85
(1 << DesktopFileReader::kNameIndex) | (1 << DesktopFileReader::kCommentIndex)
86
| (1 << DesktopFileReader::kIconIndex) | (1 << DesktopFileReader::kExecIndex)
87
| (1 << DesktopFileReader::kPathIndex) | (1 << DesktopFileReader::kStageHintIndex)
88
| (1 << DesktopFileReader::kSplashTitleIndex) | (1 << DesktopFileReader::kSplashImageIndex)
89
| (1 << DesktopFileReader::kSplashShowHeaderIndex) | (1 << DesktopFileReader::kSplashColorIndex)
90
| (1 << DesktopFileReader::kSplashColorHeaderIndex) | (1 << DesktopFileReader::kSplashColorFooterIndex);
91
const unsigned int kMandatoryEntriesMask =
92
(1 << DesktopFileReader::kNameIndex) | (1 << DesktopFileReader::kIconIndex)
93
| (1 << DesktopFileReader::kExecIndex);
94
const int kEntriesCount = ARRAY_SIZE(kEntryNames);
95
const int kBufferSize = 256;
96
static char buffer[kBufferSize];
97
QFile file(desktopFile);
100
if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
101
qWarning() << "Can't open file:" << file.errorString();
105
// Validate "magic key" (standard group header).
106
if (file.readLine(buffer, kBufferSize) != -1) {
107
if (strncmp(buffer, "[Desktop Entry]", sizeof("[Desktop Entry]") - 1)) {
108
qWarning() << "not a desktop file, unable to read it";
114
unsigned int entryFlags = 0;
115
while ((length = file.readLine(buffer, kBufferSize)) != -1) {
118
// Stop when reaching unsupported next group header.
119
if (buffer[0] == '[') {
120
qWarning() << "reached next group header, leaving loop";
123
// Lookup entries ignoring duplicates if any.
124
for (int i = 0; i < kEntriesCount; i++) {
125
if (!strncmp(buffer, kEntryNames[i].name, kEntryNames[i].size)) {
126
if (~entryFlags & kEntryNames[i].flag) {
127
buffer[length-1] = '\0';
128
entries_[i] = QString::fromUtf8(&buffer[kEntryNames[i].size]);
129
entryFlags |= kEntryNames[i].flag;
134
// Stop when matching the right number of entries.
135
if (entryFlags == kAllEntriesMask) {
141
// Check that the mandatory entries are set.
142
if ((entryFlags & kMandatoryEntriesMask) == kMandatoryEntriesMask) {
143
qDebug("loaded desktop file with name='%s', comment='%s', icon='%s', exec='%s', path='%s', stagehint='%s'",
144
qPrintable(entries_[DesktopFileReader::kNameIndex]),
145
qPrintable(entries_[DesktopFileReader::kCommentIndex]),
146
qPrintable(entries_[DesktopFileReader::kIconIndex]),
147
qPrintable(entries_[DesktopFileReader::kExecIndex]),
148
qPrintable(entries_[DesktopFileReader::kPathIndex]),
149
qPrintable(entries_[DesktopFileReader::kStageHintIndex]));
152
qWarning() << "not a valid desktop file, missing mandatory entries in the standard group header";
93
QString DesktopFileReader::file() const
95
Q_D(const DesktopFileReader);
99
QString DesktopFileReader::appId() const
101
Q_D(const DesktopFileReader);
105
QString DesktopFileReader::name() const
107
Q_D(const DesktopFileReader);
108
if (!d->loaded()) return QString();
110
return QString::fromUtf8(g_app_info_get_name(d->appInfo.data()));
113
QString DesktopFileReader::comment() const
115
Q_D(const DesktopFileReader);
116
if (!d->loaded()) return QString();
118
return QString::fromUtf8(g_app_info_get_description(d->appInfo.data()));
121
QString DesktopFileReader::icon() const
123
Q_D(const DesktopFileReader);
124
return d->getKey("Icon");
127
QString DesktopFileReader::exec() const
129
Q_D(const DesktopFileReader);
130
if (!d->loaded()) return QString();
132
return QString::fromUtf8(g_app_info_get_commandline(d->appInfo.data()));
135
QString DesktopFileReader::path() const
137
Q_D(const DesktopFileReader);
138
return d->getKey("Path");
141
QString DesktopFileReader::stageHint() const
143
Q_D(const DesktopFileReader);
144
return d->getKey("X-Ubuntu-StageHint");
147
QString DesktopFileReader::splashTitle() const
149
Q_D(const DesktopFileReader);
150
if (!d->loaded()) return QString();
152
/* Sadly GDesktopAppInfo only considers Name, GenericName, Comments and Keywords to be keys
153
* which can have locale-specific entries. So we need to work to make X-Ubuntu-Splash-Title
154
* locale-aware, by generating a locale-correct key name and seeing if that exists. If yes,
155
* get the value and return it. Else fallback to the non-localized value.
157
GDesktopAppInfo *info = (GDesktopAppInfo*)d->appInfo.data();
158
QLocale defaultLocale;
159
QStringList locales = defaultLocale.uiLanguages();
161
QString keyTemplate("X-Ubuntu-Splash-Title[%1]");
162
for (QString locale: locales) {
163
// Desktop files use local specifiers with underscore separators but Qt uses hyphens
164
locale = locale.replace('-', '_');
165
const char* key = keyTemplate.arg(locale).toUtf8().constData();
166
if (g_desktop_app_info_has_key(info, key)) {
167
return d->getKey(key);
171
// Fallback to the non-localized string, if available
172
return d->getKey("X-Ubuntu-Splash-Title");
175
QString DesktopFileReader::splashImage() const
177
Q_D(const DesktopFileReader);
178
return d->getKey("X-Ubuntu-Splash-Image");
181
QString DesktopFileReader::splashShowHeader() const
183
Q_D(const DesktopFileReader);
184
return d->getKey("X-Ubuntu-Splash-Show-Header");
187
QString DesktopFileReader::splashColor() const
189
Q_D(const DesktopFileReader);
190
return d->getKey("X-Ubuntu-Splash-Color");
193
QString DesktopFileReader::splashColorHeader() const
195
Q_D(const DesktopFileReader);
196
return d->getKey("X-Ubuntu-Splash-Color-Header");
199
QString DesktopFileReader::splashColorFooter() const
201
Q_D(const DesktopFileReader);
202
return d->getKey("X-Ubuntu-Splash-Color-Footer");
205
bool DesktopFileReader::loaded() const
207
Q_D(const DesktopFileReader);
157
211
} // namespace qtmir