1 /* This file is part of the KDE project
2 Copyright (C) 2005-2006 Peter Simonsson <psn@linux.se>
3 Copyright 2012 Friedrich W. H. Kossebau <kossebau@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library 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 GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19 */
20
21 #include "KoRecentDocumentsPane.h"
22
23 #include <QFile>
24 #include <QStandardItemModel>
25 #include <QUrl>
26
27 #include <KSharedConfig>
28 #include <KConfigGroup>
29 #include <kfileitem.h>
30 #include <kio/previewjob.h>
31
32 #include <KoIcon.h>
33
34
35 static const int MAX_RECENTFILES_ENTRIES = 10;
36
37
38 enum KoRecentDocumentRoles {
39 PreviewRole = Qt::UserRole
40 };
41
42 class KoFileListItem : public QStandardItem
43 {
44 public:
KoFileListItem(const QIcon & icon,const QString & text,const KFileItem & item)45 KoFileListItem(const QIcon &icon, const QString &text, const KFileItem& item)
46 : QStandardItem(icon, text)
47 , m_fileItem(item){
48 }
49
~KoFileListItem()50 ~KoFileListItem() override {
51 }
52
fileItem() const53 const KFileItem &fileItem() const {
54 return m_fileItem;
55 }
56
57 private:
58 KFileItem m_fileItem;
59 };
60
61
62 class KoRecentDocumentsPanePrivate
63 {
64 public:
KoRecentDocumentsPanePrivate()65 KoRecentDocumentsPanePrivate()
66 {
67 }
68
~KoRecentDocumentsPanePrivate()69 ~KoRecentDocumentsPanePrivate()
70 {
71 foreach(KJob* job, m_previewJobs)
72 job->kill();
73 }
74
75 QList<KJob*> m_previewJobs;
76 };
77
78
KoRecentDocumentsPane(QWidget * parent,const QString & header)79 KoRecentDocumentsPane::KoRecentDocumentsPane(QWidget* parent, const QString& header)
80 : KoDetailsPane(parent, header)
81 , d(new KoRecentDocumentsPanePrivate)
82 {
83 setFocusProxy(m_documentList);
84 m_openButton->setText(i18n("Open This Document"));
85 m_openButton->setIcon(koIcon("document-open"));
86
87 m_alwaysUseCheckBox->hide();
88
89 model()->setSortRole(0); // Disable sorting
90
91 // load list of recent files from config
92 KConfigGroup config(KSharedConfig::openConfig(), "RecentFiles");
93
94 QString fileKey;
95 QString fileValue;
96 QUrl url;
97 QString nameValue;
98 KFileItemList fileList;
99 QStandardItem* rootItem = model()->invisibleRootItem();
100
101 for (int i = 1; i <= MAX_RECENTFILES_ENTRIES; ++i) {
102 fileValue = config.readPathEntry(QString("File%1").arg(i), QString());
103
104 // ignore empty entries
105 if (fileValue.isEmpty()) {
106 continue;
107 }
108
109 url = QUrl::fromUserInput(fileValue);
110
111 // ignore entries for files known to no longer exist
112 if (url.isLocalFile() && !QFile::exists(url.toLocalFile())) {
113 continue;
114 }
115 // ignore duplicated entries
116 if (!fileList.findByUrl(url).isNull()) {
117 continue;
118 }
119
120 nameValue = config.readPathEntry(QString("Name%1").arg(i), QString());
121 // handle name entries with empty strings
122 if (nameValue.isEmpty()) {
123 nameValue = url.fileName();
124 }
125
126 KFileItem fileItem(url);
127 fileList.prepend(fileItem);
128 const QIcon icon = QIcon::fromTheme(fileItem.iconName());
129 KoFileListItem* item = new KoFileListItem(icon, nameValue, fileItem);
130 item->setEditable(false);
131 rootItem->insertRow(0, item);
132 }
133
134
135 //Select the first file
136 QModelIndex firstIndex = model()->indexFromItem(model()->item(0));
137 m_documentList->selectionModel()->select(firstIndex, QItemSelectionModel::Select);
138 m_documentList->selectionModel()->setCurrentIndex(firstIndex, QItemSelectionModel::Select);
139
140 QStringList availablePlugins = KIO::PreviewJob::availablePlugins();
141 KIO::PreviewJob *previewJob = KIO::filePreview(fileList, QSize(IconExtent, IconExtent), &availablePlugins);
142
143 d->m_previewJobs.append(previewJob);
144 connect(previewJob, SIGNAL(result(KJob*)), SLOT(previewResult(KJob*)));
145 connect(previewJob, SIGNAL(gotPreview(KFileItem,QPixmap)),
146 SLOT(updateIcon(KFileItem,QPixmap)));
147 }
148
~KoRecentDocumentsPane()149 KoRecentDocumentsPane::~KoRecentDocumentsPane()
150 {
151 delete d;
152 }
153
selectionChanged(const QModelIndex & index)154 void KoRecentDocumentsPane::selectionChanged(const QModelIndex& index)
155 {
156 if (index.isValid()) {
157 KoFileListItem* item = static_cast<KoFileListItem*>(model()->itemFromIndex(index));
158 const KFileItem fileItem = item->fileItem();
159
160 m_openButton->setEnabled(true);
161 m_titleLabel->setText(item->data(Qt::DisplayRole).toString());
162
163 QPixmap preview = item->data(PreviewRole).value<QPixmap>();
164 if (preview.isNull()) {
165 // need to fetch preview
166 const KFileItemList fileList = KFileItemList() << fileItem;
167 QStringList availablePlugins = KIO::PreviewJob::availablePlugins();
168 KIO::PreviewJob *previewJob = KIO::filePreview(fileList, QSize(PreviewExtent, PreviewExtent), &availablePlugins);
169
170 d->m_previewJobs.append(previewJob);
171 connect(previewJob, SIGNAL(result(KJob*)), SLOT(previewResult(KJob*)));
172 connect(previewJob, SIGNAL(gotPreview(KFileItem,QPixmap)),
173 SLOT(updatePreview(KFileItem,QPixmap)));
174
175 // for now set preview to icon
176 preview = item->icon().pixmap(PreviewExtent);
177 if (preview.width() < PreviewExtent && preview.height() < PreviewExtent) {
178 preview = preview.scaled(PreviewExtent, PreviewExtent, Qt::KeepAspectRatio, Qt::SmoothTransformation);
179 }
180 }
181 m_previewLabel->setPixmap(preview);
182
183 if (!fileItem.isNull()) {
184 // TODO: think about not displaying Modified/Accessed if not available
185 QString details = QString("<center>%1<br>").arg(fileItem.url().toDisplayString(QUrl::PreferLocalFile)) +
186 "<table border=\"0\">" +
187 i18nc("File modification date and time. %1 is date time",
188 "<tr><td><b>Modified:</b></td><td>%1</td></tr>",
189 QString(fileItem.timeString(KFileItem::ModificationTime))) +
190 i18nc("File access date and time. %1 is date time",
191 "<tr><td><b>Accessed:</b></td><td>%1</td></tr>",
192 QString(fileItem.timeString(KFileItem::AccessTime))) +
193 "</table></center>";
194 m_detailsLabel->setHtml(details);
195 } else {
196 m_detailsLabel->clear();
197 }
198 } else {
199 m_openButton->setEnabled(false);
200 m_titleLabel->clear();
201 m_previewLabel->setPixmap(QPixmap());
202 m_detailsLabel->clear();
203 }
204 }
205
openFile()206 void KoRecentDocumentsPane::openFile()
207 {
208 KoDetailsPane::openFile();
209 }
210
openFile(const QModelIndex & index)211 void KoRecentDocumentsPane::openFile(const QModelIndex& index)
212 {
213 if (!index.isValid()) return;
214
215 KConfigGroup cfgGrp(KSharedConfig::openConfig(), "TemplateChooserDialog");
216 cfgGrp.writeEntry("LastReturnType", "File");
217
218 KoFileListItem* item = static_cast<KoFileListItem*>(model()->itemFromIndex(index));
219 KFileItem fileItem = item->fileItem();
220
221 if (!fileItem.isNull()) {
222 emit openUrl(fileItem.url());
223 }
224 }
225
previewResult(KJob * job)226 void KoRecentDocumentsPane::previewResult(KJob* job)
227 {
228 d->m_previewJobs.removeOne(job);
229 }
230
updatePreview(const KFileItem & fileItem,const QPixmap & preview)231 void KoRecentDocumentsPane::updatePreview(const KFileItem& fileItem, const QPixmap& preview)
232 {
233 if (preview.isNull()) {
234 return;
235 }
236
237 QStandardItem* rootItem = model()->invisibleRootItem();
238
239 for (int i = 0; i < rootItem->rowCount(); ++i) {
240 KoFileListItem* item = static_cast<KoFileListItem*>(rootItem->child(i));
241 if (item->fileItem().url() == fileItem.url()) {
242 item->setData(preview, PreviewRole);
243
244 if (m_documentList->selectionModel()->currentIndex() == item->index()) {
245 m_previewLabel->setPixmap(preview);
246 }
247
248 break;
249 }
250 }
251 }
252
updateIcon(const KFileItem & fileItem,const QPixmap & pixmap)253 void KoRecentDocumentsPane::updateIcon(const KFileItem& fileItem, const QPixmap& pixmap)
254 {
255 if (pixmap.isNull()) {
256 return;
257 }
258
259 QStandardItem *rootItem = model()->invisibleRootItem();
260
261 for (int i = 0; i < rootItem->rowCount(); ++i) {
262 KoFileListItem *item = static_cast<KoFileListItem*>(rootItem->child(i));
263 if (item->fileItem().url() == fileItem.url()) {
264 // ensure squareness
265 QImage icon = pixmap.toImage();
266 icon = icon.convertToFormat(QImage::Format_ARGB32);
267 icon = icon.copy((icon.width() - IconExtent) / 2, (icon.height() - IconExtent) / 2, IconExtent, IconExtent);
268
269 item->setIcon(QIcon(QPixmap::fromImage(icon)));
270
271 break;
272 }
273 }
274 }
275