Akonadi

entitytreeview.cpp
1 /*
2  Copyright (c) 2006 - 2007 Volker Krause <[email protected]>
3  Copyright (c) 2008 Stephen Kelly <[email protected]>
4  Copyright (C) 2012-2020 Laurent Montel <[email protected]>
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 "entitytreeview.h"
23 
24 #include "dragdropmanager_p.h"
25 
26 #include <QTimer>
27 #include <QApplication>
28 #include <QDragMoveEvent>
29 #include <QHeaderView>
30 #include <QMenu>
31 
32 #include "collection.h"
33 #include "controlgui.h"
34 #include "item.h"
35 #include "entitytreemodel.h"
36 
37 #include <KXMLGUIClient>
38 #include <KXMLGUIFactory>
39 
40 #include "progressspinnerdelegate_p.h"
41 
42 using namespace Akonadi;
43 
47 class Q_DECL_HIDDEN EntityTreeView::Private
48 {
49 public:
50  Private(EntityTreeView *parent)
51  : mParent(parent)
52 #ifndef QT_NO_DRAGANDDROP
53  , mDragDropManager(new DragDropManager(mParent))
54 #endif
55  , mDefaultPopupMenu(QStringLiteral("akonadi_collectionview_contextmenu"))
56  {
57  }
58 
59  void init();
60  void itemClicked(const QModelIndex &index);
61  void itemDoubleClicked(const QModelIndex &index);
62  void itemCurrentChanged(const QModelIndex &index);
63 
64  void slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
65 
66  EntityTreeView *mParent = nullptr;
67  QBasicTimer mDragExpandTimer;
68  DragDropManager *mDragDropManager = nullptr;
69  KXMLGUIClient *mXmlGuiClient = nullptr;
70  QString mDefaultPopupMenu;
71 };
72 
73 void EntityTreeView::Private::init()
74 {
75  Akonadi::DelegateAnimator *animator = new Akonadi::DelegateAnimator(mParent);
76  Akonadi::ProgressSpinnerDelegate *customDelegate = new Akonadi::ProgressSpinnerDelegate(animator, mParent);
77  mParent->setItemDelegate(customDelegate);
78 
79  mParent->header()->setSectionsClickable(true);
80  mParent->header()->setStretchLastSection(false);
81 // mParent->setRootIsDecorated( false );
82 
83  // QTreeView::autoExpandDelay has very strange behaviour. It toggles the collapse/expand state
84  // of the item the cursor is currently over when a timer event fires.
85  // The behaviour we want is to expand a collapsed row on drag-over, but not collapse it.
86  // mDragExpandTimer is used to achieve this.
87 // mParent->setAutoExpandDelay ( QApplication::startDragTime() );
88 
89  mParent->setSortingEnabled(true);
90  mParent->sortByColumn(0, Qt::AscendingOrder);
91  mParent->setEditTriggers(QAbstractItemView::EditKeyPressed);
92  mParent->setAcceptDrops(true);
93 #ifndef QT_NO_DRAGANDDROP
94  mParent->setDropIndicatorShown(true);
95  mParent->setDragDropMode(DragDrop);
96  mParent->setDragEnabled(true);
97 #endif
98 
99  mParent->connect(mParent, SIGNAL(clicked(QModelIndex)), mParent, SLOT(itemClicked(QModelIndex)));
100  mParent->connect(mParent, SIGNAL(doubleClicked(QModelIndex)), mParent, SLOT(itemDoubleClicked(QModelIndex)));
101 
103 }
104 
105 void EntityTreeView::Private::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
106 {
107  Q_UNUSED(deselected)
108  const int column = 0;
109  for (const QItemSelectionRange &range : selected) {
110  const QModelIndex index = range.topLeft();
111 
112  if (index.column() > 0) {
113  continue;
114  }
115 
116  for (int row = index.row(); row <= range.bottomRight().row(); ++row) {
117  // Don't use canFetchMore here. We need to bypass the check in
118  // the EntityFilterModel when it shows only collections.
119  mParent->model()->fetchMore(index.sibling(row, column));
120  }
121  }
122 
123  if (selected.size() == 1) {
124  const QItemSelectionRange &range = selected.first();
125  if (range.topLeft().row() == range.bottomRight().row()) {
126  mParent->scrollTo(range.topLeft(), QTreeView::EnsureVisible);
127  }
128  }
129 }
130 
131 void EntityTreeView::Private::itemClicked(const QModelIndex &index)
132 {
133  if (!index.isValid()) {
134  return;
135  }
136  QModelIndex idx = index.sibling(index.row(), 0);
137 
138  const Collection collection = idx.model()->data(idx, EntityTreeModel::CollectionRole).value<Collection>();
139  if (collection.isValid()) {
140  Q_EMIT mParent->clicked(collection);
141  } else {
142  const Item item = idx.model()->data(idx, EntityTreeModel::ItemRole).value<Item>();
143  if (item.isValid()) {
144  Q_EMIT mParent->clicked(item);
145  }
146  }
147 }
148 
149 void EntityTreeView::Private::itemDoubleClicked(const QModelIndex &index)
150 {
151  if (!index.isValid()) {
152  return;
153  }
154  QModelIndex idx = index.sibling(index.row(), 0);
155  const Collection collection = idx.model()->data(idx, EntityTreeModel::CollectionRole).value<Collection>();
156  if (collection.isValid()) {
157  Q_EMIT mParent->doubleClicked(collection);
158  } else {
159  const Item item = idx.model()->data(idx, EntityTreeModel::ItemRole).value<Item>();
160  if (item.isValid()) {
161  Q_EMIT mParent->doubleClicked(item);
162  }
163  }
164 }
165 
166 void EntityTreeView::Private::itemCurrentChanged(const QModelIndex &index)
167 {
168  if (!index.isValid()) {
169  return;
170  }
171  QModelIndex idx = index.sibling(index.row(), 0);
172  const Collection collection = idx.model()->data(idx, EntityTreeModel::CollectionRole).value<Collection>();
173  if (collection.isValid()) {
174  Q_EMIT mParent->currentChanged(collection);
175  } else {
176  const Item item = idx.model()->data(idx, EntityTreeModel::ItemRole).value<Item>();
177  if (item.isValid()) {
178  Q_EMIT mParent->currentChanged(item);
179  }
180  }
181 }
182 
184  : QTreeView(parent)
185  , d(new Private(this))
186 {
187  setSelectionMode(QAbstractItemView::SingleSelection);
188  d->init();
189 }
190 
192  : QTreeView(parent)
193  , d(new Private(this))
194 {
195  d->mXmlGuiClient = xmlGuiClient;
196  d->init();
197 }
198 
200 {
201  delete d->mDragDropManager;
202  delete d;
203 }
204 
206 {
207  if (selectionModel()) {
209  this, SLOT(itemCurrentChanged(QModelIndex)));
210 
212  this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection)));
213  }
214 
215  QTreeView::setModel(model);
216  header()->setStretchLastSection(true);
217 
219  SLOT(itemCurrentChanged(QModelIndex)));
220 
222  SLOT(slotSelectionChanged(QItemSelection,QItemSelection)));
223 }
224 
225 void EntityTreeView::timerEvent(QTimerEvent *event)
226 {
227  if (event->timerId() == d->mDragExpandTimer.timerId()) {
229  if (state() == QAbstractItemView::DraggingState && viewport()->rect().contains(pos)) {
230  setExpanded(indexAt(pos), true);
231  }
232  }
233 
234  QTreeView::timerEvent(event);
235 }
236 
237 #ifndef QT_NO_DRAGANDDROP
238 void EntityTreeView::dragMoveEvent(QDragMoveEvent *event)
239 {
240  d->mDragExpandTimer.start(QApplication::startDragTime(), this);
241 
242  if (d->mDragDropManager->dropAllowed(event)) {
243  // All urls are supported. process the event.
245  return;
246  }
247 
248  event->setDropAction(Qt::IgnoreAction);
249 }
250 
251 void EntityTreeView::dropEvent(QDropEvent *event)
252 {
253  d->mDragExpandTimer.stop();
254  bool menuCanceled = false;
255  if (d->mDragDropManager->processDropEvent(event, menuCanceled, (dropIndicatorPosition() == QAbstractItemView::OnItem))) {
256  QTreeView::dropEvent(event);
257  }
258 }
259 #endif
260 
261 #ifndef QT_NO_CONTEXTMENU
262 void EntityTreeView::contextMenuEvent(QContextMenuEvent *event)
263 {
264  if (!d->mXmlGuiClient || !model()) {
265  return;
266  }
267 
268  const QModelIndex index = indexAt(event->pos());
269  QString popupName = d->mDefaultPopupMenu;
270 
271  if (index.isValid()) { // popup not over empty space
272  // check whether the index under the cursor is a collection or item
273  const Item item = model()->data(index, EntityTreeModel::ItemRole).value<Item>();
274  popupName = (item.isValid() ? QStringLiteral("akonadi_itemview_contextmenu") :
275  QStringLiteral("akonadi_collectionview_contextmenu"));
276  }
277 
278  QMenu *popup = static_cast<QMenu *>(d->mXmlGuiClient->factory()->container(popupName, d->mXmlGuiClient));
279  if (popup) {
280  popup->exec(event->globalPos());
281  }
282 }
283 #endif
284 
286 {
287  d->mXmlGuiClient = xmlGuiClient;
288 }
289 
291 {
292  return d->mXmlGuiClient;
293 }
294 
295 #ifndef QT_NO_DRAGANDDROP
296 void EntityTreeView::startDrag(Qt::DropActions supportedActions)
297 {
298  d->mDragDropManager->startDrag(supportedActions);
299 }
300 #endif
301 
303 {
304 #ifndef QT_NO_DRAGANDDROP
305  d->mDragDropManager->setShowDropActionMenu(enabled);
306 #endif
307 }
308 
310 {
311 #ifndef QT_NO_DRAGANDDROP
312  return d->mDragDropManager->showDropActionMenu();
313 #else
314  return false;
315 #endif
316 }
317 
319 {
320 #ifndef QT_NO_DRAGANDDROP
321  d->mDragDropManager->setManualSortingActive(active);
322 #endif
323 }
324 
326 {
327 #ifndef QT_NO_DRAGANDDROP
328  return d->mDragDropManager->isManualSortingActive();
329 #else
330  return false;
331 #endif
332 }
333 
335 {
336  d->mDefaultPopupMenu = name;
337 }
338 
339 #include "moc_entitytreeview.cpp"
virtual void dragMoveEvent(QDragMoveEvent *event) override
bool isValid() const
Returns whether the collection is valid.
Definition: collection.cpp:137
QAbstractItemView::DropIndicatorPosition dropIndicatorPosition() const const
void setSelectionMode(QAbstractItemView::SelectionMode mode)
QItemSelectionModel * selectionModel() const const
void setDefaultPopupMenu(const QString &name)
Set the name of the default popup menu (retrieved from the application&#39;s XMLGUI file).
Represents a collection of PIM items.
Definition: collection.h:76
const QPersistentModelIndex & bottomRight() const const
T value() const const
QWidget * viewport() const const
bool isManualSortingActive() const
Return true if we use an manual sorting Necessary to fix dnd menu We must show just move when we move...
void setManualSortingActive(bool active)
Set true if we automatic sorting.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
const QPoint & globalPos() const const
QUICKADDONS_EXPORT void init()
void setExpanded(const QModelIndex &index, bool expanded)
bool isValid() const const
static void widgetNeedsAkonadi(QWidget *widget)
Disable the given widget when Akonadi is not operational and show an error overlay (given enough spac...
Definition: controlgui.cpp:266
KXMLGUIClient * xmlGuiClient() const
Return the XML GUI client which the view is used in.
virtual void timerEvent(QTimerEvent *event) override
A view to show an item/collection tree provided by an EntityTreeModel.
void setDropActionMenuEnabled(bool enabled)
Sets whether the drop action menu is enabled and will be shown on drop operation. ...
EntityTreeView(QWidget *parent=nullptr)
Creates a new entity tree view.
int row() const const
bool isDropActionMenuEnabled() const
Returns whether the drop action menu is enabled and will be shown on drop operation.
QPoint pos() const const
virtual QVariant data(const QModelIndex &index, int role) const const =0
QAction * exec()
QRect rect() const const
void setModel(QAbstractItemModel *model) override
QAbstractItemView::State state() const const
virtual bool event(QEvent *event) override
int startDragTime()
const QPoint & pos() const const
const QAbstractItemModel * model() const const
QPoint pos()
QModelIndex sibling(int row, int column) const const
Helper integration between Akonadi and Qt.
virtual void setModel(QAbstractItemModel *model) override
virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override
virtual void dropEvent(QDropEvent *event) override
QPoint mapFromGlobal(const QPoint &pos) const const
int column() const const
int timerId() const const
QHeaderView * header() const const
QAbstractItemModel * model() const const
~EntityTreeView() override
Destroys the entity tree view.
void setStretchLastSection(bool stretch)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setXmlGuiClient(KXMLGUIClient *xmlGuiClient)
Sets the XML GUI client which the view is used in.
QObject * parent() const const
typedef DropActions
void currentChanged(const Akonadi::Collection &collection)
This signal is emitted whenever the current collection in the view has changed.
int row() const const
virtual QModelIndex indexAt(const QPoint &point) const const override
const QPersistentModelIndex & topLeft() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon May 25 2020 22:46:10 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.