~mir-team/qtmir/compatibility-with-mir-API-changes

« back to all changes in this revision

Viewing changes to src/modules/Unity/Application/desktopfilereader.cpp

  • Committer: Cemil Azizoglu
  • Date: 2015-01-05 16:25:16 UTC
  • mfrom: (266.1.30 qtmir)
  • Revision ID: cemil.azizoglu@canonical.com-20150105162516-vt9dmnjdvng11yu8
Merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
// local
18
18
#include "desktopfilereader.h"
 
19
#include "gscopedpointer.h"
19
20
#include "logging.h"
20
21
 
21
22
// Qt
22
23
#include <QFile>
 
24
#include <QLocale>
 
25
 
 
26
// GIO
 
27
#include <gio/gdesktopappinfo.h>
23
28
 
24
29
namespace qtmir
25
30
{
26
31
 
27
 
DesktopFileReader::Factory::Factory()
28
 
{
29
 
}
30
 
 
31
 
DesktopFileReader::Factory::~Factory()
32
 
{
33
 
}
34
32
 
35
33
DesktopFileReader* DesktopFileReader::Factory::createInstance(const QString &appId, const QFileInfo& fi)
36
34
{
37
35
    return new DesktopFileReader(appId, fi);
38
36
}
39
37
 
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;
 
39
 
 
40
struct DesktopFileReaderPrivate
 
41
{
 
42
    DesktopFileReaderPrivate(DesktopFileReader *parent):
 
43
            q_ptr( parent )
 
44
    {}
 
45
 
 
46
    QString getKey(const char *key) const
 
47
    {
 
48
        if (!loaded()) return QString();
 
49
 
 
50
        return QString::fromUtf8(g_desktop_app_info_get_string((GDesktopAppInfo*)appInfo.data(), key));
 
51
    }
 
52
 
 
53
    bool loaded() const
 
54
    {
 
55
        return !appInfo.isNull();
 
56
    }
 
57
 
 
58
    DesktopFileReader * const q_ptr;
 
59
    Q_DECLARE_PUBLIC(DesktopFileReader)
 
60
 
 
61
    QString appId;
 
62
    QString file;
 
63
    GAppInfoPointer appInfo; // GAppInfo is actually implemented by GDesktopAppInfo
 
64
};
 
65
 
43
66
 
44
67
DesktopFileReader::DesktopFileReader(const QString &appId, const QFileInfo &desktopFile)
45
 
    : appId_(appId)
46
 
    , entries_(DesktopFileReader::kNumberOfEntries, "")
 
68
    : d_ptr(new DesktopFileReaderPrivate(this))
47
69
{
48
 
    qCDebug(QTMIR_APPLICATIONS) << "DesktopFileReader::DesktopFileReader - this=" << this << "appId=" << appId;
49
 
 
50
 
    file_ = desktopFile.absoluteFilePath();
51
 
    loaded_ = loadDesktopFile(file_);
 
70
    Q_D(DesktopFileReader);
 
71
 
 
72
    d->appId = appId;
 
73
    d->file = desktopFile.absoluteFilePath();
 
74
    d->appInfo.reset((GAppInfo*) g_desktop_app_info_new_from_filename(d->file.toUtf8().constData()));
 
75
 
 
76
    if (!d->loaded()) {
 
77
        if (!desktopFile.exists()) {
 
78
            qCWarning(QTMIR_APPLICATIONS) << "Desktop file for appId:" << appId << "at:" << d->file
 
79
                                          << "does not exist";
 
80
        } else {
 
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!";
 
84
        }
 
85
    }
52
86
}
53
87
 
54
88
DesktopFileReader::~DesktopFileReader()
55
89
{
56
 
    qCDebug(QTMIR_APPLICATIONS) << "DesktopFileReader::~DesktopFileReader";
57
 
    entries_.clear();
58
 
}
59
 
 
60
 
bool DesktopFileReader::loadDesktopFile(QString desktopFile)
61
 
{
62
 
    qCDebug(QTMIR_APPLICATIONS) << "DesktopFileReader::loadDesktopFile - this=" << this << "desktopFile=" << desktopFile;
63
 
 
64
 
    if (this->file().isNull() || this->file().isEmpty()) {
65
 
        qCritical() << "No desktop file found for appId:" << appId_;
66
 
        return false;
67
 
    }
68
 
 
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 }
83
 
    };
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);
98
 
 
99
 
    // Open file.
100
 
    if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
101
 
        qWarning() << "Can't open file:" << file.errorString();
102
 
        return false;
103
 
    }
104
 
 
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";
109
 
            return false;
110
 
        }
111
 
    }
112
 
 
113
 
    int length;
114
 
    unsigned int entryFlags = 0;
115
 
    while ((length = file.readLine(buffer, kBufferSize)) != -1) {
116
 
        // Skip empty lines.
117
 
        if (length > 1) {
118
 
            // Stop when reaching unsupported next group header.
119
 
            if (buffer[0] == '[') {
120
 
                qWarning() << "reached next group header, leaving loop";
121
 
                break;
122
 
            }
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;
130
 
                        break;
131
 
                    }
132
 
                }
133
 
            }
134
 
            // Stop when matching the right number of entries.
135
 
            if (entryFlags == kAllEntriesMask) {
136
 
                break;
137
 
            }
138
 
        }
139
 
    }
140
 
 
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]));
150
 
        return true;
151
 
    } else {
152
 
        qWarning() << "not a valid desktop file, missing mandatory entries in the standard group header";
153
 
        return false;
154
 
    }
 
90
    delete d_ptr;
 
91
}
 
92
 
 
93
QString DesktopFileReader::file() const
 
94
{
 
95
    Q_D(const DesktopFileReader);
 
96
    return d->file;
 
97
}
 
98
 
 
99
QString DesktopFileReader::appId() const
 
100
{
 
101
    Q_D(const DesktopFileReader);
 
102
    return d->appId;
 
103
}
 
104
 
 
105
QString DesktopFileReader::name() const
 
106
{
 
107
    Q_D(const DesktopFileReader);
 
108
    if (!d->loaded()) return QString();
 
109
 
 
110
    return QString::fromUtf8(g_app_info_get_name(d->appInfo.data()));
 
111
}
 
112
 
 
113
QString DesktopFileReader::comment() const
 
114
{
 
115
    Q_D(const DesktopFileReader);
 
116
    if (!d->loaded()) return QString();
 
117
 
 
118
    return QString::fromUtf8(g_app_info_get_description(d->appInfo.data()));
 
119
}
 
120
 
 
121
QString DesktopFileReader::icon() const
 
122
{
 
123
    Q_D(const DesktopFileReader);
 
124
    return d->getKey("Icon");
 
125
}
 
126
 
 
127
QString DesktopFileReader::exec() const
 
128
{
 
129
    Q_D(const DesktopFileReader);
 
130
    if (!d->loaded()) return QString();
 
131
 
 
132
    return QString::fromUtf8(g_app_info_get_commandline(d->appInfo.data()));
 
133
}
 
134
 
 
135
QString DesktopFileReader::path() const
 
136
{
 
137
    Q_D(const DesktopFileReader);
 
138
    return d->getKey("Path");
 
139
}
 
140
 
 
141
QString DesktopFileReader::stageHint() const
 
142
{
 
143
    Q_D(const DesktopFileReader);
 
144
    return d->getKey("X-Ubuntu-StageHint");
 
145
}
 
146
 
 
147
QString DesktopFileReader::splashTitle() const
 
148
{
 
149
    Q_D(const DesktopFileReader);
 
150
    if (!d->loaded()) return QString();
 
151
 
 
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.
 
156
     */
 
157
    GDesktopAppInfo *info = (GDesktopAppInfo*)d->appInfo.data();
 
158
    QLocale defaultLocale;
 
159
    QStringList locales = defaultLocale.uiLanguages();
 
160
 
 
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);
 
168
        }
 
169
    }
 
170
 
 
171
    // Fallback to the non-localized string, if available
 
172
    return d->getKey("X-Ubuntu-Splash-Title");
 
173
}
 
174
 
 
175
QString DesktopFileReader::splashImage() const
 
176
{
 
177
    Q_D(const DesktopFileReader);
 
178
    return d->getKey("X-Ubuntu-Splash-Image");
 
179
}
 
180
 
 
181
QString DesktopFileReader::splashShowHeader() const
 
182
{
 
183
    Q_D(const DesktopFileReader);
 
184
    return d->getKey("X-Ubuntu-Splash-Show-Header");
 
185
}
 
186
 
 
187
QString DesktopFileReader::splashColor() const
 
188
{
 
189
    Q_D(const DesktopFileReader);
 
190
    return d->getKey("X-Ubuntu-Splash-Color");
 
191
}
 
192
 
 
193
QString DesktopFileReader::splashColorHeader() const
 
194
{
 
195
    Q_D(const DesktopFileReader);
 
196
    return d->getKey("X-Ubuntu-Splash-Color-Header");
 
197
}
 
198
 
 
199
QString DesktopFileReader::splashColorFooter() const
 
200
{
 
201
    Q_D(const DesktopFileReader);
 
202
    return d->getKey("X-Ubuntu-Splash-Color-Footer");
 
203
}
 
204
 
 
205
bool DesktopFileReader::loaded() const
 
206
{
 
207
    Q_D(const DesktopFileReader);
 
208
    return d->loaded();
155
209
}
156
210
 
157
211
} // namespace qtmir