1 | /* |
2 | Copyright (c) 2006-2008 Tobias Koenig <tokoe@kde.org> |
3 | |
4 | This library is free software; you can redistribute it and/or modify it |
5 | under the terms of the GNU Library General Public License as published by |
6 | the Free Software Foundation; either version 2 of the License, or (at your |
7 | option) any later version. |
8 | |
9 | This library is distributed in the hope that it will be useful, but WITHOUT |
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public |
12 | License for more details. |
13 | |
14 | You should have received a copy of the GNU Library General Public License |
15 | along with this library; see the file COPYING.LIB. If not, write to the |
16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
17 | 02110-1301, USA. |
18 | */ |
19 | |
20 | #include "agentinstancewidget.h" |
21 | |
22 | #include "agentfilterproxymodel.h" |
23 | #include "agentinstance.h" |
24 | #include "agentinstancemodel.h" |
25 | |
26 | #include <KIcon> |
27 | #include <KIconLoader> |
28 | |
29 | #include <QtCore/QUrl> |
30 | #include <QApplication> |
31 | #include <QHBoxLayout> |
32 | #include <QListView> |
33 | #include <QPainter> |
34 | |
35 | namespace Akonadi { |
36 | namespace Internal { |
37 | |
38 | static void iconsEarlyCleanup(); |
39 | |
40 | struct Icons |
41 | { |
42 | Icons() |
43 | : readyPixmap(KIcon(QLatin1String("user-online" )).pixmap(QSize(16, 16))) |
44 | , syncPixmap(KIcon(QLatin1String("network-connect" )).pixmap(QSize(16, 16))) |
45 | , errorPixmap(KIcon(QLatin1String("dialog-error" )).pixmap(QSize(16, 16))) |
46 | , offlinePixmap(KIcon(QLatin1String("network-disconnect" )).pixmap(QSize(16, 16))) |
47 | { |
48 | qAddPostRoutine(iconsEarlyCleanup); |
49 | } |
50 | QPixmap readyPixmap, syncPixmap, errorPixmap, offlinePixmap; |
51 | }; |
52 | |
53 | K_GLOBAL_STATIC(Icons, s_icons) |
54 | |
55 | // called as a Qt post routine, to prevent pixmap leaking |
56 | void iconsEarlyCleanup() { |
57 | Icons *const ic = s_icons; |
58 | ic->readyPixmap = ic->syncPixmap = ic->errorPixmap = ic->offlinePixmap = QPixmap(); |
59 | } |
60 | |
61 | static const int s_delegatePaddingSize = 7; |
62 | |
63 | /** |
64 | * @internal |
65 | */ |
66 | |
67 | class AgentInstanceWidgetDelegate : public QAbstractItemDelegate |
68 | { |
69 | public: |
70 | AgentInstanceWidgetDelegate(QObject *parent = 0); |
71 | |
72 | virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; |
73 | virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; |
74 | }; |
75 | |
76 | } |
77 | |
78 | using Akonadi::Internal::AgentInstanceWidgetDelegate; |
79 | |
80 | /** |
81 | * @internal |
82 | */ |
83 | class AgentInstanceWidget::Private |
84 | { |
85 | public: |
86 | Private(AgentInstanceWidget *parent) |
87 | : mParent(parent) |
88 | , mView(0) |
89 | , mModel(0) |
90 | , proxy(0) |
91 | { |
92 | } |
93 | |
94 | void currentAgentInstanceChanged(const QModelIndex &, const QModelIndex &); |
95 | void currentAgentInstanceDoubleClicked(const QModelIndex &); |
96 | void currentAgentInstanceClicked(const QModelIndex ¤tIndex); |
97 | |
98 | AgentInstanceWidget *mParent; |
99 | QListView *mView; |
100 | AgentInstanceModel *mModel; |
101 | AgentFilterProxyModel *proxy; |
102 | }; |
103 | |
104 | void AgentInstanceWidget::Private::currentAgentInstanceChanged(const QModelIndex ¤tIndex, const QModelIndex &previousIndex) |
105 | { |
106 | AgentInstance currentInstance; |
107 | if (currentIndex.isValid()) { |
108 | currentInstance = currentIndex.data(AgentInstanceModel::InstanceRole).value<AgentInstance>(); |
109 | } |
110 | |
111 | AgentInstance previousInstance; |
112 | if (previousIndex.isValid()) { |
113 | previousInstance = previousIndex.data(AgentInstanceModel::InstanceRole).value<AgentInstance>(); |
114 | } |
115 | |
116 | emit mParent->currentChanged(currentInstance, previousInstance); |
117 | } |
118 | |
119 | void AgentInstanceWidget::Private::currentAgentInstanceDoubleClicked(const QModelIndex ¤tIndex) |
120 | { |
121 | AgentInstance currentInstance; |
122 | if (currentIndex.isValid()) { |
123 | currentInstance = currentIndex.data(AgentInstanceModel::InstanceRole).value<AgentInstance>(); |
124 | } |
125 | |
126 | emit mParent->doubleClicked(currentInstance); |
127 | } |
128 | |
129 | void AgentInstanceWidget::Private::currentAgentInstanceClicked(const QModelIndex ¤tIndex) |
130 | { |
131 | AgentInstance currentInstance; |
132 | if (currentIndex.isValid()) { |
133 | currentInstance = currentIndex.data(AgentInstanceModel::InstanceRole).value<AgentInstance>(); |
134 | } |
135 | |
136 | emit mParent->clicked(currentInstance); |
137 | } |
138 | |
139 | AgentInstanceWidget::AgentInstanceWidget(QWidget *parent) |
140 | : QWidget(parent) |
141 | , d(new Private(this)) |
142 | { |
143 | QHBoxLayout *layout = new QHBoxLayout(this); |
144 | layout->setMargin(0); |
145 | |
146 | d->mView = new QListView(this); |
147 | d->mView->setContextMenuPolicy(Qt::NoContextMenu); |
148 | d->mView->setItemDelegate(new Internal::AgentInstanceWidgetDelegate(d->mView)); |
149 | d->mView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); |
150 | d->mView->setAlternatingRowColors(true); |
151 | d->mView->setSelectionMode(QAbstractItemView::ExtendedSelection); |
152 | layout->addWidget(d->mView); |
153 | |
154 | d->mModel = new AgentInstanceModel(this); |
155 | |
156 | d->proxy = new AgentFilterProxyModel(this); |
157 | d->proxy->setSourceModel(d->mModel); |
158 | d->mView->setModel(d->proxy); |
159 | |
160 | d->mView->selectionModel()->setCurrentIndex(d->mView->model()->index(0, 0), QItemSelectionModel::Select); |
161 | d->mView->scrollTo(d->mView->model()->index(0, 0)); |
162 | |
163 | connect(d->mView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), |
164 | this, SLOT(currentAgentInstanceChanged(QModelIndex,QModelIndex))); |
165 | connect(d->mView, SIGNAL(doubleClicked(QModelIndex)), |
166 | this, SLOT(currentAgentInstanceDoubleClicked(QModelIndex))); |
167 | connect(d->mView, SIGNAL(clicked(QModelIndex)), |
168 | this, SLOT(currentAgentInstanceClicked(QModelIndex))); |
169 | } |
170 | |
171 | AgentInstanceWidget::~AgentInstanceWidget() |
172 | { |
173 | delete d; |
174 | } |
175 | |
176 | AgentInstance AgentInstanceWidget::currentAgentInstance() const |
177 | { |
178 | QItemSelectionModel *selectionModel = d->mView->selectionModel(); |
179 | if (!selectionModel) { |
180 | return AgentInstance(); |
181 | } |
182 | |
183 | QModelIndex index = selectionModel->currentIndex(); |
184 | if (!index.isValid()) { |
185 | return AgentInstance(); |
186 | } |
187 | |
188 | return index.data(AgentInstanceModel::InstanceRole).value<AgentInstance>(); |
189 | } |
190 | |
191 | QList<AgentInstance> AgentInstanceWidget::selectedAgentInstances() const |
192 | { |
193 | QList<AgentInstance> list; |
194 | QItemSelectionModel *selectionModel = d->mView->selectionModel(); |
195 | if (!selectionModel) { |
196 | return list; |
197 | } |
198 | |
199 | const QModelIndexList indexes = selectionModel->selection().indexes(); |
200 | |
201 | foreach (const QModelIndex &index, indexes) { |
202 | list.append(index.data(AgentInstanceModel::InstanceRole).value<AgentInstance>()); |
203 | } |
204 | |
205 | return list; |
206 | } |
207 | |
208 | QAbstractItemView *AgentInstanceWidget::view() const |
209 | { |
210 | return d->mView; |
211 | } |
212 | |
213 | AgentFilterProxyModel *AgentInstanceWidget::agentFilterProxyModel() const |
214 | { |
215 | return d->proxy; |
216 | } |
217 | |
218 | AgentInstanceWidgetDelegate::AgentInstanceWidgetDelegate(QObject *parent) |
219 | : QAbstractItemDelegate(parent) |
220 | { |
221 | } |
222 | |
223 | void AgentInstanceWidgetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const |
224 | { |
225 | if (!index.isValid()) { |
226 | return; |
227 | } |
228 | |
229 | QStyle *style = QApplication::style(); |
230 | style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, 0); |
231 | |
232 | QIcon icon = index.data(Qt::DecorationRole).value<QIcon>(); |
233 | const QString name = index.model()->data(index, Qt::DisplayRole).toString(); |
234 | int status = index.model()->data(index, AgentInstanceModel::StatusRole).toInt(); |
235 | uint progress = index.model()->data(index, AgentInstanceModel::ProgressRole).toUInt(); |
236 | QString statusMessage = index.model()->data(index, AgentInstanceModel::StatusMessageRole).toString(); |
237 | |
238 | QPixmap statusPixmap; |
239 | |
240 | if (!index.data(AgentInstanceModel::OnlineRole).toBool()) { |
241 | statusPixmap = s_icons->offlinePixmap; |
242 | } else if (status == AgentInstance::Idle) { |
243 | statusPixmap = s_icons->readyPixmap; |
244 | } else if (status == AgentInstance::Running) { |
245 | statusPixmap = s_icons->syncPixmap; |
246 | } else { |
247 | statusPixmap = s_icons->errorPixmap; |
248 | } |
249 | |
250 | if (status == 1) { |
251 | statusMessage.append(QString::fromLatin1(" (%1%)" ).arg(progress)); |
252 | } |
253 | |
254 | QRect innerRect = option.rect.adjusted(s_delegatePaddingSize, s_delegatePaddingSize, -s_delegatePaddingSize, -s_delegatePaddingSize); //add some padding round entire delegate |
255 | |
256 | const QSize decorationSize(KIconLoader::global()->currentSize(KIconLoader::Desktop), KIconLoader::global()->currentSize(KIconLoader::Desktop)); |
257 | const QSize statusIconSize = QSize(16, 16); //= KIconLoader::global()->currentSize(KIconLoader::Small); |
258 | |
259 | QFont nameFont = option.font; |
260 | nameFont.setBold(true); |
261 | |
262 | QFont statusTextFont = option.font; |
263 | const QRect decorationRect(innerRect.left(), innerRect.top(), decorationSize.width(), innerRect.height()); |
264 | const QRect nameTextRect(decorationRect.topRight() + QPoint(4, 0), innerRect.topRight() + QPoint(0, innerRect.height() / 2)); |
265 | const QRect statusTextRect(decorationRect.bottomRight() + QPoint(4, - innerRect.height() / 2), innerRect.bottomRight()); |
266 | |
267 | const QPixmap iconPixmap = icon.pixmap(decorationSize); |
268 | |
269 | QPalette::ColorGroup cg = option.state &QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; |
270 | if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) { |
271 | cg = QPalette::Inactive; |
272 | } |
273 | |
274 | if (option.state & QStyle::State_Selected) { |
275 | painter->setPen(option.palette.color(cg, QPalette::HighlightedText)); |
276 | } else { |
277 | painter->setPen(option.palette.color(cg, QPalette::Text)); |
278 | } |
279 | |
280 | painter->drawPixmap(style->itemPixmapRect(decorationRect, Qt::AlignCenter, iconPixmap), iconPixmap); |
281 | |
282 | painter->setFont(nameFont); |
283 | painter->drawText(nameTextRect, Qt::AlignVCenter | Qt::AlignLeft, name); |
284 | |
285 | painter->setFont(statusTextFont); |
286 | painter->drawText(statusTextRect.adjusted(statusIconSize.width() + 4, 0, 0, 0), Qt::AlignVCenter | Qt::AlignLeft, statusMessage); |
287 | painter->drawPixmap(style->itemPixmapRect(statusTextRect, Qt::AlignVCenter | Qt::AlignLeft, statusPixmap), statusPixmap); |
288 | } |
289 | |
290 | QSize AgentInstanceWidgetDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const |
291 | { |
292 | Q_UNUSED(index); |
293 | |
294 | const int iconHeight = KIconLoader::global()->currentSize(KIconLoader::Desktop) + (s_delegatePaddingSize * 2); //icon height + padding either side |
295 | const int textHeight = option.fontMetrics.height() + qMax(option.fontMetrics.height(), 16) + (s_delegatePaddingSize * 2); //height of text + icon/text + padding either side |
296 | |
297 | return QSize(1, qMax(iconHeight, textHeight)); //any width,the view will give us the whole thing in list mode |
298 | } |
299 | |
300 | } |
301 | |
302 | #include "moc_agentinstancewidget.cpp" |
303 | |