22 #include "../kio/kio/defaultviewadapter_p.h"
23 #include "../kio/kio/imagefilter_p.h"
33 #include <QApplication>
34 #include <QAbstractItemView>
35 #include <QAbstractProxyModel>
46 #if defined(Q_WS_X11) && defined(HAVE_XRENDER)
48 # include <X11/Xlib.h>
49 # include <X11/extensions/Xrender.h>
72 class KFilePreviewGenerator::LayoutBlocker
76 m_uniformSizes(false),
80 m_uniformSizes = m_view->uniformItemSizes();
81 m_view->setUniformItemSizes(
true);
88 m_view->setUniformItemSizes(m_uniformSizes);
98 class KFilePreviewGenerator::TileSet
101 enum { LeftMargin = 3, TopMargin = 2, RightMargin = 3, BottomMargin = 4 };
103 enum Tile { TopLeftCorner = 0, TopSide, TopRightCorner, LeftSide,
104 RightSide, BottomLeftCorner, BottomSide, BottomRightCorner,
109 QImage image(8 * 3, 8 * 3, QImage::Format_ARGB32_Premultiplied);
112 p.setCompositionMode(QPainter::CompositionMode_Source);
113 p.fillRect(image.rect(), Qt::transparent);
114 p.fillRect(image.rect().adjusted(3, 3, -3, -3), Qt::black);
120 m_tiles[TopLeftCorner] = pixmap.
copy(0, 0, 8, 8);
121 m_tiles[TopSide] = pixmap.
copy(8, 0, 8, 8);
122 m_tiles[TopRightCorner] = pixmap.
copy(16, 0, 8, 8);
123 m_tiles[LeftSide] = pixmap.
copy(0, 8, 8, 8);
124 m_tiles[RightSide] = pixmap.
copy(16, 8, 8, 8);
125 m_tiles[BottomLeftCorner] = pixmap.
copy(0, 16, 8, 8);
126 m_tiles[BottomSide] = pixmap.
copy(8, 16, 8, 8);
127 m_tiles[BottomRightCorner] = pixmap.
copy(16, 16, 8, 8);
133 if (r.
width() - 16 > 0) {
137 if (r.
height() - 16 > 0) {
142 if (r.
width() - 16 > 0) {
147 const QRect contentRect = r.
adjusted(LeftMargin + 1, TopMargin + 1,
148 -(RightMargin + 1), -(BottomMargin + 1));
149 p->
fillRect(contentRect, Qt::transparent);
156 class KFilePreviewGenerator::Private
168 void requestSequenceIcon(
const QModelIndex& index,
int sequenceIndex);
192 void slotPreviewJobFinished(
KJob* job);
195 void updateCutItems();
201 void clearCutItemsCache();
207 void dispatchIconUpdateQueue();
214 void pauseIconUpdates();
221 void resumeIconUpdates();
227 void startMimeTypeResolving();
233 void resolveMimeType();
239 bool isCutItem(
const KFileItem& item)
const;
251 bool applyImageFrame(
QPixmap& icon);
270 void startPreviewJob(
const KFileItemList& items,
int width,
int height);
273 void killPreviewJobs();
287 bool decodeIsCutSelection(
const QMimeData* mimeData);
299 void delayedIconUpdate();
317 class DataChangeObtainer
320 DataChangeObtainer(KFilePreviewGenerator::Private* generator) :
321 m_gen(generator) { ++m_gen->m_internalDataChange; }
322 ~DataChangeObtainer() { --m_gen->m_internalDataChange; }
324 KFilePreviewGenerator::Private* m_gen;
333 bool m_clearItemQueues;
338 bool m_hasCutSelection;
344 bool m_iconUpdatesPaused;
351 int m_internalDataChange;
353 int m_pendingVisibleIconUpdates;
357 QTimer* m_iconUpdateTimer;
358 QTimer* m_scrollAreaTimer;
381 QTimer* m_changedItemsTimer;
409 m_previewShown(true),
410 m_clearItemQueues(true),
411 m_hasCutSelection(false),
412 m_iconUpdatesPaused(false),
413 m_internalDataChange(0),
414 m_pendingVisibleIconUpdates(0),
415 m_viewAdapter(viewAdapter),
417 m_iconUpdateTimer(0),
418 m_scrollAreaTimer(0),
425 m_changedItemsTimer(0),
428 m_resolvedMimeTypes(),
433 if (!m_viewAdapter->iconSize().isValid()) {
434 m_previewShown =
false;
438 m_dirModel = (m_proxyModel == 0) ?
439 qobject_cast<KDirModel*>(model) :
440 qobject_cast<
KDirModel*>(m_proxyModel->sourceModel());
443 m_previewShown =
false;
453 q, SLOT(rowsAboutToBeRemoved(
QModelIndex,
int,
int)));
457 connect(clipboard, SIGNAL(dataChanged()),
458 q, SLOT(updateCutItems()));
460 m_iconUpdateTimer =
new QTimer(q);
461 m_iconUpdateTimer->setSingleShot(
true);
462 m_iconUpdateTimer->setInterval(200);
463 connect(m_iconUpdateTimer, SIGNAL(
timeout()), q, SLOT(dispatchIconUpdateQueue()));
469 m_scrollAreaTimer =
new QTimer(q);
470 m_scrollAreaTimer->setSingleShot(
true);
471 m_scrollAreaTimer->setInterval(200);
473 q, SLOT(resumeIconUpdates()));
475 q, SLOT(pauseIconUpdates()));
477 m_changedItemsTimer =
new QTimer(q);
478 m_changedItemsTimer->setSingleShot(
true);
479 m_changedItemsTimer->setInterval(5000);
481 q, SLOT(delayedIconUpdate()));
484 m_enabledPlugins = globalConfig.readEntry(
"Plugins",
QStringList()
485 <<
"directorythumbnail"
491 if(m_enabledPlugins.contains(
QLatin1String(
"jpegrotatedthumbnail"))) {
492 m_enabledPlugins.removeAll(
QLatin1String(
"jpegrotatedthumbnail"));
494 globalConfig.writeEntry(
"Plugins", m_enabledPlugins);
499 KFilePreviewGenerator::Private::~Private()
502 m_pendingItems.clear();
503 m_dispatchedItems.clear();
507 void KFilePreviewGenerator::Private::requestSequenceIcon(
const QModelIndex& index,
510 if (m_pendingItems.isEmpty() || (sequenceIndex == 0)) {
517 if (sequenceIndex == 0) {
518 m_sequenceIndices.remove(item.
url());
520 m_sequenceIndices.insert(item.
url(), sequenceIndex);
528 void KFilePreviewGenerator::Private::updateIcons(
const KFileItemList& items)
534 applyCutItemEffect(items);
537 orderItems(orderedItems);
539 foreach (
const KFileItem& item, orderedItems) {
540 m_pendingItems.append(item);
543 if (m_previewShown) {
544 createPreviews(orderedItems);
546 startMimeTypeResolving();
550 void KFilePreviewGenerator::Private::updateIcons(
const QModelIndex& topLeft,
553 if (m_internalDataChange > 0) {
571 for (
int row = topLeft.
row(); row <= bottomRight.
row(); ++row) {
579 if (m_previewShown) {
581 const bool hasChanged = m_changedItems.contains(url);
582 m_changedItems.insert(url, hasChanged);
594 updateIcons(itemList);
595 m_changedItemsTimer->start();
598 void KFilePreviewGenerator::Private::addToPreviewQueue(
const KFileItem& item,
const QPixmap& pixmap)
601 Q_ASSERT(senderJob != 0);
602 if (senderJob != 0) {
611 m_sequenceIndices.erase(it);
614 if (!m_previewShown) {
626 bool isOldPreview =
true;
628 KUrl itemParentDir(item.
url());
629 itemParentDir.
setPath(itemParentDir.directory());
632 if (dir == itemParentDir || !dir.
hasPath()) {
633 isOldPreview =
false;
645 const QString mimeTypeGroup = mimeType.
left(slashIndex);
646 if ((mimeTypeGroup !=
QLatin1String(
"image")) || !applyImageFrame(icon)) {
647 limitToSize(icon, m_viewAdapter->iconSize());
650 if (m_hasCutSelection && isCutItem(item)) {
662 preview.url = item.
url();
663 preview.pixmap = icon;
664 m_previews.append(preview);
666 m_dispatchedItems.append(item);
669 void KFilePreviewGenerator::Private::slotPreviewJobFinished(
KJob* job)
671 const int index = m_previewJobs.indexOf(job);
672 m_previewJobs.removeAt(index);
674 if (m_previewJobs.isEmpty()) {
675 if (m_clearItemQueues) {
676 m_pendingItems.clear();
677 m_dispatchedItems.clear();
678 m_pendingVisibleIconUpdates = 0;
681 m_sequenceIndices.clear();
685 void KFilePreviewGenerator::Private::updateCutItems()
692 DataChangeObtainer obt(
this);
693 clearCutItemsCache();
698 foreach (
const KUrl& url, dirs) {
701 applyCutItemEffect(items);
704 void KFilePreviewGenerator::Private::clearCutItemsCache()
711 DataChangeObtainer obt(
this);
715 foreach (
const KUrl& url, m_cutItemsCache.keys()) {
719 if (m_previewShown) {
724 m_cutItemsCache.clear();
726 if (previews.
size() > 0) {
728 Q_ASSERT(m_previewShown);
729 orderItems(previews);
730 updateIcons(previews);
734 void KFilePreviewGenerator::Private::dispatchIconUpdateQueue()
741 const int count = m_previewShown ? m_previews.count()
742 : m_resolvedMimeTypes.count();
744 LayoutBlocker blocker(m_itemView);
745 DataChangeObtainer obt(
this);
747 if (m_previewShown) {
749 foreach (
const ItemInfo& preview, m_previews) {
752 dirModel->
setData(idx,
QIcon(preview.pixmap), Qt::DecorationRole);
758 foreach (
const KFileItem& item, m_resolvedMimeTypes) {
762 m_resolvedMimeTypes.clear();
765 m_pendingVisibleIconUpdates -= count;
766 if (m_pendingVisibleIconUpdates < 0) {
767 m_pendingVisibleIconUpdates = 0;
771 if (m_pendingVisibleIconUpdates > 0) {
775 m_iconUpdateTimer->start();
779 void KFilePreviewGenerator::Private::pauseIconUpdates()
781 m_iconUpdatesPaused =
true;
782 foreach (
KJob* job, m_previewJobs) {
786 m_scrollAreaTimer->start();
789 void KFilePreviewGenerator::Private::resumeIconUpdates()
791 m_iconUpdatesPaused =
false;
799 foreach (
const KFileItem& item, m_dispatchedItems) {
800 KFileItemList::iterator
begin = m_pendingItems.begin();
801 KFileItemList::iterator
end = m_pendingItems.end();
802 for (KFileItemList::iterator it = begin; it != end; ++it) {
803 if ((*it).url() == item.
url()) {
804 m_pendingItems.erase(it);
809 m_dispatchedItems.clear();
811 m_pendingVisibleIconUpdates = 0;
812 dispatchIconUpdateQueue();
815 if (m_previewShown) {
817 orderItems(orderedItems);
823 m_clearItemQueues =
false;
825 m_clearItemQueues =
true;
827 createPreviews(orderedItems);
829 orderItems(m_pendingItems);
830 startMimeTypeResolving();
834 void KFilePreviewGenerator::Private::startMimeTypeResolving()
837 m_iconUpdateTimer->start();
840 void KFilePreviewGenerator::Private::resolveMimeType()
842 if (m_pendingItems.isEmpty()) {
847 bool resolved =
false;
849 KFileItem item = m_pendingItems.takeFirst();
851 if (m_pendingVisibleIconUpdates > 0) {
854 --m_pendingVisibleIconUpdates;
863 m_resolvedMimeTypes.append(item);
866 }
while (!resolved && !m_pendingItems.isEmpty());
868 if (m_pendingItems.isEmpty()) {
872 dispatchIconUpdateQueue();
873 }
else if (!m_iconUpdatesPaused) {
880 bool KFilePreviewGenerator::Private::isCutItem(
const KFileItem& item)
const
887 void KFilePreviewGenerator::Private::applyCutItemEffect(
const KFileItemList& items)
890 m_hasCutSelection = decodeIsCutSelection(mimeData);
891 if (!m_hasCutSelection) {
902 DataChangeObtainer obt(
this);
907 const QVariant value = dirModel->
data(index, Qt::DecorationRole);
908 if (value.
type() == QVariant::Icon) {
909 const QIcon icon(qvariant_cast<QIcon>(value));
911 QPixmap pixmap = icon.pixmap(actualSize);
914 if ((cacheIt == m_cutItemsCache.
constEnd()) || (cacheIt->cacheKey() != pixmap.
cacheKey())) {
916 dirModel->
setData(index,
QIcon(pixmap), Qt::DecorationRole);
918 m_cutItemsCache.insert(item.
url(), pixmap);
925 bool KFilePreviewGenerator::Private::applyImageFrame(
QPixmap& icon)
927 const QSize maxSize = m_viewAdapter->iconSize();
932 const bool isIconCandidate = (icon.
width() == icon.
height()) &&
933 ((icon.
width() & 0x7) == 0);
944 const QSize size(maxSize.
width() - TileSet::LeftMargin - TileSet::RightMargin,
945 maxSize.
height() - TileSet::TopMargin - TileSet::BottomMargin);
946 limitToSize(icon, size);
948 if (m_tileSet == 0) {
949 m_tileSet =
new TileSet();
952 QPixmap framedIcon(icon.
size().
width() + TileSet::LeftMargin + TileSet::RightMargin,
953 icon.
size().
height() + TileSet::TopMargin + TileSet::BottomMargin);
954 framedIcon.
fill(Qt::transparent);
957 painter.
begin(&framedIcon);
959 m_tileSet->paint(&painter, framedIcon.rect());
961 painter.
drawPixmap(TileSet::LeftMargin, TileSet::TopMargin, icon);
968 void KFilePreviewGenerator::Private::limitToSize(
QPixmap& icon,
const QSize& maxSize)
971 #if defined(Q_WS_X11) && defined(HAVE_XRENDER)
975 size.
scale(maxSize, Qt::KeepAspectRatio);
977 const qreal factor = size.
width() / qreal(icon.
width());
979 XTransform xform = {{
980 { XDoubleToFixed(1 / factor), 0, 0 },
981 { 0, XDoubleToFixed(1 / factor), 0 },
982 { 0, 0, XDoubleToFixed(1) }
986 pixmap.
fill(Qt::transparent);
990 XRenderPictureAttributes attr;
991 attr.repeat = RepeatPad;
994 XRenderSetPictureFilter(dpy, icon.
x11PictureHandle(), FilterBilinear, 0, 0);
997 0, 0, 0, 0, 0, 0, pixmap.
width(), pixmap.
height());
1000 icon = icon.
scaled(maxSize, Qt::KeepAspectRatio, Qt::FastTransformation);
1003 icon = icon.
scaled(maxSize, Qt::KeepAspectRatio, Qt::FastTransformation);
1008 void KFilePreviewGenerator::Private::createPreviews(
const KFileItemList& items)
1010 if (items.
count() == 0) {
1015 m_hasCutSelection = decodeIsCutSelection(mimeData);
1026 foreach (
const KFileItem& item, items) {
1029 mimeTypeGroup = mimeType.
left(slashIndex);
1036 const QSize size = m_viewAdapter->iconSize();
1037 startPreviewJob(otherItems, size.
width(), size.
height());
1039 const int cacheSize = (size.
width() > 128) || (size.
height() > 128) ? 256 : 128;
1040 startPreviewJob(imageItems, cacheSize, cacheSize);
1042 m_iconUpdateTimer->start();
1045 void KFilePreviewGenerator::Private::startPreviewJob(
const KFileItemList& items,
int width,
int height)
1047 if (items.
count() > 0) {
1052 if (!m_sequenceIndices.isEmpty() && (items.
count() == 1)) {
1054 if (it != m_sequenceIndices.
end()) {
1061 connect(job, SIGNAL(finished(
KJob*)),
1062 q, SLOT(slotPreviewJobFinished(
KJob*)));
1063 m_previewJobs.append(job);
1067 void KFilePreviewGenerator::Private::killPreviewJobs()
1069 foreach (
KJob* job, m_previewJobs) {
1073 m_previewJobs.clear();
1074 m_sequenceIndices.clear();
1076 m_iconUpdateTimer->stop();
1077 m_scrollAreaTimer->stop();
1078 m_changedItemsTimer->stop();
1081 void KFilePreviewGenerator::Private::orderItems(
KFileItemList& items)
1090 const bool hasProxy = (m_proxyModel != 0);
1091 const int itemCount = items.
count();
1092 const QRect visibleArea = m_viewAdapter->visibleArea();
1097 for (
int i = 0; i < itemCount; ++i) {
1100 const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
1101 itemRect = m_viewAdapter->visualRect(proxyIndex);
1103 itemRect = m_viewAdapter->visualRect(dirIndex);
1110 items.
insert(insertPos, items.
at(i));
1113 ++m_pendingVisibleIconUpdates;
1118 bool KFilePreviewGenerator::Private::decodeIsCutSelection(
const QMimeData* mimeData)
1120 const QByteArray data = mimeData->
data(
"application/x-kde-cutselection");
1135 const int rowCount = dirModel->
rowCount(index);
1136 for (
int row = 0; row < rowCount; ++row) {
1141 if (dirModel->
rowCount(subIndex) > 0) {
1143 addItemsToList(subIndex, list);
1148 void KFilePreviewGenerator::Private::delayedIconUpdate()
1162 while (it != m_changedItems.
constEnd()) {
1163 const bool hasChanged = it.
value();
1171 m_changedItems.
clear();
1173 updateIcons(itemList);
1176 void KFilePreviewGenerator::Private::rowsAboutToBeRemoved(
const QModelIndex& parent,
int start,
int end)
1178 if (m_changedItems.isEmpty()) {
1187 for (
int row = start; row <= end; row++) {
1192 m_changedItems.remove(item.
url());
1196 rowsAboutToBeRemoved(index, 0, dirModel->
rowCount(index) - 1);
1203 d(new Private(this, new KIO::DefaultViewAdapter(parent, this), parent->model()))
1210 d(new Private(this, parent, model))
1221 if (d->m_previewShown == show) {
1225 KDirModel* dirModel = d->m_dirModel.data();
1226 if (show && (!d->m_viewAdapter->iconSize().isValid() || !dirModel)) {
1232 d->m_previewShown = show;
1243 foreach (
const KFileItem& item, itemList) {
1246 indexesWithKnownMimeType.
append(index);
1255 foreach (
const QModelIndex& index, indexesWithKnownMimeType) {
1264 return d->m_previewShown;
1275 d->killPreviewJobs();
1277 d->clearCutItemsCache();
1278 d->m_pendingItems.clear();
1279 d->m_dispatchedItems.clear();
1284 d->updateIcons(itemList);
1289 d->killPreviewJobs();
1290 d->m_pendingItems.clear();
1291 d->m_dispatchedItems.clear();
1297 d->m_enabledPlugins = plugins;
1302 return d->m_enabledPlugins;
1305 #include "kfilepreviewgenerator.moc"
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const
void cancelPreviews()
Cancels all pending previews.
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
QByteArray data(const QString &mimeType) const
KMimeType::Ptr determineMimeType() const
const Key key(const T &value) const
void fillRect(const QRectF &rectangle, const QBrush &brush)
void setCompositionMode(CompositionMode mode)
QPixmap copy(int x, int y, int width, int height) const
KFilePreviewGenerator(QAbstractItemView *parent)
const QMimeData * mimeData(Mode mode) const
void fill(const QColor &color)
Generates previews for files of an item view.
KUrl::List directories() const
static KIconLoader * global()
void itemChanged(const QModelIndex &index)
virtual QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
const_iterator constBegin() const
const T & at(int i) const
bool intersects(const QRect &rectangle) const
virtual bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole)
QModelIndex indexForUrl(const KUrl &url) const
virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const
QPixmap fromImage(const QImage &image, QFlags< Qt::ImageConversionFlag > flags)
void drawTiledPixmap(const QRectF &rectangle, const QPixmap &pixmap, const QPointF &position)
const_iterator constFind(const Key &key) const
void scale(int width, int height, Qt::AspectRatioMode mode)
virtual ~KFilePreviewGenerator()
KDirLister * dirLister() const
QModelIndex indexForItem(const KFileItem *) const
QStringList enabledPlugins() const
Returns the list of enabled thumbnail plugins.
KSharedConfigPtr config()
KFileItem itemForIndex(const QModelIndex &index) const
void setPath(const QString &path)
int sequenceIndex() const
int count(const T &value) const
void append(const T &value)
const_iterator constEnd() const
static KUrl::List fromMimeData(const QMimeData *mimeData, KUrl::MetaDataMap *metaData=0)
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
KAction * actualSize(const QObject *recvr, const char *slot, QObject *parent)
void setPreviewShown(bool show)
If show is set to true, a preview is generated for each item.
QPixmap scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const
virtual bool hasChildren(const QModelIndex &parent=QModelIndex()) const
bool signalsBlocked() const
KIconEffect * iconEffect() const
QRect adjusted(int dx1, int dy1, int dx2, int dy2) const
const T value(const Key &key) const
bool blockSignals(bool block)
bool contains(const T &value) const
void updateIcons()
Updates the icons for all items.
bool contains(const T &value) const
bool isPreviewShown() const
void insert(int i, const T &value)
Qt::HANDLE x11PictureHandle() const
QString left(int n) const
PreviewJob * filePreview(const KFileItemList &items, int width, int height=0, int iconSize=0, int iconAlpha=70, bool scale=true, bool save=true, const QStringList *enabledPlugins=0)
QImage apply(const QImage &src, int group, int state) const
void drawOverlays(const QStringList &overlays, QPixmap &pixmap, KIconLoader::Group group, int state=KIconLoader::DefaultState) const
bool isMimeTypeKnown() const
static void shadowBlur(QImage &image, float radius, const QColor &color)
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool begin(QPaintDevice *device)
iterator find(const Key &key)
QStringList overlays() const
const KShortcut & begin()
void setEnabledPlugins(const QStringList &list)
Sets the list of enabled thumbnail plugins.
KFileItemList itemsForDir(const KUrl &dir, WhichItems which=FilteredItems) const
void setSequenceIndex(int index)