1 | /* |
2 | Copyright (c) 2006 - 2007 Volker Krause <vkrause@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 "collectionview.h" |
21 | |
22 | #include "collection.h" |
23 | #include "collectionmodel.h" |
24 | #include "control.h" |
25 | |
26 | #include <kicon.h> |
27 | #include <kaction.h> |
28 | #include <kdebug.h> |
29 | #include <klocalizedstring.h> |
30 | #include <kmessagebox.h> |
31 | #include <kurl.h> |
32 | #include <kxmlguifactory.h> |
33 | #include <kxmlguiwindow.h> |
34 | |
35 | #include <QtCore/QDebug> |
36 | #include <QtCore/QTimer> |
37 | #include <QApplication> |
38 | #include <QDragMoveEvent> |
39 | #include <QHeaderView> |
40 | #include <QMenu> |
41 | |
42 | using namespace Akonadi; |
43 | |
44 | /** |
45 | * @internal |
46 | */ |
47 | class CollectionView::Private |
48 | { |
49 | public: |
50 | Private(CollectionView *parent) |
51 | : mParent(parent) |
52 | , xmlGuiClient(0) |
53 | { |
54 | } |
55 | |
56 | void init(); |
57 | void dragExpand(); |
58 | void itemClicked(const QModelIndex &); |
59 | void itemCurrentChanged(const QModelIndex &); |
60 | bool hasParent(const QModelIndex &idx, Collection::Id parentId); |
61 | |
62 | CollectionView *mParent; |
63 | QModelIndex dragOverIndex; |
64 | QTimer dragExpandTimer; |
65 | |
66 | KXMLGUIClient *xmlGuiClient; |
67 | }; |
68 | |
69 | void CollectionView::Private::init() |
70 | { |
71 | mParent->header()->setClickable(true); |
72 | mParent->header()->setStretchLastSection(false); |
73 | |
74 | mParent->setSortingEnabled(true); |
75 | mParent->sortByColumn(0, Qt::AscendingOrder); |
76 | mParent->setEditTriggers(QAbstractItemView::EditKeyPressed); |
77 | mParent->setAcceptDrops(true); |
78 | mParent->setDropIndicatorShown(true); |
79 | mParent->setDragDropMode(DragDrop); |
80 | mParent->setDragEnabled(true); |
81 | |
82 | dragExpandTimer.setSingleShot(true); |
83 | mParent->connect(&dragExpandTimer, SIGNAL(timeout()), SLOT(dragExpand())); |
84 | |
85 | mParent->connect(mParent, SIGNAL(clicked(QModelIndex)), |
86 | mParent, SLOT(itemClicked(QModelIndex))); |
87 | |
88 | Control::widgetNeedsAkonadi(mParent); |
89 | } |
90 | |
91 | bool CollectionView::Private::hasParent(const QModelIndex &idx, Collection::Id parentId) |
92 | { |
93 | QModelIndex idx2 = idx; |
94 | while (idx2.isValid()) { |
95 | if (mParent->model()->data(idx2, CollectionModel::CollectionIdRole).toLongLong() == parentId) { |
96 | return true; |
97 | } |
98 | |
99 | idx2 = idx2.parent(); |
100 | } |
101 | return false; |
102 | } |
103 | |
104 | void CollectionView::Private::dragExpand() |
105 | { |
106 | mParent->setExpanded(dragOverIndex, true); |
107 | dragOverIndex = QModelIndex(); |
108 | } |
109 | |
110 | void CollectionView::Private::itemClicked(const QModelIndex &index) |
111 | { |
112 | if (!index.isValid()) { |
113 | return; |
114 | } |
115 | |
116 | const Collection collection = index.model()->data(index, CollectionModel::CollectionRole).value<Collection>(); |
117 | if (!collection.isValid()) { |
118 | return; |
119 | } |
120 | |
121 | emit mParent->clicked(collection); |
122 | } |
123 | |
124 | void CollectionView::Private::itemCurrentChanged(const QModelIndex &index) |
125 | { |
126 | if (!index.isValid()) { |
127 | return; |
128 | } |
129 | |
130 | const Collection collection = index.model()->data(index, CollectionModel::CollectionRole).value<Collection>(); |
131 | if (!collection.isValid()) { |
132 | return; |
133 | } |
134 | |
135 | emit mParent->currentChanged(collection); |
136 | } |
137 | |
138 | CollectionView::CollectionView(QWidget *parent) |
139 | : QTreeView(parent) |
140 | , d(new Private(this)) |
141 | { |
142 | d->init(); |
143 | } |
144 | |
145 | CollectionView::CollectionView(KXMLGUIClient *xmlGuiClient, QWidget *parent) |
146 | : QTreeView(parent) |
147 | , d(new Private(this)) |
148 | { |
149 | d->xmlGuiClient = xmlGuiClient; |
150 | d->init(); |
151 | } |
152 | |
153 | CollectionView::CollectionView(KXmlGuiWindow *xmlGuiWindow, QWidget *parent) |
154 | : QTreeView(parent) |
155 | , d(new Private(this)) |
156 | { |
157 | d->xmlGuiClient = static_cast<KXMLGUIClient *>(xmlGuiWindow); |
158 | d->init(); |
159 | } |
160 | |
161 | CollectionView::~CollectionView() |
162 | { |
163 | delete d; |
164 | } |
165 | |
166 | void CollectionView::setModel(QAbstractItemModel *model) |
167 | { |
168 | QTreeView::setModel(model); |
169 | header()->setStretchLastSection(true); |
170 | |
171 | connect(selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), |
172 | this, SLOT(itemCurrentChanged(QModelIndex))); |
173 | } |
174 | |
175 | void CollectionView::dragMoveEvent(QDragMoveEvent *event) |
176 | { |
177 | QModelIndex index = indexAt(event->pos()); |
178 | if (d->dragOverIndex != index) { |
179 | d->dragExpandTimer.stop(); |
180 | if (index.isValid() && !isExpanded(index) && itemsExpandable()) { |
181 | d->dragExpandTimer.start(QApplication::startDragTime()); |
182 | d->dragOverIndex = index; |
183 | } |
184 | } |
185 | |
186 | // Check if the collection under the cursor accepts this data type |
187 | const QStringList supportedContentTypes = model()->data(index, CollectionModel::CollectionRole).value<Collection>().contentMimeTypes(); |
188 | const QMimeData *mimeData = event->mimeData(); |
189 | const KUrl::List urls = KUrl::List::fromMimeData(mimeData); |
190 | foreach (const KUrl &url, urls) { |
191 | |
192 | const Collection collection = Collection::fromUrl(url); |
193 | if (collection.isValid()) { |
194 | if (!supportedContentTypes.contains(QString::fromLatin1("inode/directory" ))) { |
195 | break; |
196 | } |
197 | |
198 | // Check if we don't try to drop on one of the children |
199 | if (d->hasParent(index, collection.id())) { |
200 | break; |
201 | } |
202 | } else { |
203 | const QString type = url.queryItems()[QString::fromLatin1("type" )]; |
204 | if (!supportedContentTypes.contains(type)) { |
205 | break; |
206 | } |
207 | } |
208 | |
209 | QTreeView::dragMoveEvent(event); |
210 | return; |
211 | } |
212 | |
213 | event->setDropAction(Qt::IgnoreAction); |
214 | } |
215 | |
216 | void CollectionView::dragLeaveEvent(QDragLeaveEvent *event) |
217 | { |
218 | d->dragExpandTimer.stop(); |
219 | d->dragOverIndex = QModelIndex(); |
220 | QTreeView::dragLeaveEvent(event); |
221 | } |
222 | |
223 | void CollectionView::dropEvent(QDropEvent *event) |
224 | { |
225 | d->dragExpandTimer.stop(); |
226 | d->dragOverIndex = QModelIndex(); |
227 | |
228 | // open a context menu offering different drop actions (move, copy and cancel) |
229 | // TODO If possible, hide non available actions ... |
230 | QMenu (this); |
231 | QAction *moveDropAction = popup.addAction(KIcon(QString::fromLatin1("edit-rename" )), i18n("&Move here" )); |
232 | QAction *copyDropAction = popup.addAction(KIcon(QString::fromLatin1("edit-copy" )), i18n("&Copy here" )); |
233 | popup.addSeparator(); |
234 | popup.addAction(KIcon(QString::fromLatin1("process-stop" )), i18n("Cancel" )); |
235 | |
236 | QAction *activatedAction = popup.exec(QCursor::pos()); |
237 | if (activatedAction == moveDropAction) { |
238 | event->setDropAction(Qt::MoveAction); |
239 | } else if (activatedAction == copyDropAction) { |
240 | event->setDropAction(Qt::CopyAction); |
241 | } else { |
242 | return; |
243 | } |
244 | |
245 | QTreeView::dropEvent(event); |
246 | } |
247 | |
248 | void CollectionView::(QContextMenuEvent *event) |
249 | { |
250 | if (!d->xmlGuiClient) { |
251 | return; |
252 | } |
253 | QMenu * = static_cast<QMenu *>(d->xmlGuiClient->factory()->container( |
254 | QLatin1String("akonadi_collectionview_contextmenu" ), d->xmlGuiClient)); |
255 | if (popup) { |
256 | popup->exec(event->globalPos()); |
257 | } |
258 | } |
259 | |
260 | void CollectionView::setXmlGuiClient(KXMLGUIClient *xmlGuiClient) |
261 | { |
262 | d->xmlGuiClient = xmlGuiClient; |
263 | } |
264 | |
265 | void CollectionView::setXmlGuiWindow(KXmlGuiWindow *xmlGuiWindow) |
266 | { |
267 | d->xmlGuiClient = static_cast<KXMLGUIClient *>(xmlGuiWindow); |
268 | } |
269 | |
270 | #include "moc_collectionview.cpp" |
271 | |