~ubuntu-branches/ubuntu/quantal/kdeplasma-addons/quantal

« back to all changes in this revision

Viewing changes to applets/icontasks/recentdocuments.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2011-12-15 14:17:52 UTC
  • mfrom: (0.7.13)
  • Revision ID: package-import@ubuntu.com-20111215141752-np5msqi5czjpvtmq
Tags: 4:4.7.90-0ubuntu1
* new upstream beta release
* Remove kubuntu_04_kimpanel_disable_scim.diff now scim applet has gone
* Remove ibus bits from debian/rules
* Remove kimpanel packages
* Add build-dep on libdbusmenu-qt-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Icon Task Manager
 
3
 *
 
4
 * Copyright 2011 Craig Drummond <craig@kde.org>
 
5
 *
 
6
 * ----
 
7
 *
 
8
 * This program is free software; you can redistribute it and/or modify
 
9
 * it under the terms of the GNU General Public License as published by
 
10
 * the Free Software Foundation; either version 2 of the License, or
 
11
 * (at your option) any later version.
 
12
 *
 
13
 * This program is distributed in the hope that it will be useful,
 
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
16
 * General Public License for more details.
 
17
 *
 
18
 * You should have received a copy of the GNU General Public License
 
19
 * along with this program; see the file COPYING.  If not, write to
 
20
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 
21
 * Boston, MA 02110-1301, USA.
 
22
 */
 
23
 
 
24
#include "recentdocuments.h"
 
25
#include <KDE/KRecentDocument>
 
26
#include <KDE/KDirWatch>
 
27
#include <KDE/KGlobal>
 
28
#include <KDE/KDesktopFile>
 
29
#include <KDE/KConfigGroup>
 
30
#include <KDE/KIcon>
 
31
#include <KDE/KRun>
 
32
#include <KDE/KStandardDirs>
 
33
#include <KDE/KSycoca>
 
34
#include <KDE/KDebug>
 
35
#include <KDE/KServiceTypeTrader>
 
36
#include <KDE/KService>
 
37
#include <KDE/KMimeType>
 
38
#include <QtXml/QDomDocument>
 
39
#include <QtXml/QDomElement>
 
40
#include <QtXml/QDomNode>
 
41
#include <QtXml/QDomText>
 
42
#include <QtCore/QDir>
 
43
#include <QtCore/QFile>
 
44
#include <QtCore/QDateTime>
 
45
 
 
46
K_GLOBAL_STATIC(RecentDocuments, recentDocs)
 
47
 
 
48
static QLatin1String constXbel("recently-used.xbel");
 
49
 
 
50
static QList<QAction *>::ConstIterator findUrl(const QList<QAction *> &list, const QString &url)
 
51
{
 
52
    QList<QAction *>::ConstIterator it(list.constBegin()),
 
53
          end(list.constEnd());
 
54
    for (; it != end; ++it) {
 
55
        if ((*it)->property("url") == url) {
 
56
            break;
 
57
        }
 
58
    }
 
59
    return it;
 
60
}
 
61
 
 
62
static bool hasUrl(const QList<QAction *> &list, const QString &url)
 
63
{
 
64
    return list.end() != findUrl(list, url);
 
65
}
 
66
 
 
67
static QString dirSyntax(const QString &d)
 
68
{
 
69
    if (!d.isEmpty()) {
 
70
        QString ds(d);
 
71
 
 
72
        ds.replace("//", "/");
 
73
 
 
74
        int slashPos(ds.lastIndexOf('/'));
 
75
 
 
76
        if (slashPos != (((int)ds.length()) - 1))
 
77
            ds.append('/');
 
78
 
 
79
        return ds;
 
80
    }
 
81
 
 
82
    return d;
 
83
}
 
84
 
 
85
RecentDocuments * RecentDocuments::self()
 
86
{
 
87
    return recentDocs;
 
88
}
 
89
 
 
90
RecentDocuments::RecentDocuments()
 
91
    : m_enabled(false)
 
92
    , m_watcher(0)
 
93
    , m_menu(0)
 
94
{
 
95
}
 
96
 
 
97
RecentDocuments::~RecentDocuments()
 
98
{
 
99
    if (m_menu) {
 
100
        m_menu->deleteLater();
 
101
    }
 
102
}
 
103
 
 
104
void RecentDocuments::setEnabled(bool enabled)
 
105
{
 
106
    if (m_enabled != enabled) {
 
107
        if (enabled) {
 
108
            if (m_files.isEmpty()) {
 
109
                m_files << File(File::Xbel, dirSyntax(KGlobal::dirs()->localxdgdatadir()) + constXbel)
 
110
                        << File(File::Xbel, dirSyntax(QDir::homePath()) + "." + constXbel)
 
111
                        << File(File::Office, dirSyntax(QDir::homePath()) + ".recently-used");
 
112
            }
 
113
 
 
114
            m_watcher = new KDirWatch(this);
 
115
            m_watcher->addDir(KRecentDocument::recentDocumentDirectory(), KDirWatch::WatchFiles);
 
116
            foreach (File f, m_files) {
 
117
                m_watcher->addFile(f.path);
 
118
            }
 
119
            connect(m_watcher, SIGNAL(created(QString)), this, SLOT(added(QString)));
 
120
            connect(m_watcher, SIGNAL(deleted(QString)), this, SLOT(removed(QString)));
 
121
            connect(m_watcher, SIGNAL(dirty(QString)), this, SLOT(modified(QString)));
 
122
            connect(KSycoca::self(), SIGNAL(databaseChanged(QStringList)), this, SLOT(sycocaChanged(const QStringList &)));
 
123
            readCurrentDocs();
 
124
        } else if (m_enabled) {
 
125
            disconnect(m_watcher, SIGNAL(created(QString)), this, SLOT(added(QString)));
 
126
            disconnect(m_watcher, SIGNAL(deleted(QString)), this, SLOT(removed(QString)));
 
127
            disconnect(m_watcher, SIGNAL(dirty(QString)), this, SLOT(modified(QString)));
 
128
            disconnect(KSycoca::self(), SIGNAL(databaseChanged(QStringList)), this, SLOT(sycocaChanged(const QStringList &)));
 
129
            delete m_watcher;
 
130
            m_watcher = 0;
 
131
 
 
132
            QMap<QString, QList<QAction *> >::Iterator it(m_docs.begin()),
 
133
                 end(m_docs.end());
 
134
 
 
135
            for (; it != end; ++it) {
 
136
                foreach (const QAction * act, *it) {
 
137
                    delete act;
 
138
                }
 
139
            }
 
140
            m_docs.clear();
 
141
            m_apps.clear();
 
142
        }
 
143
        m_enabled = enabled;
 
144
    }
 
145
}
 
146
 
 
147
QList<QAction *> RecentDocuments::get(const QString &app)
 
148
{
 
149
    if (m_enabled) {
 
150
        load();
 
151
        if (m_docs.contains(app)) {
 
152
            if (m_docs[app].count() > 1) {
 
153
                if (!m_menu) {
 
154
                    m_menu = new TaskManager::ToolTipMenu(0, i18n("Recent Documents"));
 
155
                }
 
156
 
 
157
                QList<QAction *> old=m_menu->actions();
 
158
                foreach (QAction * act, old) {
 
159
                    m_menu->removeAction(act);
 
160
                }
 
161
 
 
162
                foreach (QAction * act, m_docs[app]) {
 
163
                    m_menu->addAction(act);
 
164
                }
 
165
 
 
166
                QList<QAction *> acts;
 
167
                acts.append(m_menu->menuAction());
 
168
                return acts;
 
169
            }
 
170
            return m_docs[app];
 
171
        }
 
172
    }
 
173
    return QList<QAction *>();
 
174
}
 
175
 
 
176
void RecentDocuments::added(const QString &path)
 
177
{
 
178
    if (KDesktopFile::isDesktopFile(path)) {
 
179
        removed(path); // Remove first!
 
180
        KDesktopFile df(path);
 
181
        KConfigGroup de(&df, "Desktop Entry");
 
182
        QString url = de.readEntry("URL", QString());
 
183
        QString name = KUrl(url).fileName();
 
184
        QString app = de.readEntry("X-KDE-LastOpenedWith", QString());
 
185
 
 
186
        if (!name.isEmpty() && !app.isEmpty() && !url.isEmpty() && !hasUrl(m_docs[app], url)) {
 
187
            QString icon = de.readEntry("Icon", QString());
 
188
            QAction *act = icon.isEmpty() ? new QAction(name, this) :  new QAction(KIcon(icon), name, this);
 
189
            act->setToolTip(KUrl(url).prettyUrl());
 
190
            act->setProperty("timestamp", (qulonglong)0);
 
191
            act->setProperty("path", path);
 
192
            act->setProperty("url", url);
 
193
            connect(act, SIGNAL(triggered()), SLOT(loadDoc()));
 
194
            m_docs[app].append(act);
 
195
        }
 
196
    } else {
 
197
        QList<File>::Iterator it(m_files.begin()),
 
198
              end(m_files.end());
 
199
        for (; it != end; ++it) {
 
200
            if ((*it).path == path) {
 
201
                (*it).dirty = true;
 
202
                break;
 
203
            }
 
204
        }
 
205
    }
 
206
}
 
207
 
 
208
void RecentDocuments::removed(const QString &path)
 
209
{
 
210
    if (path.endsWith(".desktop")) {
 
211
        QMap<QString, QList<QAction *> >::Iterator it(m_docs.begin()),
 
212
             end(m_docs.end());
 
213
 
 
214
        for (; it != end; ++it) {
 
215
            foreach (QAction * act, *it) {
 
216
                if (act->property("path").toString() == path) {
 
217
                    disconnect(act, SIGNAL(triggered()), this, SLOT(loadDoc()));
 
218
                    delete act;
 
219
                    (*it).removeAll(act);
 
220
                    if ((*it).isEmpty()) {
 
221
                        m_docs.erase(it);
 
222
                    }
 
223
                    return;
 
224
                }
 
225
            }
 
226
        }
 
227
    } else {
 
228
        QList<File>::Iterator it(m_files.begin()),
 
229
              end(m_files.end());
 
230
        for (; it != end; ++it) {
 
231
            if ((*it).path == path) {
 
232
                (*it).dirty = true;
 
233
                break;
 
234
            }
 
235
        }
 
236
    }
 
237
}
 
238
 
 
239
void RecentDocuments::modified(const QString &path)
 
240
{
 
241
    QList<File>::Iterator it(m_files.begin()),
 
242
          end(m_files.end());
 
243
    for (; it != end; ++it) {
 
244
        if ((*it).path == path) {
 
245
            (*it).dirty = true;
 
246
            break;
 
247
        }
 
248
    }
 
249
}
 
250
 
 
251
void RecentDocuments::sycocaChanged(const QStringList &types)
 
252
{
 
253
    if (types.contains("apps")) {
 
254
        m_apps.clear();
 
255
        QList<File>::Iterator it(m_files.begin()),
 
256
              end(m_files.end());
 
257
        for (; it != end; ++it) {
 
258
            if (File::Xbel == (*it).type) {
 
259
                (*it).dirty = true;
 
260
            }
 
261
        }
 
262
    }
 
263
}
 
264
 
 
265
void RecentDocuments::loadDoc()
 
266
{
 
267
    QObject *s = sender();
 
268
    if (s && qobject_cast<QAction *>(s)) {
 
269
        QAction *item = static_cast<QAction *>(s);
 
270
        QString path = item->property("path").toString();
 
271
 
 
272
        if (path.isEmpty()) {
 
273
            QString exec = item->property("exec").toString();
 
274
            KUrl url = KUrl(item->property("url").toString());
 
275
 
 
276
            if (url.isValid() && !exec.isEmpty()) {
 
277
                KRun::run(exec, KUrl::List() << url, 0, QString(), QString(), "0");
 
278
            }
 
279
        } else {
 
280
            new KRun(KUrl(path), 0);
 
281
        }
 
282
    }
 
283
}
 
284
 
 
285
void RecentDocuments::readCurrentDocs()
 
286
{
 
287
    const QStringList documents = KRecentDocument::recentDocuments();
 
288
    foreach (const QString & document, documents) {
 
289
        added(document);
 
290
    }
 
291
}
 
292
 
 
293
void RecentDocuments::load()
 
294
{
 
295
    qulonglong now = (qulonglong)QDateTime::currentMSecsSinceEpoch();
 
296
    QList<File>::Iterator it(m_files.begin()),
 
297
          end(m_files.end());
 
298
    for (; it != end; ++it) {
 
299
        if ((*it).dirty) {
 
300
            if (File::Xbel == (*it).type) {
 
301
                loadXbel((*it).path, now);
 
302
            } else if (File::Office == (*it).type) {
 
303
                loadOffice((*it).path, now);
 
304
            }
 
305
            (*it).dirty = false;
 
306
        }
 
307
    }
 
308
}
 
309
 
 
310
static QString convertMimeType(const QString &mimeType, const KUrl &url)
 
311
{
 
312
    return mimeType == "text/plain" && url.fileName().endsWith(".csv")
 
313
        ? QLatin1String("text/csv") : mimeType;
 
314
}
 
315
 
 
316
RecentDocuments::App RecentDocuments::officeAppForMimeType(const QString &mimeType)
 
317
{
 
318
    if (m_apps.contains(mimeType)) {
 
319
        return m_apps[mimeType];
 
320
    } else {
 
321
        KService::List services = KServiceTypeTrader::self()->query("Application",
 
322
                                    QString("exist Exec and (exist ServiceTypes) and ('libreoffice' ~ Exec) and ('%1' in ServiceTypes)").arg(mimeType));
 
323
 
 
324
        if (!services.empty()) {
 
325
            QString desktopFile = services[0]->entryPath();
 
326
            KDesktopFile df(desktopFile);
 
327
            KConfigGroup grp(&df, "Desktop Entry");
 
328
            QString exec = grp.readEntry("Exec", QString());
 
329
 
 
330
            if (!exec.isEmpty()) {
 
331
                App app(KUrl::fromPath(desktopFile).fileName().remove(".desktop"), exec);
 
332
                m_apps.insert(mimeType, app);
 
333
                return app;
 
334
            }
 
335
        }
 
336
    }
 
337
 
 
338
    return App();
 
339
}
 
340
 
 
341
RecentDocuments::App RecentDocuments::appForExec(const QString &execString)
 
342
{
 
343
    if (m_apps.contains(execString)) {
 
344
        return m_apps[execString];
 
345
    } else {
 
346
        KService::List services = KServiceTypeTrader::self()->query("Application",
 
347
                                    QString("exist Exec and ('%1' =~ Exec)").arg(execString));
 
348
        if (services.empty()) {
 
349
            QString execApp = execString;
 
350
            int space = execApp.indexOf(' ');
 
351
            if (-1 != space) {
 
352
                execApp = execApp.left(space);
 
353
            }
 
354
            services = KServiceTypeTrader::self()->query("Application",
 
355
                        QString("exist TryExec and ('%1' =~ TryExec)").arg(execApp));
 
356
        }
 
357
        if (!services.empty()) {
 
358
            QString desktopFile = services[0]->entryPath();
 
359
            KDesktopFile df(desktopFile);
 
360
            KConfigGroup grp(&df, "Desktop Entry");
 
361
            QString exec = grp.readEntry("Exec", QString());
 
362
 
 
363
            if (!exec.isEmpty()) {
 
364
                App app(KUrl::fromPath(desktopFile).fileName().remove(".desktop"), exec);
 
365
                m_apps.insert(execString, app);
 
366
                return app;
 
367
            }
 
368
        }
 
369
    }
 
370
 
 
371
    return App();
 
372
}
 
373
 
 
374
void RecentDocuments::loadXbel(const QString &path, qulonglong now)
 
375
{
 
376
    QDomDocument doc("xbel");
 
377
    QFile f(path);
 
378
 
 
379
    if (f.open(QIODevice::ReadOnly) && doc.setContent(&f)) {
 
380
        QDomElement root = doc.documentElement();
 
381
        if ("xbel" == root.tagName() && root.hasAttribute("version") && "1.0" == root.attribute("version")) {
 
382
            QDomElement bookmark = root.firstChildElement("bookmark");
 
383
            while (!bookmark.isNull()) {
 
384
                if (bookmark.hasAttribute("href")) {
 
385
                    QDomElement info = bookmark.firstChildElement("info");
 
386
                    if (!info.isNull()) {
 
387
                        QDomElement metadata = info.firstChildElement("metadata");
 
388
                        if (!metadata.isNull() && metadata.hasAttribute("owner") && "http://freedesktop.org" == metadata.attribute("owner")) {
 
389
                            QDomElement applications = metadata.firstChildElement("bookmark:applications");
 
390
                            if (!applications.isNull()) {
 
391
                                QDomElement application = applications.firstChildElement("bookmark:application");
 
392
                                if (!application.isNull() && application.hasAttribute("exec")) {
 
393
                                    KUrl url = bookmark.attribute("href");
 
394
                                    if (url.isValid() && (!url.isLocalFile() || QFile::exists(url.toLocalFile()))) {
 
395
                                        QString exec = application.attribute("exec");
 
396
                                        QDomElement mimeType = metadata.firstChildElement("mime:mime-type");
 
397
                                        QString mType;
 
398
                                        KMimeType::Ptr mime;
 
399
                                        if (!mimeType.isNull() && mimeType.hasAttribute("type")) {
 
400
                                            mType = convertMimeType(mimeType.attribute("type"), url);
 
401
                                            mime = KMimeType::mimeType(mType);
 
402
                                        }
 
403
 
 
404
                                        exec.remove('\'');
 
405
 
 
406
                                        App app = mime && QLatin1String("soffice %u")==exec
 
407
                                                    ? officeAppForMimeType(mType)
 
408
                                                    : appForExec(exec);
 
409
 
 
410
                                        if (!app.name.isEmpty()) {
 
411
                                            QString name = KUrl(url).fileName();
 
412
 
 
413
                                            if (!name.isEmpty()) {
 
414
                                                bool found = false;
 
415
                                                if (!m_docs[app.name].isEmpty()) {
 
416
                                                    QList<QAction *>::ConstIterator it = findUrl(m_docs[app.name], url.url());
 
417
                                                    if (it != m_docs[app.name].constEnd()) {
 
418
                                                        found = true;
 
419
                                                        if ((*it)->property("timestamp").toULongLong() > 0) {
 
420
                                                            (*it)->setProperty("timestamp", now);
 
421
                                                        }
 
422
                                                    }
 
423
                                                }
 
424
                                                if (!found) {
 
425
                                                    QAction *act = mime
 
426
                                                                    ? new QAction(KIcon(mime->iconName()), name, this)
 
427
                                                                    : new QAction(name, this);
 
428
 
 
429
                                                    act->setToolTip(KUrl(url).prettyUrl());
 
430
                                                    act->setProperty("timestamp", now);
 
431
                                                    act->setProperty("url", url.url());
 
432
                                                    act->setProperty("exec", app.exec);
 
433
                                                    act->setProperty("type", (int)File::Xbel);
 
434
                                                    connect(act, SIGNAL(triggered()), SLOT(loadDoc()));
 
435
                                                    m_docs[app.name].append(act);
 
436
                                                }
 
437
                                            }
 
438
                                        }
 
439
                                    }
 
440
                                }
 
441
                            }
 
442
                        }
 
443
                    }
 
444
                }
 
445
                bookmark = bookmark.nextSiblingElement("bookmark");
 
446
            }
 
447
        }
 
448
    }
 
449
 
 
450
    removeOld(now, File::Xbel);
 
451
}
 
452
 
 
453
void RecentDocuments::loadOffice(const QString &path, qulonglong now)
 
454
{
 
455
    QDomDocument doc("RecentFiles");
 
456
    QFile f(path);
 
457
 
 
458
    if (f.open(QIODevice::ReadOnly) && doc.setContent(&f)) {
 
459
        QDomElement root = doc.documentElement();
 
460
        if ("RecentFiles" == root.tagName()) {
 
461
            QDomElement recentItem = root.firstChildElement("RecentItem");
 
462
            while (!recentItem.isNull()) {
 
463
                QDomElement groups = recentItem.firstChildElement("Groups");
 
464
                if (!groups.isNull()) {
 
465
                    QDomElement group = groups.firstChildElement("Group");
 
466
                    bool ok = false;
 
467
                    while (!group.isNull()) {
 
468
                        if (group.text() == "openoffice.org") {
 
469
                            ok = true;
 
470
                            break;
 
471
                        }
 
472
                        group = group.nextSiblingElement("Group");
 
473
                    }
 
474
 
 
475
                    if (ok) {
 
476
                        QDomElement uri = recentItem.firstChildElement("URI");
 
477
                        QDomElement mimeType = recentItem.firstChildElement("Mime-Type");
 
478
 
 
479
                        if (!uri.isNull() && !mimeType.isNull()) {
 
480
                            KUrl url(uri.text());
 
481
 
 
482
                            if (url.isValid() && (!url.isLocalFile() || QFile::exists(url.toLocalFile()))) {
 
483
                                QString mType = convertMimeType(mimeType.text(), url);
 
484
                                App app = officeAppForMimeType(mType);
 
485
 
 
486
                                if (!app.name.isEmpty() && !app.exec.isEmpty()) {
 
487
                                    QString name = KUrl(url).fileName();
 
488
 
 
489
                                    if (!name.isEmpty()) {
 
490
                                        bool found = false;
 
491
                                        if (!m_docs[app.name].isEmpty()) {
 
492
                                            QList<QAction *>::ConstIterator it = findUrl(m_docs[app.name], url.url());
 
493
                                            if (it != m_docs[app.name].constEnd()) {
 
494
                                                found = true;
 
495
                                                if ((*it)->property("timestamp").toULongLong() > 0) {
 
496
                                                    (*it)->setProperty("timestamp", now);
 
497
                                                }
 
498
                                            }
 
499
                                        }
 
500
                                        if (!found) {
 
501
                                            KMimeType::Ptr mime = KMimeType::mimeType(mType);
 
502
                                            QAction *act = mime
 
503
                                                           ? new QAction(KIcon(mime->iconName()), name, this)
 
504
                                                           : new QAction(name, this);
 
505
 
 
506
                                            act->setToolTip(KUrl(url).prettyUrl());
 
507
                                            act->setProperty("local", false);
 
508
                                            act->setProperty("timestamp", now);
 
509
                                            act->setProperty("url", url.url());
 
510
                                            act->setProperty("exec", app.exec);
 
511
                                            act->setProperty("type", (int)File::Office);
 
512
                                            connect(act, SIGNAL(triggered()), SLOT(loadDoc()));
 
513
                                            m_docs[app.name].append(act);
 
514
                                        }
 
515
                                    }
 
516
                                }
 
517
                            }
 
518
                        }
 
519
                    }
 
520
                }
 
521
                recentItem = recentItem.nextSiblingElement("RecentItem");
 
522
            }
 
523
        }
 
524
    }
 
525
 
 
526
    removeOld(now, File::Office);
 
527
}
 
528
 
 
529
void RecentDocuments::removeOld(qulonglong now, File::Type type)
 
530
{
 
531
    QMap<QString, QList<QAction *> >::Iterator it(m_docs.begin()),
 
532
         end(m_docs.end());
 
533
    while (it != end) {
 
534
        QList<QAction *> old;
 
535
 
 
536
        foreach (QAction * act, (*it)) {
 
537
            qulonglong t = act->property("timestamp").toULongLong();
 
538
            if (type==act->property("type").toInt() && t > 0 && t < now) {
 
539
                old.append(act);
 
540
            }
 
541
        }
 
542
 
 
543
        foreach (QAction * act, old) {
 
544
            act->deleteLater();
 
545
            (*it).removeAll(act);
 
546
        }
 
547
 
 
548
        if ((*it).isEmpty()) {
 
549
            QMap<QString, QList<QAction *> >::Iterator cur = it;
 
550
            it++;
 
551
            m_docs.erase(cur);
 
552
        } else {
 
553
            it++;
 
554
        }
 
555
    }
 
556
}
 
557
 
 
558
#include "recentdocuments.moc"