1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2009-04-20
7 * Description : Qt model-view for items - category drawer
8 *
9 * Copyright (C) 2009-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
10 * Copyright (C) 2011 by Andi Clemens <andi dot clemens 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 "itemcategorydrawer.h"
26
27 // Qt includes
28
29 #include <QPainter>
30 #include <QApplication>
31 #include <QLocale>
32
33 // KDE includes
34
35 #include <klocalizedstring.h>
36
37 // Local includes
38
39 #include "album.h"
40 #include "albummanager.h"
41 #include "itemalbummodel.h"
42 #include "itemcategorizedview.h"
43 #include "itemdelegate.h"
44 #include "itemfiltermodel.h"
45 #include "itemmodel.h"
46 #include "itemscanner.h"
47 #include "searchfolderview.h"
48 #include "facetagsiface.h"
49
50 namespace Digikam
51 {
52
53 class Q_DECL_HIDDEN ItemCategoryDrawer::Private
54 {
55 public:
56
Private()57 explicit Private()
58 : lowerSpacing(0),
59 view (nullptr)
60 {
61 }
62
63 QFont font;
64 QRect rect;
65 QPixmap pixmap;
66 int lowerSpacing;
67 ItemCategorizedView* view;
68 };
69
ItemCategoryDrawer(ItemCategorizedView * const parent)70 ItemCategoryDrawer::ItemCategoryDrawer(ItemCategorizedView* const parent)
71 : DCategoryDrawer(nullptr),
72 d (new Private)
73 {
74 d->view = parent;
75 }
76
~ItemCategoryDrawer()77 ItemCategoryDrawer::~ItemCategoryDrawer()
78 {
79 delete d;
80 }
81
categoryHeight(const QModelIndex &,const QStyleOption &) const82 int ItemCategoryDrawer::categoryHeight(const QModelIndex& /*index*/, const QStyleOption& /*option*/) const
83 {
84 return (d->rect.height() + d->lowerSpacing);
85 }
86
maximumHeight() const87 int ItemCategoryDrawer::maximumHeight() const
88 {
89 return (d->rect.height() + d->lowerSpacing);
90 }
91
setLowerSpacing(int spacing)92 void ItemCategoryDrawer::setLowerSpacing(int spacing)
93 {
94 d->lowerSpacing = spacing;
95 }
96
setDefaultViewOptions(const QStyleOptionViewItem & option)97 void ItemCategoryDrawer::setDefaultViewOptions(const QStyleOptionViewItem& option)
98 {
99 d->font = option.font;
100
101 if (option.rect.width() != d->rect.width())
102 {
103 updateRectsAndPixmaps(option.rect.width());
104 }
105 }
106
invalidatePaintingCache()107 void ItemCategoryDrawer::invalidatePaintingCache()
108 {
109 if (d->rect.isNull())
110 {
111 return;
112 }
113
114 updateRectsAndPixmaps(d->rect.width());
115 }
116
drawCategory(const QModelIndex & index,int,const QStyleOption & option,QPainter * p) const117 void ItemCategoryDrawer::drawCategory(const QModelIndex& index, int /*sortRole*/,
118 const QStyleOption& option, QPainter* p) const
119 {
120 if (option.rect.width() != d->rect.width())
121 {
122 const_cast<ItemCategoryDrawer*>(this)->updateRectsAndPixmaps(option.rect.width());
123 }
124
125 p->save();
126
127 p->translate(option.rect.topLeft());
128
129 ItemSortSettings::CategorizationMode mode = (ItemSortSettings::CategorizationMode)index.data(ItemFilterModel::CategorizationModeRole).toInt();
130
131 p->drawPixmap(0, 0, d->pixmap);
132
133 QFont fontBold(d->font);
134 QFont fontNormal(d->font);
135 fontBold.setBold(true);
136 int fnSize = fontBold.pointSize();
137
138 if (fnSize > 0)
139 {
140 fontBold.setPointSize(fnSize+2);
141 }
142 else
143 {
144 fnSize = fontBold.pixelSize();
145 fontBold.setPixelSize(fnSize+2);
146 }
147
148 QString header;
149 QString subLine;
150
151 switch (mode)
152 {
153 case ItemSortSettings::NoCategories:
154 break;
155
156 case ItemSortSettings::OneCategory:
157 viewHeaderText(index, &header, &subLine);
158 break;
159
160 case ItemSortSettings::CategoryByAlbum:
161 textForAlbum(index, &header, &subLine);
162 break;
163
164 case ItemSortSettings::CategoryByFormat:
165 textForFormat(index, &header, &subLine);
166 break;
167
168 case ItemSortSettings::CategoryByMonth:
169 textForMonth(index, &header, &subLine);
170 break;
171
172 case ItemSortSettings::CategoryByFaces:
173 textForFace(index, &header, &subLine);
174 break;
175 }
176
177 p->setPen(qApp->palette().color(QPalette::HighlightedText));
178 p->setFont(fontBold);
179
180 QRect tr;
181 p->drawText(5, 5, d->rect.width(), d->rect.height(),
182 Qt::AlignLeft | Qt::AlignTop,
183 p->fontMetrics().elidedText(header, Qt::ElideRight, d->rect.width() - 10), &tr);
184
185 int y = tr.height() + 2;
186
187 p->setFont(fontNormal);
188
189 p->drawText(5, y, d->rect.width(), d->rect.height() - y,
190 Qt::AlignLeft | Qt::AlignVCenter,
191 p->fontMetrics().elidedText(subLine, Qt::ElideRight, d->rect.width() - 10));
192
193 p->restore();
194 }
195
viewHeaderText(const QModelIndex & index,QString * header,QString * subLine) const196 void ItemCategoryDrawer::viewHeaderText(const QModelIndex& index, QString* header, QString* subLine) const
197 {
198 ItemModel* const sourceModel = index.data(ItemModel::ItemModelPointerRole).value<ItemModel*>();
199
200 if (!sourceModel)
201 {
202 return;
203 }
204
205 int count = d->view->categoryRange(index).height();
206
207 // Add here further model subclasses in use with ItemCategoryDrawer.
208 // Note you need a Q_OBJECT in the class's header for this to work.
209
210 ItemAlbumModel* const albumModel = qobject_cast<ItemAlbumModel*>(sourceModel);
211
212 if (albumModel)
213 {
214 QList<Album*> albums = albumModel->currentAlbums();
215 Album* album = nullptr;
216
217 if (albums.isEmpty())
218 {
219 return;
220 }
221
222 album = albums.first();
223
224 if (!album)
225 {
226 return;
227 }
228
229 switch (album->type())
230 {
231 case Album::PHYSICAL:
232 textForPAlbum(static_cast<PAlbum*>(album), albumModel->isRecursingAlbums(), count, header, subLine);
233 break;
234
235 case Album::TAG:
236 textForTAlbum(static_cast<TAlbum*>(album), albumModel->isRecursingTags(), count, header, subLine);
237 break;
238
239 case Album::DATE:
240 textForDAlbum(static_cast<DAlbum*>(album), count, header, subLine);
241 break;
242
243 case Album::SEARCH:
244 textForSAlbum(static_cast<SAlbum*>(album), count, header, subLine);
245 break;
246
247 case Album::FACE:
248 default:
249 break;
250 }
251 }
252 }
253
textForAlbum(const QModelIndex & index,QString * header,QString * subLine) const254 void ItemCategoryDrawer::textForAlbum(const QModelIndex& index, QString* header, QString* subLine) const
255 {
256 int albumId = index.data(ItemFilterModel::CategoryAlbumIdRole).toInt();
257 PAlbum* const album = AlbumManager::instance()->findPAlbum(albumId);
258 int count = d->view->categoryRange(index).height();
259 textForPAlbum(album, false, count, header, subLine);
260 }
261
textForFormat(const QModelIndex & index,QString * header,QString * subLine) const262 void ItemCategoryDrawer::textForFormat(const QModelIndex& index, QString* header, QString* subLine) const
263 {
264 QString format = index.data(ItemFilterModel::CategoryFormatRole).toString();
265 format = ItemScanner::formatToString(format);
266 *header = format;
267 int count = d->view->categoryRange(index).height();
268 *subLine = i18np("1 Item", "%1 Items", count);
269 }
270
textForMonth(const QModelIndex & index,QString * header,QString * subLine) const271 void ItemCategoryDrawer::textForMonth(const QModelIndex& index, QString* header, QString* subLine) const
272 {
273 QDate date = index.data(ItemFilterModel::CategoryDateRole).toDate();
274 *header = date.toString(QLatin1String("MMM yyyy"));
275 int count = d->view->categoryRange(index).height();
276 *subLine = i18np("1 Item", "%1 Items", count);
277 }
278
textForFace(const QModelIndex & index,QString * header,QString * subLine) const279 void ItemCategoryDrawer::textForFace(const QModelIndex& index, QString* header, QString* subLine) const
280 {
281 *header = index.data(ItemFilterModel::CategoryFaceRole).toString();
282 int count = d->view->categoryRange(index).height();
283 *subLine = i18np("1 Item", "%1 Items", count);
284 }
285
textForPAlbum(PAlbum * album,bool recursive,int count,QString * header,QString * subLine) const286 void ItemCategoryDrawer::textForPAlbum(PAlbum* album, bool recursive, int count, QString* header, QString* subLine) const
287 {
288 Q_UNUSED(recursive);
289
290 if (!album)
291 {
292 return;
293 }
294
295 QDate date = album->date();
296
297 QLocale tmpLocale;
298
299 // day of month with two digits
300
301 QString day = tmpLocale.toString(date, QLatin1String("dd"));
302
303 // short form of the month
304
305 QString month = tmpLocale.toString(date, QLatin1String("MMM"));
306
307 // long form of the year
308
309 QString year = tmpLocale.toString(date, QLatin1String("yyyy"));
310
311 *subLine = i18ncp("%1: day of month with two digits, %2: short month name, %3: year",
312 "Album Date: %2 %3 %4 - 1 Item", "Album Date: %2 %3 %4 - %1 Items",
313 count, day, month, year);
314
315 if (!album->caption().isEmpty())
316 {
317 QString caption = album->caption();
318 *subLine += QLatin1String(" - ") + caption.replace(QLatin1Char('\n'), QLatin1Char(' '));
319 }
320
321 *header = album->prettyUrl();
322 }
323
textForTAlbum(TAlbum * talbum,bool recursive,int count,QString * header,QString * subLine) const324 void ItemCategoryDrawer::textForTAlbum(TAlbum* talbum, bool recursive, int count, QString* header,
325 QString* subLine) const
326 {
327 *header = talbum->title();
328
329 if (recursive && talbum->firstChild())
330 {
331 int n=0;
332
333 for (AlbumIterator it(talbum) ; it.current() ; ++it)
334 {
335 n++;
336 }
337
338 QString firstPart = i18ncp("%2: a tag title; %3: number of subtags",
339 "%2 including 1 subtag", "%2 including %1 subtags",
340 n, talbum->tagPath(false));
341
342 *subLine = i18ncp("%2: the previous string (e.g. 'Foo including 7 subtags'); %1: number of items in tag",
343 "%2 - 1 Item", "%2 - %1 Items",
344 count, firstPart);
345 }
346 else
347 {
348 *subLine = i18np("%2 - 1 Item", "%2 - %1 Items", count, talbum->tagPath(false));
349 }
350 }
351
textForSAlbum(SAlbum * salbum,int count,QString * header,QString * subLine) const352 void ItemCategoryDrawer::textForSAlbum(SAlbum* salbum, int count, QString* header, QString* subLine) const
353 {
354 QString title = salbum->displayTitle();
355
356 *header = title;
357
358 if (salbum->isNormalSearch())
359 {
360 *subLine = i18np("Keyword Search - 1 Item", "Keyword Search - %1 Items", count);
361 }
362 else if (salbum->isAdvancedSearch())
363 {
364 *subLine = i18np("Advanced Search - 1 Item", "Advanced Search - %1 Items", count);
365 }
366 else
367 {
368 *subLine = i18np("1 Item", "%1 Items", count);
369 }
370 }
371
textForDAlbum(DAlbum * album,int count,QString * header,QString * subLine) const372 void ItemCategoryDrawer::textForDAlbum(DAlbum* album, int count, QString* header, QString* subLine) const
373 {
374 QString year = QLocale().toString(album->date(), QLatin1String("yyyy"));
375
376 if (album->range() == DAlbum::Month)
377 {
378 *header = i18nc("Month String - Year String", "%1 %2",
379 QLocale().standaloneMonthName(album->date().month(), QLocale::LongFormat), year);
380 }
381 else
382 {
383 *header = year;
384 }
385
386 *subLine = i18np("1 Item", "%1 Items", count);
387 }
388
updateRectsAndPixmaps(int width)389 void ItemCategoryDrawer::updateRectsAndPixmaps(int width)
390 {
391 d->rect = QRect(0, 0, 0, 0);
392
393 // Title --------------------------------------------------------
394
395 QFont fn(d->font);
396 int fnSize = fn.pointSize();
397 bool usePointSize;
398
399 if (fnSize > 0)
400 {
401 fn.setPointSize(fnSize+2);
402 usePointSize = true;
403 }
404 else
405 {
406 fnSize = fn.pixelSize();
407 fn.setPixelSize(fnSize+2);
408 usePointSize = false;
409 }
410
411 fn.setBold(true);
412 QFontMetrics fm(fn);
413 QRect tr = fm.boundingRect(0, 0, width,
414 0xFFFFFFFF, Qt::AlignLeft | Qt::AlignVCenter,
415 QLatin1String("XXX"));
416 d->rect.setHeight(tr.height());
417
418 if (usePointSize)
419 {
420 fn.setPointSize(d->font.pointSize());
421 }
422 else
423 {
424 fn.setPixelSize(d->font.pixelSize());
425 }
426
427 fn.setBold(false);
428 fm = QFontMetrics(fn);
429 tr = fm.boundingRect(0, 0, width,
430 0xFFFFFFFF, Qt::AlignLeft | Qt::AlignVCenter,
431 QLatin1String("XXX"));
432
433 d->rect.setHeight(d->rect.height() + tr.height() + 10);
434 d->rect.setWidth(width);
435
436 d->pixmap = QPixmap(d->rect.width(), d->rect.height());
437 d->pixmap.fill(qApp->palette().color(QPalette::Highlight));
438 }
439
440 } // namespace Digikam
441