1
/***************************************************************************
2
* Copyright (C) 2004-2009 by Thomas Fischer *
3
* fischer@unix-ag.uni-kl.de *
5
* This program is free software; you can redistribute it and/or modify *
6
* it under the terms of the GNU General Public License as published by *
7
* the Free Software Foundation; either version 2 of the License, or *
8
* (at your option) any later version. *
10
* This program is distributed in the hope that it will be useful, *
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13
* GNU General Public License for more details. *
15
* You should have received a copy of the GNU General Public License *
16
* along with this program; if not, write to the *
17
* Free Software Foundation, Inc., *
18
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19
***************************************************************************/
22
#include <QLatin1String>
26
#include <KConfigGroup>
29
#include <KMimeTypeTrader>
31
#include <KMessageBox>
32
#include <kparts/part.h>
34
#include <fileimporterpdf.h>
35
#include "openfileinfo.h"
37
const QString OpenFileInfo::mimetypeBibTeX = QLatin1String("text/x-bibtex");
39
class OpenFileInfo::OpenFileInfoPrivate
42
static int globalCounter;
46
static const QString keyLastAccess;
47
static const QString keyURL;
48
static const QString dateTimeFormat;
52
KParts::ReadOnlyPart* part;
53
KService::Ptr internalServicePtr;
54
QWidget *internalWidgetParent;
55
QDateTime lastAccessDateTime;
57
OpenFileInfoManager *openFileInfoManager;
61
OpenFileInfoPrivate(OpenFileInfoManager *openFileInfoManager, const KUrl &url, const QString &mimeType, OpenFileInfo *p)
62
: m_counter(-1), p(p), part(NULL), internalServicePtr(KService::Ptr()), internalWidgetParent(NULL), flags(0) {
63
this->openFileInfoManager = openFileInfoManager;
65
this->mimeType = mimeType;
68
~OpenFileInfoPrivate() {
70
KParts::ReadWritePart *rwp = dynamic_cast<KParts::ReadWritePart*>(part);
77
KParts::ReadOnlyPart* createPart(QWidget *newWidgetParent, KService::Ptr newServicePtr = KService::Ptr()) {
78
if (!p->flags().testFlag(OpenFileInfo::Open)) {
79
kWarning() << "Cannot create part for a file which is not open";
83
Q_ASSERT(internalWidgetParent == NULL || internalWidgetParent == newWidgetParent);
85
/** use cached part for this parent if possible */
86
if (internalWidgetParent == newWidgetParent && (newServicePtr == KService::Ptr() || internalServicePtr == newServicePtr)) {
87
Q_ASSERT(part != NULL);
89
} else if (part != NULL) {
90
KParts::ReadWritePart *rwp = dynamic_cast<KParts::ReadWritePart*>(part);
97
/// reset to invalid values in case something goes wrong
98
internalServicePtr = KService::Ptr();
99
internalWidgetParent = NULL;
101
if (newServicePtr.isNull()) {
102
/// no valid KService has been passed
103
/// try to find a read-write part to open file
104
newServicePtr = p->defaultService();
106
if (newServicePtr.isNull()) {
107
kError() << "Cannot find service to handle mimetype " << mimeType << endl;
111
kDebug() << "using service " << newServicePtr->name() << "(name) " << newServicePtr->library() << "(library)";
112
part = newServicePtr->createInstance<KParts::ReadWritePart>(newWidgetParent, (QObject*)newWidgetParent);
114
/// creating a read-write part failed, so maybe it is read-only (like Okular's PDF viewer)?
115
part = newServicePtr->createInstance<KParts::ReadOnlyPart>(newWidgetParent, (QObject*)newWidgetParent);
118
/// still cannot create part, must be error
119
kError() << "Cannot find part for mimetype " << mimeType << endl;
126
/// update document list widget accordingly
127
p->addFlags(OpenFileInfo::RecentlyUsed);
128
p->addFlags(OpenFileInfo::HasName);
130
/// initialize part with empty document
131
part->openUrl(KUrl());
133
p->addFlags(OpenFileInfo::Open);
135
internalServicePtr = newServicePtr;
136
internalWidgetParent = newWidgetParent;
138
Q_ASSERT(part != NULL); /// test should not be necessary, but just to be save ...
143
if (!url.isValid() && m_counter < 0)
144
m_counter = ++globalCounter;
145
else if (url.isValid())
146
kWarning() << "This function should not be called if URL is valid";
152
int OpenFileInfo::OpenFileInfoPrivate::globalCounter = 0;
153
const QString OpenFileInfo::OpenFileInfoPrivate::dateTimeFormat = QLatin1String("yyyy-MM-dd-hh-mm-ss-zzz");
154
const QString OpenFileInfo::OpenFileInfoPrivate::keyLastAccess = QLatin1String("LastAccess");
155
const QString OpenFileInfo::OpenFileInfoPrivate::keyURL = QLatin1String("URL");
157
OpenFileInfo::OpenFileInfo(OpenFileInfoManager *openFileInfoManager, const KUrl &url)
158
: d(new OpenFileInfoPrivate(openFileInfoManager, url, KMimeType::findByUrl(url)->name(), this))
163
OpenFileInfo::OpenFileInfo(OpenFileInfoManager *openFileInfoManager, const QString &mimeType)
164
: d(new OpenFileInfoPrivate(openFileInfoManager, KUrl(), mimeType, this))
169
OpenFileInfo::~OpenFileInfo()
174
void OpenFileInfo::setUrl(const KUrl& url)
176
Q_ASSERT(url.isValid());
178
d->mimeType = KMimeType::findByUrl(url)->name();
179
addFlags(OpenFileInfo::HasName);
182
KUrl OpenFileInfo::url() const
187
bool OpenFileInfo::isModified() const
189
KParts::ReadWritePart *rwPart = dynamic_cast< KParts::ReadWritePart*>(d->part);
193
return rwPart->isModified();
196
bool OpenFileInfo::save()
198
KParts::ReadWritePart *rwPart = dynamic_cast< KParts::ReadWritePart*>(d->part);
202
return rwPart->save();
205
bool OpenFileInfo::close()
207
if (d->part == NULL) {
208
/// if there is no part, closing always "succeeds"
212
KParts::ReadWritePart *rwp = dynamic_cast<KParts::ReadWritePart*>(d->part);
213
if (rwp == NULL || rwp->closeUrl(true)) {
214
d->part->deleteLater();
216
d->internalWidgetParent = NULL;
222
QString OpenFileInfo::mimeType() const
227
QString OpenFileInfo::shortCaption() const
229
if (d->url.isValid())
230
return d->url.fileName();
232
return i18n("Unnamed-%1", d->counter());
235
QString OpenFileInfo::fullCaption() const
237
if (d->url.isValid())
238
return d->url.pathOrUrl();
240
return shortCaption();
243
KParts::ReadOnlyPart* OpenFileInfo::part(QWidget *parent, KService::Ptr servicePtr)
245
return d->createPart(parent, servicePtr);
248
OpenFileInfo::StatusFlags OpenFileInfo::flags() const
253
void OpenFileInfo::setFlags(StatusFlags statusFlags)
255
/// disallow files without name or valid url to become favorites
256
if (!d->url.isValid() || !d->flags.testFlag(HasName)) statusFlags &= ~Favorite;
258
bool hasChanged = d->flags != statusFlags;
259
d->flags = statusFlags;
261
emit flagsChanged(statusFlags);
264
void OpenFileInfo::addFlags(StatusFlags statusFlags)
266
/// disallow files without name or valid url to become favorites
267
if (!d->url.isValid() || !d->flags.testFlag(HasName)) statusFlags &= ~Favorite;
269
bool hasChanged = (~d->flags & statusFlags) > 0;
270
d->flags |= statusFlags;
272
emit flagsChanged(statusFlags);
275
void OpenFileInfo::removeFlags(StatusFlags statusFlags)
277
bool hasChanged = (d->flags & statusFlags) > 0;
278
d->flags &= ~statusFlags;
280
emit flagsChanged(statusFlags);
283
QDateTime OpenFileInfo::lastAccess() const
285
return d->lastAccessDateTime;
288
void OpenFileInfo::setLastAccess(const QDateTime& dateTime)
290
d->lastAccessDateTime = dateTime;
291
emit flagsChanged(OpenFileInfo::RecentlyUsed);
294
KService::List OpenFileInfo::listOfServices()
296
KService::List result = KMimeTypeTrader::self()->query(mimeType(), QLatin1String("KParts/ReadWritePart"));
297
if (result.isEmpty())
298
result = KMimeTypeTrader::self()->query(mimeType(), QLatin1String("KParts/ReadOnlyPart"));
302
KService::Ptr OpenFileInfo::defaultService()
304
const QString mt = mimeType();
305
kDebug() << "Looking for service for mimetype " << mt;
306
KService::Ptr result = KMimeTypeTrader::self()->preferredService(mt, QLatin1String("KParts/ReadWritePart"));
308
result = KMimeTypeTrader::self()->preferredService(mt, QLatin1String("KParts/ReadOnlyPart"));
312
KService::Ptr OpenFileInfo::currentService()
314
return d->internalServicePtr;
317
class OpenFileInfoManager::OpenFileInfoManagerPrivate
320
static const QString configGroupNameRecentlyUsed;
321
static const QString configGroupNameFavorites;
322
static const int maxNumRecentlyUsedFiles, maxNumFavoriteFiles;
325
OpenFileInfoManager *p;
327
QList<OpenFileInfo*> openFileInfoList;
328
OpenFileInfo *currentFileInfo;
330
OpenFileInfoManagerPrivate(OpenFileInfoManager *parent)
331
: p(parent), currentFileInfo(NULL) {
335
~OpenFileInfoManagerPrivate() {
336
for (QList<OpenFileInfo*>::Iterator it = openFileInfoList.begin(); it != openFileInfoList.end(); ++it) {
337
OpenFileInfo *ofi = *it;
342
static bool byNameLessThan(const OpenFileInfo *left, const OpenFileInfo *right) {
343
return left->shortCaption() < right->shortCaption();
346
static bool byLRULessThan(const OpenFileInfo *left, const OpenFileInfo *right) {
347
return left->lastAccess() > right->lastAccess(); /// reverse sorting!
351
readConfig(OpenFileInfo::RecentlyUsed, configGroupNameRecentlyUsed, maxNumRecentlyUsedFiles);
352
readConfig(OpenFileInfo::Favorite, configGroupNameFavorites, maxNumFavoriteFiles);
356
writeConfig(OpenFileInfo::RecentlyUsed, configGroupNameRecentlyUsed, maxNumRecentlyUsedFiles);
357
writeConfig(OpenFileInfo::Favorite, configGroupNameFavorites, maxNumFavoriteFiles);
360
void readConfig(OpenFileInfo::StatusFlag statusFlag, const QString& configGroupName, int maxNumFiles) {
361
KSharedConfig::Ptr config = KGlobal::config();
363
KConfigGroup cg(config, configGroupName);
364
for (int i = 0; i < maxNumFiles; ++i) {
365
KUrl fileUrl = KUrl(cg.readEntry(QString("%1-%2").arg(OpenFileInfo::OpenFileInfoPrivate::keyURL).arg(i), ""));
366
if (!fileUrl.isValid()) break;
367
OpenFileInfo *ofi = p->contains(fileUrl);
369
ofi = p->open(fileUrl);
371
ofi->addFlags(statusFlag);
372
ofi->addFlags(OpenFileInfo::HasName);
373
ofi->setLastAccess(QDateTime::fromString(cg.readEntry(QString("%1-%2").arg(OpenFileInfo::OpenFileInfoPrivate::keyLastAccess).arg(i), ""), OpenFileInfo::OpenFileInfoPrivate::dateTimeFormat));
377
void writeConfig(OpenFileInfo::StatusFlag statusFlag, const QString& configGroupName, int maxNumFiles) {
378
KSharedConfig::Ptr config = KGlobal::config();
379
KConfigGroup cg(config, configGroupName);
380
QList<OpenFileInfo*> list = p->filteredItems(statusFlag);
383
for (QList<OpenFileInfo*>::Iterator it = list.begin(); i < maxNumFiles && it != list.end(); ++it, ++i) {
384
OpenFileInfo *ofi = *it;
386
cg.writeEntry(QString("%1-%2").arg(OpenFileInfo::OpenFileInfoPrivate::keyURL).arg(i), ofi->url().pathOrUrl());
387
cg.writeEntry(QString("%1-%2").arg(OpenFileInfo::OpenFileInfoPrivate::keyLastAccess).arg(i), ofi->lastAccess().toString(OpenFileInfo::OpenFileInfoPrivate::dateTimeFormat));
393
const QString OpenFileInfoManager::OpenFileInfoManagerPrivate::configGroupNameRecentlyUsed = QLatin1String("DocumentList-RecentlyUsed");
394
const QString OpenFileInfoManager::OpenFileInfoManagerPrivate::configGroupNameFavorites = QLatin1String("DocumentList-Favorites");
395
const int OpenFileInfoManager::OpenFileInfoManagerPrivate::maxNumFavoriteFiles = 256;
396
const int OpenFileInfoManager::OpenFileInfoManagerPrivate::maxNumRecentlyUsedFiles = 8;
398
OpenFileInfoManager *OpenFileInfoManager::singletonOpenFileInfoManager = NULL;
400
OpenFileInfoManager* OpenFileInfoManager::getOpenFileInfoManager()
402
if (singletonOpenFileInfoManager == NULL)
403
singletonOpenFileInfoManager = new OpenFileInfoManager();
404
return singletonOpenFileInfoManager;
407
OpenFileInfoManager::OpenFileInfoManager()
408
: d(new OpenFileInfoManagerPrivate(this))
413
OpenFileInfoManager::~OpenFileInfoManager()
419
OpenFileInfo *OpenFileInfoManager::createNew(const QString& mimeType)
421
OpenFileInfo *result = new OpenFileInfo(this, mimeType);
422
connect(result, SIGNAL(flagsChanged(OpenFileInfo::StatusFlags)), this, SIGNAL(flagsChanged(OpenFileInfo::StatusFlags)));
423
d->openFileInfoList << result;
424
result->setLastAccess();
428
OpenFileInfo *OpenFileInfoManager::open(const KUrl & url)
430
Q_ASSERT(url.isValid());
432
OpenFileInfo *result = contains(url);
433
if (result == NULL) {
434
/// file not yet open
435
result = new OpenFileInfo(this, url);
436
connect(result, SIGNAL(flagsChanged(OpenFileInfo::StatusFlags)), this, SIGNAL(flagsChanged(OpenFileInfo::StatusFlags)));
437
d->openFileInfoList << result;
439
result->setLastAccess();
443
OpenFileInfo *OpenFileInfoManager::contains(const KUrl&url) const
445
if (!url.isValid()) return NULL; /// can only be unnamed file
447
for (QList<OpenFileInfo*>::Iterator it = d->openFileInfoList.begin(); it != d->openFileInfoList.end(); ++it) {
448
OpenFileInfo *ofi = *it;
449
if (ofi->url().equals(url))
455
bool OpenFileInfoManager::changeUrl(OpenFileInfo *openFileInfo, const KUrl & url)
457
OpenFileInfo *previouslyContained = contains(url);
459
/// check if old url differs from new url and old url is valid
460
if (previouslyContained != NULL && previouslyContained->flags().testFlag(OpenFileInfo::Open) && previouslyContained != openFileInfo) {
461
kDebug() << "Open file with same URL already exists, forcefully closing it" << endl;
462
close(previouslyContained);
465
KUrl oldUrl = openFileInfo->url();
466
openFileInfo->setUrl(url);
468
if (!url.equals(oldUrl) && oldUrl.isValid()) {
469
/// current document was most probabily renamed (e.g. due to "Save As")
470
/// add old URL to recently used files, but exclude the open files list
471
OpenFileInfo *ofi = open(oldUrl);
472
OpenFileInfo::StatusFlags statusFlags = (openFileInfo->flags() & (~OpenFileInfo::Open)) | OpenFileInfo::RecentlyUsed;
473
ofi->setFlags(statusFlags);
475
if (previouslyContained != NULL) {
476
/// keep Favorite flag if set in file that have previously same URL
477
if (previouslyContained->flags().testFlag(OpenFileInfo::Favorite))
478
openFileInfo->setFlags(openFileInfo->flags() | OpenFileInfo::Favorite);
480
/// remove the old entry with the same url has it will be replaced by the new one
481
d->openFileInfoList.removeOne(previouslyContained);
482
previouslyContained->deleteLater();
483
OpenFileInfo::StatusFlags statusFlags = OpenFileInfo::Open;
484
statusFlags |= OpenFileInfo::RecentlyUsed;
485
statusFlags |= OpenFileInfo::Favorite;
486
emit flagsChanged(statusFlags);
489
if (openFileInfo == d->currentFileInfo)
490
emit currentChanged(openFileInfo, KService::Ptr());
491
emit flagsChanged(openFileInfo->flags());
496
bool OpenFileInfoManager::close(OpenFileInfo *openFileInfo)
498
Q_ASSERT_X(openFileInfo != NULL, "void OpenFileInfoManager::close(OpenFileInfo *openFileInfo)", "Cannot close openFileInfo which is NULL");
499
bool isClosing = false;
500
openFileInfo->setLastAccess();
502
/// remove flag "open" from file to be closed and determine which file to show instead
503
OpenFileInfo *nextCurrent = (d->currentFileInfo == openFileInfo) ? NULL : d->currentFileInfo;
504
for (QList<OpenFileInfo*>::Iterator it = d->openFileInfoList.begin(); it != d->openFileInfoList.end(); ++it) {
505
OpenFileInfo *ofi = *it;
506
if (!isClosing && ofi == openFileInfo && openFileInfo->close()) {
508
openFileInfo->removeFlags(OpenFileInfo::Open);
509
if (openFileInfo->flags().testFlag(OpenFileInfo::HasName))
510
openFileInfo->addFlags(OpenFileInfo::RecentlyUsed);
511
} else if (nextCurrent == NULL && ofi->flags().testFlag(OpenFileInfo::Open))
514
setCurrentFile(nextCurrent);
519
OpenFileInfo *OpenFileInfoManager::currentFile() const
521
return d->currentFileInfo;
524
void OpenFileInfoManager::setCurrentFile(OpenFileInfo *openFileInfo, KService::Ptr servicePtr)
526
bool hasChanged = d->currentFileInfo != openFileInfo;
527
OpenFileInfo *previous = d->currentFileInfo;
528
d->currentFileInfo = openFileInfo;
530
if (d->currentFileInfo != NULL) {
531
d->currentFileInfo->addFlags(OpenFileInfo::Open);
532
d->currentFileInfo->setLastAccess();
535
if (previous != NULL) previous->setLastAccess();
536
emit currentChanged(openFileInfo, servicePtr);
537
} else if (servicePtr != openFileInfo->currentService())
538
emit currentChanged(openFileInfo, servicePtr);
541
QList<OpenFileInfo*> OpenFileInfoManager::filteredItems(OpenFileInfo::StatusFlags required, OpenFileInfo::StatusFlags forbidden)
543
QList<OpenFileInfo*> result;
545
for (QList<OpenFileInfo*>::Iterator it = d->openFileInfoList.begin(); it != d->openFileInfoList.end(); ++it) {
546
OpenFileInfo *ofi = *it;
547
if ((ofi->flags() & required) == required && (ofi->flags() & forbidden) == 0)
551
if (required == OpenFileInfo::RecentlyUsed)
552
qSort(result.begin(), result.end(), OpenFileInfoManagerPrivate::byLRULessThan);
553
else if (required == OpenFileInfo::Favorite || required == OpenFileInfo::Open)
554
qSort(result.begin(), result.end(), OpenFileInfoManagerPrivate::byNameLessThan);
560
void OpenFileInfoManager::deferredListsChanged()
562
kDebug() << "deferredListsChanged" << endl;
563
OpenFileInfo::StatusFlags statusFlags = OpenFileInfo::Open;
564
statusFlags |= OpenFileInfo::RecentlyUsed;
565
statusFlags |= OpenFileInfo::Favorite;
566
emit flagsChanged(statusFlags);