Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
objectinspector.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3// Qt-Security score:significant reason:default
4
7#include "formwindow.h"
8
9// sdk
10#include <QtDesigner/abstractformeditor.h>
11#include <QtDesigner/taskmenu.h>
12#include <QtDesigner/qextensionmanager.h>
13#include <QtDesigner/abstractformwindowcursor.h>
14#include <QtDesigner/abstractformwindowmanager.h>
15#include <QtDesigner/container.h>
16#include <QtDesigner/abstractmetadatabase.h>
17#include <QtDesigner/abstractpropertyeditor.h>
18
19// shared
20#include <qdesigner_utils_p.h>
21#include <formwindowbase_p.h>
22#include <qdesigner_dnditem_p.h>
23#include <textpropertyeditor_p.h>
24#include <qdesigner_command_p.h>
25#include <grid_p.h>
26
27// Qt
28#include <QtWidgets/qapplication.h>
29#include <QtWidgets/qheaderview.h>
30#include <QtWidgets/qlineedit.h>
31#include <QtWidgets/qscrollbar.h>
32#include <QtGui/qpainter.h>
33#include <QtWidgets/qboxlayout.h>
34#include <QtCore/qitemselectionmodel.h>
35#include <QtWidgets/qmenu.h>
36#include <QtWidgets/qtreeview.h>
37#include <QtWidgets/qstyleditemdelegate.h>
38#include <QtGui/qevent.h>
39
40#include <QtCore/qdebug.h>
41#include <QtCore/qlist.h>
42#include <QtCore/qpointer.h>
43#include <QtCore/qsortfilterproxymodel.h>
44
46
47namespace {
48 // Selections: Basically, ObjectInspector has to ensure a consistent
49 // selection, that is, either form-managed widgets (represented
50 // by the cursor interface selection), or unmanaged widgets/objects,
51 // for example actions, container pages, menu bars, tool bars
52 // and the like. The selection state of the latter is managed only in the object inspector.
53 // As soon as a managed widget is selected, unmanaged objects
54 // have to be unselected
55 // Normally, an empty selection is not allowed, the main container
56 // should be selected in this case (applyCursorSelection()).
57 // An exception is when clearSelection is called directly for example
58 // by the action editor that puts an unassociated action into the property
59 // editor. A hack exists to avoid the update in this case.
60
63 // A QObject that has a meta database entry
65 // Unmanaged widget, menu bar or the like
67 // A widget managed by the form window cursor
69}
70
71static inline SelectionType selectionType(const QDesignerFormWindowInterface *fw, QObject *o)
72{
73 if (!o->isWidgetType())
74 return fw->core()->metaDataBase()->item(o) ? QObjectSelection : NoSelection;
75 return fw->isManaged(qobject_cast<QWidget *>(o)) ? ManagedWidgetSelection : UnmanagedWidgetSelection;
76}
77
78// Return an offset for dropping (when dropping widgets on the object
79// inspector, we fake a position on the form based on the widget dropped on).
80// Position the dropped widget with form grid offset to avoid overlapping unless we
81// drop on a layout. Position doesn't matter in the layout case
82// and this enables us to drop on a squeezed layout widget of size zero
83
84static inline QPoint dropPointOffset(const qdesigner_internal::FormWindowBase *fw, const QWidget *dropTarget)
85{
86 if (!dropTarget || dropTarget->layout())
87 return QPoint(0, 0);
88 return QPoint(fw->designerGrid().deltaX(), fw->designerGrid().deltaY());
89}
90
91namespace qdesigner_internal {
92// Delegate with object name validator for the object name column
94public:
96
97 QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
98};
99
100QWidget *ObjectInspectorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & option, const QModelIndex &index) const
101{
102 if (index.column() != ObjectInspectorModel::ObjectNameColumn)
103 return QStyledItemDelegate::createEditor(parent, option, index);
104 // Object name editor
105 const bool isMainContainer = !index.parent().isValid();
106 return new TextPropertyEditor(parent, TextPropertyEditor::EmbeddingTreeView,
107 isMainContainer ? ValidationObjectNameScope : ValidationObjectName);
108}
109
110// ------------ ObjectInspectorTreeView:
111// - Makes the Space key start editing
112// - Suppresses a range selection by dragging or Shift-up/down, which does not really work due
113// to the need to maintain a consistent selection.
114
116public:
117 using QTreeView::QTreeView;
118
119protected:
120 void mouseMoveEvent (QMouseEvent * event) override;
121 void keyPressEvent(QKeyEvent *event) override;
122
123};
124
126{
127 event->ignore(); // suppress a range selection by dragging
128}
129
131{
132 bool handled = false;
133 switch (event->key()) {
134 case Qt::Key_Up:
135 case Qt::Key_Down: // suppress shift-up/down range selection
136 if (event->modifiers() & Qt::ShiftModifier) {
137 event->ignore();
138 handled = true;
139 }
140 break;
141 case Qt::Key_Space: { // Space pressed: Start editing
142 const QModelIndex index = currentIndex();
143 if (index.isValid() && index.column() == 0 && !model()->hasChildren(index) && model()->flags(index) & Qt::ItemIsEditable) {
144 event->accept();
145 handled = true;
146 edit(index);
147 }
148 }
149 break;
150 default:
151 break;
152 }
153 if (!handled)
154 QTreeView::keyPressEvent(event);
155}
156
157// ------------ ObjectInspectorPrivate
158
161public:
164
165 QLineEdit *filterLineEdit() const { return m_filterLineEdit; }
166 QTreeView *treeView() const { return m_treeView; }
167 QDesignerFormEditorInterface *core() const { return m_core; }
168 const QPointer<FormWindowBase> &formWindow() const { return m_formWindow; }
169
170 void clear();
171 void setFormWindow(QDesignerFormWindowInterface *fwi);
172
173 QWidget *managedWidgetAt(const QPoint &global_mouse_pos);
174
176 void handleDragEnterMoveEvent(const QWidget *objectInspectorWidget, QDragMoveEvent * event, bool isDragEnter);
177 void dropEvent (QDropEvent * event);
178
180 bool selectObject(QObject *o);
181 void slotSelectionChanged(const QItemSelection & selected, const QItemSelection &deselected);
182 void getSelection(Selection &s) const;
183
184 QModelIndexList indexesOf(QObject *o) const;
185 QObject *objectAt(const QModelIndex &index) const;
186 QObjectList indexesToObjects(const QModelIndexList &indexes) const;
187
188 void slotHeaderDoubleClicked(int column) { m_treeView->resizeColumnToContents(column); }
189 void slotPopupContextMenu(QWidget *parent, const QPoint &pos);
190
191private:
192 void setFormWindowBlocked(QDesignerFormWindowInterface *fwi);
193 void applyCursorSelection();
194 void synchronizeSelection(const QItemSelection & selected, const QItemSelection &deselected);
195 bool checkManagedWidgetSelection(const QModelIndexList &selection);
196 void showContainersCurrentPage(QWidget *widget);
197
198 enum SelectionFlags { AddToSelection = 1, MakeCurrent = 2};
199 void selectIndexRange(const QModelIndexList &indexes, unsigned flags);
200
201 QDesignerFormEditorInterface *m_core;
202 QLineEdit *m_filterLineEdit;
203 QTreeView *m_treeView;
204 ObjectInspectorModel *m_model;
205 QSortFilterProxyModel *m_filterModel;
206 QPointer<FormWindowBase> m_formWindow;
207 QPointer<QWidget> m_formFakeDropTarget;
208 bool m_withinClearSelection;
209};
210
211ObjectInspector::ObjectInspectorPrivate::ObjectInspectorPrivate(QDesignerFormEditorInterface *core) :
212 m_core(core),
213 m_filterLineEdit(new QLineEdit),
214 m_treeView(new ObjectInspectorTreeView),
215 m_model(new ObjectInspectorModel(m_treeView)),
216 m_filterModel(new QSortFilterProxyModel(m_treeView)),
217 m_withinClearSelection(false)
218{
219 m_filterModel->setRecursiveFilteringEnabled(true);
220 m_filterLineEdit->setPlaceholderText(ObjectInspector::tr("Filter"));
221 m_filterLineEdit->setClearButtonEnabled(true);
222 connect(m_filterLineEdit, &QLineEdit::textChanged,
223 m_filterModel, &QSortFilterProxyModel::setFilterFixedString);
224 // Filtering text collapses nodes, expand on clear.
225 connect(m_filterLineEdit, &QLineEdit::textChanged,
226 m_core, [this] (const QString &text) {
227 if (text.isEmpty())
228 this->m_treeView->expandAll();
229 });
230 m_filterModel->setSourceModel(m_model);
231 m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
232 m_treeView->setModel(m_filterModel);
233 m_treeView->setSortingEnabled(true);
234 m_treeView->sortByColumn(0, Qt::AscendingOrder);
235 m_treeView->setItemDelegate(new ObjectInspectorDelegate);
236 m_treeView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
237 m_treeView->header()->setSectionResizeMode(1, QHeaderView::Stretch);
238 m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection);
239 m_treeView->setSelectionBehavior(QAbstractItemView::SelectRows);
240 m_treeView->setAlternatingRowColors(true);
241 m_treeView->setTextElideMode (Qt::ElideMiddle);
242
243 m_treeView->setContextMenuPolicy(Qt::CustomContextMenu);
244}
245
246ObjectInspector::ObjectInspectorPrivate::~ObjectInspectorPrivate()
247{
248 delete m_treeView->itemDelegate();
249}
250
251void ObjectInspector::ObjectInspectorPrivate::clearSelection()
252{
253 m_withinClearSelection = true;
254 m_treeView->clearSelection();
255 m_withinClearSelection = false;
256}
257
258QWidget *ObjectInspector::ObjectInspectorPrivate::managedWidgetAt(const QPoint &global_mouse_pos)
259{
260 if (!m_formWindow)
261 return nullptr;
262
263 const QPoint pos = m_treeView->viewport()->mapFromGlobal(global_mouse_pos);
264 QObject *o = objectAt(m_treeView->indexAt(pos));
265
266 if (!o || !o->isWidgetType())
267 return nullptr;
268
269 QWidget *rc = qobject_cast<QWidget *>(o);
270 if (!m_formWindow->isManaged(rc))
271 return nullptr;
272 return rc;
273}
274
275void ObjectInspector::ObjectInspectorPrivate::showContainersCurrentPage(QWidget *widget)
276{
277 if (!widget)
278 return;
279
280 FormWindow *fw = FormWindow::findFormWindow(widget);
281 if (!fw)
282 return;
283
284 QWidget *w = widget->parentWidget();
285 bool macroStarted = false;
286 // Find a multipage container (tab widgets, etc.) in the hierarchy and set the right page.
287 while (w != nullptr) {
288 if (fw->isManaged(w) && !qobject_cast<QMainWindow *>(w)) { // Rule out unmanaged internal scroll areas, for example, on QToolBoxes.
289 if (QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(m_core->extensionManager(), w)) {
290 const int count = c->count();
291 if (count > 1 && !c->widget(c->currentIndex())->isAncestorOf(widget)) {
292 for (int i = 0; i < count; i++)
293 if (c->widget(i)->isAncestorOf(widget)) {
294 if (!macroStarted) {
295 macroStarted = true;
296 fw->beginCommand(tr("Change Current Page"));
297 }
298 ChangeCurrentPageCommand *cmd = new ChangeCurrentPageCommand(fw);
299 cmd->init(w, i);
300 fw->commandHistory()->push(cmd);
301 break;
302 }
303 }
304 }
305 }
306 w = w->parentWidget();
307 }
308 if (macroStarted)
309 fw->endCommand();
310}
311
312void ObjectInspector::ObjectInspectorPrivate::restoreDropHighlighting()
313{
314 if (m_formFakeDropTarget) {
315 if (m_formWindow) {
316 m_formWindow->highlightWidget(m_formFakeDropTarget, QPoint(5, 5), FormWindow::Restore);
317 }
318 m_formFakeDropTarget = nullptr;
319 }
320}
321
322void ObjectInspector::ObjectInspectorPrivate::handleDragEnterMoveEvent(const QWidget *objectInspectorWidget, QDragMoveEvent * event, bool isDragEnter)
323{
324 if (!m_formWindow) {
325 event->ignore();
326 return;
327 }
328
329 const QDesignerMimeData *mimeData = qobject_cast<const QDesignerMimeData *>(event->mimeData());
330 if (!mimeData) {
331 event->ignore();
332 return;
333 }
334
335 QWidget *dropTarget = nullptr;
336 QPoint fakeDropTargetOffset = QPoint(0, 0);
337 if (QWidget *managedWidget = managedWidgetAt(objectInspectorWidget->mapToGlobal(event->position().toPoint()))) {
338 fakeDropTargetOffset = dropPointOffset(m_formWindow, managedWidget);
339 // pretend we drag over the managed widget on the form
340 const QPoint fakeFormPos = m_formWindow->mapFromGlobal(managedWidget->mapToGlobal(fakeDropTargetOffset));
341 const FormWindowBase::WidgetUnderMouseMode wum = mimeData->items().size() == 1 ? FormWindowBase::FindSingleSelectionDropTarget : FormWindowBase::FindMultiSelectionDropTarget;
342 dropTarget = m_formWindow->widgetUnderMouse(fakeFormPos, wum);
343 }
344
345 if (m_formFakeDropTarget && dropTarget != m_formFakeDropTarget)
346 m_formWindow->highlightWidget(m_formFakeDropTarget, fakeDropTargetOffset, FormWindow::Restore);
347
348 m_formFakeDropTarget = dropTarget;
349 if (m_formFakeDropTarget)
350 m_formWindow->highlightWidget(m_formFakeDropTarget, fakeDropTargetOffset, FormWindow::Highlight);
351
352 // Do not refuse drag enter even if the area is not droppable
353 if (isDragEnter || m_formFakeDropTarget)
354 mimeData->acceptEvent(event);
355 else
356 event->ignore();
357}
358void ObjectInspector::ObjectInspectorPrivate::dropEvent (QDropEvent * event)
359{
360 if (!m_formWindow || !m_formFakeDropTarget) {
361 event->ignore();
362 return;
363 }
364
365 const QDesignerMimeData *mimeData = qobject_cast<const QDesignerMimeData *>(event->mimeData());
366 if (!mimeData) {
367 event->ignore();
368 return;
369 }
370 const QPoint fakeGlobalDropFormPos = m_formFakeDropTarget->mapToGlobal(dropPointOffset(m_formWindow , m_formFakeDropTarget));
371 mimeData->moveDecoration(fakeGlobalDropFormPos + mimeData->hotSpot());
372 if (!m_formWindow->dropWidgets(mimeData->items(), m_formFakeDropTarget, fakeGlobalDropFormPos)) {
373 event->ignore();
374 return;
375 }
376 mimeData->acceptEvent(event);
377}
378
379QModelIndexList ObjectInspector::ObjectInspectorPrivate::indexesOf(QObject *o) const
380{
381 QModelIndexList result;
382 const auto srcIndexes = m_model->indexesOf(o);
383 if (!srcIndexes.isEmpty()) {
384 result.reserve(srcIndexes.size());
385 for (const auto &srcIndex : srcIndexes)
386 result.append(m_filterModel->mapFromSource(srcIndex));
387 }
388 return result;
389}
390
391QObject *ObjectInspector::ObjectInspectorPrivate::objectAt(const QModelIndex &index) const
392{
393 return m_model->objectAt(m_filterModel->mapToSource(index));
394}
395
396bool ObjectInspector::ObjectInspectorPrivate::selectObject(QObject *o)
397{
398 if (!m_core->metaDataBase()->item(o))
399 return false;
400
401 using ModelIndexSet = QSet<QModelIndex>;
402
403 const QModelIndexList objectIndexes = indexesOf(o);
404 if (objectIndexes.isEmpty())
405 return false;
406
407 QItemSelectionModel *selectionModel = m_treeView->selectionModel();
408 const auto currentSelectedItemList = selectionModel->selectedRows(0);
409 const ModelIndexSet currentSelectedItems(currentSelectedItemList.cbegin(), currentSelectedItemList.cend());
410
411 // Change in selection?
412 if (!currentSelectedItems.isEmpty()
413 && currentSelectedItems == ModelIndexSet(objectIndexes.cbegin(), objectIndexes.cend())) {
414 return true;
415 }
416
417 // do select and update
418 selectIndexRange(objectIndexes, MakeCurrent);
419 return true;
420}
421
422void ObjectInspector::ObjectInspectorPrivate::selectIndexRange(const QModelIndexList &indexes, unsigned flags)
423{
424 if (indexes.isEmpty())
425 return;
426
427 QItemSelectionModel::SelectionFlags selectFlags = QItemSelectionModel::Select|QItemSelectionModel::Rows;
428 if (!(flags & AddToSelection))
429 selectFlags |= QItemSelectionModel::Clear;
430 if (flags & MakeCurrent)
431 selectFlags |= QItemSelectionModel::Current;
432
433 QItemSelectionModel *selectionModel = m_treeView->selectionModel();
434 for (const auto &mi : indexes) {
435 if (mi.column() == 0) {
436 selectionModel->select(mi, selectFlags);
437 selectFlags &= ~(QItemSelectionModel::Clear|QItemSelectionModel::Current);
438 }
439 }
440 if (flags & MakeCurrent)
441 m_treeView->scrollTo(indexes.constFirst(), QAbstractItemView::EnsureVisible);
442}
443
444void ObjectInspector::ObjectInspectorPrivate::clear()
445{
446 m_formFakeDropTarget = nullptr;
447 m_formWindow = nullptr;
448}
449
450// Form window cursor is in state 'main container only'
451static inline bool mainContainerIsCurrent(const QDesignerFormWindowInterface *fw)
452{
453 const QDesignerFormWindowCursorInterface *cursor = fw->cursor();
454 if (cursor->selectedWidgetCount() > 1)
455 return false;
456 const QWidget *current = cursor->current();
457 return current == fw || current == fw->mainContainer();
458}
459
460void ObjectInspector::ObjectInspectorPrivate::setFormWindow(QDesignerFormWindowInterface *fwi)
461{
462 const bool blocked = m_treeView->selectionModel()->blockSignals(true);
463 {
464 UpdateBlocker ub(m_treeView);
465 setFormWindowBlocked(fwi);
466 }
467
468 m_treeView->update();
469 m_treeView->selectionModel()->blockSignals(blocked);
470}
471
472void ObjectInspector::ObjectInspectorPrivate::setFormWindowBlocked(QDesignerFormWindowInterface *fwi)
473{
474 FormWindowBase *fw = qobject_cast<FormWindowBase *>(fwi);
475 const bool formWindowChanged = m_formWindow != fw;
476
477 m_formWindow = fw;
478
479 const int oldWidth = m_treeView->columnWidth(0);
480 const int xoffset = m_treeView->horizontalScrollBar()->value();
481 const int yoffset = m_treeView->verticalScrollBar()->value();
482
483 if (formWindowChanged)
484 m_formFakeDropTarget = nullptr;
485
486 switch (m_model->update(m_formWindow)) {
487 case ObjectInspectorModel::NoForm:
488 clear();
489 return;
490 case ObjectInspectorModel::Rebuilt: // Complete rebuild: Just apply cursor selection
491 applyCursorSelection();
492 m_treeView->expandAll();
493 if (formWindowChanged) {
494 m_treeView->resizeColumnToContents(0);
495 } else {
496 m_treeView->setColumnWidth(0, oldWidth);
497 m_treeView->horizontalScrollBar()->setValue(xoffset);
498 m_treeView->verticalScrollBar()->setValue(yoffset);
499 }
500 break;
501 case ObjectInspectorModel::Updated: {
502 // Same structure (property changed or click on the form)
503 // We maintain a selection of unmanaged objects
504 // only if the cursor is in state "mainContainer() == current".
505 // and we have a non-managed selection.
506 // Else we take over the cursor selection.
507 bool applySelection = !mainContainerIsCurrent(m_formWindow);
508 if (!applySelection) {
509 const QModelIndexList currentIndexes = m_treeView->selectionModel()->selectedRows(0);
510 if (currentIndexes.isEmpty()) {
511 applySelection = true;
512 } else {
513 applySelection = selectionType(m_formWindow, objectAt(currentIndexes.constFirst())) == ManagedWidgetSelection;
514 }
515 }
516 if (applySelection)
517 applyCursorSelection();
518 }
519 break;
520 }
521}
522
523// Apply selection of form window cursor to object inspector, set current
524void ObjectInspector::ObjectInspectorPrivate::applyCursorSelection()
525{
526 const QDesignerFormWindowCursorInterface *cursor = m_formWindow->cursor();
527 const int count = cursor->selectedWidgetCount();
528 if (!count)
529 return;
530
531 // Set the current widget first which also clears the selection
532 QWidget *currentWidget = cursor->current();
533 if (currentWidget)
534 selectIndexRange(indexesOf(currentWidget), MakeCurrent);
535 else
536 m_treeView->selectionModel()->clearSelection();
537
538 for (int i = 0;i < count; i++) {
539 QWidget *widget = cursor->selectedWidget(i);
540 if (widget != currentWidget)
541 selectIndexRange(indexesOf(widget), AddToSelection);
542 }
543}
544
545// Synchronize managed widget in the form (select in cursor). Block updates
546static int selectInCursor(FormWindowBase *fw, const QObjectList &objects, bool value)
547{
548 int rc = 0;
549 const bool blocked = fw->blockSelectionChanged(true);
550 for (auto *o : objects) {
551 if (selectionType(fw, o) == ManagedWidgetSelection) {
552 fw->selectWidget(static_cast<QWidget *>(o), value);
553 rc++;
554 }
555 }
556 fw->blockSelectionChanged(blocked);
557 return rc;
558}
559
560void ObjectInspector::ObjectInspectorPrivate::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
561{
562 if (m_formWindow) {
563 synchronizeSelection(selected, deselected);
564 QMetaObject::invokeMethod(m_core->formWindowManager(), "slotUpdateActions");
565 }
566}
567
568// Convert indexes to object vectors taking into account that
569// some index lists are multicolumn ranges
570QObjectList ObjectInspector::ObjectInspectorPrivate::indexesToObjects(const QModelIndexList &indexes) const
571{
572 QObjectList rc;
573 if (indexes.isEmpty())
574 return rc;
575 rc.reserve(indexes.size());
576 for (const auto &mi : indexes) {
577 if (mi.column() == 0)
578 rc.append(objectAt(mi));
579 }
580 return rc;
581}
582
583// Check if any managed widgets are selected. If so, iterate over
584// selection and deselect all unmanaged objects
585bool ObjectInspector::ObjectInspectorPrivate::checkManagedWidgetSelection(const QModelIndexList &rowSelection)
586{
587 bool isManagedWidgetSelection = false;
588 QItemSelectionModel *selectionModel = m_treeView->selectionModel();
589 for (const auto &mi : rowSelection) {
590 QObject *object = objectAt(mi);
591 if (selectionType(m_formWindow, object) == ManagedWidgetSelection) {
592 isManagedWidgetSelection = true;
593 break;
594 }
595 }
596
597 if (!isManagedWidgetSelection)
598 return false;
599 // Need to unselect unmanaged ones
600 const bool blocked = selectionModel->blockSignals(true);
601 for (const auto &mi : rowSelection) {
602 QObject *object = objectAt(mi);
603 if (selectionType(m_formWindow, object) != ManagedWidgetSelection)
604 selectionModel->select(mi, QItemSelectionModel::Deselect|QItemSelectionModel::Rows);
605 }
606 selectionModel->blockSignals(blocked);
607 return true;
608}
609
610void ObjectInspector::ObjectInspectorPrivate::synchronizeSelection(const QItemSelection & selectedSelection, const QItemSelection &deselectedSelection)
611{
612 // Synchronize form window cursor.
613 const QObjectList deselected = indexesToObjects(deselectedSelection.indexes());
614 const QObjectList newlySelected = indexesToObjects(selectedSelection.indexes());
615
616 const QModelIndexList currentSelectedIndexes = m_treeView->selectionModel()->selectedRows(0);
617
618 int deselectedManagedWidgetCount = 0;
619 if (!deselected.isEmpty())
620 deselectedManagedWidgetCount = selectInCursor(m_formWindow, deselected, false);
621
622 if (newlySelected.isEmpty()) { // Nothing selected
623 if (currentSelectedIndexes.isEmpty()) // Do not allow a null-selection, reset to main container
624 m_formWindow->clearSelection(!m_withinClearSelection);
625 return;
626 }
627
628 const int selectManagedWidgetCount = selectInCursor(m_formWindow, newlySelected, true);
629 // Check consistency: Make sure either managed widgets or unmanaged objects are selected.
630 // No newly-selected managed widgets: Unless there are ones in the (old) current selection,
631 // select the unmanaged object
632 if (selectManagedWidgetCount == 0) {
633 if (checkManagedWidgetSelection(currentSelectedIndexes)) {
634 // Managed selection exists, refuse and update if necessary
635 if (deselectedManagedWidgetCount != 0 || selectManagedWidgetCount != 0)
636 m_formWindow->emitSelectionChanged();
637 return;
638 }
639 // And now for the unmanaged selection
640 m_formWindow->clearSelection(false);
641 QObject *unmanagedObject = newlySelected.constFirst();
642 m_core->propertyEditor()->setObject(unmanagedObject);
643 m_core->propertyEditor()->setEnabled(true);
644 // open container page if it is a single widget
645 if (newlySelected.size() == 1 && unmanagedObject->isWidgetType())
646 showContainersCurrentPage(static_cast<QWidget*>(unmanagedObject));
647 return;
648 }
649 // Open container page if it is a single widget
650 if (newlySelected.size() == 1) {
651 QObject *object = newlySelected.constFirst();
652 if (object->isWidgetType())
653 showContainersCurrentPage(static_cast<QWidget*>(object));
654 }
655
656 // A managed widget was newly selected. Make sure there are no unmanaged objects
657 // in the whole unless just single selection
658 if (currentSelectedIndexes.size() > selectManagedWidgetCount)
659 checkManagedWidgetSelection(currentSelectedIndexes);
660 // Update form
661 if (deselectedManagedWidgetCount != 0 || selectManagedWidgetCount != 0)
662 m_formWindow->emitSelectionChanged();
663}
664
665
666void ObjectInspector::ObjectInspectorPrivate::getSelection(Selection &s) const
667{
668 s.clear();
669
670 if (!m_formWindow)
671 return;
672
673 const QModelIndexList currentSelectedIndexes = m_treeView->selectionModel()->selectedRows(0);
674 if (currentSelectedIndexes.isEmpty())
675 return;
676
677 // sort objects
678 for (const QModelIndex &index : currentSelectedIndexes) {
679 if (QObject *object = objectAt(index)) {
680 switch (selectionType(m_formWindow, object)) {
681 case NoSelection:
682 break;
683 case QObjectSelection:
684 // It is actually possible to select an action twice if it is in a menu bar
685 // and in a tool bar.
686 if (!s.objects.contains(object))
687 s.objects.push_back(object);
688 break;
689 case UnmanagedWidgetSelection:
690 s.unmanaged.push_back(qobject_cast<QWidget *>(object));
691 break;
692 case ManagedWidgetSelection:
693 s.managed.push_back(qobject_cast<QWidget *>(object));
694 break;
695 }
696 }
697 }
698}
699
700// Utility to create a task menu
701static inline QMenu *createTaskMenu(QObject *object, QDesignerFormWindowInterface *fw)
702{
703 // 1) Objects
704 if (!object->isWidgetType())
705 return FormWindowBase::createExtensionTaskMenu(fw, object, false);
706 // 2) Unmanaged widgets
707 QWidget *w = static_cast<QWidget *>(object);
708 if (!fw->isManaged(w))
709 return FormWindowBase::createExtensionTaskMenu(fw, w, false);
710 // 3) Mananaged widgets
711 if (qdesigner_internal::FormWindowBase *fwb = qobject_cast<qdesigner_internal::FormWindowBase*>(fw))
712 return fwb->initializePopupMenu(w);
713 return nullptr;
714}
715
716void ObjectInspector::ObjectInspectorPrivate::slotPopupContextMenu(QWidget * /*parent*/, const QPoint &pos)
717{
718 if (m_formWindow == nullptr || m_formWindow->currentTool() != 0)
719 return;
720
721 if (QObject *object = objectAt(m_treeView->indexAt(pos))) {
722 if (QMenu *menu = createTaskMenu(object, m_formWindow)) {
723 menu->exec(m_treeView->viewport()->mapToGlobal(pos));
724 delete menu;
725 }
726 }
727}
728
729// ------------ ObjectInspector
730ObjectInspector::ObjectInspector(QDesignerFormEditorInterface *core, QWidget *parent) :
731 QDesignerObjectInspector(parent),
732 m_impl(new ObjectInspectorPrivate(core))
733{
734 QVBoxLayout *vbox = new QVBoxLayout(this);
735 vbox->setContentsMargins(QMargins());
736
737 vbox->addWidget(m_impl->filterLineEdit());
738 QTreeView *treeView = m_impl->treeView();
739 vbox->addWidget(treeView);
740
741 connect(treeView, &QWidget::customContextMenuRequested,
742 this, &ObjectInspector::slotPopupContextMenu);
743
744 connect(treeView->selectionModel(), &QItemSelectionModel::selectionChanged,
745 this, &ObjectInspector::slotSelectionChanged);
746
747 connect(treeView->header(), &QHeaderView::sectionDoubleClicked,
748 this, &ObjectInspector::slotHeaderDoubleClicked);
749 setAcceptDrops(true);
750}
751
752ObjectInspector::~ObjectInspector()
753{
754 delete m_impl;
755}
756
757QDesignerFormEditorInterface *ObjectInspector::core() const
758{
759 return m_impl->core();
760}
761
762void ObjectInspector::slotPopupContextMenu(const QPoint &pos)
763{
764 m_impl->slotPopupContextMenu(this, pos);
765}
766
767void ObjectInspector::setFormWindow(QDesignerFormWindowInterface *fwi)
768{
769 m_impl->setFormWindow(fwi);
770}
771
772void ObjectInspector::slotSelectionChanged(const QItemSelection & selected, const QItemSelection &deselected)
773{
774 m_impl->slotSelectionChanged(selected, deselected);
775}
776
777void ObjectInspector::getSelection(Selection &s) const
778{
779 m_impl->getSelection(s);
780}
781
782bool ObjectInspector::selectObject(QObject *o)
783{
784 return m_impl->selectObject(o);
785}
786
787void ObjectInspector::clearSelection()
788{
789 m_impl->clearSelection();
790}
791
792void ObjectInspector::slotHeaderDoubleClicked(int column)
793{
794 m_impl->slotHeaderDoubleClicked(column);
795}
796
797void ObjectInspector::mainContainerChanged()
798{
799 // Invalidate references to objects kept in items
800 if (sender() == m_impl->formWindow())
801 setFormWindow(nullptr);
802}
803
804void ObjectInspector::dragEnterEvent (QDragEnterEvent * event)
805{
806 m_impl->handleDragEnterMoveEvent(this, event, true);
807}
808
809void ObjectInspector::dragMoveEvent(QDragMoveEvent * event)
810{
811 m_impl->handleDragEnterMoveEvent(this, event, false);
812}
813
814void ObjectInspector::dragLeaveEvent(QDragLeaveEvent * /* event*/)
815{
816 m_impl->restoreDropHighlighting();
817}
818
819void ObjectInspector::dropEvent (QDropEvent * event)
820{
821 m_impl->dropEvent(event);
822
823QT_END_NAMESPACE
824}
825}
friend class QWidget
Definition qpainter.h:432
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Returns the editor to be used for editing the data item with the given index.
void mouseMoveEvent(QMouseEvent *event) override
void keyPressEvent(QKeyEvent *event) override
QObjectList indexesToObjects(const QModelIndexList &indexes) const
QWidget * managedWidgetAt(const QPoint &global_mouse_pos)
void slotPopupContextMenu(QWidget *parent, const QPoint &pos)
const QPointer< FormWindowBase > & formWindow() const
void handleDragEnterMoveEvent(const QWidget *objectInspectorWidget, QDragMoveEvent *event, bool isDragEnter)
void slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
Combined button and popup list for selecting options.
Auxiliary methods to store/retrieve settings.
static SelectionType selectionType(const QDesignerFormWindowInterface *fw, QObject *o)
static QPoint dropPointOffset(const qdesigner_internal::FormWindowBase *fw, const QWidget *dropTarget)