Akonadi

agentinstancewidget.cpp
1 /*
2  Copyright (c) 2006-2008 Tobias Koenig <[email protected]>
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 
27 #include <QApplication>
28 #include <QHBoxLayout>
29 #include <QIcon>
30 #include <QListView>
31 #include <QPainter>
32 #include <QStyle>
33 
34 namespace Akonadi
35 {
36 namespace Internal
37 {
38 
39 static void iconsEarlyCleanup();
40 
41 struct Icons {
42  Icons()
43  : readyPixmap(QIcon::fromTheme(QStringLiteral("user-online")).pixmap(QSize(16, 16)))
44  , syncPixmap(QIcon::fromTheme(QStringLiteral("network-connect")).pixmap(QSize(16, 16)))
45  , errorPixmap(QIcon::fromTheme(QStringLiteral("dialog-error")).pixmap(QSize(16, 16)))
46  , offlinePixmap(QIcon::fromTheme(QStringLiteral("network-disconnect")).pixmap(QSize(16, 16)))
47  {
48  qAddPostRoutine(iconsEarlyCleanup);
49  }
50  QPixmap readyPixmap, syncPixmap, errorPixmap, offlinePixmap;
51 };
52 
53 Q_GLOBAL_STATIC(Icons, s_icons)
54 
55 // called as a Qt post routine, to prevent pixmap leaking
56 void iconsEarlyCleanup()
57 {
58  Icons *const ic = s_icons;
59  ic->readyPixmap = ic->syncPixmap = ic->errorPixmap = ic->offlinePixmap = QPixmap();
60 }
61 
62 static const int s_delegatePaddingSize = 7;
63 
68 class AgentInstanceWidgetDelegate : public QAbstractItemDelegate
69 {
70 public:
71  explicit AgentInstanceWidgetDelegate(QObject *parent = nullptr);
72 
73  void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
74  QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
75 };
76 
77 }
78 
79 using Akonadi::Internal::AgentInstanceWidgetDelegate;
80 
84 class Q_DECL_HIDDEN AgentInstanceWidget::Private
85 {
86 public:
87  Private(AgentInstanceWidget *parent)
88  : mParent(parent)
89  , mView(nullptr)
90  , mModel(nullptr)
91  , proxy(nullptr)
92  {
93  }
94 
95  void currentAgentInstanceChanged(const QModelIndex &currentIndex, const QModelIndex &previousIndex);
96  void currentAgentInstanceDoubleClicked(const QModelIndex &currentIndex);
97  void currentAgentInstanceClicked(const QModelIndex &currentIndex);
98 
99  AgentInstanceWidget *mParent = nullptr;
100  QListView *mView = nullptr;
101  AgentInstanceModel *mModel = nullptr;
102  AgentFilterProxyModel *proxy = nullptr;
103 };
104 
105 void AgentInstanceWidget::Private::currentAgentInstanceChanged(const QModelIndex &currentIndex, const QModelIndex &previousIndex)
106 {
107  AgentInstance currentInstance;
108  if (currentIndex.isValid()) {
109  currentInstance = currentIndex.data(AgentInstanceModel::InstanceRole).value<AgentInstance>();
110  }
111 
112  AgentInstance previousInstance;
113  if (previousIndex.isValid()) {
114  previousInstance = previousIndex.data(AgentInstanceModel::InstanceRole).value<AgentInstance>();
115  }
116 
117  Q_EMIT mParent->currentChanged(currentInstance, previousInstance);
118 }
119 
120 void AgentInstanceWidget::Private::currentAgentInstanceDoubleClicked(const QModelIndex &currentIndex)
121 {
122  AgentInstance currentInstance;
123  if (currentIndex.isValid()) {
124  currentInstance = currentIndex.data(AgentInstanceModel::InstanceRole).value<AgentInstance>();
125  }
126 
127  Q_EMIT mParent->doubleClicked(currentInstance);
128 }
129 
130 void AgentInstanceWidget::Private::currentAgentInstanceClicked(const QModelIndex &currentIndex)
131 {
132  AgentInstance currentInstance;
133  if (currentIndex.isValid()) {
134  currentInstance = currentIndex.data(AgentInstanceModel::InstanceRole).value<AgentInstance>();
135  }
136 
137  Q_EMIT mParent->clicked(currentInstance);
138 }
139 
141  : QWidget(parent)
142  , d(new Private(this))
143 {
144  QHBoxLayout *layout = new QHBoxLayout(this);
145  layout->setContentsMargins(0, 0, 0, 0);
146 
147  d->mView = new QListView(this);
148  d->mView->setContextMenuPolicy(Qt::NoContextMenu);
149  d->mView->setItemDelegate(new Internal::AgentInstanceWidgetDelegate(d->mView));
150  d->mView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
151  d->mView->setAlternatingRowColors(true);
152  d->mView->setSelectionMode(QAbstractItemView::ExtendedSelection);
153  layout->addWidget(d->mView);
154 
155  d->mModel = new AgentInstanceModel(this);
156 
157  d->proxy = new AgentFilterProxyModel(this);
158  d->proxy->setDynamicSortFilter(true);
159  d->proxy->sort(0);
160  d->proxy->setSortCaseSensitivity(Qt::CaseInsensitive);
161  d->proxy->setSourceModel(d->mModel);
162  d->mView->setModel(d->proxy);
163 
164  d->mView->selectionModel()->setCurrentIndex(d->mView->model()->index(0, 0), QItemSelectionModel::Select);
165  d->mView->scrollTo(d->mView->model()->index(0, 0));
166 
167  connect(d->mView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
168  this, SLOT(currentAgentInstanceChanged(QModelIndex,QModelIndex)));
170  this, [this](const QModelIndex &currentIndex) { d->currentAgentInstanceDoubleClicked(currentIndex); });
171  connect(d->mView, SIGNAL(clicked(QModelIndex)),
172  this, SLOT(currentAgentInstanceClicked(QModelIndex)));
173 }
174 
176 {
177  delete d;
178 }
179 
181 {
182  QItemSelectionModel *selectionModel = d->mView->selectionModel();
183  if (!selectionModel) {
184  return AgentInstance();
185  }
186 
187  QModelIndex index = selectionModel->currentIndex();
188  if (!index.isValid()) {
189  return AgentInstance();
190  }
191 
193 }
194 
196 {
197  AgentInstance::List list;
198  QItemSelectionModel *selectionModel = d->mView->selectionModel();
199  if (!selectionModel) {
200  return list;
201  }
202 
203  const QModelIndexList indexes = selectionModel->selection().indexes();
204  list.reserve(indexes.count());
205  for (const QModelIndex &index : indexes) {
206  list.append(index.data(AgentInstanceModel::InstanceRole).value<AgentInstance>());
207  }
208 
209  return list;
210 }
211 
213 {
214  return d->mView;
215 }
216 
218 {
219  return d->proxy;
220 }
221 
222 AgentInstanceWidgetDelegate::AgentInstanceWidgetDelegate(QObject *parent)
223  : QAbstractItemDelegate(parent)
224 {
225 }
226 
227 void AgentInstanceWidgetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
228 {
229  if (!index.isValid()) {
230  return;
231  }
232 
234  style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, nullptr);
235 
236  QIcon icon = index.data(Qt::DecorationRole).value<QIcon>();
237  const QString name = index.model()->data(index, Qt::DisplayRole).toString();
238  int status = index.model()->data(index, AgentInstanceModel::StatusRole).toInt();
239  uint progress = index.model()->data(index, AgentInstanceModel::ProgressRole).toUInt();
240  QString statusMessage = index.model()->data(index, AgentInstanceModel::StatusMessageRole).toString();
241 
242  QPixmap statusPixmap;
243 
245  statusPixmap = s_icons->offlinePixmap;
246  } else if (status == AgentInstance::Idle) {
247  statusPixmap = s_icons->readyPixmap;
248  } else if (status == AgentInstance::Running) {
249  statusPixmap = s_icons->syncPixmap;
250  } else {
251  statusPixmap = s_icons->errorPixmap;
252  }
253 
254  if (status == 1) {
255  statusMessage.append(QStringLiteral(" (%1%)").arg(progress));
256  }
257 
258  const QPixmap iconPixmap = icon.pixmap(style->pixelMetric(QStyle::PM_MessageBoxIconSize));
259  QRect innerRect = option.rect.adjusted(s_delegatePaddingSize, s_delegatePaddingSize, -s_delegatePaddingSize, -s_delegatePaddingSize); //add some padding round entire delegate
260 
261  const QSize decorationSize = iconPixmap.size();
262  const QSize statusIconSize = statusPixmap.size();//= KIconLoader::global()->currentSize(KIconLoader::Small);
263 
264  QFont nameFont = option.font;
265  nameFont.setBold(true);
266 
267  QFont statusTextFont = option.font;
268  const QRect decorationRect(innerRect.left(), innerRect.top(), decorationSize.width(), innerRect.height());
269  const QRect nameTextRect(decorationRect.topRight() + QPoint(4, 0), innerRect.topRight() + QPoint(0, innerRect.height() / 2));
270  const QRect statusTextRect(decorationRect.bottomRight() + QPoint(4, - innerRect.height() / 2), innerRect.bottomRight());
271 
272  QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled;
273  if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) {
274  cg = QPalette::Inactive;
275  }
276 
277  if (option.state & QStyle::State_Selected) {
278  painter->setPen(option.palette.color(cg, QPalette::HighlightedText));
279  } else {
280  painter->setPen(option.palette.color(cg, QPalette::Text));
281  }
282 
283  painter->drawPixmap(style->itemPixmapRect(decorationRect, Qt::AlignCenter, iconPixmap), iconPixmap);
284 
285  painter->setFont(nameFont);
286  painter->drawText(nameTextRect, Qt::AlignVCenter | Qt::AlignLeft, name);
287 
288  painter->setFont(statusTextFont);
289  painter->drawText(statusTextRect.adjusted(statusIconSize.width() + 4, 0, 0, 0), Qt::AlignVCenter | Qt::AlignLeft, statusMessage);
290  painter->drawPixmap(style->itemPixmapRect(statusTextRect, Qt::AlignVCenter | Qt::AlignLeft, statusPixmap), statusPixmap);
291 }
292 
293 QSize AgentInstanceWidgetDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
294 {
295  Q_UNUSED(index);
296 
297  const int iconHeight = QApplication::style()->pixelMetric(QStyle::PM_MessageBoxIconSize) + (s_delegatePaddingSize * 2); //icon height + padding either side
298  const int textHeight = option.fontMetrics.height() + qMax(option.fontMetrics.height(), 16) + (s_delegatePaddingSize * 2); //height of text + icon/text + padding either side
299 
300  return QSize(1, qMax(iconHeight, textHeight)); //any width,the view will give us the whole thing in list mode
301 }
302 
303 }
304 
305 #include "moc_agentinstancewidget.cpp"
QLayout * layout() const const
void doubleClicked(const QModelIndex &index)
QModelIndexList indexes() const const
void currentChanged(const Akonadi::AgentInstance &current, const Akonadi::AgentInstance &previous)
This signal is emitted whenever the current agent instance changes.
AgentInstance currentAgentInstance() const
Returns the current agent instance or an invalid agent instance if no agent instance is selected...
QSize size() const const
QString & append(QChar ch)
void setContentsMargins(int left, int top, int right, int bottom)
QPoint topRight() const const
int width() const const
The agent instance does currently nothing.
QModelIndex currentIndex() const const
A textual presentation of the current status.
void append(const T &value)
The current online/offline status.
The current status (numerical) of the instance.
AgentFilterProxyModel * agentFilterProxyModel() const
Returns the agent filter proxy model, use this to filter by agent mimetype or capabilities.
QStyle * style() const const
virtual int pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option, const QWidget *widget) const const =0
QPoint bottomRight() const const
T value() const const
The agent instance is working on something.
int height() const const
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) const const
void setBold(bool enable)
Represents one agent instance and takes care of communication with it.
bool isValid() const const
void setFont(const QFont &font)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
uint toUInt(bool *ok) const const
int toInt(bool *ok) const const
int top() const const
void setPen(const QColor &color)
const QItemSelection selection() const const
int left() const const
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
void drawText(const QPointF &position, const QString &text)
Provides a data model for agent instances.
virtual QVariant data(const QModelIndex &index, int role) const const =0
void reserve(int size)
QAbstractItemView * view() const
Returns the view used in the widget.
The current progress (numerical in percent) of an operation.
const QAbstractItemModel * model() const const
QVariant data(int role) const const
QStyle * style()
Helper integration between Akonadi and Qt.
A proxy model for filtering AgentType or AgentInstance.
bool toBool() const const
virtual void drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const const =0
A representation of an agent instance.
virtual QRect itemPixmapRect(const QRect &rectangle, int alignment, const QPixmap &pixmap) const const
void clicked(const Akonadi::AgentInstance &current)
This signal is emitted whenever there is a click on an agent instance.
QVector< AgentInstance > selectedAgentInstances() const
Returns the selected agent instances.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
~AgentInstanceWidget()
Destroys the agent instance widget.
QString toString() const const
AgentInstanceWidget(QWidget *parent=nullptr)
Creates a new agent instance widget.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon May 25 2020 22:46:07 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.