1/*
2 Copyright (C) 2010 Klarälvdalens Datakonsult AB,
3 a KDAB Group company, info@kdab.net,
4 author Stephen Kelly <stephen@kdab.com>
5
6 This library is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Library General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or (at your
9 option) any later version.
10
11 This library is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
14 License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301, USA.
20*/
21
22#include "progressspinnerdelegate_p.h"
23
24#include "entitytreemodel.h"
25
26#include <QTimerEvent>
27#include <QAbstractItemView>
28
29using namespace Akonadi;
30
31DelegateAnimator::DelegateAnimator(QAbstractItemView *view)
32 : QObject(view)
33 , m_view(view)
34 , m_timerId(-1)
35{
36 m_pixmapSequence = KPixmapSequence(QLatin1String("process-working"), 22);
37}
38
39void DelegateAnimator::push(const QModelIndex &index)
40{
41 if (m_animations.isEmpty()) {
42 m_timerId = startTimer(200);
43 }
44 m_animations.insert(Animation(index));
45}
46
47void DelegateAnimator::pop(const QModelIndex &index)
48{
49 if (m_animations.remove(Animation(index))) {
50 if (m_animations.isEmpty() && m_timerId != -1) {
51 killTimer(m_timerId);
52 m_timerId = -1;
53 }
54 }
55}
56
57void DelegateAnimator::timerEvent(QTimerEvent *event)
58{
59 if (!(event->timerId() == m_timerId && m_view)) {
60 return QObject::timerEvent(event);
61 }
62
63 QRegion region;
64 foreach (const Animation &animation, m_animations) {
65 // Check if loading is finished (we might not be notified, if the index is scrolled out of view)
66 const QVariant fetchState = animation.index.data(Akonadi::EntityTreeModel::FetchStateRole);
67 if (fetchState.toInt() != Akonadi::EntityTreeModel::FetchingState) {
68 pop(animation.index);
69 continue;
70 }
71
72 // This repaints the entire delegate (icon and text).
73 // TODO: See if there's a way to repaint only part of it (the icon).
74 animation.nextFrame();
75 const QRect rect = m_view->visualRect(animation.index);
76 region += rect;
77 }
78
79 if (!region.isEmpty()) {
80 m_view->viewport()->update(region);
81 }
82}
83
84QPixmap DelegateAnimator::sequenceFrame(const QModelIndex &index)
85{
86 foreach (const Animation &animation, m_animations) {
87 if (animation.index == index) {
88 return m_pixmapSequence.frameAt(animation.frame);
89 }
90 }
91 return QPixmap();
92}
93
94ProgressSpinnerDelegate::ProgressSpinnerDelegate(DelegateAnimator *animator, QObject *parent)
95 : QStyledItemDelegate(parent)
96 , m_animator(animator)
97{
98
99}
100
101void ProgressSpinnerDelegate::initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const
102{
103 QStyledItemDelegate::initStyleOption(option, index);
104
105 const QVariant fetchState = index.data(Akonadi::EntityTreeModel::FetchStateRole);
106 if (!fetchState.isValid() || fetchState.toInt() != Akonadi::EntityTreeModel::FetchingState) {
107 m_animator->pop(index);
108 return;
109 }
110
111 m_animator->push(index);
112
113 if (QStyleOptionViewItemV4 *v4 = qstyleoption_cast<QStyleOptionViewItemV4 *>(option)) {
114 v4->icon = m_animator->sequenceFrame(index);
115 }
116}
117
118uint Akonadi::qHash(Akonadi::DelegateAnimator::Animation anim)
119{
120 return qHash(anim.index);
121}
122