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
23 #include "pwmdMainWindow.h"
24 #include "pwmdTreeWidget.h"
25 #include "pwmdTreeWidgetItemData.h"
28 static QMenu
*popupMenu
;
29 static QAction
*copyAction
, *moveAction
, *targetAction
;
32 PwmdTreeWidget::PwmdTreeWidget (QWidget
* p
) : QTreeWidget (p
)
36 expandTimer
= new QTimer (this);
37 expandTimer
->setSingleShot (true);
38 connect (expandTimer
, SIGNAL (timeout ()), this,
39 SLOT (slotExpandTimeout ()));
44 PwmdTreeWidget::setPwmdParent (PwmdMainWindow
*p
)
47 connect (p
, SIGNAL (attributesRetrieved (QTreeWidgetItem
*)), this,
48 SLOT (slotAttributesRetrieved (QTreeWidgetItem
*)));
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)));
59 PwmdTreeWidget::slotAttributesRetrieved (QTreeWidgetItem
*item
)
61 if (popupMenu
&& popupMenu
->isVisible ())
62 popupDragContextMenu (item
);
66 PwmdTreeWidget::resizeEvent (QResizeEvent
*ev
)
68 emit
treeWidgetResized (ev
->size ());
69 QTreeWidget::resizeEvent (ev
);
73 PwmdTreeWidget::keyPressEvent (QKeyEvent
* ev
)
77 QTreeWidget::keyPressEvent (ev
);
85 if (ev
->key () == Qt::Key_Return
&& ev
->nativeModifiers () == 8)
87 QCursor::setPos (mapToGlobal
88 (QPoint (visualItemRect (currentItem ()).topRight ())));
89 _parent
->popupContextMenu ();
93 else if (ev
->key () == Qt::Key_Escape
&& !ev
->nativeModifiers ())
95 _parent
->resetSelectedItems ();
100 QTreeWidget::keyPressEvent (ev
);
104 PwmdTreeWidget::mouseReleaseEvent (QMouseEvent
* ev
)
108 if (!_parent
|| ev
->button () != Qt::RightButton
)
111 _parent
->popupContextMenu ();
115 PwmdTreeWidget::mousePressEvent (QMouseEvent
* ev
)
117 if (_parent
&& _parent
->isBusy
)
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.
131 PwmdTreeWidget::dropIsChild (QTreeWidgetItem
* item
, QTreeWidgetItem
*drop
)
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
))
148 PwmdTreeWidget::isValidDndMove (QTreeWidgetItem
* drop
, bool move
)
150 PwmdTreeWidgetItemData
*data
= drop
? qvariant_cast
<PwmdTreeWidgetItemData
* >(drop
->data (0, Qt::UserRole
)) : nullptr;
157 if (data
->badTarget () || data
->badPermissions () || data
->targetLoop ())
160 else if (!drop
) // New root element
163 if (_parent
->isParentOf (_parent
->selectedElement
, drop
)
164 || _parent
->selectedElement
== drop
165 || (!_parent
->selectedElement
->parent () && !drop
))
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 ())
178 else if (!move
&& !_parent
->isElementOwner (_parent
->selectedElement
)
179 && data
->badTarget ())
183 for (; move
&& a
; a
= a
->parent ())
188 if (_parent
->isParentOf (_parent
->selectedElement
, dropElement
)
189 || _parent
->selectedElement
== a
190 || (!_parent
->selectedElement
->parent () && !a
))
199 for (; move
&& a
; a
= a
->parent ())
204 if (dropElement
== _parent
->resolveElementPath (b
->parent ()))
207 if (_parent
->isParentOf (_parent
->selectedElement
, a
)
208 || _parent
->selectedElement
== a
209 || (!_parent
->selectedElement
->parent () && !a
))
213 if (dropIsChild (srcElement
, dropElement
))
220 PwmdTreeWidget::mousePosition ()
222 return _mousePosition
;
226 PwmdTreeWidget::validTargetDropElement (QTreeWidgetItem
*item
)
231 PwmdTreeWidgetItemData
*data
= qvariant_cast
<PwmdTreeWidgetItemData
* >(item
->data (0, Qt::UserRole
));
232 if (!_parent
->isElementOwner (item
) && (data
->badTarget ()
233 || data
->targetLoop ()
234 || data
->badPermissions ()))
236 else if (_parent
->isParentOf (_parent
->selectedElement
, item
))
243 PwmdTreeWidget::slotBusy (int, bool b
)
248 refCount
+= b
? 1 : -1;
255 setPopupMenuItemsEnabled ();
261 PwmdTreeWidget::setPopupMenuItemsEnabled ()
263 if (!_parent
|| !copyAction
|| !moveAction
|| !targetAction
)
266 if (isBusy
|| !isValidDndMove (_parent
->dropElement
))
267 copyAction
->setEnabled (false);
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);
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);
284 targetAction
->setEnabled (true);
288 PwmdTreeWidget::popupDragContextMenu(QTreeWidgetItem
*item
)
290 PwmdTreeWidgetItemData
*data
= item
? qvariant_cast
<PwmdTreeWidgetItemData
* >(item
->data (0, Qt::UserRole
)) : nullptr;
292 if (item
&& item
!= _parent
->dropElement
)
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 ());
321 PwmdTreeWidget::mouseMoveEvent (QMouseEvent
* ev
)
324 _mousePosition
= ev
->pos ();
332 QTreeWidgetItem
*item
= itemAt(ev
->pos());
339 _parent
->setElementStatusTip (item
);
341 if (!ev
->buttons () && !ev
->modifiers ())
347 if ((ev
->pos () - dragStartPosition
).manhattanLength ()
348 < QApplication::startDragDistance ())
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 ();
375 _parent
->dndOperation
= PwmdMainWindow::DndNone
;
377 case Qt::AltModifier
:
378 _parent
->dndOperation
= PwmdMainWindow::DndAny
;
379 action
= drag
->exec (Qt::LinkAction
);
380 expandTimer
->stop ();
381 _parent
->slotElementEntered (nullptr, 0);
384 && action
== Qt::LinkAction
&& _parent
->dropElement
385 && validTargetDropElement (_parent
->dropElement
))
386 _parent
->slotDndCreateTarget ();
388 _parent
->dndOperation
= PwmdMainWindow::DndNone
;
398 else if (!ev
->modifiers () && (ev
->buttons () & Qt::LeftButton
))
400 else if (!ev
->modifiers () && (ev
->buttons () & Qt::RightButton
))
403 _parent
->dndOperation
= PwmdMainWindow::DndAny
;
404 drag
->exec (Qt::CopyAction
);
405 _parent
->slotElementEntered (nullptr, 0);
406 expandTimer
->stop ();
409 || drag
->target ()->parent () != _parent
->ui
.elementTree
)
411 _parent
->dndOperation
= PwmdMainWindow::DndNone
;
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);
425 _parent
->dndOperation
= PwmdMainWindow::DndAny
;
426 action
= drag
->exec (Qt::CopyAction
);
427 _parent
->slotElementEntered (nullptr, 0);
428 expandTimer
->stop ();
431 && drag
->target ()->parent () == _parent
->ui
.elementTree
432 && action
== Qt::CopyAction
)
434 if (ev
->source () == Qt::MouseEventSynthesizedBySystem
)
435 popupDragContextMenu ();
437 _parent
->slotDndMoveElement ();
440 _parent
->dndOperation
= PwmdMainWindow::DndNone
;
447 PwmdTreeWidget::dragMoveEvent (QDragMoveEvent
* ev
)
449 QTreeWidget::dragMoveEvent (ev
);
451 if (!_parent
|| !_parent
->selectedElement
)
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 ();
468 if (lastItem
== item
)
470 if (!expandTimer
->isActive ())
471 expandTimer
->start (500);
478 _parent
->slotElementEntered (item
, 0);
479 expandTimer
->stop ();
483 if (!isValidDndMove (item
, true))
488 expandTimer
->start (500);
497 expandTimer
->start (500);
504 PwmdTreeWidget::slotExpandTimeout ()
506 lastItem
->setExpanded (true);
510 PwmdTreeWidget::dragEnterEvent (QDragEnterEvent
* ev
)
512 if (!_parent
|| ev
->source () != _parent
->ui
.elementTree
)
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.
523 PwmdTreeWidget::dropMimeData (QTreeWidgetItem
* dst
, int, const QMimeData
*,
529 expandTimer
->stop ();
530 _parent
->dropElement
= dst
;
535 PwmdTreeWidget::mimeTypes () const
539 l
.append ("text/plain");
543 Qt::DropActions
PwmdTreeWidget::supportedDropActions () const
545 return Qt::MoveAction
| Qt::CopyAction
| Qt::LinkAction
;