diff options
Diffstat (limited to 'src/designer/src/components/objectinspector/objectinspector.cpp')
-rw-r--r-- | src/designer/src/components/objectinspector/objectinspector.cpp | 835 |
1 files changed, 835 insertions, 0 deletions
diff --git a/src/designer/src/components/objectinspector/objectinspector.cpp b/src/designer/src/components/objectinspector/objectinspector.cpp new file mode 100644 index 000000000..8bcd49e15 --- /dev/null +++ b/src/designer/src/components/objectinspector/objectinspector.cpp @@ -0,0 +1,835 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "objectinspector.h" +#include "objectinspectormodel_p.h" +#include "formwindow.h" + +// sdk +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerTaskMenuExtension> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerFormWindowCursorInterface> +#include <QtDesigner/QDesignerFormWindowManagerInterface> +#include <QtDesigner/QDesignerContainerExtension> +#include <QtDesigner/QDesignerMetaDataBaseInterface> +#include <QtDesigner/QDesignerPropertyEditorInterface> + +// shared +#include <qdesigner_utils_p.h> +#include <formwindowbase_p.h> +#include <itemviewfindwidget.h> +#include <qdesigner_dnditem_p.h> +#include <textpropertyeditor_p.h> +#include <qdesigner_command_p.h> +#include <grid_p.h> + +// Qt +#include <QtGui/QApplication> +#include <QtGui/QHeaderView> +#include <QtGui/QScrollBar> +#include <QtGui/QPainter> +#include <QtGui/QVBoxLayout> +#include <QtGui/QItemSelectionModel> +#include <QtGui/QMenu> +#include <QtGui/QTreeView> +#include <QtGui/QItemDelegate> +#include <QtGui/qevent.h> + +#include <QtCore/QVector> +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE + +namespace { + // Selections: Basically, ObjectInspector has to ensure a consistent + // selection, that is, either form-managed widgets (represented + // by the cursor interface selection), or unmanaged widgets/objects, + // for example actions, container pages, menu bars, tool bars + // and the like. The selection state of the latter is managed only in the object inspector. + // As soon as a managed widget is selected, unmanaged objects + // have to be unselected + // Normally, an empty selection is not allowed, the main container + // should be selected in this case (applyCursorSelection()). + // An exception is when clearSelection is called directly for example + // by the action editor that puts an unassociated action into the property + // editor. A hack exists to avoid the update in this case. + + enum SelectionType { + NoSelection, + // A QObject that has a meta database entry + QObjectSelection, + // Unmanaged widget, menu bar or the like + UnmanagedWidgetSelection, + // A widget managed by the form window cursor + ManagedWidgetSelection }; + + typedef QVector<QObject*> QObjectVector; +} + +static inline SelectionType selectionType(const QDesignerFormWindowInterface *fw, QObject *o) +{ + if (!o->isWidgetType()) + return fw->core()->metaDataBase()->item(o) ? QObjectSelection : NoSelection; + return fw->isManaged(qobject_cast<QWidget *>(o)) ? ManagedWidgetSelection : UnmanagedWidgetSelection; +} + +// Return an offset for dropping (when dropping widgets on the object +// inspector, we fake a position on the form based on the widget dropped on). +// Position the dropped widget with form grid offset to avoid overlapping unless we +// drop on a layout. Position doesn't matter in the layout case +// and this enables us to drop on a squeezed layout widget of size zero + +static inline QPoint dropPointOffset(const qdesigner_internal::FormWindowBase *fw, const QWidget *dropTarget) +{ + if (!dropTarget || dropTarget->layout()) + return QPoint(0, 0); + return QPoint(fw->designerGrid().deltaX(), fw->designerGrid().deltaY()); +} + +namespace qdesigner_internal { +// Delegate with object name validator for the object name column +class ObjectInspectorDelegate : public QItemDelegate { +public: + explicit ObjectInspectorDelegate(QObject *parent = 0); + + virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; + +ObjectInspectorDelegate::ObjectInspectorDelegate(QObject *parent) : + QItemDelegate(parent) +{ +} + +QWidget *ObjectInspectorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & option, const QModelIndex &index) const +{ + if (index.column() != ObjectInspectorModel::ObjectNameColumn) + return QItemDelegate::createEditor(parent, option, index); + // Object name editor + const bool isMainContainer = !index.parent().isValid(); + return new TextPropertyEditor(parent, TextPropertyEditor::EmbeddingTreeView, + isMainContainer ? ValidationObjectNameScope : ValidationObjectName); +} + +// ------------ ObjectInspectorTreeView: +// - Makes the Space key start editing +// - Suppresses a range selection by dragging or Shift-up/down, which does not really work due +// to the need to maintain a consistent selection. + +class ObjectInspectorTreeView : public QTreeView { +public: + ObjectInspectorTreeView(QWidget *parent = 0) : QTreeView(parent) {} + +protected: + virtual void mouseMoveEvent (QMouseEvent * event); + virtual void keyPressEvent(QKeyEvent *event); + +}; + +void ObjectInspectorTreeView::mouseMoveEvent(QMouseEvent *event) +{ + event->ignore(); // suppress a range selection by dragging +} + +void ObjectInspectorTreeView::keyPressEvent(QKeyEvent *event) +{ + bool handled = false; + switch (event->key()) { + case Qt::Key_Up: + case Qt::Key_Down: // suppress shift-up/down range selection + if (event->modifiers() & Qt::ShiftModifier) { + event->ignore(); + handled = true; + } + break; + case Qt::Key_Space: { // Space pressed: Start editing + const QModelIndex index = currentIndex(); + if (index.isValid() && index.column() == 0 && !model()->hasChildren(index) && model()->flags(index) & Qt::ItemIsEditable) { + event->accept(); + handled = true; + edit(index); + } + } + break; + default: + break; + } + if (!handled) + QTreeView::keyPressEvent(event); +} + +// ------------ ObjectInspectorPrivate + +class ObjectInspector::ObjectInspectorPrivate { +public: + ObjectInspectorPrivate(QDesignerFormEditorInterface *core); + ~ObjectInspectorPrivate(); + + QTreeView *treeView() const { return m_treeView; } + ItemViewFindWidget *findWidget() const { return m_findWidget; } + QDesignerFormEditorInterface *core() const { return m_core; } + const QPointer<FormWindowBase> &formWindow() const { return m_formWindow; } + + void clear(); + void setFormWindow(QDesignerFormWindowInterface *fwi); + + QWidget *managedWidgetAt(const QPoint &global_mouse_pos); + + void restoreDropHighlighting(); + void handleDragEnterMoveEvent(const QWidget *objectInspectorWidget, QDragMoveEvent * event, bool isDragEnter); + void dropEvent (QDropEvent * event); + + void clearSelection(); + bool selectObject(QObject *o); + void slotSelectionChanged(const QItemSelection & selected, const QItemSelection &deselected); + void getSelection(Selection &s) const; + + void slotHeaderDoubleClicked(int column) { m_treeView->resizeColumnToContents(column); } + void slotPopupContextMenu(QWidget *parent, const QPoint &pos); + +private: + void setFormWindowBlocked(QDesignerFormWindowInterface *fwi); + void applyCursorSelection(); + void synchronizeSelection(const QItemSelection & selected, const QItemSelection &deselected); + bool checkManagedWidgetSelection(const QModelIndexList &selection); + void showContainersCurrentPage(QWidget *widget); + + enum SelectionFlags { AddToSelection = 1, MakeCurrent = 2}; + void selectIndexRange(const QModelIndexList &indexes, unsigned flags); + + QDesignerFormEditorInterface *m_core; + QTreeView *m_treeView; + ObjectInspectorModel *m_model; + ItemViewFindWidget *m_findWidget; + QPointer<FormWindowBase> m_formWindow; + QPointer<QWidget> m_formFakeDropTarget; + bool m_withinClearSelection; +}; + +ObjectInspector::ObjectInspectorPrivate::ObjectInspectorPrivate(QDesignerFormEditorInterface *core) : + m_core(core), + m_treeView(new ObjectInspectorTreeView), + m_model(new ObjectInspectorModel(m_treeView)), + m_findWidget(new ItemViewFindWidget( + ItemViewFindWidget::NarrowLayout | ItemViewFindWidget::NoWholeWords)), + m_withinClearSelection(false) +{ + m_treeView->setModel(m_model); + m_treeView->setItemDelegate(new ObjectInspectorDelegate); + m_treeView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + m_treeView->header()->setResizeMode(1, QHeaderView::Stretch); + m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_treeView->setAlternatingRowColors(true); + m_treeView->setTextElideMode (Qt::ElideMiddle); + + m_treeView->setContextMenuPolicy(Qt::CustomContextMenu); +} + +ObjectInspector::ObjectInspectorPrivate::~ObjectInspectorPrivate() +{ + delete m_treeView->itemDelegate(); +} + +void ObjectInspector::ObjectInspectorPrivate::clearSelection() +{ + m_withinClearSelection = true; + m_treeView->clearSelection(); + m_withinClearSelection = false; +} + +QWidget *ObjectInspector::ObjectInspectorPrivate::managedWidgetAt(const QPoint &global_mouse_pos) +{ + if (!m_formWindow) + return 0; + + const QPoint pos = m_treeView->viewport()->mapFromGlobal(global_mouse_pos); + QObject *o = m_model->objectAt(m_treeView->indexAt(pos)); + + if (!o || !o->isWidgetType()) + return 0; + + QWidget *rc = qobject_cast<QWidget *>(o); + if (!m_formWindow->isManaged(rc)) + return 0; + return rc; +} + +void ObjectInspector::ObjectInspectorPrivate::showContainersCurrentPage(QWidget *widget) +{ + if (!widget) + return; + + FormWindow *fw = FormWindow::findFormWindow(widget); + if (!fw) + return; + + QWidget *w = widget->parentWidget(); + bool macroStarted = false; + // Find a multipage container (tab widgets, etc.) in the hierarchy and set the right page. + while (w != 0) { + if (fw->isManaged(w)) { // Rule out unmanaged internal scroll areas, for example, on QToolBoxes. + if (QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(m_core->extensionManager(), w)) { + const int count = c->count(); + if (count > 1 && !c->widget(c->currentIndex())->isAncestorOf(widget)) { + for (int i = 0; i < count; i++) + if (c->widget(i)->isAncestorOf(widget)) { + if (macroStarted == false) { + macroStarted = true; + fw->beginCommand(tr("Change Current Page")); + } + ChangeCurrentPageCommand *cmd = new ChangeCurrentPageCommand(fw); + cmd->init(w, i); + fw->commandHistory()->push(cmd); + break; + } + } + } + } + w = w->parentWidget(); + } + if (macroStarted == true) + fw->endCommand(); +} + +void ObjectInspector::ObjectInspectorPrivate::restoreDropHighlighting() +{ + if (m_formFakeDropTarget) { + if (m_formWindow) { + m_formWindow->highlightWidget(m_formFakeDropTarget, QPoint(5, 5), FormWindow::Restore); + } + m_formFakeDropTarget = 0; + } +} + +void ObjectInspector::ObjectInspectorPrivate::handleDragEnterMoveEvent(const QWidget *objectInspectorWidget, QDragMoveEvent * event, bool isDragEnter) +{ + if (!m_formWindow) { + event->ignore(); + return; + } + + const QDesignerMimeData *mimeData = qobject_cast<const QDesignerMimeData *>(event->mimeData()); + if (!mimeData) { + event->ignore(); + return; + } + + QWidget *dropTarget = 0; + QPoint fakeDropTargetOffset = QPoint(0, 0); + if (QWidget *managedWidget = managedWidgetAt(objectInspectorWidget->mapToGlobal(event->pos()))) { + fakeDropTargetOffset = dropPointOffset(m_formWindow, managedWidget); + // pretend we drag over the managed widget on the form + const QPoint fakeFormPos = m_formWindow->mapFromGlobal(managedWidget->mapToGlobal(fakeDropTargetOffset)); + const FormWindowBase::WidgetUnderMouseMode wum = mimeData->items().size() == 1 ? FormWindowBase::FindSingleSelectionDropTarget : FormWindowBase::FindMultiSelectionDropTarget; + dropTarget = m_formWindow->widgetUnderMouse(fakeFormPos, wum); + } + + if (m_formFakeDropTarget && dropTarget != m_formFakeDropTarget) + m_formWindow->highlightWidget(m_formFakeDropTarget, fakeDropTargetOffset, FormWindow::Restore); + + m_formFakeDropTarget = dropTarget; + if (m_formFakeDropTarget) + m_formWindow->highlightWidget(m_formFakeDropTarget, fakeDropTargetOffset, FormWindow::Highlight); + + // Do not refuse drag enter even if the area is not droppable + if (isDragEnter || m_formFakeDropTarget) + mimeData->acceptEvent(event); + else + event->ignore(); +} +void ObjectInspector::ObjectInspectorPrivate::dropEvent (QDropEvent * event) +{ + if (!m_formWindow || !m_formFakeDropTarget) { + event->ignore(); + return; + } + + const QDesignerMimeData *mimeData = qobject_cast<const QDesignerMimeData *>(event->mimeData()); + if (!mimeData) { + event->ignore(); + return; + } + const QPoint fakeGlobalDropFormPos = m_formFakeDropTarget->mapToGlobal(dropPointOffset(m_formWindow , m_formFakeDropTarget)); + mimeData->moveDecoration(fakeGlobalDropFormPos + mimeData->hotSpot()); + if (!m_formWindow->dropWidgets(mimeData->items(), m_formFakeDropTarget, fakeGlobalDropFormPos)) { + event->ignore(); + return; + } + mimeData->acceptEvent(event); +} + +bool ObjectInspector::ObjectInspectorPrivate::selectObject(QObject *o) +{ + if (!m_core->metaDataBase()->item(o)) + return false; + + typedef QSet<QModelIndex> ModelIndexSet; + + const QModelIndexList objectIndexes = m_model->indexesOf(o); + if (objectIndexes.empty()) + return false; + + QItemSelectionModel *selectionModel = m_treeView->selectionModel(); + const ModelIndexSet currentSelectedItems = selectionModel->selectedRows(0).toSet(); + + // Change in selection? + if (!currentSelectedItems.empty() && currentSelectedItems == objectIndexes.toSet()) + return true; + + // do select and update + selectIndexRange(objectIndexes, MakeCurrent); + return true; +} + +void ObjectInspector::ObjectInspectorPrivate::selectIndexRange(const QModelIndexList &indexes, unsigned flags) +{ + if (indexes.empty()) + return; + + QItemSelectionModel::SelectionFlags selectFlags = QItemSelectionModel::Select|QItemSelectionModel::Rows; + if (!(flags & AddToSelection)) + selectFlags |= QItemSelectionModel::Clear; + if (flags & MakeCurrent) + selectFlags |= QItemSelectionModel::Current; + + QItemSelectionModel *selectionModel = m_treeView->selectionModel(); + const QModelIndexList::const_iterator cend = indexes.constEnd(); + for (QModelIndexList::const_iterator it = indexes.constBegin(); it != cend; ++it) + if (it->column() == 0) { + selectionModel->select(*it, selectFlags); + selectFlags &= ~(QItemSelectionModel::Clear|QItemSelectionModel::Current); + } + if (flags & MakeCurrent) + m_treeView->scrollTo(indexes.front(), QAbstractItemView::EnsureVisible); +} + +void ObjectInspector::ObjectInspectorPrivate::clear() +{ + m_formFakeDropTarget = 0; + m_formWindow = 0; +} + +// Form window cursor is in state 'main container only' +static inline bool mainContainerIsCurrent(const QDesignerFormWindowInterface *fw) +{ + const QDesignerFormWindowCursorInterface *cursor = fw->cursor(); + if (cursor->selectedWidgetCount() > 1) + return false; + const QWidget *current = cursor->current(); + return current == fw || current == fw->mainContainer(); +} + +void ObjectInspector::ObjectInspectorPrivate::setFormWindow(QDesignerFormWindowInterface *fwi) +{ + const bool blocked = m_treeView->selectionModel()->blockSignals(true); + { + UpdateBlocker ub(m_treeView); + setFormWindowBlocked(fwi); + } + + m_treeView->update(); + m_treeView->selectionModel()->blockSignals(blocked); +} + +void ObjectInspector::ObjectInspectorPrivate::setFormWindowBlocked(QDesignerFormWindowInterface *fwi) +{ + FormWindowBase *fw = qobject_cast<FormWindowBase *>(fwi); + const bool formWindowChanged = m_formWindow != fw; + + m_formWindow = fw; + + const int oldWidth = m_treeView->columnWidth(0); + const int xoffset = m_treeView->horizontalScrollBar()->value(); + const int yoffset = m_treeView->verticalScrollBar()->value(); + + if (formWindowChanged) + m_formFakeDropTarget = 0; + + switch (m_model->update(m_formWindow)) { + case ObjectInspectorModel::NoForm: + clear(); + return; + case ObjectInspectorModel::Rebuilt: // Complete rebuild: Just apply cursor selection + applyCursorSelection(); + m_treeView->expandAll(); + if (formWindowChanged) { + m_treeView->resizeColumnToContents(0); + } else { + m_treeView->setColumnWidth(0, oldWidth); + m_treeView->horizontalScrollBar()->setValue(xoffset); + m_treeView->verticalScrollBar()->setValue(yoffset); + } + break; + case ObjectInspectorModel::Updated: { + // Same structure (property changed or click on the form) + // We maintain a selection of unmanaged objects + // only if the cursor is in state "mainContainer() == current". + // and we have a non-managed selection. + // Else we take over the cursor selection. + bool applySelection = !mainContainerIsCurrent(m_formWindow); + if (!applySelection) { + const QModelIndexList currentIndexes = m_treeView->selectionModel()->selectedRows(0); + if (currentIndexes.empty()) { + applySelection = true; + } else { + applySelection = selectionType(m_formWindow, m_model->objectAt(currentIndexes.front())) == ManagedWidgetSelection; + } + } + if (applySelection) + applyCursorSelection(); + } + break; + } +} + +// Apply selection of form window cursor to object inspector, set current +void ObjectInspector::ObjectInspectorPrivate::applyCursorSelection() +{ + const QDesignerFormWindowCursorInterface *cursor = m_formWindow->cursor(); + const int count = cursor->selectedWidgetCount(); + if (!count) + return; + + // Set the current widget first which also clears the selection + QWidget *currentWidget = cursor->current(); + if (currentWidget) + selectIndexRange(m_model->indexesOf(currentWidget), MakeCurrent); + else + m_treeView->selectionModel()->clearSelection(); + + for (int i = 0;i < count; i++) { + QWidget *widget = cursor->selectedWidget(i); + if (widget != currentWidget) + selectIndexRange(m_model->indexesOf(widget), AddToSelection); + } +} + +// Synchronize managed widget in the form (select in cursor). Block updates +static int selectInCursor(FormWindowBase *fw, const QObjectVector &objects, bool value) +{ + int rc = 0; + const bool blocked = fw->blockSelectionChanged(true); + const QObjectVector::const_iterator ocend = objects.constEnd(); + for (QObjectVector::const_iterator it = objects.constBegin(); it != ocend; ++it) + if (selectionType(fw, *it) == ManagedWidgetSelection) { + fw->selectWidget(static_cast<QWidget *>(*it), value); + rc++; + } + fw->blockSelectionChanged(blocked); + return rc; +} + +void ObjectInspector::ObjectInspectorPrivate::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + if (m_formWindow) { + synchronizeSelection(selected, deselected); + QMetaObject::invokeMethod(m_core->formWindowManager(), "slotUpdateActions"); + } +} + +// Convert indexes to object vectors taking into account that +// some index lists are multicolumn ranges +static inline QObjectVector indexesToObjects(const ObjectInspectorModel *model, const QModelIndexList &indexes) +{ + if (indexes.empty()) + return QObjectVector(); + QObjectVector rc; + rc.reserve(indexes.size()); + const QModelIndexList::const_iterator icend = indexes.constEnd(); + for (QModelIndexList::const_iterator it = indexes.constBegin(); it != icend; ++it) + if (it->column() == 0) + rc.push_back(model->objectAt(*it)); + return rc; +} + +// Check if any managed widgets are selected. If so, iterate over +// selection and deselect all unmanaged objects +bool ObjectInspector::ObjectInspectorPrivate::checkManagedWidgetSelection(const QModelIndexList &rowSelection) +{ + bool isManagedWidgetSelection = false; + QItemSelectionModel *selectionModel = m_treeView->selectionModel(); + const QModelIndexList::const_iterator cscend = rowSelection.constEnd(); + for (QModelIndexList::const_iterator it = rowSelection.constBegin(); it != cscend; ++it) { + QObject *object = m_model->objectAt(*it); + if (selectionType(m_formWindow, object) == ManagedWidgetSelection) { + isManagedWidgetSelection = true; + break; + } + } + + if (!isManagedWidgetSelection) + return false; + // Need to unselect unmanaged ones + const bool blocked = selectionModel->blockSignals(true); + for (QModelIndexList::const_iterator it = rowSelection.constBegin(); it != cscend; ++it) { + QObject *object = m_model->objectAt(*it); + if (selectionType(m_formWindow, object) != ManagedWidgetSelection) + selectionModel->select(*it, QItemSelectionModel::Deselect|QItemSelectionModel::Rows); + } + selectionModel->blockSignals(blocked); + return true; +} + +void ObjectInspector::ObjectInspectorPrivate::synchronizeSelection(const QItemSelection & selectedSelection, const QItemSelection &deselectedSelection) +{ + // Synchronize form window cursor. + const QObjectVector deselected = indexesToObjects(m_model, deselectedSelection.indexes()); + const QObjectVector newlySelected = indexesToObjects(m_model, selectedSelection.indexes()); + + const QModelIndexList currentSelectedIndexes = m_treeView->selectionModel()->selectedRows(0); + + int deselectedManagedWidgetCount = 0; + if (!deselected.empty()) + deselectedManagedWidgetCount = selectInCursor(m_formWindow, deselected, false); + + if (newlySelected.empty()) { // Nothing selected + if (currentSelectedIndexes.empty()) // Do not allow a null-selection, reset to main container + m_formWindow->clearSelection(!m_withinClearSelection); + return; + } + + const int selectManagedWidgetCount = selectInCursor(m_formWindow, newlySelected, true); + // Check consistency: Make sure either managed widgets or unmanaged objects are selected. + // No newly-selected managed widgets: Unless there are ones in the (old) current selection, + // select the unmanaged object + if (selectManagedWidgetCount == 0) { + if (checkManagedWidgetSelection(currentSelectedIndexes)) { + // Managed selection exists, refuse and update if necessary + if (deselectedManagedWidgetCount != 0 || selectManagedWidgetCount != 0) + m_formWindow->emitSelectionChanged(); + return; + } + // And now for the unmanaged selection + m_formWindow->clearSelection(false); + QObject *unmanagedObject = newlySelected.front(); + m_core->propertyEditor()->setObject(unmanagedObject); + m_core->propertyEditor()->setEnabled(true); + // open container page if it is a single widget + if (newlySelected.size() == 1 && unmanagedObject->isWidgetType()) + showContainersCurrentPage(static_cast<QWidget*>(unmanagedObject)); + return; + } + // Open container page if it is a single widget + if (newlySelected.size() == 1) { + QObject *object = newlySelected.back(); + if (object->isWidgetType()) + showContainersCurrentPage(static_cast<QWidget*>(object)); + } + + // A managed widget was newly selected. Make sure there are no unmanaged objects + // in the whole unless just single selection + if (currentSelectedIndexes.size() > selectManagedWidgetCount) + checkManagedWidgetSelection(currentSelectedIndexes); + // Update form + if (deselectedManagedWidgetCount != 0 || selectManagedWidgetCount != 0) + m_formWindow->emitSelectionChanged(); +} + + +void ObjectInspector::ObjectInspectorPrivate::getSelection(Selection &s) const +{ + s.clear(); + + if (!m_formWindow) + return; + + const QModelIndexList currentSelectedIndexes = m_treeView->selectionModel()->selectedRows(0); + if (currentSelectedIndexes.empty()) + return; + + // sort objects + foreach (const QModelIndex &index, currentSelectedIndexes) + if (QObject *object = m_model->objectAt(index)) + switch (selectionType(m_formWindow, object)) { + case NoSelection: + break; + case QObjectSelection: + // It is actually possible to select an action twice if it is in a menu bar + // and in a tool bar. + if (!s.objects.contains(object)) + s.objects.push_back(object); + break; + case UnmanagedWidgetSelection: + s.unmanaged.push_back(qobject_cast<QWidget *>(object)); + break; + case ManagedWidgetSelection: + s.managed.push_back(qobject_cast<QWidget *>(object)); + break; + } +} + +// Utility to create a task menu +static inline QMenu *createTaskMenu(QObject *object, QDesignerFormWindowInterface *fw) +{ + // 1) Objects + if (!object->isWidgetType()) + return FormWindowBase::createExtensionTaskMenu(fw, object, false); + // 2) Unmanaged widgets + QWidget *w = static_cast<QWidget *>(object); + if (!fw->isManaged(w)) + return FormWindowBase::createExtensionTaskMenu(fw, w, false); + // 3) Mananaged widgets + if (qdesigner_internal::FormWindowBase *fwb = qobject_cast<qdesigner_internal::FormWindowBase*>(fw)) + return fwb->initializePopupMenu(w); + return 0; +} + +void ObjectInspector::ObjectInspectorPrivate::slotPopupContextMenu(QWidget * /*parent*/, const QPoint &pos) +{ + if (m_formWindow == 0 || m_formWindow->currentTool() != 0) + return; + + const QModelIndex index = m_treeView->indexAt (pos); + if (QObject *object = m_model->objectAt(m_treeView->indexAt(pos))) + if (QMenu *menu = createTaskMenu(object, m_formWindow)) { + menu->exec(m_treeView->viewport()->mapToGlobal(pos)); + delete menu; + } +} + +// ------------ ObjectInspector +ObjectInspector::ObjectInspector(QDesignerFormEditorInterface *core, QWidget *parent) : + QDesignerObjectInspector(parent), + m_impl(new ObjectInspectorPrivate(core)) +{ + QVBoxLayout *vbox = new QVBoxLayout(this); + vbox->setMargin(0); + + QTreeView *treeView = m_impl->treeView(); + vbox->addWidget(treeView); + + connect(treeView, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(slotPopupContextMenu(QPoint))); + + connect(treeView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection))); + + connect(treeView->header(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(slotHeaderDoubleClicked(int))); + setAcceptDrops(true); + + ItemViewFindWidget *findWidget = m_impl->findWidget(); + vbox->addWidget(findWidget); + + findWidget->setItemView(treeView); + QAction *findAction = new QAction( + ItemViewFindWidget::findIconSet(), + tr("&Find in Text..."), + this); + findAction->setShortcut(QKeySequence::Find); + findAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); + addAction(findAction); + connect(findAction, SIGNAL(triggered(bool)), findWidget, SLOT(activate())); +} + +ObjectInspector::~ObjectInspector() +{ + delete m_impl; +} + +QDesignerFormEditorInterface *ObjectInspector::core() const +{ + return m_impl->core(); +} + +void ObjectInspector::slotPopupContextMenu(const QPoint &pos) +{ + m_impl->slotPopupContextMenu(this, pos); +} + +void ObjectInspector::setFormWindow(QDesignerFormWindowInterface *fwi) +{ + m_impl->setFormWindow(fwi); +} + +void ObjectInspector::slotSelectionChanged(const QItemSelection & selected, const QItemSelection &deselected) +{ + m_impl->slotSelectionChanged(selected, deselected); +} + +void ObjectInspector::getSelection(Selection &s) const +{ + m_impl->getSelection(s); +} + +bool ObjectInspector::selectObject(QObject *o) +{ + return m_impl->selectObject(o); +} + +void ObjectInspector::clearSelection() +{ + m_impl->clearSelection(); +} + +void ObjectInspector::slotHeaderDoubleClicked(int column) +{ + m_impl->slotHeaderDoubleClicked(column); +} + +void ObjectInspector::mainContainerChanged() +{ + // Invalidate references to objects kept in items + if (sender() == m_impl->formWindow()) + setFormWindow(0); +} + +void ObjectInspector::dragEnterEvent (QDragEnterEvent * event) +{ + m_impl->handleDragEnterMoveEvent(this, event, true); +} + +void ObjectInspector::dragMoveEvent(QDragMoveEvent * event) +{ + m_impl->handleDragEnterMoveEvent(this, event, false); +} + +void ObjectInspector::dragLeaveEvent(QDragLeaveEvent * /* event*/) +{ + m_impl->restoreDropHighlighting(); +} + +void ObjectInspector::dropEvent (QDropEvent * event) +{ + m_impl->dropEvent(event); + +QT_END_NAMESPACE +} +} |