1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2013-08-01
7 * Description : Qt model view for Showfoto item - the delegate
8 *
9 * Copyright (C) 2013 by Mohamed_Anwer <m_dot_anwer at gmx dot com>
10 * Copyright (C) 2013-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
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)
16 * any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * ============================================================ */
24
25 #include "showfotodelegate_p.h"
26
27 namespace ShowFoto
28 {
29
clearRects()30 void ShowfotoDelegate::ShowfotoDelegatePrivate::clearRects()
31 {
32 ShowfotoItemViewDelegatePrivate::clearRects();
33 dateRect = QRect(0, 0, 0, 0);
34 pixmapRect = QRect(0, 0, 0, 0);
35 nameRect = QRect(0, 0, 0, 0);
36 resolutionRect = QRect(0, 0, 0, 0);
37 sizeRect = QRect(0, 0, 0, 0);
38 imageInformationRect = QRect(0, 0, 0, 0);
39 coordinatesRect = QRect(0, 0, 0, 0);
40 }
41
ShowfotoDelegate(QObject * const parent)42 ShowfotoDelegate::ShowfotoDelegate(QObject* const parent)
43 : ShowfotoItemViewDelegate(*new ShowfotoDelegatePrivate, parent)
44 {
45 }
46
ShowfotoDelegate(ShowfotoDelegate::ShowfotoDelegatePrivate & dd,QObject * const parent)47 ShowfotoDelegate::ShowfotoDelegate(ShowfotoDelegate::ShowfotoDelegatePrivate& dd, QObject* const parent)
48 : ShowfotoItemViewDelegate(dd, parent)
49 {
50 }
51
~ShowfotoDelegate()52 ShowfotoDelegate::~ShowfotoDelegate()
53 {
54 Q_D(ShowfotoDelegate);
55 Q_UNUSED(d); // To please compiler about warnings.
56 }
57
setView(ShowfotoThumbnailBar * view)58 void ShowfotoDelegate::setView(ShowfotoThumbnailBar* view)
59 {
60 Q_D(ShowfotoDelegate);
61 setViewOnAllOverlays(view);
62
63 if (d->currentView)
64 {
65 disconnect(d->currentView, SIGNAL(modelChanged()),
66 this, SLOT(modelChanged()));
67 }
68
69 d->currentView = view;
70
71 setModel(view ? view->model() : nullptr);
72
73 if (d->currentView)
74 {
75 connect(d->currentView, SIGNAL(modelChanged()),
76 this, SLOT(modelChanged()));
77 }
78 }
79
setModel(QAbstractItemModel * model)80 void ShowfotoDelegate::setModel(QAbstractItemModel* model)
81 {
82 Q_D(ShowfotoDelegate);
83
84 // 1) We only need the model to invalidate model-index based caches on change
85 // 2) We do not need to care for overlays. The view calls setActive() on them on model change
86
87 if (model == d->currentModel)
88 {
89 return;
90 }
91
92 if (d->currentModel)
93 {
94 disconnect(d->currentModel, nullptr, this, nullptr);
95 }
96
97 d->currentModel = model;
98
99 if (d->currentModel)
100 {
101 connect(d->currentModel, SIGNAL(layoutAboutToBeChanged()),
102 this, SLOT(modelContentsChanged()));
103
104 connect(d->currentModel, SIGNAL(modelAboutToBeReset()),
105 this, SLOT(modelContentsChanged()));
106
107 connect(d->currentModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
108 this, SLOT(modelContentsChanged()));
109
110 connect(d->currentModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
111 this, SLOT(modelContentsChanged()));
112
113 connect(d->currentModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
114 this, SLOT(modelContentsChanged()));
115 }
116 }
117
setSpacing(int spacing)118 void ShowfotoDelegate::setSpacing(int spacing)
119 {
120 ShowfotoItemViewDelegate::setSpacing(spacing);
121 }
122
pixmapRect() const123 QRect ShowfotoDelegate::pixmapRect() const
124 {
125 Q_D(const ShowfotoDelegate);
126
127 return d->pixmapRect;
128 }
129
imageInformationRect() const130 QRect ShowfotoDelegate::imageInformationRect() const
131 {
132 Q_D(const ShowfotoDelegate);
133
134 return d->imageInformationRect;
135 }
136
groupIndicatorRect() const137 QRect ShowfotoDelegate::groupIndicatorRect() const
138 {
139 Q_D(const ShowfotoDelegate);
140
141 return d->groupRect;
142 }
143
coordinatesIndicatorRect() const144 QRect ShowfotoDelegate::coordinatesIndicatorRect() const
145 {
146 Q_D(const ShowfotoDelegate);
147
148 return d->coordinatesRect;
149 }
150
retrieveThumbnailPixmap(const QModelIndex & index,int thumbnailSize)151 QPixmap ShowfotoDelegate::retrieveThumbnailPixmap(const QModelIndex& index, int thumbnailSize)
152 {
153 // work around constness
154
155 QAbstractItemModel* const model = const_cast<QAbstractItemModel*>(index.model());
156
157 // set requested thumbnail size
158
159 model->setData(index, thumbnailSize, ShowfotoItemModel::ThumbnailRole);
160
161 // get data from model
162
163 QVariant thumbData = index.data(ShowfotoItemModel::ThumbnailRole);
164
165 return thumbData.value<QPixmap>();
166 }
167
thumbnailPixmap(const QModelIndex & index) const168 QPixmap ShowfotoDelegate::thumbnailPixmap(const QModelIndex& index) const
169 {
170 Q_D(const ShowfotoDelegate);
171
172 return retrieveThumbnailPixmap(index, d->thumbSize.size());
173 }
174
paint(QPainter * p,const QStyleOptionViewItem & option,const QModelIndex & index) const175 void ShowfotoDelegate::paint(QPainter* p, const QStyleOptionViewItem& option, const QModelIndex& index) const
176 {
177 Q_D(const ShowfotoDelegate);
178 ShowfotoItemInfo info = ShowfotoItemModel::retrieveShowfotoItemInfo(index);
179 KSharedConfig::Ptr config = KSharedConfig::openConfig();
180 KConfigGroup group = config->group("ImageViewer Settings");
181
182 if (info.isNull())
183 {
184 return;
185 }
186
187 // state of painter must not be changed
188
189 p->save();
190 p->translate(option.rect.topLeft());
191
192 bool isSelected = (option.state & QStyle::State_Selected);
193
194 // Thumbnail
195
196 QPixmap pix;
197
198 if (isSelected)
199 {
200 pix = d->selPixmap;
201 }
202 else
203 {
204 pix = d->regPixmap;
205 }
206
207 QRect actualPixmapRect = drawThumbnail(p, d->pixmapRect, pix, thumbnailPixmap(index));
208
209 if (!actualPixmapRect.isNull())
210 {
211 const_cast<ShowfotoDelegate*>(this)->updateActualPixmapRect(index, actualPixmapRect);
212 }
213
214 p->setPen(isSelected ? qApp->palette().color(QPalette::HighlightedText)
215 : qApp->palette().color(QPalette::Text));
216
217 if (!d->nameRect.isNull())
218 {
219 drawName(p, d->nameRect, info.name);
220 }
221
222 if (!d->dateRect.isNull())
223 {
224 drawCreationDate(p, d->dateRect, (info.ctime.isValid() ? info.ctime : info.dtime));
225 }
226
227 if (!d->sizeRect.isNull())
228 {
229 drawFileSize(p, d->sizeRect, info.size);
230 }
231
232 if (ShowfotoSettings::instance()->getShowFormatOverThumbnail())
233 {
234 QString frm = info.mime;
235 drawImageFormat(p, actualPixmapRect, frm);
236 }
237
238 if (ShowfotoSettings::instance()->getShowCoordinates() && info.photoInfo.hasCoordinates)
239 {
240 drawGeolocationIndicator(p, d->coordinatesRect);
241 }
242
243 if (d->drawFocusFrame)
244 {
245 drawFocusRect(p, option, isSelected);
246 }
247
248 if (d->drawMouseOverFrame)
249 {
250 drawMouseOverRect(p, option);
251 }
252
253 p->restore();
254
255 drawOverlays(p, option, index);
256 }
257
pixmapForDrag(const QStyleOptionViewItem & option,const QList<QModelIndex> & indexes) const258 QPixmap ShowfotoDelegate::pixmapForDrag(const QStyleOptionViewItem& option,
259 const QList<QModelIndex>& indexes) const
260 {
261 QPixmap icon;
262
263 if (!indexes.isEmpty())
264 {
265 icon = thumbnailPixmap(indexes.first());
266 }
267
268 return makeDragPixmap(option, indexes, icon);
269 }
270
acceptsToolTip(const QPoint & pos,const QRect & visualRect,const QModelIndex & index,QRect * toolTipRect) const271 bool ShowfotoDelegate::acceptsToolTip(const QPoint& pos, const QRect& visualRect, const QModelIndex& index,
272 QRect* toolTipRect) const
273 {
274 return onActualPixmapRect(pos, visualRect, index, toolTipRect);
275 }
276
acceptsActivation(const QPoint & pos,const QRect & visualRect,const QModelIndex & index,QRect * activationRect) const277 bool ShowfotoDelegate::acceptsActivation(const QPoint& pos, const QRect& visualRect, const QModelIndex& index,
278 QRect* activationRect) const
279 {
280 return onActualPixmapRect(pos, visualRect, index, activationRect);
281 }
282
onActualPixmapRect(const QPoint & pos,const QRect & visualRect,const QModelIndex & index,QRect * returnRect) const283 bool ShowfotoDelegate::onActualPixmapRect(const QPoint& pos, const QRect& visualRect, const QModelIndex& index,
284 QRect* returnRect) const
285 {
286 QRect actualRect = actualPixmapRect(index);
287
288 if (actualRect.isNull())
289 {
290 return false;
291 }
292
293 actualRect.translate(visualRect.topLeft());
294
295 if (returnRect)
296 {
297 *returnRect = actualRect;
298 }
299
300 return actualRect.contains(pos);
301 }
302
setDefaultViewOptions(const QStyleOptionViewItem & option)303 void ShowfotoDelegate::setDefaultViewOptions(const QStyleOptionViewItem& option)
304 {
305
306 ShowfotoItemViewDelegate::setDefaultViewOptions(option);
307 }
308
invalidatePaintingCache()309 void ShowfotoDelegate::invalidatePaintingCache()
310 {
311 ShowfotoItemViewDelegate::invalidatePaintingCache();
312 }
313
updateContentWidth()314 void ShowfotoDelegate::updateContentWidth()
315 {
316 Q_D(ShowfotoDelegate);
317 d->contentWidth = d->thumbSize.size() + 2*d->radius;
318 }
319
updateSizeRectsAndPixmaps()320 void ShowfotoDelegate::updateSizeRectsAndPixmaps()
321 {
322 Q_D(ShowfotoDelegate);
323
324 // ---- Reset rects and prepare fonts ----
325
326 d->clearRects();
327 prepareFonts();
328
329 // ---- Fixed sizes and metrics ----
330
331 updateContentWidth();
332 prepareMetrics(d->contentWidth);
333
334 // ---- Calculate rects ----
335
336 updateRects();
337
338 // ---- Cached pixmaps ----
339
340 prepareBackground();
341
342 // ---- Drawing related caches ----
343
344 clearCaches();
345 }
346
clearCaches()347 void ShowfotoDelegate::clearCaches()
348 {
349 Q_D(ShowfotoDelegate);
350 ShowfotoItemViewDelegate::clearCaches();
351 d->actualPixmapRectCache.clear();
352 }
353
clearModelDataCaches()354 void ShowfotoDelegate::clearModelDataCaches()
355 {
356 Q_D(ShowfotoDelegate);
357 d->actualPixmapRectCache.clear();
358 }
359
modelChanged()360 void ShowfotoDelegate::modelChanged()
361 {
362 Q_D(ShowfotoDelegate);
363 clearModelDataCaches();
364 setModel(d->currentView ? d->currentView->model() : nullptr);
365 }
366
modelContentsChanged()367 void ShowfotoDelegate::modelContentsChanged()
368 {
369 clearModelDataCaches();
370 }
371
actualPixmapRect(const QModelIndex & index) const372 QRect ShowfotoDelegate::actualPixmapRect(const QModelIndex& index) const
373 {
374 Q_D(const ShowfotoDelegate);
375
376 // We do not recompute if not found. Assumption is cache is always properly updated.
377
378 QRect* const rect = d->actualPixmapRectCache.object(index.row());
379
380 if (rect)
381 {
382 return *rect;
383 }
384 else
385 {
386 return d->pixmapRect;
387 }
388 }
389
updateActualPixmapRect(const QModelIndex & index,const QRect & rect)390 void ShowfotoDelegate::updateActualPixmapRect(const QModelIndex& index, const QRect& rect)
391 {
392 Q_D(ShowfotoDelegate);
393 QRect* const old = d->actualPixmapRectCache.object(index.row());
394
395 if (!old || (*old != rect))
396 {
397 d->actualPixmapRectCache.insert(index.row(), new QRect(rect));
398 }
399 }
400
calculatethumbSizeToFit(int ws)401 int ShowfotoDelegate::calculatethumbSizeToFit(int ws)
402 {
403 Q_D(ShowfotoDelegate);
404
405 int ts = thumbnailSize().size();
406 int gs = gridSize().width();
407 int sp = spacing();
408 ws = ws - 2*sp;
409
410 // Thumbnails size loop to check (upper/lower)
411
412 int ts1, ts2;
413
414 // New grid size used in loop
415
416 int ngs;
417
418 double rs1 = fmod((double)ws, (double)gs);
419
420 for (ts1 = ts ; ts1 < ThumbnailSize::Huge ; ++ts1)
421 {
422 ngs = ts1 + 2*(d->margin + d->radius) + sp;
423 double nrs = fmod((double)ws, (double)ngs);
424
425 if (nrs <= rs1)
426 {
427 rs1 = nrs;
428 }
429 else
430 {
431 break;
432 }
433 }
434
435 double rs2 = fmod((double)ws, (double)gs);
436
437 for (ts2 = ts ; ts2 > ThumbnailSize::Small ; --ts2)
438 {
439 ngs = ts2 + 2*(d->margin + d->radius) + sp;
440 double nrs = fmod((double)ws, (double)ngs);
441
442 if (nrs >= rs2)
443 {
444 rs2 = nrs;
445 }
446 else
447 {
448 rs2 = nrs;
449 break;
450 }
451 }
452
453 if (rs1 > rs2)
454 {
455 return (ts2);
456 }
457
458 return (ts1);
459 }
460
461 // --- ShowfotoThumbnailDelegate ---------------------------------------
462
init(ShowfotoThumbnailDelegate * const q)463 void ShowfotoThumbnailDelegatePrivate::init(ShowfotoThumbnailDelegate* const q)
464 {
465 Q_UNUSED(q);
466 }
467
468 // ------------------------------------------------------------------------------------------------
469
ShowfotoThumbnailDelegate(ShowfotoThumbnailBar * const parent)470 ShowfotoThumbnailDelegate::ShowfotoThumbnailDelegate(ShowfotoThumbnailBar* const parent)
471 : ShowfotoDelegate(*new ShowfotoThumbnailDelegatePrivate, parent)
472 {
473 Q_D(ShowfotoThumbnailDelegate);
474 d->init(this);
475 }
476
~ShowfotoThumbnailDelegate()477 ShowfotoThumbnailDelegate::~ShowfotoThumbnailDelegate()
478 {
479 }
480
setFlow(QListView::Flow flow)481 void ShowfotoThumbnailDelegate::setFlow(QListView::Flow flow)
482 {
483 Q_D(ShowfotoThumbnailDelegate);
484 d->flow = flow;
485 }
486
setDefaultViewOptions(const QStyleOptionViewItem & option)487 void ShowfotoThumbnailDelegate::setDefaultViewOptions(const QStyleOptionViewItem& option)
488 {
489 Q_D(ShowfotoThumbnailDelegate);
490
491 // store before calling parent class
492
493 d->viewSize = option.rect;
494 ShowfotoDelegate::setDefaultViewOptions(option);
495 }
496
maximumSize() const497 int ShowfotoThumbnailDelegate::maximumSize() const
498 {
499 Q_D(const ShowfotoThumbnailDelegate);
500
501 return (ThumbnailSize::Huge + (2*d->radius + 2*d->margin));
502 }
503
minimumSize() const504 int ShowfotoThumbnailDelegate::minimumSize() const
505 {
506 Q_D(const ShowfotoThumbnailDelegate);
507
508 return (ThumbnailSize::Small + 2*d->radius + 2*d->margin);
509 }
510
acceptsActivation(const QPoint & pos,const QRect & visualRect,const QModelIndex & index,QRect * activationRect) const511 bool ShowfotoThumbnailDelegate::acceptsActivation(const QPoint& pos, const QRect& visualRect,
512 const QModelIndex& index, QRect* activationRect) const
513 {
514 // reuse implementation from grandparent
515
516 return ShowfotoItemViewDelegate::acceptsActivation(pos, visualRect, index, activationRect); // clazy:exclude=skipped-base-method
517 }
518
updateContentWidth()519 void ShowfotoThumbnailDelegate::updateContentWidth()
520 {
521 Q_D(ShowfotoThumbnailDelegate);
522
523 int maxSize;
524
525 if (d->flow == QListView::LeftToRight)
526 {
527 maxSize = d->viewSize.height();
528 }
529 else
530 {
531 maxSize = d->viewSize.width();
532 }
533
534 d->thumbSize = ThumbnailSize(thumbnailPixmapSize(true, maxSize - 2*d->radius - 2*d->margin));
535
536 ShowfotoDelegate::updateContentWidth();
537 }
538
thumbnailPixmapSize(bool withHighlight,int size)539 int ShowfotoThumbnailDelegate::thumbnailPixmapSize(bool withHighlight, int size)
540 {
541 if (withHighlight && (size >= 10))
542 {
543 return (size + 2);
544 }
545
546 return size;
547 }
548
updateRects()549 void ShowfotoThumbnailDelegate::updateRects()
550 {
551 Q_D(ShowfotoThumbnailDelegate);
552
553 d->pixmapRect = QRect(d->margin, d->margin, d->contentWidth, d->contentWidth);
554 d->rect = QRect(0, 0, d->contentWidth + 2*d->margin, d->contentWidth + 2*d->margin);
555 d->coordinatesRect = QRect(d->contentWidth - 16+2, d->pixmapRect.top(), 16, 16);
556
557 if (d->flow == QListView::LeftToRight)
558 {
559 d->gridSize = QSize(d->rect.width() + d->spacing, d->rect.height());
560 }
561 else
562 {
563 d->gridSize = QSize(d->rect.width(), d->rect.height() + d->spacing);
564 }
565 }
566
567 // --- ShowfotoNormalDelegate -----------------------------------------------------------------------
568
init(ShowfotoNormalDelegate * const q,ShowfotoThumbnailBar * const parent)569 void ShowfotoNormalDelegatePrivate::init(ShowfotoNormalDelegate* const q, ShowfotoThumbnailBar* const parent)
570 {
571 /*
572 categoryDrawer = new ShowfotoCategoryDrawer(parent);
573 */
574 Q_UNUSED(parent);
575 Q_UNUSED(q);
576 }
577
578 // ------------------------------------------------------------------------------------------------
579
ShowfotoNormalDelegate(ShowfotoThumbnailBar * const bar,QObject * const)580 ShowfotoNormalDelegate::ShowfotoNormalDelegate(ShowfotoThumbnailBar* const bar,
581 QObject* const)
582 : ShowfotoDelegate(*new ShowfotoNormalDelegatePrivate, bar)
583 {
584 Q_D(ShowfotoNormalDelegate);
585
586 d->init(this, bar);
587 }
588
ShowfotoNormalDelegate(ShowfotoNormalDelegatePrivate & dd,ShowfotoThumbnailBar * const bar,QObject * const)589 ShowfotoNormalDelegate::ShowfotoNormalDelegate(ShowfotoNormalDelegatePrivate& dd,
590 ShowfotoThumbnailBar* const bar,
591 QObject* const)
592 : ShowfotoDelegate(dd, bar)
593 {
594 Q_D(ShowfotoNormalDelegate);
595
596 d->init(this, bar);
597 }
598
~ShowfotoNormalDelegate()599 ShowfotoNormalDelegate::~ShowfotoNormalDelegate()
600 {
601 }
602
updateRects()603 void ShowfotoNormalDelegate::updateRects()
604 {
605 Q_D(ShowfotoNormalDelegate);
606
607 int y = d->margin;
608 d->pixmapRect = QRect(d->margin, y, d->contentWidth, d->contentWidth);
609 y = d->pixmapRect.bottom();
610 d->imageInformationRect = QRect(d->margin, y, d->contentWidth, 0);
611
612 d->imageInformationRect.setBottom(y);
613
614 d->rect = QRect(0, 0, d->contentWidth + 2*d->margin, y+d->margin+d->radius);
615 d->gridSize = QSize(d->rect.width() + d->spacing, d->rect.height() + d->spacing);
616 }
617
618 } // namespace ShowFoto
619