Drop Qt5 support.
[qpwmc.git] / pwmdTreeWidget.cpp
blobea4c64d8322455df0ce4c535b70110085c61a4d3
1 /*
2 Copyright (C) 2010-2023 Ben Kibbey <bjk@luxsci.net>
4 This file is part of qpwmc.
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 USA
21 #include <QMenu>
22 #include <QDrag>
23 #include "pwmdMainWindow.h"
24 #include "pwmdTreeWidget.h"
25 #include "pwmdTreeWidgetItemData.h"
26 #include "pwmd.h"
28 static QMenu *popupMenu;
29 static QAction *copyAction, *moveAction, *targetAction;
30 static bool isBusy;
32 PwmdTreeWidget::PwmdTreeWidget (QWidget * p) : QTreeWidget (p)
34 _parent = nullptr;
35 lastItem = nullptr;
36 expandTimer = new QTimer (this);
37 expandTimer->setSingleShot (true);
38 connect (expandTimer, SIGNAL (timeout ()), this,
39 SLOT (slotExpandTimeout ()));
40 dndMove = false;
43 void
44 PwmdTreeWidget::setPwmdParent (PwmdMainWindow *p)
46 _parent = p;
47 connect (p, SIGNAL (attributesRetrieved (QTreeWidgetItem *)), this,
48 SLOT (slotAttributesRetrieved (QTreeWidgetItem *)));
51 void
52 PwmdTreeWidget::setPwmdHandle (Pwmd *p)
54 disconnect (p, SIGNAL (busy (int, bool)), this, SLOT (slotBusy (int, bool)));
55 connect (p, SIGNAL (busy (int, bool)), this, SLOT (slotBusy (int, bool)));
58 void
59 PwmdTreeWidget::slotAttributesRetrieved (QTreeWidgetItem *item)
61 if (popupMenu && popupMenu->isVisible ())
62 popupDragContextMenu (item);
65 void
66 PwmdTreeWidget::resizeEvent (QResizeEvent *ev)
68 emit treeWidgetResized (ev->size ());
69 QTreeWidget::resizeEvent (ev);
72 void
73 PwmdTreeWidget::keyPressEvent (QKeyEvent * ev)
75 if (!_parent)
77 QTreeWidget::keyPressEvent (ev);
78 return;
81 if (_parent->isBusy)
82 return;
84 // Alt-Enter
85 if (ev->key () == Qt::Key_Return && ev->nativeModifiers () == 8)
87 QCursor::setPos (mapToGlobal
88 (QPoint (visualItemRect (currentItem ()).topRight ())));
89 _parent->popupContextMenu ();
90 ev->accept ();
91 return;
93 else if (ev->key () == Qt::Key_Escape && !ev->nativeModifiers ())
95 _parent->resetSelectedItems ();
96 ev->accept ();
97 return;
100 QTreeWidget::keyPressEvent (ev);
103 void
104 PwmdTreeWidget::mouseReleaseEvent (QMouseEvent * ev)
106 ev->accept ();
108 if (!_parent || ev->button () != Qt::RightButton)
109 return;
111 _parent->popupContextMenu ();
114 void
115 PwmdTreeWidget::mousePressEvent (QMouseEvent * ev)
117 if (_parent && _parent->isBusy)
119 ev->accept ();
120 return;
123 dragStartPosition = ev->pos ();
124 QTreeWidget::mousePressEvent (ev);
127 /* Test if the source element is being dropped to the resolved path of a
128 * child of the source.
130 bool
131 PwmdTreeWidget::dropIsChild (QTreeWidgetItem * item, QTreeWidgetItem *drop)
133 if (!_parent)
134 return true;
136 for (int i = 0, t = item->childCount (); i < t; i++)
138 QTreeWidgetItem *p = _parent->resolveElementPath (item->child (i));
140 if (p == drop || dropIsChild (item->child (i), drop))
141 return true;
144 return false;
147 bool
148 PwmdTreeWidget::isValidDndMove (QTreeWidgetItem * drop, bool move)
150 PwmdTreeWidgetItemData *data = drop ? qvariant_cast <PwmdTreeWidgetItemData * >(drop->data (0, Qt::UserRole)) : nullptr;
152 if (!_parent)
153 return false;
155 if (data)
157 if (data->badTarget () || data->badPermissions () || data->targetLoop ())
158 return false;
160 else if (!drop) // New root element
161 return true;
163 if (_parent->isParentOf (_parent->selectedElement, drop)
164 || _parent->selectedElement == drop
165 || (!_parent->selectedElement->parent () && !drop))
166 return false;
168 QTreeWidgetItem *srcElement = _parent->resolveElementPath (_parent->selectedElement);
169 QTreeWidgetItem *dropElement = _parent->resolveElementPath (drop);
170 QTreeWidgetItem *a = dropElement, *b = srcElement;
172 if (_parent->selectedElement)
174 data = qvariant_cast <PwmdTreeWidgetItemData * >(_parent->selectedElement->data(0, Qt::UserRole));
175 if (!_parent->isElementOwner (_parent->selectedElement)
176 && data->badPermissions ())
177 return false;
178 else if (!move && !_parent->isElementOwner (_parent->selectedElement)
179 && data->badTarget ())
180 return false;
183 for (; move && a; a = a->parent ())
185 if (a == b)
186 return false;
188 if (_parent->isParentOf (_parent->selectedElement, dropElement)
189 || _parent->selectedElement == a
190 || (!_parent->selectedElement->parent () && !a))
191 return false;
194 a = dropElement;
195 b = srcElement;
196 if (!b || !a)
197 return true;
199 for (; move && a; a = a->parent ())
201 if (a == b)
202 return false;
204 if (dropElement == _parent->resolveElementPath (b->parent ()))
205 return false;
207 if (_parent->isParentOf (_parent->selectedElement, a)
208 || _parent->selectedElement == a
209 || (!_parent->selectedElement->parent () && !a))
210 return false;
213 if (dropIsChild (srcElement, dropElement))
214 return false;
216 return true;
219 QPoint
220 PwmdTreeWidget::mousePosition ()
222 return _mousePosition;
225 bool
226 PwmdTreeWidget::validTargetDropElement (QTreeWidgetItem *item)
228 if (!item)
229 return false;
231 PwmdTreeWidgetItemData *data = qvariant_cast <PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole));
232 if (!_parent->isElementOwner (item) && (data->badTarget ()
233 || data->targetLoop ()
234 || data->badPermissions ()))
235 return false;
236 else if (_parent->isParentOf (_parent->selectedElement, item))
237 return false;
239 return true;
242 void
243 PwmdTreeWidget::slotBusy (int, bool b)
245 static int refCount;
246 static bool last;
248 refCount += b ? 1 : -1;
249 if (refCount < 0)
250 refCount = 0;
252 isBusy = !!refCount;
254 if (isBusy != last)
255 setPopupMenuItemsEnabled ();
257 last = isBusy;
260 void
261 PwmdTreeWidget::setPopupMenuItemsEnabled ()
263 if (!_parent || !copyAction || !moveAction || !targetAction)
264 return;
266 if (isBusy || !isValidDndMove (_parent->dropElement))
267 copyAction->setEnabled (false);
268 else
269 copyAction->setEnabled (true);
271 if (isBusy || !isValidDndMove (_parent->dropElement, true)
272 || (!_parent->dropElement && _parent->ui.elementTree->indexOfTopLevelItem (_parent->selectedElement) != -1))
273 moveAction->setEnabled (false);
274 else
275 moveAction->setEnabled (true);
277 if (isBusy || !_parent->dropElement
278 || _parent->selectedElement == _parent->dropElement
279 || !validTargetDropElement (_parent->dropElement)
280 || _parent->isParentOf (_parent->dropElement, _parent->selectedElement)
281 || !_parent->isElementOwner (_parent->selectedElement))
282 targetAction->setEnabled (false);
283 else
284 targetAction->setEnabled (true);
287 void
288 PwmdTreeWidget::popupDragContextMenu(QTreeWidgetItem *item)
290 PwmdTreeWidgetItemData *data = item ? qvariant_cast <PwmdTreeWidgetItemData * >(item->data (0, Qt::UserRole)) : nullptr;
292 if (item && item != _parent->dropElement)
293 return;
295 if (!popupMenu)
297 popupMenu = new QMenu (this);
298 copyAction = popupMenu->addAction (tr ("&Copy here"), _parent,
299 SLOT (slotDndCopyElement ()));
300 moveAction = popupMenu->addAction (tr ("&Move here"), _parent,
301 SLOT (slotDndMoveElement ()));
302 targetAction = popupMenu->addAction (tr ("Set as &target"), _parent,
303 SLOT (slotDndCreateTarget ()));
306 setPopupMenuItemsEnabled ();
308 if (!item || (item != _parent->dropElement && item != _parent->selectedElement))
310 if (!data && _parent->dropElement)
311 data = qvariant_cast <PwmdTreeWidgetItemData * >(_parent->dropElement->data (0, Qt::UserRole));
313 if (!data || data->attributes ()->isEmpty ())
314 _parent->refreshAttributeList (_parent->dropElement, false, false, true);
317 popupMenu->popup (QCursor::pos ());
320 void
321 PwmdTreeWidget::mouseMoveEvent (QMouseEvent * ev)
323 dndMove = false;
324 _mousePosition = ev->pos ();
326 if (!_parent)
328 ev->accept ();
329 return;
332 QTreeWidgetItem *item = itemAt(ev->pos());
333 if (!item)
335 ev->accept ();
336 return;
339 _parent->setElementStatusTip (item);
341 if (!ev->buttons () && !ev->modifiers ())
343 ev->accept ();
344 return;
347 if ((ev->pos () - dragStartPosition).manhattanLength ()
348 < QApplication::startDragDistance ())
350 ev->accept ();
351 return;
354 QDrag *drag = new QDrag (this);
355 QMimeData *m = new QMimeData;
356 m->setText (_parent->elementPath (_parent->selectedElement));
357 drag->setMimeData (m);
358 Qt::DropAction action;
360 if ((ev->buttons () & Qt::LeftButton) && ev->modifiers ())
362 _parent->dndOperation = PwmdMainWindow::DndNone;
363 switch (ev->modifiers ())
365 case Qt::ControlModifier:
366 _parent->dndOperation = PwmdMainWindow::DndAny;
367 action = drag->exec (Qt::CopyAction);
368 _parent->slotElementEntered (nullptr, 0);
369 expandTimer->stop ();
371 if (drag->target () && action == Qt::CopyAction
372 && isValidDndMove (_parent->dropElement))
373 _parent->slotDndCopyElement ();
374 else
375 _parent->dndOperation = PwmdMainWindow::DndNone;
376 break;
377 case Qt::AltModifier:
378 _parent->dndOperation = PwmdMainWindow::DndAny;
379 action = drag->exec (Qt::LinkAction);
380 expandTimer->stop ();
381 _parent->slotElementEntered (nullptr, 0);
383 if (drag->target ()
384 && action == Qt::LinkAction && _parent->dropElement
385 && validTargetDropElement (_parent->dropElement))
386 _parent->slotDndCreateTarget ();
387 else
388 _parent->dndOperation = PwmdMainWindow::DndNone;
389 break;
390 default:
391 break;
394 ev->accept ();
395 return;
397 #ifdef Q_OS_ANDROID
398 else if (!ev->modifiers () && (ev->buttons () & Qt::LeftButton))
399 #else
400 else if (!ev->modifiers () && (ev->buttons () & Qt::RightButton))
401 #endif
403 _parent->dndOperation = PwmdMainWindow::DndAny;
404 drag->exec (Qt::CopyAction);
405 _parent->slotElementEntered (nullptr, 0);
406 expandTimer->stop ();
408 if (!drag->target ()
409 || drag->target ()->parent () != _parent->ui.elementTree)
411 _parent->dndOperation = PwmdMainWindow::DndNone;
412 ev->accept ();
413 return;
416 popupDragContextMenu();
418 else if (!ev->modifiers () && (ev->buttons () & Qt::LeftButton))
420 // Fake a move action. Would be good if Qt would let us get the cursor
421 // displayed while doing a Qt::MoveAction since we're doing a
422 // Qt::CopyAction. Oh well.
423 //drag->setDragCursor(moveactionpixmap, Qt::CopyAction);
424 dndMove = true;
425 _parent->dndOperation = PwmdMainWindow::DndAny;
426 action = drag->exec (Qt::CopyAction);
427 _parent->slotElementEntered (nullptr, 0);
428 expandTimer->stop ();
430 if (drag->target ()
431 && drag->target ()->parent () == _parent->ui.elementTree
432 && action == Qt::CopyAction)
434 if (ev->source () == Qt::MouseEventSynthesizedBySystem)
435 popupDragContextMenu ();
436 else
437 _parent->slotDndMoveElement ();
439 else
440 _parent->dndOperation = PwmdMainWindow::DndNone;
442 ev->accept ();
446 void
447 PwmdTreeWidget::dragMoveEvent (QDragMoveEvent * ev)
449 QTreeWidget::dragMoveEvent (ev);
451 if (!_parent || !_parent->selectedElement)
453 ev->ignore ();
454 return;
457 QTreeWidgetItem *item = _parent->ui.elementTree->itemAt (ev->position ().toPoint ());
459 if (ev->dropAction () == Qt::LinkAction)
461 if (!item || item == _parent->selectedElement)
463 expandTimer->stop ();
464 ev->ignore ();
465 return;
468 if (lastItem == item)
470 if (!expandTimer->isActive ())
471 expandTimer->start (500);
473 ev->accept ();
474 return;
478 _parent->slotElementEntered (item, 0);
479 expandTimer->stop ();
481 if (dndMove)
483 if (!isValidDndMove (item, true))
485 if (item)
487 lastItem = item;
488 expandTimer->start (500);
491 ev->ignore ();
492 return;
496 if (item)
497 expandTimer->start (500);
499 lastItem = item;
500 ev->accept ();
503 void
504 PwmdTreeWidget::slotExpandTimeout ()
506 lastItem->setExpanded (true);
509 void
510 PwmdTreeWidget::dragEnterEvent (QDragEnterEvent * ev)
512 if (!_parent || ev->source () != _parent->ui.elementTree)
513 return;
515 if (ev->mimeData ()->hasFormat ("text/plain"))
516 ev->acceptProposedAction ();
519 // Can't do the actual modifiying of elements here in case of pwmd error the
520 // elementTree would already be modified. This only happens with
521 // Qt::MoveAction for some reason.
522 bool
523 PwmdTreeWidget::dropMimeData (QTreeWidgetItem * dst, int, const QMimeData *,
524 Qt::DropAction)
526 if (!_parent)
527 return false;
529 expandTimer->stop ();
530 _parent->dropElement = dst;
531 return true;
534 QStringList
535 PwmdTreeWidget::mimeTypes () const
537 QStringList l;
539 l.append ("text/plain");
540 return l;
543 Qt::DropActions PwmdTreeWidget::supportedDropActions () const
545 return Qt::MoveAction | Qt::CopyAction | Qt::LinkAction;