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