00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "kfilepreviewgenerator.h"
00021
00022 #include "../kio/kio/defaultviewadapter_p.h"
00023 #include "../kio/kio/imagefilter_p.h"
00024 #include <config.h>
00025 #include <kconfiggroup.h>
00026 #include <kfileitem.h>
00027 #include <kiconeffect.h>
00028 #include <kio/previewjob.h>
00029 #include <kdirlister.h>
00030 #include <kdirmodel.h>
00031 #include <ksharedconfig.h>
00032
00033 #include <QApplication>
00034 #include <QAbstractItemView>
00035 #include <QAbstractProxyModel>
00036 #include <QClipboard>
00037 #include <QColor>
00038 #include <QHash>
00039 #include <QList>
00040 #include <QListView>
00041 #include <QPainter>
00042 #include <QPixmap>
00043 #include <QScrollBar>
00044 #include <QIcon>
00045
00046 #if defined(Q_WS_X11) && defined(HAVE_XRENDER)
00047 # include <QX11Info>
00048 # include <X11/Xlib.h>
00049 # include <X11/extensions/Xrender.h>
00050 #endif
00051
00072 class LayoutBlocker
00073 {
00074 public:
00075 LayoutBlocker(QAbstractItemView* view) :
00076 m_uniformSizes(false),
00077 m_view(qobject_cast<QListView*>(view))
00078 {
00079 if (m_view != 0) {
00080 m_uniformSizes = m_view->uniformItemSizes();
00081 m_view->setUniformItemSizes(true);
00082 }
00083 }
00084
00085 ~LayoutBlocker()
00086 {
00087 if (m_view != 0) {
00088 m_view->setUniformItemSizes(m_uniformSizes);
00089 }
00090 }
00091
00092 private:
00093 bool m_uniformSizes;
00094 QListView* m_view;
00095 };
00096
00098 class TileSet
00099 {
00100 public:
00101 enum { LeftMargin = 3, TopMargin = 2, RightMargin = 3, BottomMargin = 4 };
00102
00103 enum Tile { TopLeftCorner = 0, TopSide, TopRightCorner, LeftSide,
00104 RightSide, BottomLeftCorner, BottomSide, BottomRightCorner,
00105 NumTiles };
00106
00107 TileSet()
00108 {
00109 QImage image(8 * 3, 8 * 3, QImage::Format_ARGB32_Premultiplied);
00110
00111 QPainter p(&image);
00112 p.setCompositionMode(QPainter::CompositionMode_Source);
00113 p.fillRect(image.rect(), Qt::transparent);
00114 p.fillRect(image.rect().adjusted(3, 3, -3, -3), Qt::black);
00115 p.end();
00116
00117 KIO::ImageFilter::shadowBlur(image, 3, Qt::black);
00118
00119 QPixmap pixmap = QPixmap::fromImage(image);
00120 m_tiles[TopLeftCorner] = pixmap.copy(0, 0, 8, 8);
00121 m_tiles[TopSide] = pixmap.copy(8, 0, 8, 8);
00122 m_tiles[TopRightCorner] = pixmap.copy(16, 0, 8, 8);
00123 m_tiles[LeftSide] = pixmap.copy(0, 8, 8, 8);
00124 m_tiles[RightSide] = pixmap.copy(16, 8, 8, 8);
00125 m_tiles[BottomLeftCorner] = pixmap.copy(0, 16, 8, 8);
00126 m_tiles[BottomSide] = pixmap.copy(8, 16, 8, 8);
00127 m_tiles[BottomRightCorner] = pixmap.copy(16, 16, 8, 8);
00128 }
00129
00130 void paint(QPainter* p, const QRect& r)
00131 {
00132 p->drawPixmap(r.topLeft(), m_tiles[TopLeftCorner]);
00133 if (r.width() - 16 > 0) {
00134 p->drawTiledPixmap(r.x() + 8, r.y(), r.width() - 16, 8, m_tiles[TopSide]);
00135 }
00136 p->drawPixmap(r.right() - 8 + 1, r.y(), m_tiles[TopRightCorner]);
00137 if (r.height() - 16 > 0) {
00138 p->drawTiledPixmap(r.x(), r.y() + 8, 8, r.height() - 16, m_tiles[LeftSide]);
00139 p->drawTiledPixmap(r.right() - 8 + 1, r.y() + 8, 8, r.height() - 16, m_tiles[RightSide]);
00140 }
00141 p->drawPixmap(r.x(), r.bottom() - 8 + 1, m_tiles[BottomLeftCorner]);
00142 if (r.width() - 16 > 0) {
00143 p->drawTiledPixmap(r.x() + 8, r.bottom() - 8 + 1, r.width() - 16, 8, m_tiles[BottomSide]);
00144 }
00145 p->drawPixmap(r.right() - 8 + 1, r.bottom() - 8 + 1, m_tiles[BottomRightCorner]);
00146
00147 const QRect contentRect = r.adjusted(LeftMargin + 1, TopMargin + 1,
00148 -(RightMargin + 1), -(BottomMargin + 1));
00149 p->fillRect(contentRect, Qt::transparent);
00150 }
00151
00152 private:
00153 QPixmap m_tiles[NumTiles];
00154 };
00155
00156 class KFilePreviewGenerator::Private
00157 {
00158 public:
00159 Private(KFilePreviewGenerator* parent,
00160 KAbstractViewAdapter* viewAdapter,
00161 QAbstractItemModel* model);
00162 ~Private();
00163
00168 void requestSequenceIcon(const QModelIndex& index, int sequenceIndex);
00169
00173 void updateIcons(const KFileItemList& items);
00174
00179 void updateIcons(const QModelIndex& topLeft, const QModelIndex& bottomRight);
00180
00186 void addToPreviewQueue(const KFileItem& item, const QPixmap& pixmap);
00187
00192 void slotPreviewJobFinished(KJob* job);
00193
00195 void updateCutItems();
00196
00201 void clearCutItemsCache();
00202
00207 void dispatchIconUpdateQueue();
00208
00214 void pauseIconUpdates();
00215
00221 void resumeIconUpdates();
00222
00227 void startMimeTypeResolving();
00228
00233 void resolveMimeType();
00234
00239 bool isCutItem(const KFileItem& item) const;
00240
00245 void applyCutItemEffect(const KFileItemList& items);
00246
00251 bool applyImageFrame(QPixmap& icon);
00252
00258 void limitToSize(QPixmap& icon, const QSize& maxSize);
00259
00264 void createPreviews(const KFileItemList& items);
00265
00270 void startPreviewJob(const KFileItemList& items, int width, int height);
00271
00273 void killPreviewJobs();
00274
00281 void orderItems(KFileItemList& items);
00282
00287 bool decodeIsCutSelection(const QMimeData* mimeData);
00288
00293 void addItemsToList(const QModelIndex& index, KFileItemList& list);
00294
00299 void delayedIconUpdate();
00300
00302 struct ItemInfo
00303 {
00304 KUrl url;
00305 QPixmap pixmap;
00306 };
00307
00312 class DataChangeObtainer
00313 {
00314 public:
00315 DataChangeObtainer(KFilePreviewGenerator::Private* generator) :
00316 m_gen(generator) { ++m_gen->m_internalDataChange; }
00317 ~DataChangeObtainer() { --m_gen->m_internalDataChange; }
00318 private:
00319 KFilePreviewGenerator::Private* m_gen;
00320 };
00321
00322 bool m_previewShown;
00323
00328 bool m_clearItemQueues;
00329
00333 bool m_hasCutSelection;
00334
00339 bool m_iconUpdatesPaused;
00340
00346 int m_internalDataChange;
00347
00348 int m_pendingVisibleIconUpdates;
00349
00350 KAbstractViewAdapter* m_viewAdapter;
00351 QAbstractItemView* m_itemView;
00352 QTimer* m_iconUpdateTimer;
00353 QTimer* m_scrollAreaTimer;
00354 QList<KJob*> m_previewJobs;
00355 KDirModel* m_dirModel;
00356 QAbstractProxyModel* m_proxyModel;
00357
00365 QHash<KUrl, QPixmap> m_cutItemsCache;
00366 QList<ItemInfo> m_previews;
00367 QMap<KUrl, int> m_sequenceIndices;
00368
00375 QHash<KUrl, bool> m_changedItems;
00376 QTimer* m_changedItemsTimer;
00377
00382 KFileItemList m_pendingItems;
00383
00388 KFileItemList m_dispatchedItems;
00389
00390 KFileItemList m_resolvedMimeTypes;
00391
00392 QStringList m_enabledPlugins;
00393
00394 TileSet* m_tileSet;
00395
00396 private:
00397 KFilePreviewGenerator* const q;
00398
00399 };
00400
00401 KFilePreviewGenerator::Private::Private(KFilePreviewGenerator* parent,
00402 KAbstractViewAdapter* viewAdapter,
00403 QAbstractItemModel* model) :
00404 m_previewShown(true),
00405 m_clearItemQueues(true),
00406 m_hasCutSelection(false),
00407 m_iconUpdatesPaused(false),
00408 m_internalDataChange(0),
00409 m_pendingVisibleIconUpdates(0),
00410 m_viewAdapter(viewAdapter),
00411 m_itemView(0),
00412 m_iconUpdateTimer(0),
00413 m_scrollAreaTimer(0),
00414 m_previewJobs(),
00415 m_dirModel(0),
00416 m_proxyModel(0),
00417 m_cutItemsCache(),
00418 m_previews(),
00419 m_sequenceIndices(),
00420 m_changedItems(),
00421 m_changedItemsTimer(0),
00422 m_pendingItems(),
00423 m_dispatchedItems(),
00424 m_resolvedMimeTypes(),
00425 m_tileSet(0),
00426 q(parent)
00427 {
00428 if (!m_viewAdapter->iconSize().isValid()) {
00429 m_previewShown = false;
00430 }
00431
00432 m_proxyModel = qobject_cast<QAbstractProxyModel*>(model);
00433 m_dirModel = (m_proxyModel == 0) ?
00434 qobject_cast<KDirModel*>(model) :
00435 qobject_cast<KDirModel*>(m_proxyModel->sourceModel());
00436 if (m_dirModel == 0) {
00437
00438 m_previewShown = false;
00439 } else {
00440 connect(m_dirModel->dirLister(), SIGNAL(newItems(const KFileItemList&)),
00441 q, SLOT(updateIcons(const KFileItemList&)));
00442 connect(m_dirModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
00443 q, SLOT(updateIcons(const QModelIndex&, const QModelIndex&)));
00444 connect(m_dirModel, SIGNAL(needSequenceIcon(const QModelIndex&,int)),
00445 q, SLOT(requestSequenceIcon(const QModelIndex&, int)));
00446 }
00447
00448 QClipboard* clipboard = QApplication::clipboard();
00449 connect(clipboard, SIGNAL(dataChanged()),
00450 q, SLOT(updateCutItems()));
00451
00452 m_iconUpdateTimer = new QTimer(q);
00453 m_iconUpdateTimer->setSingleShot(true);
00454 connect(m_iconUpdateTimer, SIGNAL(timeout()), q, SLOT(dispatchIconUpdateQueue()));
00455
00456
00457
00458
00459
00460 m_scrollAreaTimer = new QTimer(q);
00461 m_scrollAreaTimer->setSingleShot(true);
00462 m_scrollAreaTimer->setInterval(200);
00463 connect(m_scrollAreaTimer, SIGNAL(timeout()),
00464 q, SLOT(resumeIconUpdates()));
00465 m_viewAdapter->connect(KAbstractViewAdapter::ScrollBarValueChanged,
00466 q, SLOT(pauseIconUpdates()));
00467
00468 m_changedItemsTimer = new QTimer(q);
00469 m_changedItemsTimer->setSingleShot(true);
00470 m_changedItemsTimer->setInterval(5000);
00471 connect(m_changedItemsTimer, SIGNAL(timeout()),
00472 q, SLOT(delayedIconUpdate()));
00473 }
00474
00475 KFilePreviewGenerator::Private::~Private()
00476 {
00477 killPreviewJobs();
00478 m_pendingItems.clear();
00479 m_dispatchedItems.clear();
00480 delete m_tileSet;
00481 }
00482
00483 void KFilePreviewGenerator::Private::requestSequenceIcon(const QModelIndex& index,
00484 int sequenceIndex)
00485 {
00486 if (m_pendingItems.isEmpty() || (sequenceIndex == 0)) {
00487 KFileItem item = m_dirModel->itemForIndex(index);
00488 if (sequenceIndex == 0) {
00489 m_sequenceIndices.remove(item.url());
00490 } else {
00491 m_sequenceIndices.insert(item.url(), sequenceIndex);
00492 }
00493
00495 updateIcons(KFileItemList() << item);
00496 }
00497 }
00498
00499 void KFilePreviewGenerator::Private::updateIcons(const KFileItemList& items)
00500 {
00501 if (items.count() <= 0) {
00502 return;
00503 }
00504
00505 applyCutItemEffect(items);
00506
00507 KFileItemList orderedItems = items;
00508 orderItems(orderedItems);
00509
00510 foreach (const KFileItem& item, orderedItems) {
00511 m_pendingItems.append(item);
00512 }
00513
00514 if (m_previewShown) {
00515 createPreviews(orderedItems);
00516 } else {
00517 startMimeTypeResolving();
00518 }
00519 }
00520
00521 void KFilePreviewGenerator::Private::updateIcons(const QModelIndex& topLeft,
00522 const QModelIndex& bottomRight)
00523 {
00524 if (m_internalDataChange > 0) {
00525
00526
00527
00528 return;
00529 }
00530
00531 if (!topLeft.isValid() || !bottomRight.isValid()) {
00532 return;
00533 }
00534
00535 KFileItemList itemList;
00536 for (int row = topLeft.row(); row <= bottomRight.row(); ++row) {
00537 const QModelIndex index = m_dirModel->index(row, 0);
00538 const KFileItem item = m_dirModel->itemForIndex(index);
00539
00540 if (m_previewShown) {
00541 const KUrl url = item.url();
00542 const bool hasChanged = m_changedItems.contains(url);
00543 m_changedItems.insert(url, hasChanged);
00544 if (!hasChanged) {
00545
00546
00547
00548 itemList.append(item);
00549 }
00550 } else {
00551 itemList.append(item);
00552 }
00553 }
00554
00555 updateIcons(itemList);
00556 m_changedItemsTimer->start();
00557 }
00558
00559 void KFilePreviewGenerator::Private::addToPreviewQueue(const KFileItem& item, const QPixmap& pixmap)
00560 {
00561 KIO::PreviewJob* senderJob = qobject_cast<KIO::PreviewJob*>(q->sender());
00562 Q_ASSERT(senderJob != 0);
00563 if (senderJob != 0) {
00564 QMap<KUrl, int>::iterator it = m_sequenceIndices.find(item.url());
00565 if (senderJob->sequenceIndex() && (it == m_sequenceIndices.end() || *it != senderJob->sequenceIndex())) {
00566 return;
00567 }
00568 if (!senderJob->sequenceIndex() && it != m_sequenceIndices.end()) {
00569 return;
00570 }
00571
00572 m_sequenceIndices.erase(it);
00573 }
00574
00575 if (!m_previewShown) {
00576
00577 return;
00578 }
00579
00580
00581
00582 const KUrl url = item.url();
00583 KDirLister* dirLister = m_dirModel->dirLister();
00584 bool isOldPreview = true;
00585 const KUrl::List dirs = dirLister->directories();
00586 const QString itemDir = url.directory();
00587 foreach (const KUrl& url, dirs) {
00588 if (url.path() == itemDir) {
00589 isOldPreview = false;
00590 break;
00591 }
00592 }
00593 if (isOldPreview) {
00594 return;
00595 }
00596
00597 QPixmap icon = pixmap;
00598
00599 const QString mimeType = item.mimetype();
00600 const QString mimeTypeGroup = mimeType.left(mimeType.indexOf('/'));
00601 if ((mimeTypeGroup != "image") || !applyImageFrame(icon)) {
00602 limitToSize(icon, m_viewAdapter->iconSize());
00603 }
00604
00605 if (m_hasCutSelection && isCutItem(item)) {
00606
00607
00608 KIconEffect iconEffect;
00609 icon = iconEffect.apply(icon, KIconLoader::Desktop, KIconLoader::DisabledState);
00610 }
00611
00612
00613
00614 ItemInfo preview;
00615 preview.url = url;
00616 preview.pixmap = icon;
00617 m_previews.append(preview);
00618
00619 m_dispatchedItems.append(item);
00620 }
00621
00622 void KFilePreviewGenerator::Private::slotPreviewJobFinished(KJob* job)
00623 {
00624 const int index = m_previewJobs.indexOf(job);
00625 m_previewJobs.removeAt(index);
00626
00627 if (m_previewJobs.isEmpty()) {
00628 if (m_clearItemQueues) {
00629 m_pendingItems.clear();
00630 m_dispatchedItems.clear();
00631 m_pendingVisibleIconUpdates = 0;
00632 QMetaObject::invokeMethod(q, "dispatchIconUpdateQueue", Qt::QueuedConnection);
00633 }
00634 m_sequenceIndices.clear();
00635 }
00636 }
00637
00638 void KFilePreviewGenerator::Private::updateCutItems()
00639 {
00640 DataChangeObtainer obt(this);
00641 clearCutItemsCache();
00642
00643 KFileItemList items;
00644 KDirLister* dirLister = m_dirModel->dirLister();
00645 const KUrl::List dirs = dirLister->directories();
00646 foreach (const KUrl& url, dirs) {
00647 items << dirLister->itemsForDir(url);
00648 }
00649 applyCutItemEffect(items);
00650 }
00651
00652 void KFilePreviewGenerator::Private::clearCutItemsCache()
00653 {
00654 DataChangeObtainer obt(this);
00655 KFileItemList previews;
00656
00657
00658 foreach (const KUrl& url, m_cutItemsCache.keys()) {
00659 const QModelIndex index = m_dirModel->indexForUrl(url);
00660 if (index.isValid()) {
00661 m_dirModel->setData(index, QIcon(), Qt::DecorationRole);
00662 if (m_previewShown) {
00663 previews.append(m_dirModel->itemForIndex(index));
00664 }
00665 }
00666 }
00667 m_cutItemsCache.clear();
00668
00669 if (previews.size() > 0) {
00670
00671 Q_ASSERT(m_previewShown);
00672 orderItems(previews);
00673 updateIcons(previews);
00674 }
00675 }
00676
00677 void KFilePreviewGenerator::Private::dispatchIconUpdateQueue()
00678 {
00679 const int count = m_previewShown ? m_previews.count()
00680 : m_resolvedMimeTypes.count();
00681 if (count > 0) {
00682 LayoutBlocker blocker(m_itemView);
00683 DataChangeObtainer obt(this);
00684
00685 if (m_previewShown) {
00686
00687 foreach (const ItemInfo& preview, m_previews) {
00688 const QModelIndex idx = m_dirModel->indexForUrl(preview.url);
00689 if (idx.isValid() && (idx.column() == 0)) {
00690 m_dirModel->setData(idx, QIcon(preview.pixmap), Qt::DecorationRole);
00691 }
00692 }
00693 m_previews.clear();
00694 } else {
00695
00696 foreach (const KFileItem& item, m_resolvedMimeTypes) {
00697 const QModelIndex idx = m_dirModel->indexForItem(item);
00698 m_dirModel->itemChanged(idx);
00699 }
00700 m_resolvedMimeTypes.clear();
00701 }
00702
00703 m_pendingVisibleIconUpdates -= count;
00704 if (m_pendingVisibleIconUpdates < 0) {
00705 m_pendingVisibleIconUpdates = 0;
00706 }
00707 }
00708
00709 if (m_pendingVisibleIconUpdates > 0) {
00710
00711
00712
00713 m_iconUpdateTimer->start(200);
00714 }
00715 }
00716
00717 void KFilePreviewGenerator::Private::pauseIconUpdates()
00718 {
00719 m_iconUpdatesPaused = true;
00720 foreach (KJob* job, m_previewJobs) {
00721 Q_ASSERT(job != 0);
00722 job->suspend();
00723 }
00724 m_scrollAreaTimer->start();
00725 }
00726
00727 void KFilePreviewGenerator::Private::resumeIconUpdates()
00728 {
00729 m_iconUpdatesPaused = false;
00730
00731
00732
00733
00734
00735
00736
00737 foreach (const KFileItem& item, m_dispatchedItems) {
00738 KFileItemList::iterator begin = m_pendingItems.begin();
00739 KFileItemList::iterator end = m_pendingItems.end();
00740 for (KFileItemList::iterator it = begin; it != end; ++it) {
00741 if ((*it).url() == item.url()) {
00742 m_pendingItems.erase(it);
00743 break;
00744 }
00745 }
00746 }
00747 m_dispatchedItems.clear();
00748
00749 m_pendingVisibleIconUpdates = 0;
00750 dispatchIconUpdateQueue();
00751
00752
00753 if (m_previewShown) {
00754 KFileItemList orderedItems = m_pendingItems;
00755 orderItems(orderedItems);
00756
00757
00758
00759
00760
00761 m_clearItemQueues = false;
00762 killPreviewJobs();
00763 m_clearItemQueues = true;
00764
00765 createPreviews(orderedItems);
00766 } else {
00767 orderItems(m_pendingItems);
00768 startMimeTypeResolving();
00769 }
00770 }
00771
00772 void KFilePreviewGenerator::Private::startMimeTypeResolving()
00773 {
00774 resolveMimeType();
00775 m_iconUpdateTimer->start(200);
00776 }
00777
00778 void KFilePreviewGenerator::Private::resolveMimeType()
00779 {
00780 if (m_pendingItems.isEmpty()) {
00781 return;
00782 }
00783
00784
00785 bool resolved = false;
00786 do {
00787 KFileItem item = m_pendingItems.takeFirst();
00788 if (item.isMimeTypeKnown()) {
00789 if (m_pendingVisibleIconUpdates > 0) {
00790
00791
00792 --m_pendingVisibleIconUpdates;
00793 }
00794 } else {
00795
00796
00797
00798
00799
00800 item.determineMimeType();
00801 m_resolvedMimeTypes.append(item);
00802 resolved = true;
00803 }
00804 } while (!resolved && !m_pendingItems.isEmpty());
00805
00806 if (m_pendingItems.isEmpty()) {
00807
00808
00809
00810 dispatchIconUpdateQueue();
00811 } else if (!m_iconUpdatesPaused) {
00812
00813
00814 QMetaObject::invokeMethod(q, "resolveMimeType", Qt::QueuedConnection);
00815 }
00816 }
00817
00818 bool KFilePreviewGenerator::Private::isCutItem(const KFileItem& item) const
00819 {
00820 const QMimeData* mimeData = QApplication::clipboard()->mimeData();
00821 const KUrl::List cutUrls = KUrl::List::fromMimeData(mimeData);
00822 return cutUrls.contains(item.url());
00823 }
00824
00825 void KFilePreviewGenerator::Private::applyCutItemEffect(const KFileItemList& items)
00826 {
00827 const QMimeData* mimeData = QApplication::clipboard()->mimeData();
00828 m_hasCutSelection = decodeIsCutSelection(mimeData);
00829 if (!m_hasCutSelection) {
00830 return;
00831 }
00832
00833 const QSet<KUrl> cutUrls = KUrl::List::fromMimeData(mimeData).toSet();
00834
00835 DataChangeObtainer obt(this);
00836 KIconEffect iconEffect;
00837 foreach (const KFileItem& item, items) {
00838 if (cutUrls.contains(item.url())) {
00839 const QModelIndex index = m_dirModel->indexForItem(item);
00840 const QVariant value = m_dirModel->data(index, Qt::DecorationRole);
00841 if (value.type() == QVariant::Icon) {
00842 const QIcon icon(qvariant_cast<QIcon>(value));
00843 const QSize actualSize = icon.actualSize(m_viewAdapter->iconSize());
00844 QPixmap pixmap = icon.pixmap(actualSize);
00845
00846 QHash< KUrl, QPixmap >::iterator cacheIt = m_cutItemsCache.find(item.url());
00847 if(cacheIt != m_cutItemsCache.end() && cacheIt->cacheKey() == pixmap.cacheKey())
00848 continue;
00849
00850 pixmap = iconEffect.apply(pixmap, KIconLoader::Desktop, KIconLoader::DisabledState);
00851 m_dirModel->setData(index, QIcon(pixmap), Qt::DecorationRole);
00852
00853 m_cutItemsCache.insert(item.url(), pixmap);
00854 }
00855 }
00856 }
00857 }
00858
00859 bool KFilePreviewGenerator::Private::applyImageFrame(QPixmap& icon)
00860 {
00861 const QSize maxSize = m_viewAdapter->iconSize();
00862 const bool applyFrame = (maxSize.width() > KIconLoader::SizeSmallMedium) &&
00863 (maxSize.height() > KIconLoader::SizeSmallMedium) &&
00864 ((icon.width() > KIconLoader::SizeLarge) ||
00865 (icon.height() > KIconLoader::SizeLarge));
00866 if (!applyFrame) {
00867
00868 return false;
00869 }
00870
00871
00872 const QSize size(maxSize.width() - TileSet::LeftMargin - TileSet::RightMargin,
00873 maxSize.height() - TileSet::TopMargin - TileSet::BottomMargin);
00874 limitToSize(icon, size);
00875
00876 if (m_tileSet == 0) {
00877 m_tileSet = new TileSet();
00878 }
00879
00880 QPixmap framedIcon(icon.size().width() + TileSet::LeftMargin + TileSet::RightMargin,
00881 icon.size().height() + TileSet::TopMargin + TileSet::BottomMargin);
00882 framedIcon.fill(Qt::transparent);
00883
00884 QPainter painter;
00885 painter.begin(&framedIcon);
00886 painter.setCompositionMode(QPainter::CompositionMode_Source);
00887 m_tileSet->paint(&painter, framedIcon.rect());
00888 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
00889 painter.drawPixmap(TileSet::LeftMargin, TileSet::TopMargin, icon);
00890 painter.end();
00891
00892 icon = framedIcon;
00893 return true;
00894 }
00895
00896 void KFilePreviewGenerator::Private::limitToSize(QPixmap& icon, const QSize& maxSize)
00897 {
00898 if ((icon.width() > maxSize.width()) || (icon.height() > maxSize.height())) {
00899 #if defined(Q_WS_X11) && defined(HAVE_XRENDER)
00900
00901 if ((icon.width() <= 2048) && (icon.height() <= 2048) && icon.x11PictureHandle()) {
00902 QSize size = icon.size();
00903 size.scale(maxSize, Qt::KeepAspectRatio);
00904
00905 const qreal factor = size.width() / qreal(icon.width());
00906
00907 XTransform xform = {{
00908 { XDoubleToFixed(1 / factor), 0, 0 },
00909 { 0, XDoubleToFixed(1 / factor), 0 },
00910 { 0, 0, XDoubleToFixed(1) }
00911 }};
00912
00913 QPixmap pixmap(size);
00914 pixmap.fill(Qt::transparent);
00915
00916 Display* dpy = QX11Info::display();
00917 XRenderSetPictureFilter(dpy, icon.x11PictureHandle(), FilterBilinear, 0, 0);
00918 XRenderSetPictureTransform(dpy, icon.x11PictureHandle(), &xform);
00919 XRenderComposite(dpy, PictOpOver, icon.x11PictureHandle(), None, pixmap.x11PictureHandle(),
00920 0, 0, 0, 0, 0, 0, pixmap.width(), pixmap.height());
00921 icon = pixmap;
00922 } else {
00923 icon = icon.scaled(maxSize, Qt::KeepAspectRatio, Qt::FastTransformation);
00924 }
00925 #else
00926 icon = icon.scaled(maxSize, Qt::KeepAspectRatio, Qt::FastTransformation);
00927 #endif
00928 }
00929 }
00930
00931 void KFilePreviewGenerator::Private::createPreviews(const KFileItemList& items)
00932 {
00933 if (items.count() == 0) {
00934 return;
00935 }
00936
00937 const QMimeData* mimeData = QApplication::clipboard()->mimeData();
00938 m_hasCutSelection = decodeIsCutSelection(mimeData);
00939
00940
00941
00942
00943
00944
00945 KFileItemList imageItems;
00946 KFileItemList otherItems;
00947 QString mimeType;
00948 QString mimeTypeGroup;
00949 foreach (const KFileItem& item, items) {
00950 mimeType = item.mimetype();
00951 mimeTypeGroup = mimeType.left(mimeType.indexOf('/'));
00952 if (mimeTypeGroup == "image") {
00953 imageItems.append(item);
00954 } else {
00955 otherItems.append(item);
00956 }
00957 }
00958 const QSize size = m_viewAdapter->iconSize();
00959 startPreviewJob(otherItems, size.width(), size.height());
00960
00961 const int cacheSize = (size.width() > 128) || (size.height() > 128) ? 256 : 128;
00962 startPreviewJob(imageItems, cacheSize, cacheSize);
00963
00964 m_iconUpdateTimer->start(200);
00965 }
00966
00967 void KFilePreviewGenerator::Private::startPreviewJob(const KFileItemList& items, int width, int height)
00968 {
00969 if (items.count() > 0) {
00970 if (m_enabledPlugins.isEmpty()) {
00971 const KConfigGroup globalConfig(KGlobal::config(), "PreviewSettings");
00972 m_enabledPlugins = globalConfig.readEntry("Plugins", QStringList()
00973 << "directorythumbnail"
00974 << "imagethumbnail"
00975 << "jpegthumbnail");
00976 }
00977
00978 KIO::PreviewJob* job = KIO::filePreview(items, width, height, 0, 70, true, true, &m_enabledPlugins);
00979
00980
00981
00982 if (!m_sequenceIndices.isEmpty() && (items.count() == 1)) {
00983 QMap<KUrl, int>::iterator it = m_sequenceIndices.find(items[0].url());
00984 if (it != m_sequenceIndices.end()) {
00985 job->setSequenceIndex(*it);
00986 }
00987 }
00988
00989 connect(job, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)),
00990 q, SLOT(addToPreviewQueue(const KFileItem&, const QPixmap&)));
00991 connect(job, SIGNAL(finished(KJob*)),
00992 q, SLOT(slotPreviewJobFinished(KJob*)));
00993 m_previewJobs.append(job);
00994 }
00995 }
00996
00997 void KFilePreviewGenerator::Private::killPreviewJobs()
00998 {
00999 foreach (KJob* job, m_previewJobs) {
01000 Q_ASSERT(job != 0);
01001 job->kill();
01002 }
01003 m_previewJobs.clear();
01004 m_sequenceIndices.clear();
01005
01006 m_iconUpdateTimer->stop();
01007 m_scrollAreaTimer->stop();
01008 m_changedItemsTimer->stop();
01009 }
01010
01011 void KFilePreviewGenerator::Private::orderItems(KFileItemList& items)
01012 {
01013
01014
01015 const bool hasProxy = (m_proxyModel != 0);
01016 const int itemCount = items.count();
01017 const QRect visibleArea = m_viewAdapter->visibleArea();
01018
01019 QModelIndex dirIndex;
01020 QRect itemRect;
01021 int insertPos = 0;
01022 for (int i = 0; i < itemCount; ++i) {
01023 dirIndex = m_dirModel->indexForItem(items.at(i));
01024 if (hasProxy) {
01025 const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
01026 itemRect = m_viewAdapter->visualRect(proxyIndex);
01027 } else {
01028 itemRect = m_viewAdapter->visualRect(dirIndex);
01029 }
01030
01031 if (itemRect.intersects(visibleArea)) {
01032
01033
01034
01035 items.insert(insertPos, items.at(i));
01036 items.removeAt(i + 1);
01037 ++insertPos;
01038 ++m_pendingVisibleIconUpdates;
01039 }
01040 }
01041 }
01042
01043 bool KFilePreviewGenerator::Private::decodeIsCutSelection(const QMimeData* mimeData)
01044 {
01045 const QByteArray data = mimeData->data("application/x-kde-cutselection");
01046 if (data.isEmpty()) {
01047 return false;
01048 } else {
01049 return data.at(0) == '1';
01050 }
01051 }
01052
01053 void KFilePreviewGenerator::Private::addItemsToList(const QModelIndex& index, KFileItemList& list)
01054 {
01055 const int rowCount = m_dirModel->rowCount(index);
01056 for (int row = 0; row < rowCount; ++row) {
01057 const QModelIndex subIndex = m_dirModel->index(row, 0, index);
01058 KFileItem item = m_dirModel->itemForIndex(subIndex);
01059 list.append(item);
01060
01061 if (m_dirModel->rowCount(subIndex) > 0) {
01062
01063 addItemsToList(subIndex, list);
01064 }
01065 }
01066 }
01067
01068 void KFilePreviewGenerator::Private::delayedIconUpdate()
01069 {
01070
01071
01072
01073
01074 KFileItemList itemList;
01075
01076 QHash<KUrl, bool>::const_iterator it = m_changedItems.constBegin();
01077 while (it != m_changedItems.constEnd()) {
01078 const bool hasChanged = it.value();
01079 if (hasChanged) {
01080 const QModelIndex index = m_dirModel->indexForUrl(it.key());
01081 const KFileItem item = m_dirModel->itemForIndex(index);
01082 itemList.append(item);
01083 }
01084 ++it;
01085 }
01086 m_changedItems.clear();
01087
01088 updateIcons(itemList);
01089 }
01090
01091 KFilePreviewGenerator::KFilePreviewGenerator(QAbstractItemView* parent) :
01092 QObject(parent),
01093 d(new Private(this, new KIO::DefaultViewAdapter(parent, this), parent->model()))
01094 {
01095 d->m_itemView = parent;
01096 }
01097
01098 KFilePreviewGenerator::KFilePreviewGenerator(KAbstractViewAdapter* parent, QAbstractProxyModel* model) :
01099 QObject(parent),
01100 d(new Private(this, parent, model))
01101 {
01102 }
01103
01104 KFilePreviewGenerator::~KFilePreviewGenerator()
01105 {
01106 delete d;
01107 }
01108
01109 void KFilePreviewGenerator::setPreviewShown(bool show)
01110 {
01111 if (show && (!d->m_viewAdapter->iconSize().isValid() || (d->m_dirModel == 0))) {
01112
01113
01114 return;
01115 }
01116
01117 if (d->m_previewShown != show) {
01118 d->m_previewShown = show;
01119 updateIcons();
01120 }
01121 }
01122
01123 bool KFilePreviewGenerator::isPreviewShown() const
01124 {
01125 return d->m_previewShown;
01126 }
01127
01128
01129 void KFilePreviewGenerator::updatePreviews()
01130 {
01131 updateIcons();
01132 }
01133
01134 void KFilePreviewGenerator::updateIcons()
01135 {
01136 d->killPreviewJobs();
01137
01138 d->clearCutItemsCache();
01139 d->m_pendingItems.clear();
01140 d->m_dispatchedItems.clear();
01141
01142 KFileItemList itemList;
01143 d->addItemsToList(QModelIndex(), itemList);
01144
01145 d->updateIcons(itemList);
01146 }
01147
01148 void KFilePreviewGenerator::cancelPreviews()
01149 {
01150 d->killPreviewJobs();
01151 d->m_pendingItems.clear();
01152 d->m_dispatchedItems.clear();
01153 updateIcons();
01154 }
01155
01156 void KFilePreviewGenerator::setEnabledPlugins(const QStringList& plugins)
01157 {
01158 d->m_enabledPlugins = plugins;
01159 }
01160
01161 QStringList KFilePreviewGenerator::enabledPlugins() const
01162 {
01163 return d->m_enabledPlugins;
01164 }
01165
01166 #include "kfilepreviewgenerator.moc"