1
/* ============================================================
3
* This file is a part of digiKam project
4
* http://www.digikam.org
7
* Description : Qt categorized item view for camera items
9
* Copyright (C) 2012 by Islam Wazery <wazery at ubuntu dot com>
11
* This program is free software; you can redistribute it
12
* and/or modify it under the terms of the GNU General
13
* Public License as published by the Free Software Foundation;
14
* either version 2, or (at your option)
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.
22
* ============================================================ */
24
#include "importcategorizedview.moc"
36
#include "loadingcacheinterface.h"
37
#include "imageselectionoverlay.h"
38
#include "camitemsortsettings.h"
39
#include "itemviewtooltip.h"
40
#include "importdelegate.h"
41
#include "importtooltipfiller.h"
42
#include "thumbnailloadthread.h"
43
#include "importsettings.h"
48
class ImportItemViewToolTip : public ItemViewToolTip
52
explicit ImportItemViewToolTip(ImportCategorizedView* const view)
53
: ItemViewToolTip(view)
57
ImportCategorizedView* view() const
59
return static_cast<ImportCategorizedView*>(ItemViewToolTip::view());
64
virtual QString tipContents()
66
CamItemInfo info = ImportImageModel::retrieveCamItemInfo(currentIndex());
67
return ImportToolTipFiller::CamItemInfoTipContents(info);
71
class ImportCategorizedView::Private
86
ImportImageModel* model;
87
ImportSortFilterModel* filterModel;
89
ImportDelegate* delegate;
92
qlonglong scrollToItemId;
94
QTimer* delayedEnterTimer;
96
QMouseEvent* currentMouseEvent;
99
ImportCategorizedView::ImportCategorizedView(QWidget* const parent)
100
: DCategorizedView(parent), d(new Private)
102
setToolTip(new ImportItemViewToolTip(this));
104
LoadingCacheInterface::connectToSignalFileChanged(this,
105
SLOT(slotFileChanged(QString)));
107
d->delayedEnterTimer = new QTimer(this);
108
d->delayedEnterTimer->setInterval(10);
109
d->delayedEnterTimer->setSingleShot(true);
111
connect(d->delayedEnterTimer, SIGNAL(timeout()),
112
this, SLOT(slotDelayedEnter()));
115
ImportCategorizedView::~ImportCategorizedView()
117
d->delegate->removeAllOverlays();
121
//FIXME: Needs testing
122
void ImportCategorizedView::installDefaultModels(CameraController* const controller)
124
ImportModel* model = new ImportModel(this);
125
model->setupCameraController(controller);
126
ImportFilterModel* filterModel = new ImportFilterModel(this);
128
filterModel->setSourceImportModel(model);
130
// ImportSettings* settings = ImportSettings::instance();
132
filterModel->setSortRole(CamItemSortSettings::SortByFileName);
133
filterModel->setSortOrder(CamItemSortSettings::AscendingOrder);
134
filterModel->setCategorizationMode(CamItemSortSettings::CategoryByFormat);
135
filterModel->sort(0); // an initial sorting is necessary
137
setModels(model, filterModel);
140
void ImportCategorizedView::setModels(ImportImageModel* model, ImportSortFilterModel* filterModel)
144
d->delegate->setAllOverlaysActive(false);
149
disconnect(d->filterModel, SIGNAL(layoutAboutToBeChanged()),
150
this, SLOT(layoutAboutToBeChanged()));
152
disconnect(d->filterModel, SIGNAL(layoutChanged()),
153
this, SLOT(layoutWasChanged()));
158
disconnect(d->model, SIGNAL(itemInfosAdded(QList<CamItemInfo>)),
159
this, SLOT(slotCamItemInfosAdded()));
163
d->filterModel = filterModel;
165
setModel(d->filterModel);
167
connect(d->filterModel, SIGNAL(layoutAboutToBeChanged()),
168
this, SLOT(layoutAboutToBeChanged()));
170
connect(d->filterModel, SIGNAL(layoutChanged()),
171
this, SLOT(layoutWasChanged()),
172
Qt::QueuedConnection);
174
connect(d->model, SIGNAL(itemInfosAdded(QList<CamItemInfo>)),
175
this, SLOT(slotCamItemInfosAdded()));
181
d->delegate->setAllOverlaysActive(true);
185
ImportImageModel* ImportCategorizedView::importImageModel() const
190
ImportSortFilterModel* ImportCategorizedView::importSortFilterModel() const
192
return d->filterModel;
195
ImportFilterModel* ImportCategorizedView::importFilterModel() const
197
return d->filterModel->importFilterModel();
200
ImportThumbnailModel* ImportCategorizedView::importThumbnailModel() const
202
return qobject_cast<ImportThumbnailModel*>(d->model);
205
QSortFilterProxyModel* ImportCategorizedView::filterModel() const
207
return d->filterModel;
210
ImportDelegate* ImportCategorizedView::delegate() const
215
void ImportCategorizedView::setItemDelegate(ImportDelegate* delegate)
217
ThumbnailSize oldSize = thumbnailSize();
218
ImportDelegate* oldDelegate = d->delegate;
222
hideIndexNotification();
223
d->delegate->setAllOverlaysActive(false);
224
d->delegate->setViewOnAllOverlays(0);
226
// Note: Be precise, no wildcard disconnect!
227
disconnect(d->delegate, SIGNAL(requestNotification(QModelIndex,QString)),
228
this, SLOT(showIndexNotification(QModelIndex,QString)));
229
disconnect(d->delegate, SIGNAL(hideNotification()),
230
this, SLOT(hideIndexNotification()));
233
d->delegate = delegate;
234
delegate->setThumbnailSize(oldSize);
238
d->delegate->setSpacing(oldDelegate->spacing());
241
DCategorizedView::setItemDelegate(d->delegate);
242
setCategoryDrawer(d->delegate->categoryDrawer());
243
updateDelegateSizes();
245
d->delegate->setViewOnAllOverlays(this);
246
d->delegate->setAllOverlaysActive(true);
248
connect(d->delegate, SIGNAL(requestNotification(QModelIndex,QString)),
249
this, SLOT(showIndexNotification(QModelIndex,QString)));
251
connect(d->delegate, SIGNAL(hideNotification()),
252
this, SLOT(hideIndexNotification()));
255
CamItemInfo ImportCategorizedView::currentInfo() const
257
return d->filterModel->camItemInfo(currentIndex());
260
KUrl ImportCategorizedView::currentUrl() const
262
return currentInfo().url();
265
QList<CamItemInfo> ImportCategorizedView::selectedCamItemInfos() const
267
return d->filterModel->camItemInfos(selectedIndexes());
270
QList<CamItemInfo> ImportCategorizedView::selectedCamItemInfosCurrentFirst() const
272
QList<QModelIndex> indexes = selectedIndexes();
273
QModelIndex current = currentIndex();
274
QList<CamItemInfo> infos;
276
foreach(const QModelIndex& index, indexes)
278
CamItemInfo info = d->filterModel->camItemInfo(index);
280
if (index == current)
293
QList<CamItemInfo> ImportCategorizedView::camItemInfos() const
295
return d->filterModel->camItemInfosSorted();
298
KUrl::List ImportCategorizedView::urls() const
300
QList<CamItemInfo> infos = camItemInfos();
303
foreach(const CamItemInfo& info, infos)
311
KUrl::List ImportCategorizedView::selectedUrls() const
313
QList<CamItemInfo> infos = selectedCamItemInfos();
316
foreach(const CamItemInfo& info, infos)
324
void ImportCategorizedView::toIndex(const KUrl& url)
326
DCategorizedView::toIndex(d->filterModel->indexForPath(url.toLocalFile()));
329
CamItemInfo ImportCategorizedView::nextInOrder(const CamItemInfo& startingPoint, int nth)
331
QModelIndex index = d->filterModel->indexForCamItemInfo(startingPoint);
333
if (!index.isValid())
335
return CamItemInfo();
338
return d->filterModel->camItemInfo(d->filterModel->index(index.row() + nth, 0, QModelIndex()));
341
QModelIndex ImportCategorizedView::nextIndexHint(const QModelIndex& anchor, const QItemSelectionRange& removed) const
343
QModelIndex hint = DCategorizedView::nextIndexHint(anchor, removed);
344
CamItemInfo info = d->filterModel->camItemInfo(anchor);
346
//kDebug() << "Having initial hint" << hint << "for" << anchor << d->model->numberOfIndexesForCamItemInfo(info);
348
// Fixes a special case of multiple (face) entries for the same image.
349
// If one is removed, any entry of the same image shall be preferred.
350
if (d->model->numberOfIndexesForCamItemInfo(info) > 1)
352
// The hint is for a different info, but we may have a hint for the same info
353
if (info != d->filterModel->camItemInfo(hint))
355
int minDiff = d->filterModel->rowCount();
356
QList<QModelIndex> indexesForCamItemInfo = d->filterModel->mapListFromSource(d->model->indexesForCamItemInfo(info));
358
foreach(const QModelIndex& index, indexesForCamItemInfo)
360
if (index == anchor || !index.isValid() || removed.contains(index))
365
int distance = qAbs(index.row() - anchor.row());
367
if (distance < minDiff)
371
//kDebug() << "Chose index" << hint << "at distance" << minDiff << "to" << anchor;
380
ThumbnailSize ImportCategorizedView::thumbnailSize() const
383
ImportThumbnailModel *thumbModel = importThumbnailModel();
385
return thumbModel->thumbnailSize();
389
return d->delegate->thumbnailSize();
392
return ThumbnailSize();
395
void ImportCategorizedView::setThumbnailSize(int size)
397
setThumbnailSize(ThumbnailSize(size));
400
void ImportCategorizedView::setThumbnailSize(const ThumbnailSize& s)
402
// we abuse this pair of method calls to restore scroll position
403
layoutAboutToBeChanged();
404
d->delegate->setThumbnailSize(s);
408
void ImportCategorizedView::setCurrentWhenAvailable(qlonglong camItemId)
410
d->scrollToItemId = camItemId;
413
void ImportCategorizedView::setCurrentUrl(const KUrl& url)
418
setCurrentIndex(QModelIndex());
422
QString path = url.toLocalFile();
423
QModelIndex index = d->filterModel->indexForPath(path);
425
if (!index.isValid())
431
setCurrentIndex(index);
434
void ImportCategorizedView::setCurrentInfo(const CamItemInfo& info)
436
QModelIndex index = d->filterModel->indexForCamItemInfo(info);
438
setCurrentIndex(index);
441
void ImportCategorizedView::setSelectedUrls(const KUrl::List& urlList)
443
QItemSelection mySelection;
445
for (KUrl::List::const_iterator it = urlList.constBegin(); it!=urlList.constEnd(); ++it)
447
const QString path = it->path();
448
const QModelIndex index = d->filterModel->indexForPath(path);
450
if (!index.isValid())
452
kWarning() << "no QModelIndex found for" << *it;
456
// TODO: is there a better way?
457
mySelection.select(index, index);
462
selectionModel()->select(mySelection, QItemSelectionModel::Select);
465
void ImportCategorizedView::setSelectedCamItemInfos(const QList<CamItemInfo>& infos)
467
QItemSelection mySelection;
469
foreach(const CamItemInfo& info, infos)
471
QModelIndex index = d->filterModel->indexForCamItemInfo(info);
472
mySelection.select(index, index);
475
selectionModel()->select(mySelection, QItemSelectionModel::ClearAndSelect);
478
void ImportCategorizedView::hintAt(const CamItemInfo& info)
485
QModelIndex index = d->filterModel->indexForCamItemInfo(info);
487
if (!index.isValid())
492
selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
496
void ImportCategorizedView::addOverlay(ImageDelegateOverlay* overlay, ImportDelegate* delegate)
500
delegate = d->delegate;
503
delegate->installOverlay(overlay);
505
if (delegate == d->delegate)
507
overlay->setView(this);
508
overlay->setActive(true);
512
void ImportCategorizedView::removeOverlay(ImageDelegateOverlay* overlay)
514
ImportDelegate* delegate = dynamic_cast<ImportDelegate*>(overlay->delegate());
518
delegate->removeOverlay(overlay);
524
void ImportCategorizedView::updateGeometries()
526
DCategorizedView::updateGeometries();
527
d->delayedEnterTimer->start();
530
void ImportCategorizedView::slotDelayedEnter()
532
// re-emit entered() for index under mouse (after layout).
533
QModelIndex mouseIndex = indexAt(mapFromGlobal(QCursor::pos()));
535
if (mouseIndex.isValid())
537
emit DigikamKCategorizedView::entered(mouseIndex);
541
void ImportCategorizedView::addSelectionOverlay(ImportDelegate* delegate)
543
addOverlay(new ImageSelectionOverlay(this), delegate);
546
void ImportCategorizedView::scrollToStoredItem()
548
if (d->scrollToItemId)
550
if (d->model->hasImage(d->scrollToItemId))
552
QModelIndex index = d->filterModel->indexForCamItemId(d->scrollToItemId);
553
setCurrentIndex(index);
554
scrollToRelaxed(index, QAbstractItemView::PositionAtCenter);
555
d->scrollToItemId = 0;
560
void ImportCategorizedView::slotCamItemInfosAdded()
562
if (d->scrollToItemId)
564
scrollToStoredItem();
568
void ImportCategorizedView::slotFileChanged(const QString& filePath)
570
QModelIndex index = d->filterModel->indexForPath(filePath);
578
void ImportCategorizedView::indexActivated(const QModelIndex& index)
580
CamItemInfo info = d->filterModel->camItemInfo(index);
585
emit camItemInfoActivated(info);
589
void ImportCategorizedView::currentChanged(const QModelIndex& index, const QModelIndex& previous)
591
DCategorizedView::currentChanged(index, previous);
593
emit currentChanged(d->filterModel->camItemInfo(index));
596
void ImportCategorizedView::selectionChanged(const QItemSelection& selectedItems, const QItemSelection& deselectedItems)
598
DCategorizedView::selectionChanged(selectedItems, deselectedItems);
600
if (!selectedItems.isEmpty())
602
emit selected(d->filterModel->camItemInfos(selectedItems.indexes()));
605
if (!deselectedItems.isEmpty())
607
emit deselected(d->filterModel->camItemInfos(deselectedItems.indexes()));
611
void ImportCategorizedView::activated(const CamItemInfo&)
613
// implemented in subclass
616
void ImportCategorizedView::showContextMenuOnIndex(QContextMenuEvent* event, const QModelIndex& index)
618
CamItemInfo info = d->filterModel->camItemInfo(index);
619
showContextMenuOnInfo(event, info);
622
void ImportCategorizedView::showContextMenuOnInfo(QContextMenuEvent*, const CamItemInfo&)
624
// implemented in subclass
627
void ImportCategorizedView::paintEvent(QPaintEvent* e)
629
DCategorizedView::paintEvent(e);
632
QItemSelectionModel* ImportCategorizedView::getSelectionModel() const
634
return selectionModel();
637
AbstractItemDragDropHandler* ImportCategorizedView::dragDropHandler() const
639
return d->model->dragDropHandler();
642
} // namespace Digikam