| /**************************************************************************** |
| ** |
| ** 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$ |
| ** GNU Lesser General Public License Usage |
| ** 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. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $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 |
| } |
| } |