1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2013-02-25
7 * Description : Table view column helpers: Thumbnail column
8 *
9 * Copyright (C) 2017-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10 * Copyright (C) 2013 by Michael G. Hansen <mike at mghansen dot de>
11 *
12 * This program is free software; you can redistribute it
13 * and/or modify it under the terms of the GNU General
14 * Public License as published by the Free Software Foundation;
15 * either version 2, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * ============================================================ */
23
24 #include "tableview_column_thumbnail.h"
25
26 // Qt includes
27
28 #include <QPainter>
29 #include <QApplication>
30 #include <QStyleOptionViewItem>
31
32 // KDE includes
33
34 #include <klocalizedstring.h>
35
36 // Local includes
37
38 #include "itemfiltermodel.h"
39 #include "tableview.h"
40 #include "thumbnailloadthread.h"
41 #include "thumbnailsize.h"
42 #include "metaengine.h"
43
44 namespace
45 {
46 const int ThumbnailBorder = 2;
47 }
48
49 namespace Digikam
50 {
51
52 namespace TableViewColumns
53 {
54
ColumnThumbnail(TableViewShared * const tableViewShared,const TableViewColumnConfiguration & pConfiguration,QObject * const parent)55 ColumnThumbnail::ColumnThumbnail(TableViewShared* const tableViewShared,
56 const TableViewColumnConfiguration& pConfiguration,
57 QObject* const parent)
58 : TableViewColumn(tableViewShared, pConfiguration, parent),
59 m_thumbnailSize(s->tableView->getThumbnailSize().size())
60 {
61 connect(s->thumbnailLoadThread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QPixmap)),
62 this, SLOT(slotThumbnailLoaded(LoadingDescription,QPixmap)));
63 }
64
~ColumnThumbnail()65 ColumnThumbnail::~ColumnThumbnail()
66 {
67 }
68
CreateFromConfiguration(TableViewShared * const tableViewShared,const TableViewColumnConfiguration & pConfiguration,TableViewColumn ** const pNewColumn,QObject * const parent)69 bool ColumnThumbnail::CreateFromConfiguration(TableViewShared* const tableViewShared,
70 const TableViewColumnConfiguration& pConfiguration,
71 TableViewColumn** const pNewColumn,
72 QObject* const parent)
73 {
74 if (pConfiguration.columnId!=QLatin1String("thumbnail"))
75 {
76 return false;
77 }
78
79 *pNewColumn = new ColumnThumbnail(tableViewShared, pConfiguration, parent);
80
81 return true;
82 }
83
getDescription()84 TableViewColumnDescription ColumnThumbnail::getDescription()
85 {
86 return TableViewColumnDescription(QLatin1String("thumbnail"), i18n("Thumbnail"));
87 }
88
getColumnFlags() const89 TableViewColumn::ColumnFlags ColumnThumbnail::getColumnFlags() const
90 {
91 return ColumnCustomPainting;
92 }
93
getTitle() const94 QString ColumnThumbnail::getTitle() const
95 {
96 return i18n("Thumbnail");
97 }
98
data(TableViewModel::Item * const item,const int role) const99 QVariant ColumnThumbnail::data(TableViewModel::Item* const item, const int role) const
100 {
101 Q_UNUSED(item)
102 Q_UNUSED(role)
103
104 // we do not return any data, but paint(...) something
105
106 return QVariant();
107 }
108
paint(QPainter * const painter,const QStyleOptionViewItem & option,TableViewModel::Item * const item) const109 bool ColumnThumbnail::paint(QPainter* const painter, const QStyleOptionViewItem& option, TableViewModel::Item* const item) const
110 {
111 if (option.state & QStyle::State_Selected)
112 {
113 painter->fillRect(option.rect, option.palette.highlight());
114 }
115
116 const ItemInfo info = s->tableViewModel->infoFromItem(item);
117
118 if (!info.isNull())
119 {
120 QSize availableSize = option.rect.size() - QSize(ThumbnailBorder, ThumbnailBorder);
121 QSize imageSize = info.dimensions();
122 int maxSize = m_thumbnailSize;
123
124 // When the image is rotated, swap width and height.
125
126 if (info.orientation() > MetaEngine::ORIENTATION_VFLIP)
127 {
128 imageSize.transpose();
129 }
130
131 if (imageSize.isValid() && (imageSize.width() > imageSize.height()))
132 {
133 // for landscape pictures, try to use all available horizontal space
134
135 qreal scaleFactor = qreal(availableSize.height()) / qreal(imageSize.height());
136 maxSize = imageSize.width() * scaleFactor;
137 }
138
139 // Calculate the maximum thumbnail size
140 // The idea here is that for landscape images, we adjust the height to
141 // to be as high as the row height as long as the width can stretch enough
142 // because the column is wider than the thumbnail size.
143
144 maxSize = qMin(maxSize, availableSize.width());
145
146 // However, digiKam limits the thumbnail size, so we also do that here
147
148 maxSize = qMin(maxSize, (int)ThumbnailSize::maxThumbsSize());
149 double ratio = qApp->devicePixelRatio();
150 maxSize = qRound((double)maxSize * ratio);
151
152 QPixmap thumbnail;
153
154 if (s->thumbnailLoadThread->find(info.thumbnailIdentifier(), thumbnail, maxSize))
155 {
156 thumbnail.setDevicePixelRatio(ratio);
157
158 /// @todo Is slotThumbnailLoaded still called when the thumbnail is found right away?
159
160 QSize pixmapSize = (QSizeF(thumbnail.size()) / ratio).toSize();
161 pixmapSize = pixmapSize.boundedTo(availableSize);
162 QSize alignSize = option.rect.size();
163
164 QPoint startPoint((alignSize.width() - pixmapSize.width()) / 2,
165 (alignSize.height() - pixmapSize.height()) / 2);
166 startPoint += option.rect.topLeft();
167
168 painter->drawPixmap(QRect(startPoint, pixmapSize), thumbnail,
169 QRect(QPoint(0, 0), thumbnail.size()));
170
171 return true;
172 }
173 }
174
175 // we did not get to paint a thumbnail...
176
177 return false;
178 }
179
sizeHint(const QStyleOptionViewItem & option,TableViewModel::Item * const item) const180 QSize ColumnThumbnail::sizeHint(const QStyleOptionViewItem& option, TableViewModel::Item* const item) const
181 {
182 Q_UNUSED(option)
183 Q_UNUSED(item)
184
185 /// @todo On portrait pictures, the borders are too close. There should be a gap. Is this setting okay?
186
187 const int thumbnailSizeWithBorder = m_thumbnailSize+ThumbnailBorder;
188
189 return QSize(thumbnailSizeWithBorder, thumbnailSizeWithBorder);
190 }
191
slotThumbnailLoaded(const LoadingDescription & loadingDescription,const QPixmap & thumb)192 void ColumnThumbnail::slotThumbnailLoaded(const LoadingDescription& loadingDescription, const QPixmap& thumb)
193 {
194 if (thumb.isNull())
195 {
196 return;
197 }
198
199 /// @todo Find a way to do this without the ItemFilterModel
200
201 const QModelIndex imageModelIndex = s->imageModel->indexForPath(loadingDescription.filePath);
202
203 if (!imageModelIndex.isValid())
204 {
205 return;
206 }
207
208 const qlonglong imageId = s->imageModel->imageId(imageModelIndex);
209
210 emit signalDataChanged(imageId);
211 }
212
updateThumbnailSize()213 void ColumnThumbnail::updateThumbnailSize()
214 {
215 /// @todo Set minimum column width to m_thumbnailSize
216
217 m_thumbnailSize = s->tableView->getThumbnailSize().size();
218
219 emit signalAllDataChanged();
220 }
221
222 } // namespace TableViewColumns
223
224 } // namespace Digikam
225