]> cloud.milkyroute.net Git - dolphin.git/blob - src/infosidebarpage.cpp
SVN_SILENT: fix documentation typo
[dolphin.git] / src / infosidebarpage.cpp
1 /***************************************************************************
2 * Copyright (C) 2006 by Peter Penz <peter.penz@gmx.at> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18 ***************************************************************************/
19
20 #include "infosidebarpage.h"
21
22 #include <config-nepomuk.h>
23
24 #include <kdialog.h>
25 #include <kdirnotify.h>
26 #include <kfileplacesmodel.h>
27 #include <klocale.h>
28 #include <kstandarddirs.h>
29 #include <kio/previewjob.h>
30 #include <kfileitem.h>
31 #include <kglobalsettings.h>
32 #include <kfilemetainfo.h>
33 #include <kiconeffect.h>
34 #include <kseparator.h>
35 #include <kiconloader.h>
36
37 #include <QEvent>
38 #include <QInputDialog>
39 #include <QLabel>
40 #include <QPainter>
41 #include <QPixmap>
42 #include <QResizeEvent>
43 #include <QTimer>
44 #include <QVBoxLayout>
45
46 #include "dolphinsettings.h"
47 #include "metadatawidget.h"
48 #include "metatextlabel.h"
49 #include "pixmapviewer.h"
50
51 InfoSidebarPage::InfoSidebarPage(QWidget* parent) :
52 SidebarPage(parent),
53 m_initialized(false),
54 m_pendingPreview(false),
55 m_shownUrl(),
56 m_urlCandidate(),
57 m_fileItem(),
58 m_selection(),
59 m_nameLabel(0),
60 m_preview(0),
61 m_metaDataWidget(0),
62 m_metaTextLabel(0)
63 {
64 }
65
66 InfoSidebarPage::~InfoSidebarPage()
67 {
68 }
69
70 QSize InfoSidebarPage::sizeHint() const
71 {
72 QSize size = SidebarPage::sizeHint();
73 size.setWidth(minimumSizeHint().width());
74 return size;
75 }
76
77 void InfoSidebarPage::setUrl(const KUrl& url)
78 {
79 SidebarPage::setUrl(url);
80 if (url.isValid() && !m_shownUrl.equals(url, KUrl::CompareWithoutTrailingSlash)) {
81 if (m_initialized) {
82 cancelRequest();
83 m_shownUrl = url;
84 showItemInfo();
85 } else {
86 m_shownUrl = url;
87 }
88 }
89 }
90
91 void InfoSidebarPage::setSelection(const KFileItemList& selection)
92 {
93 if (!m_initialized) {
94 return;
95 }
96
97 if ((selection.count() == 0) && (m_selection.count() == 0)) {
98 // The selection has not really changed, only the current index.
99 // QItemSelectionModel emits a signal in this case and it is less
100 // expensive doing the check this way instead of patching
101 // DolphinView::emitSelectionChanged().
102 return;
103 }
104
105 m_selection = selection;
106
107 const int count = selection.count();
108 if (count == 0) {
109 m_shownUrl = url();
110 showItemInfo();
111 } else {
112 if ((count == 1) && !selection.first().url().isEmpty()) {
113 m_urlCandidate = selection.first().url();
114 }
115 m_timer->start(TimerDelay);
116 }
117 }
118
119 void InfoSidebarPage::requestDelayedItemInfo(const KFileItem& item)
120 {
121 if (!m_initialized) {
122 return;
123 }
124
125 cancelRequest();
126
127 m_fileItem = KFileItem();
128 if (item.isNull()) {
129 // The cursor is above the viewport. If files are selected,
130 // show information regarding the selection.
131 if (m_selection.size() > 0) {
132 m_pendingPreview = false;
133 m_timer->start(TimerDelay);
134 }
135 } else if (!item.url().isEmpty()) {
136 m_urlCandidate = item.url();
137 m_fileItem = item;
138 m_timer->start(TimerDelay);
139 }
140 }
141
142 void InfoSidebarPage::showEvent(QShowEvent* event)
143 {
144 SidebarPage::showEvent(event);
145 if (!event->spontaneous()) {
146 if (!m_initialized) {
147 // do a delayed initialization so that no performance
148 // penalty is given when Dolphin is started with a closed
149 // Information Panel
150 init();
151 }
152 showItemInfo();
153 }
154 }
155
156 void InfoSidebarPage::resizeEvent(QResizeEvent* event)
157 {
158 if (m_initialized) {
159 // If the text inside the name label or the info label cannot
160 // get wrapped, then the maximum width of the label is increased
161 // so that the width of the information sidebar gets increased.
162 // To prevent this, the maximum width is adjusted to
163 // the current width of the sidebar.
164 const int maxWidth = event->size().width() - KDialog::spacingHint() * 4;
165 m_nameLabel->setMaximumWidth(maxWidth);
166
167 // try to increase the preview as large as possible
168 m_preview->setSizeHint(QSize(maxWidth, maxWidth));
169 m_urlCandidate = m_shownUrl; // reset the URL candidate if a resizing is done
170 m_timer->start(TimerDelay);
171 }
172
173 SidebarPage::resizeEvent(event);
174 }
175
176 void InfoSidebarPage::showItemInfo()
177 {
178 if (!isVisible()) {
179 return;
180 }
181
182 cancelRequest();
183
184 const KUrl file = fileUrl();
185 if (!file.isValid()) {
186 return;
187 }
188
189 if (showMultipleSelectionInfo()) {
190 KIconLoader iconLoader;
191 QPixmap icon = iconLoader.loadIcon("dialog-information",
192 KIconLoader::NoGroup,
193 KIconLoader::SizeEnormous);
194 m_preview->setPixmap(icon);
195 m_nameLabel->setText(i18ncp("@info", "%1 item selected", "%1 items selected", m_selection.count()));
196 } else if (!applyPlace(file)) {
197 // try to get a preview pixmap from the item...
198 KUrl::List list;
199 list.append(file);
200
201 m_pendingPreview = true;
202
203 KIconEffect iconEffect;
204 QPixmap disabledPixmap = iconEffect.apply(m_preview->pixmap(), KIconLoader::Desktop, KIconLoader::DisabledState);
205 m_preview->setPixmap(disabledPixmap);
206
207 KIO::PreviewJob* job = KIO::filePreview(list,
208 m_preview->width(),
209 m_preview->height(),
210 0,
211 0,
212 true,
213 false);
214 job->setIgnoreMaximumSize(true);
215
216 connect(job, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)),
217 this, SLOT(showPreview(const KFileItem&, const QPixmap&)));
218 connect(job, SIGNAL(failed(const KFileItem&)),
219 this, SLOT(showIcon(const KFileItem&)));
220
221 m_nameLabel->setText(file.fileName());
222 }
223
224 showMetaInfo();
225 }
226
227 void InfoSidebarPage::slotTimeout()
228 {
229 m_shownUrl = m_urlCandidate;
230 showItemInfo();
231 }
232
233 void InfoSidebarPage::showIcon(const KFileItem& item)
234 {
235 m_pendingPreview = false;
236 if (!applyPlace(item.url())) {
237 m_preview->setPixmap(item.pixmap(KIconLoader::SizeEnormous));
238 }
239 }
240
241 void InfoSidebarPage::showPreview(const KFileItem& item,
242 const QPixmap& pixmap)
243 {
244 Q_UNUSED(item);
245 if (m_pendingPreview) {
246 m_preview->setPixmap(pixmap);
247 m_pendingPreview = false;
248 }
249 }
250
251 void InfoSidebarPage::slotFileRenamed(const QString& source, const QString& dest)
252 {
253 if (m_shownUrl == KUrl(source)) {
254 // the currently shown file has been renamed, hence update the item information
255 // for the renamed file
256 KFileItem item(KFileItem::Unknown, KFileItem::Unknown, KUrl(dest));
257 requestDelayedItemInfo(item);
258 }
259 }
260
261 void InfoSidebarPage::slotFilesAdded(const QString& directory)
262 {
263 if (m_shownUrl == KUrl(directory)) {
264 // If the 'trash' icon changes because the trash has been emptied or got filled,
265 // the signal filesAdded("trash:/") will be emitted.
266 KFileItem item(KFileItem::Unknown, KFileItem::Unknown, KUrl(directory));
267 requestDelayedItemInfo(item);
268 }
269 }
270
271 void InfoSidebarPage::slotFilesChanged(const QStringList& files)
272 {
273 foreach (const QString& fileName, files) {
274 if (m_shownUrl == KUrl(fileName)) {
275 showItemInfo();
276 break;
277 }
278 }
279 }
280
281 void InfoSidebarPage::slotFilesRemoved(const QStringList& files)
282 {
283 foreach (const QString& fileName, files) {
284 if (m_shownUrl == KUrl(fileName)) {
285 // the currently shown item has been removed, show
286 // the parent directory as fallback
287 m_shownUrl = url();
288 showItemInfo();
289 break;
290 }
291 }
292 }
293
294 void InfoSidebarPage::slotEnteredDirectory(const QString& directory)
295 {
296 if (m_shownUrl == KUrl(directory)) {
297 KFileItem item(KFileItem::Unknown, KFileItem::Unknown, KUrl(directory));
298 requestDelayedItemInfo(item);
299 }
300 }
301
302 void InfoSidebarPage::slotLeftDirectory(const QString& directory)
303 {
304 if (m_shownUrl == KUrl(directory)) {
305 // The signal 'leftDirectory' is also emitted when a media
306 // has been unmounted. In this case no directory change will be
307 // done in Dolphin, but the Information Panel must be updated to
308 // indicate an invalid directory.
309 m_shownUrl = url();
310 showItemInfo();
311 }
312 }
313
314 bool InfoSidebarPage::applyPlace(const KUrl& url)
315 {
316 KFilePlacesModel* placesModel = DolphinSettings::instance().placesModel();
317 int count = placesModel->rowCount();
318
319 for (int i = 0; i < count; ++i) {
320 QModelIndex index = placesModel->index(i, 0);
321
322 if (url.equals(placesModel->url(index), KUrl::CompareWithoutTrailingSlash)) {
323 m_nameLabel->setText(placesModel->text(index));
324 m_preview->setPixmap(placesModel->icon(index).pixmap(128, 128));
325 return true;
326 }
327 }
328
329 return false;
330 }
331
332 void InfoSidebarPage::cancelRequest()
333 {
334 m_timer->stop();
335 }
336
337 void InfoSidebarPage::showMetaInfo()
338 {
339 m_metaTextLabel->clear();
340
341 if (showMultipleSelectionInfo()) {
342 if (m_metaDataWidget != 0) {
343 KUrl::List urls;
344 foreach (const KFileItem& item, m_selection) {
345 urls.append(item.targetUrl());
346 }
347 m_metaDataWidget->setFiles(urls);
348 }
349
350 quint64 totalSize = 0;
351 foreach (const KFileItem& item, m_selection) {
352 // Only count the size of files, not dirs to match what
353 // DolphinViewContainer::selectionStatusBarText() does.
354 if (!item.isDir() && !item.isLink()) {
355 totalSize += item.size();
356 }
357 }
358 m_metaTextLabel->add(i18nc("@label", "Total size:"), KIO::convertSize(totalSize));
359 } else {
360 KFileItem fileItem(KFileItem::Unknown, KFileItem::Unknown, fileUrl());
361 fileItem.refresh();
362
363 if (fileItem.isDir()) {
364 m_metaTextLabel->add(i18nc("@label", "Type:"), i18nc("@label", "Folder"));
365 m_metaTextLabel->add(i18nc("@label", "Modified:"), fileItem.timeString());
366 } else {
367 m_metaTextLabel->add(i18nc("@label", "Type:"), fileItem.mimeComment());
368
369 m_metaTextLabel->add(i18nc("@label", "Size:"), KIO::convertSize(fileItem.size()));
370 m_metaTextLabel->add(i18nc("@label", "Modified:"), fileItem.timeString());
371
372 if (fileItem.isLocalFile()) {
373 // TODO: See convertMetaInfo below, find a way to display only interesting information
374 // in a readable way
375 const KFileMetaInfo::WhatFlags flags = KFileMetaInfo::Fastest |
376 KFileMetaInfo::TechnicalInfo |
377 KFileMetaInfo::ContentInfo;
378 const QString path = fileItem.url().path();
379 const KFileMetaInfo fileMetaInfo(path, QString(), flags);
380 if (fileMetaInfo.isValid()) {
381 const QHash<QString, KFileMetaInfoItem>& items = fileMetaInfo.items();
382 QHash<QString, KFileMetaInfoItem>::const_iterator it = items.constBegin();
383 const QHash<QString, KFileMetaInfoItem>::const_iterator end = items.constEnd();
384 QString labelText;
385 while (it != end) {
386 const KFileMetaInfoItem& metaInfoItem = it.value();
387 const QVariant& value = metaInfoItem.value();
388 if (value.isValid() && convertMetaInfo(metaInfoItem.name(), labelText)) {
389 m_metaTextLabel->add(labelText, value.toString());
390 }
391 ++it;
392 }
393 }
394 }
395 }
396
397 if (m_metaDataWidget != 0) {
398 m_metaDataWidget->setFile(fileItem.targetUrl());
399 }
400 }
401 }
402
403 bool InfoSidebarPage::convertMetaInfo(const QString& key, QString& text) const
404 {
405 // TODO: This code prevents that interesting meta information might be hidden
406 // and only bypasses the current problem that not all the meta information should
407 // be shown to the user. Check whether it's possible with Nepomuk to show
408 // all "user relevant" information in a readable way...
409
410 struct MetaKey {
411 const char* key;
412 const char* text;
413 };
414
415 // sorted list of keys, where its data should be shown
416 static const MetaKey keys[] = {
417 { "audio.album", "Album:" },
418 { "audio.artist", "Artist:" },
419 { "audio.title", "Title:" },
420 };
421
422 // do a binary search for the key...
423 int top = 0;
424 int bottom = sizeof(keys) / sizeof(MetaKey) - 1;
425 while (top <= bottom) {
426 const int middle = (top + bottom) / 2;
427 const int result = key.compare(keys[middle].key);
428 if (result < 0) {
429 bottom = middle - 1;
430 } else if (result > 0) {
431 top = middle + 1;
432 } else {
433 text = keys[middle].text;
434 return true;
435 }
436 }
437
438 return false;
439 }
440
441 KUrl InfoSidebarPage::fileUrl() const
442 {
443 return (!m_fileItem.isNull() || m_selection.isEmpty()) ? m_shownUrl : m_selection[0].url();
444 }
445
446 bool InfoSidebarPage::showMultipleSelectionInfo() const
447 {
448 return m_fileItem.isNull() && (m_selection.count() > 1);
449 }
450
451 void InfoSidebarPage::init()
452 {
453 const int spacing = KDialog::spacingHint();
454
455 m_timer = new QTimer(this);
456 m_timer->setSingleShot(true);
457 connect(m_timer, SIGNAL(timeout()),
458 this, SLOT(slotTimeout()));
459
460 QVBoxLayout* layout = new QVBoxLayout;
461 layout->setSpacing(spacing);
462
463 // name
464 m_nameLabel = new QLabel(this);
465 QFont font = m_nameLabel->font();
466 font.setBold(true);
467 m_nameLabel->setFont(font);
468 m_nameLabel->setAlignment(Qt::AlignHCenter);
469 m_nameLabel->setWordWrap(true);
470 m_nameLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
471
472 // preview
473 m_preview = new PixmapViewer(this);
474 m_preview->setMinimumWidth(KIconLoader::SizeEnormous + KIconLoader::SizeMedium);
475 m_preview->setMinimumHeight(KIconLoader::SizeEnormous);
476
477 if (MetaDataWidget::metaDataAvailable()) {
478 // rating, comment and tags
479 m_metaDataWidget = new MetaDataWidget(this);
480 m_metaDataWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
481 }
482
483 // general meta text information
484 m_metaTextLabel = new MetaTextLabel(this);
485 m_metaTextLabel->setMinimumWidth(spacing);
486 m_metaTextLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
487
488 layout->addWidget(m_nameLabel);
489 layout->addWidget(new KSeparator(this));
490 layout->addWidget(m_preview);
491 layout->addWidget(new KSeparator(this));
492 if (m_metaDataWidget != 0) {
493 layout->addWidget(m_metaDataWidget);
494 layout->addWidget(new KSeparator(this));
495 }
496 layout->addWidget(m_metaTextLabel);
497
498 // ensure that widgets in the information side bar are aligned towards the top
499 layout->addStretch(1);
500 setLayout(layout);
501
502 org::kde::KDirNotify* dirNotify = new org::kde::KDirNotify(QString(), QString(),
503 QDBusConnection::sessionBus(), this);
504 connect(dirNotify, SIGNAL(FileRenamed(QString, QString)), SLOT(slotFileRenamed(QString, QString)));
505 connect(dirNotify, SIGNAL(FilesAdded(QString)), SLOT(slotFilesAdded(QString)));
506 connect(dirNotify, SIGNAL(FilesChanged(QStringList)), SLOT(slotFilesChanged(QStringList)));
507 connect(dirNotify, SIGNAL(FilesRemoved(QStringList)), SLOT(slotFilesRemoved(QStringList)));
508 connect(dirNotify, SIGNAL(enteredDirectory(QString)), SLOT(slotEnteredDirectory(QString)));
509 connect(dirNotify, SIGNAL(leftDirectory(QString)), SLOT(slotLeftDirectory(QString)));
510
511 m_initialized = true;
512 }
513
514 #include "infosidebarpage.moc"