blob: 4ebc72211a4756f135fa3074e19ce513309d1d88 [file] [log] [blame]
/****************************************************************************
**
** 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 "formwindow.h"
#include "formeditor.h"
#include "formwindow_dnditem.h"
#include "formwindow_widgetstack.h"
#include "formwindowcursor.h"
#include "formwindowmanager.h"
#include "tool_widgeteditor.h"
#include "widgetselection.h"
#include "qtresourcemodel_p.h"
#include "widgetfactory_p.h"
// shared
#include <metadatabase_p.h>
#include <qdesigner_tabwidget_p.h>
#include <qdesigner_toolbox_p.h>
#include <qdesigner_stackedbox_p.h>
#include <qdesigner_resource.h>
#include <qdesigner_command_p.h>
#include <qdesigner_command2_p.h>
#include <qdesigner_propertycommand_p.h>
#include <qdesigner_taskmenu_p.h>
#include <qdesigner_widget_p.h>
#include <qdesigner_utils_p.h>
#include <qlayout_widget_p.h>
#include <spacer_widget_p.h>
#include <invisible_widget_p.h>
#include <layoutinfo_p.h>
#include <qdesigner_objectinspector_p.h>
#include <connectionedit_p.h>
#include <actionprovider_p.h>
#include <ui4_p.h>
#include <deviceprofile_p.h>
#include <shared_settings_p.h>
#include <grid_p.h>
#include <QtDesigner/QExtensionManager>
#include <QtDesigner/QDesignerWidgetDataBaseInterface>
#include <QtDesigner/QDesignerPropertySheetExtension>
#include <QtDesigner/QDesignerWidgetFactoryInterface>
#include <QtDesigner/QDesignerContainerExtension>
#include <QtDesigner/QDesignerTaskMenuExtension>
#include <QtDesigner/QDesignerWidgetBoxInterface>
#include <abstractdialoggui_p.h>
#include <QtCore/QtDebug>
#include <QtCore/QBuffer>
#include <QtCore/QTimer>
#include <QtCore/QXmlStreamReader>
#include <QtGui/QMenu>
#include <QtGui/QAction>
#include <QtGui/QActionGroup>
#include <QtGui/QClipboard>
#include <QtGui/QUndoGroup>
#include <QtGui/QScrollArea>
#include <QtGui/QRubberBand>
#include <QtGui/QApplication>
#include <QtGui/QSplitter>
#include <QtGui/QPainter>
#include <QtGui/QGroupBox>
#include <QtGui/QDockWidget>
#include <QtGui/QToolBox>
#include <QtGui/QStackedWidget>
#include <QtGui/QTabWidget>
#include <QtGui/QButtonGroup>
Q_DECLARE_METATYPE(QWidget*)
QT_BEGIN_NAMESPACE
namespace {
class BlockSelection
{
public:
BlockSelection(qdesigner_internal::FormWindow *fw)
: m_formWindow(fw),
m_blocked(m_formWindow->blockSelectionChanged(true))
{
}
~BlockSelection()
{
if (m_formWindow)
m_formWindow->blockSelectionChanged(m_blocked);
}
private:
QPointer<qdesigner_internal::FormWindow> m_formWindow;
const bool m_blocked;
};
enum { debugFormWindow = 0 };
}
namespace qdesigner_internal {
// ------------------------ FormWindow::Selection
// Maintains a pool of WidgetSelections to be used for selected widgets.
class FormWindow::Selection
{
public:
Selection();
~Selection();
// Clear
void clear();
// Also clear out the pool. Call if reparenting of the main container occurs.
void clearSelectionPool();
void repaintSelection(QWidget *w);
void repaintSelection();
bool isWidgetSelected(QWidget *w) const;
QWidgetList selectedWidgets() const;
WidgetSelection *addWidget(FormWindow* fw, QWidget *w);
// remove widget, return new current widget or 0
QWidget* removeWidget(QWidget *w);
void raiseList(const QWidgetList& l);
void raiseWidget(QWidget *w);
void updateGeometry(QWidget *w);
void hide(QWidget *w);
void show(QWidget *w);
private:
typedef QList<WidgetSelection *> SelectionPool;
SelectionPool m_selectionPool;
typedef QHash<QWidget *, WidgetSelection *> SelectionHash;
SelectionHash m_usedSelections;
};
FormWindow::Selection::Selection()
{
}
FormWindow::Selection::~Selection()
{
clearSelectionPool();
}
void FormWindow::Selection::clear()
{
if (!m_usedSelections.empty()) {
const SelectionHash::iterator mend = m_usedSelections.end();
for (SelectionHash::iterator it = m_usedSelections.begin(); it != mend; ++it) {
it.value()->setWidget(0);
}
m_usedSelections.clear();
}
}
void FormWindow::Selection::clearSelectionPool()
{
clear();
qDeleteAll(m_selectionPool);
m_selectionPool.clear();
}
WidgetSelection *FormWindow::Selection::addWidget(FormWindow* fw, QWidget *w)
{
WidgetSelection *rc = m_usedSelections.value(w);
if (rc != 0) {
rc->show();
rc->updateActive();
return rc;
}
// find a free one in the pool
const SelectionPool::iterator pend = m_selectionPool.end();
for (SelectionPool::iterator it = m_selectionPool.begin(); it != pend; ++it) {
if (! (*it)->isUsed()) {
rc = *it;
break;
}
}
if (rc == 0) {
rc = new WidgetSelection(fw);
m_selectionPool.push_back(rc);
}
m_usedSelections.insert(w, rc);
rc->setWidget(w);
return rc;
}
QWidget* FormWindow::Selection::removeWidget(QWidget *w)
{
WidgetSelection *s = m_usedSelections.value(w);
if (!s)
return w;
s->setWidget(0);
m_usedSelections.remove(w);
if (m_usedSelections.isEmpty())
return 0;
return (*m_usedSelections.begin())->widget();
}
void FormWindow::Selection::repaintSelection(QWidget *w)
{
if (WidgetSelection *s = m_usedSelections.value(w))
s->update();
}
void FormWindow::Selection::repaintSelection()
{
const SelectionHash::iterator mend = m_usedSelections.end();
for (SelectionHash::iterator it = m_usedSelections.begin(); it != mend; ++it) {
it.value()->update();
}
}
bool FormWindow::Selection::isWidgetSelected(QWidget *w) const{
return m_usedSelections.contains(w);
}
QWidgetList FormWindow::Selection::selectedWidgets() const
{
return m_usedSelections.keys();
}
void FormWindow::Selection::raiseList(const QWidgetList& l)
{
const SelectionHash::iterator mend = m_usedSelections.end();
for (SelectionHash::iterator it = m_usedSelections.begin(); it != mend; ++it) {
WidgetSelection *w = it.value();
if (l.contains(w->widget()))
w->show();
}
}
void FormWindow::Selection::raiseWidget(QWidget *w)
{
if (WidgetSelection *s = m_usedSelections.value(w))
s->show();
}
void FormWindow::Selection::updateGeometry(QWidget *w)
{
if (WidgetSelection *s = m_usedSelections.value(w)) {
s->updateGeometry();
}
}
void FormWindow::Selection::hide(QWidget *w)
{
if (WidgetSelection *s = m_usedSelections.value(w))
s->hide();
}
void FormWindow::Selection::show(QWidget *w)
{
if (WidgetSelection *s = m_usedSelections.value(w))
s->show();
}
// ------------------------ FormWindow
FormWindow::FormWindow(FormEditor *core, QWidget *parent, Qt::WindowFlags flags) :
FormWindowBase(core, parent, flags),
m_mouseState(NoMouseState),
m_core(core),
m_selection(new Selection),
m_widgetStack(new FormWindowWidgetStack(this)),
m_contextMenuPosition(-1, -1)
{
// Apply settings to formcontainer
deviceProfile().apply(core, m_widgetStack->formContainer(), qdesigner_internal::DeviceProfile::ApplyFormParent);
setLayout(m_widgetStack->layout());
init();
m_cursor = new FormWindowCursor(this, this);
core->formWindowManager()->addFormWindow(this);
setDirty(false);
setAcceptDrops(true);
}
FormWindow::~FormWindow()
{
Q_ASSERT(core() != 0);
Q_ASSERT(core()->metaDataBase() != 0);
Q_ASSERT(core()->formWindowManager() != 0);
core()->formWindowManager()->removeFormWindow(this);
core()->metaDataBase()->remove(this);
QWidgetList l = widgets();
foreach (QWidget *w, l)
core()->metaDataBase()->remove(w);
m_widgetStack = 0;
m_rubberBand = 0;
if (resourceSet())
core()->resourceModel()->removeResourceSet(resourceSet());
delete m_selection;
}
QDesignerFormEditorInterface *FormWindow::core() const
{
return m_core;
}
QDesignerFormWindowCursorInterface *FormWindow::cursor() const
{
return m_cursor;
}
void FormWindow::updateWidgets()
{
if (!m_mainContainer)
return;
}
int FormWindow::widgetDepth(const QWidget *w)
{
int d = -1;
while (w && !w->isWindow()) {
d++;
w = w->parentWidget();
}
return d;
}
bool FormWindow::isChildOf(const QWidget *c, const QWidget *p)
{
while (c) {
if (c == p)
return true;
c = c->parentWidget();
}
return false;
}
void FormWindow::setCursorToAll(const QCursor &c, QWidget *start)
{
#ifndef QT_NO_CURSOR
start->setCursor(c);
const QWidgetList widgets = qFindChildren<QWidget*>(start);
foreach (QWidget *widget, widgets) {
if (!qobject_cast<WidgetHandle*>(widget)) {
widget->setCursor(c);
}
}
#endif
}
void FormWindow::init()
{
if (FormWindowManager *manager = qobject_cast<FormWindowManager*> (core()->formWindowManager())) {
manager->undoGroup()->addStack(m_undoStack.qundoStack());
}
m_blockSelectionChanged = false;
m_defaultMargin = INT_MIN;
m_defaultSpacing = INT_MIN;
connect(m_widgetStack, SIGNAL(currentToolChanged(int)), this, SIGNAL(toolChanged(int)));
m_selectionChangedTimer = new QTimer(this);
m_selectionChangedTimer->setSingleShot(true);
connect(m_selectionChangedTimer, SIGNAL(timeout()), this, SLOT(selectionChangedTimerDone()));
m_checkSelectionTimer = new QTimer(this);
m_checkSelectionTimer->setSingleShot(true);
connect(m_checkSelectionTimer, SIGNAL(timeout()), this, SLOT(checkSelectionNow()));
m_geometryChangedTimer = new QTimer(this);
m_geometryChangedTimer->setSingleShot(true);
connect(m_geometryChangedTimer, SIGNAL(timeout()), this, SIGNAL(geometryChanged()));
m_rubberBand = 0;
setFocusPolicy(Qt::StrongFocus);
m_mainContainer = 0;
m_currentWidget = 0;
connect(&m_undoStack, SIGNAL(changed()), this, SIGNAL(changed()));
connect(&m_undoStack, SIGNAL(changed()), this, SLOT(checkSelection()));
core()->metaDataBase()->add(this);
initializeCoreTools();
QAction *a = new QAction(this);
a->setText(tr("Edit contents"));
a->setShortcut(tr("F2"));
connect(a, SIGNAL(triggered()), this, SLOT(editContents()));
addAction(a);
}
QWidget *FormWindow::mainContainer() const
{
return m_mainContainer;
}
void FormWindow::clearMainContainer()
{
if (m_mainContainer) {
setCurrentTool(0);
m_widgetStack->setMainContainer(0);
core()->metaDataBase()->remove(m_mainContainer);
unmanageWidget(m_mainContainer);
delete m_mainContainer;
m_mainContainer = 0;
}
}
void FormWindow::setMainContainer(QWidget *w)
{
if (w == m_mainContainer) {
// nothing to do
return;
}
clearMainContainer();
m_mainContainer = w;
const QSize sz = m_mainContainer->size();
m_widgetStack->setMainContainer(m_mainContainer);
m_widgetStack->setCurrentTool(m_widgetEditor);
setCurrentWidget(m_mainContainer);
manageWidget(m_mainContainer);
if (QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), m_mainContainer)) {
sheet->setVisible(sheet->indexOf(QLatin1String("windowTitle")), true);
sheet->setVisible(sheet->indexOf(QLatin1String("windowIcon")), true);
sheet->setVisible(sheet->indexOf(QLatin1String("windowModality")), true);
sheet->setVisible(sheet->indexOf(QLatin1String("windowOpacity")), true);
sheet->setVisible(sheet->indexOf(QLatin1String("windowFilePath")), true);
// ### generalize
}
m_mainContainer->setFocusPolicy(Qt::StrongFocus);
m_mainContainer->resize(sz);
emit mainContainerChanged(m_mainContainer);
}
QWidget *FormWindow::findTargetContainer(QWidget *widget) const
{
Q_ASSERT(widget);
while (QWidget *parentWidget = widget->parentWidget()) {
if (LayoutInfo::layoutType(m_core, parentWidget) == LayoutInfo::NoLayout && isManaged(widget))
return widget;
widget = parentWidget;
}
return mainContainer();
}
static inline void clearObjectInspectorSelection(const QDesignerFormEditorInterface *core)
{
if (QDesignerObjectInspector *oi = qobject_cast<QDesignerObjectInspector *>(core->objectInspector()))
oi->clearSelection();
}
// Find a parent of a desired selection state
static QWidget *findSelectedParent(QDesignerFormWindowInterface *fw, const QWidget *w, bool selected)
{
const QDesignerFormWindowCursorInterface *cursor = fw->cursor();
QWidget *mainContainer = fw->mainContainer();
for (QWidget *p = w->parentWidget(); p && p != mainContainer; p = p->parentWidget())
if (fw->isManaged(p))
if (cursor->isWidgetSelected(p) == selected)
return p;
return 0;
}
// Mouse modifiers.
enum MouseFlags { ToggleSelectionModifier = 0x1, CycleParentModifier=0x2, CopyDragModifier=0x4 };
static inline unsigned mouseFlags(Qt::KeyboardModifiers mod)
{
switch (mod) {
case Qt::ShiftModifier:
return CycleParentModifier;
break;
#ifdef Q_WS_MAC
case Qt::AltModifier: // "Alt" or "option" key on Mac means copy
return CopyDragModifier;
#endif
case Qt::ControlModifier:
return CopyDragModifier|ToggleSelectionModifier;
break;
default:
break;
}
return 0;
}
// Handle the click selection: Do toggling/cycling
// of parents according to the modifiers.
void FormWindow::handleClickSelection(QWidget *managedWidget, unsigned mouseMode)
{
const bool sameWidget = managedWidget == m_lastClickedWidget;
m_lastClickedWidget = managedWidget;
const bool selected = isWidgetSelected(managedWidget);
if (debugFormWindow)
qDebug() << "handleClickSelection" << managedWidget << " same=" << sameWidget << " mouse= " << mouseMode << " selected=" << selected;
// // toggle selection state of widget
if (mouseMode & ToggleSelectionModifier) {
selectWidget(managedWidget, !selected);
return;
}
QWidget *selectionCandidate = 0;
// Hierarchy cycling: If the same widget clicked again: Attempt to cycle
// trough the hierarchy. Find the next currently selected parent
if (sameWidget && (mouseMode & CycleParentModifier))
if (QWidget *currentlySelectedParent = selected ? managedWidget : findSelectedParent(this, managedWidget, true))
selectionCandidate = findSelectedParent(this, currentlySelectedParent, false);
// Not the same widget, list wrapped over or there was no unselected parent
if (!selectionCandidate && !selected)
selectionCandidate = managedWidget;
if (selectionCandidate)
selectSingleWidget(selectionCandidate);
}
void FormWindow::selectSingleWidget(QWidget *w)
{
clearSelection(false);
selectWidget(w, true);
raiseChildSelections(w);
}
bool FormWindow::handleMousePressEvent(QWidget * widget, QWidget *managedWidget, QMouseEvent *e)
{
m_mouseState = NoMouseState;
m_startPos = QPoint();
e->accept();
BlockSelection blocker(this);
if (core()->formWindowManager()->activeFormWindow() != this)
core()->formWindowManager()->setActiveFormWindow(this);
const Qt::MouseButtons buttons = e->buttons();
if (buttons != Qt::LeftButton && buttons != Qt::MidButton)
return true;
m_startPos = mapFromGlobal(e->globalPos());
if (debugFormWindow)
qDebug() << "handleMousePressEvent:" << widget << ',' << managedWidget;
if (buttons == Qt::MidButton || isMainContainer(managedWidget) == true) { // press was on the formwindow
clearObjectInspectorSelection(m_core); // We might have a toolbar or non-widget selected in the object inspector.
clearSelection(false);
m_mouseState = MouseDrawRubber;
m_currRect = QRect();
startRectDraw(mapFromGlobal(e->globalPos()), this, Rubber);
return true;
}
if (buttons != Qt::LeftButton)
return true;
const unsigned mouseMode = mouseFlags(e->modifiers());
/* Normally, we want to be able to click /select-on-press to drag away
* the widget in the next step. However, in the case of a widget which
* itself or whose parent is selected, we defer the selection to the
* release event.
* This is to prevent children from being dragged away from layouts
* when their layouts are selected and one wants to move the layout.
* Note that toggle selection is only deferred if the widget is already
* selected, so, it is still possible to just Ctrl+Click and CopyDrag. */
const bool deferSelection = isWidgetSelected(managedWidget) || findSelectedParent(this, managedWidget, true);
if (deferSelection) {
m_mouseState = MouseDeferredSelection;
} else {
// Cycle the parent unless we explicitly want toggle
const unsigned effectiveMouseMode = (mouseMode & ToggleSelectionModifier) ? mouseMode : static_cast<unsigned>(CycleParentModifier);
handleClickSelection(managedWidget, effectiveMouseMode);
}
return true;
}
// We can drag widget in managed layouts except splitter.
static bool canDragWidgetInLayout(const QDesignerFormEditorInterface *core, QWidget *w)
{
bool managed;
const LayoutInfo::Type type = LayoutInfo::laidoutWidgetType(core ,w, &managed);
if (!managed)
return false;
switch (type) {
case LayoutInfo::NoLayout:
case LayoutInfo::HSplitter:
case LayoutInfo::VSplitter:
return false;
default:
break;
}
return true;
}
bool FormWindow::handleMouseMoveEvent(QWidget *, QWidget *, QMouseEvent *e)
{
e->accept();
if (m_startPos.isNull())
return true;
const QPoint pos = mapFromGlobal(e->globalPos());
switch (m_mouseState) {
case MouseDrawRubber: // Rubber band with left/middle mouse
continueRectDraw(pos, this, Rubber);
return true;
case MouseMoveDrag: // Spurious move event after drag started?
return true;
default:
break;
}
if (e->buttons() != Qt::LeftButton)
return true;
const bool canStartDrag = (m_startPos - pos).manhattanLength() > QApplication::startDragDistance();
if (canStartDrag == false) {
// nothing to do
return true;
}
m_mouseState = MouseMoveDrag;
const bool blocked = blockSelectionChanged(true);
QWidgetList sel = selectedWidgets();
simplifySelection(&sel);
QSet<QWidget*> widget_set;
foreach (QWidget *child, sel) { // Move parent layout or container?
QWidget *current = child;
bool done = false;
while (!isMainContainer(current) && !done) {
if (!isManaged(current)) {
current = current->parentWidget();
continue;
} else if (LayoutInfo::isWidgetLaidout(core(), current)) {
// Go up to parent of layout if shift pressed, else do that only for splitters
if (!canDragWidgetInLayout(core(), current)) {
current = current->parentWidget();
continue;
}
}
done = true;
}
if (current == mainContainer())
continue;
widget_set.insert(current);
}
sel = widget_set.toList();
QDesignerFormWindowCursorInterface *c = cursor();
QWidget *current = c->current();
if (sel.contains(current)) {
sel.removeAll(current);
sel.prepend(current);
}
QList<QDesignerDnDItemInterface*> item_list;
const QPoint globalPos = mapToGlobal(m_startPos);
const QDesignerDnDItemInterface::DropType dropType = (mouseFlags(e->modifiers()) & CopyDragModifier) ?
QDesignerDnDItemInterface::CopyDrop : QDesignerDnDItemInterface::MoveDrop;
foreach (QWidget *widget, sel) {
item_list.append(new FormWindowDnDItem(dropType, this, widget, globalPos));
if (dropType == QDesignerDnDItemInterface::MoveDrop) {
m_selection->hide(widget);
widget->hide();
}
}
blockSelectionChanged(blocked);
if (!sel.empty()) // reshow selection?
if (QDesignerMimeData::execDrag(item_list, core()->topLevel()) == Qt::IgnoreAction && dropType == QDesignerDnDItemInterface::MoveDrop)
foreach (QWidget *widget, sel)
m_selection->show(widget);
m_startPos = QPoint();
return true;
}
bool FormWindow::handleMouseReleaseEvent(QWidget *w, QWidget *mw, QMouseEvent *e)
{
const MouseState oldState = m_mouseState;
m_mouseState = NoMouseState;
if (debugFormWindow)
qDebug() << "handleMouseeleaseEvent:" << w << ',' << mw << "state=" << oldState;
if (oldState == MouseDoubleClicked)
return true;
e->accept();
switch (oldState) {
case MouseDrawRubber: { // we were drawing a rubber selection
endRectDraw(); // get rid of the rectangle
const bool blocked = blockSelectionChanged(true);
selectWidgets(); // select widgets which intersect the rect
blockSelectionChanged(blocked);
}
break;
// Deferred select: Select the child here unless the parent was moved.
case MouseDeferredSelection:
handleClickSelection(mw, mouseFlags(e->modifiers()));
break;
default:
break;
}
m_startPos = QPoint();
/* Inform about selection changes (left/mid or context menu). Also triggers
* in the case of an empty rubber drag that cleared the selection in
* MousePressEvent. */
switch (e->button()) {
case Qt::LeftButton:
case Qt::MidButton:
case Qt::RightButton:
emitSelectionChanged();
break;
default:
break;
}
return true;
}
void FormWindow::checkPreviewGeometry(QRect &r)
{
if (!rect().contains(r)) {
if (r.left() < rect().left())
r.moveTopLeft(QPoint(0, r.top()));
if (r.right() > rect().right())
r.moveBottomRight(QPoint(rect().right(), r.bottom()));
if (r.top() < rect().top())
r.moveTopLeft(QPoint(r.left(), rect().top()));
if (r.bottom() > rect().bottom())
r.moveBottomRight(QPoint(r.right(), rect().bottom()));
}
}
void FormWindow::startRectDraw(const QPoint &pos, QWidget *, RectType t)
{
m_rectAnchor = (t == Insert) ? designerGrid().snapPoint(pos) : pos;
m_currRect = QRect(m_rectAnchor, QSize(0, 0));
if (!m_rubberBand)
m_rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
m_rubberBand->setGeometry(m_currRect);
m_rubberBand->show();
}
void FormWindow::continueRectDraw(const QPoint &pos, QWidget *, RectType t)
{
const QPoint p2 = (t == Insert) ? designerGrid().snapPoint(pos) : pos;
QRect r(m_rectAnchor, p2);
r = r.normalized();
if (m_currRect == r)
return;
if (r.width() > 1 || r.height() > 1) {
m_currRect = r;
if (m_rubberBand)
m_rubberBand->setGeometry(m_currRect);
}
}
void FormWindow::endRectDraw()
{
if (m_rubberBand) {
delete m_rubberBand;
m_rubberBand = 0;
}
}
QWidget *FormWindow::currentWidget() const
{
return m_currentWidget;
}
bool FormWindow::setCurrentWidget(QWidget *currentWidget)
{
if (debugFormWindow)
qDebug() << "setCurrentWidget:" << m_currentWidget << " --> " << currentWidget;
if (currentWidget == m_currentWidget)
return false;
// repaint the old widget unless it is the main window
if (m_currentWidget && m_currentWidget != mainContainer()) {
m_selection->repaintSelection(m_currentWidget);
}
// set new and repaint
m_currentWidget = currentWidget;
if (m_currentWidget && m_currentWidget != mainContainer()) {
m_selection->repaintSelection(m_currentWidget);
}
return true;
}
void FormWindow::selectWidget(QWidget* w, bool select)
{
if (trySelectWidget(w, select))
emitSelectionChanged();
}
// Selects a widget and determines the new current one. Returns true if a change occurs.
bool FormWindow::trySelectWidget(QWidget *w, bool select)
{
if (debugFormWindow)
qDebug() << "trySelectWidget:" << w << select;
if (!isManaged(w) && !isCentralWidget(w))
return false;
if (!select && !isWidgetSelected(w))
return false;
if (!mainContainer())
return false;
if (isMainContainer(w) || isCentralWidget(w)) {
setCurrentWidget(mainContainer());
return true;
}
if (select) {
setCurrentWidget(w);
m_selection->addWidget(this, w);
} else {
QWidget *newCurrent = m_selection->removeWidget(w);
if (!newCurrent)
newCurrent = mainContainer();
setCurrentWidget(newCurrent);
}
return true;
}
void FormWindow::clearSelection(bool changePropertyDisplay)
{
if (debugFormWindow)
qDebug() << "clearSelection(" << changePropertyDisplay << ')';
// At all events, we need a current widget.
m_selection->clear();
setCurrentWidget(mainContainer());
if (changePropertyDisplay)
emitSelectionChanged();
}
void FormWindow::emitSelectionChanged()
{
if (m_blockSelectionChanged == true) {
// nothing to do
return;
}
m_selectionChangedTimer->start(0);
}
void FormWindow::selectionChangedTimerDone()
{
emit selectionChanged();
}
bool FormWindow::isWidgetSelected(QWidget *w) const
{
return m_selection->isWidgetSelected(w);
}
bool FormWindow::isMainContainer(const QWidget *w) const
{
return w && (w == this || w == mainContainer());
}
void FormWindow::updateChildSelections(QWidget *w)
{
const QWidgetList l = qFindChildren<QWidget*>(w);
if (!l.empty()) {
const QWidgetList::const_iterator lcend = l.constEnd();
for (QWidgetList::const_iterator it = l.constBegin(); it != lcend; ++it) {
QWidget *w = *it;
if (isManaged(w))
updateSelection(w);
}
}
}
void FormWindow::repaintSelection()
{
m_selection->repaintSelection();
}
void FormWindow::raiseSelection(QWidget *w)
{
m_selection->raiseWidget(w);
}
void FormWindow::updateSelection(QWidget *w)
{
if (!w->isVisibleTo(this)) {
selectWidget(w, false);
} else {
m_selection->updateGeometry(w);
}
}
QWidget *FormWindow::designerWidget(QWidget *w) const
{
while ((w && !isMainContainer(w) && !isManaged(w)) || isCentralWidget(w))
w = w->parentWidget();
return w;
}
bool FormWindow::isCentralWidget(QWidget *w) const
{
if (QMainWindow *mainWindow = qobject_cast<QMainWindow*>(mainContainer()))
return w == mainWindow->centralWidget();
return false;
}
void FormWindow::ensureUniqueObjectName(QObject *object)
{
QString name = object->objectName();
if (name.isEmpty()) {
QDesignerWidgetDataBaseInterface *db = core()->widgetDataBase();
if (QDesignerWidgetDataBaseItemInterface *item = db->item(db->indexOfObject(object)))
name = qdesigner_internal::qtify(item->name());
}
unify(object, name, true);
object->setObjectName(name);
}
template <class Iterator>
static inline void insertNames(const QDesignerMetaDataBaseInterface *metaDataBase,
Iterator it, const Iterator &end,
QObject *excludedObject, QSet<QString> &nameSet)
{
for ( ; it != end; ++it)
if (excludedObject != *it && metaDataBase->item(*it))
nameSet.insert((*it)->objectName());
}
static QSet<QString> languageKeywords()
{
static QSet<QString> keywords;
if (keywords.isEmpty()) {
// C++ keywords
keywords.insert(QLatin1String("asm"));
keywords.insert(QLatin1String("auto"));
keywords.insert(QLatin1String("bool"));
keywords.insert(QLatin1String("break"));
keywords.insert(QLatin1String("case"));
keywords.insert(QLatin1String("catch"));
keywords.insert(QLatin1String("char"));
keywords.insert(QLatin1String("class"));
keywords.insert(QLatin1String("const"));
keywords.insert(QLatin1String("const_cast"));
keywords.insert(QLatin1String("continue"));
keywords.insert(QLatin1String("default"));
keywords.insert(QLatin1String("delete"));
keywords.insert(QLatin1String("do"));
keywords.insert(QLatin1String("double"));
keywords.insert(QLatin1String("dynamic_cast"));
keywords.insert(QLatin1String("else"));
keywords.insert(QLatin1String("enum"));
keywords.insert(QLatin1String("explicit"));
keywords.insert(QLatin1String("export"));
keywords.insert(QLatin1String("extern"));
keywords.insert(QLatin1String("false"));
keywords.insert(QLatin1String("float"));
keywords.insert(QLatin1String("for"));
keywords.insert(QLatin1String("friend"));
keywords.insert(QLatin1String("goto"));
keywords.insert(QLatin1String("if"));
keywords.insert(QLatin1String("inline"));
keywords.insert(QLatin1String("int"));
keywords.insert(QLatin1String("long"));
keywords.insert(QLatin1String("mutable"));
keywords.insert(QLatin1String("namespace"));
keywords.insert(QLatin1String("new"));
keywords.insert(QLatin1String("NULL"));
keywords.insert(QLatin1String("operator"));
keywords.insert(QLatin1String("private"));
keywords.insert(QLatin1String("protected"));
keywords.insert(QLatin1String("public"));
keywords.insert(QLatin1String("register"));
keywords.insert(QLatin1String("reinterpret_cast"));
keywords.insert(QLatin1String("return"));
keywords.insert(QLatin1String("short"));
keywords.insert(QLatin1String("signed"));
keywords.insert(QLatin1String("sizeof"));
keywords.insert(QLatin1String("static"));
keywords.insert(QLatin1String("static_cast"));
keywords.insert(QLatin1String("struct"));
keywords.insert(QLatin1String("switch"));
keywords.insert(QLatin1String("template"));
keywords.insert(QLatin1String("this"));
keywords.insert(QLatin1String("throw"));
keywords.insert(QLatin1String("true"));
keywords.insert(QLatin1String("try"));
keywords.insert(QLatin1String("typedef"));
keywords.insert(QLatin1String("typeid"));
keywords.insert(QLatin1String("typename"));
keywords.insert(QLatin1String("union"));
keywords.insert(QLatin1String("unsigned"));
keywords.insert(QLatin1String("using"));
keywords.insert(QLatin1String("virtual"));
keywords.insert(QLatin1String("void"));
keywords.insert(QLatin1String("volatile"));
keywords.insert(QLatin1String("wchar_t"));
keywords.insert(QLatin1String("while"));
// java keywords
keywords.insert(QLatin1String("abstract"));
keywords.insert(QLatin1String("assert"));
keywords.insert(QLatin1String("boolean"));
keywords.insert(QLatin1String("break"));
keywords.insert(QLatin1String("byte"));
keywords.insert(QLatin1String("case"));
keywords.insert(QLatin1String("catch"));
keywords.insert(QLatin1String("char"));
keywords.insert(QLatin1String("class"));
keywords.insert(QLatin1String("const"));
keywords.insert(QLatin1String("continue"));
keywords.insert(QLatin1String("default"));
keywords.insert(QLatin1String("do"));
keywords.insert(QLatin1String("double"));
keywords.insert(QLatin1String("else"));
keywords.insert(QLatin1String("enum"));
keywords.insert(QLatin1String("extends"));
keywords.insert(QLatin1String("false"));
keywords.insert(QLatin1String("final"));
keywords.insert(QLatin1String("finality"));
keywords.insert(QLatin1String("float"));
keywords.insert(QLatin1String("for"));
keywords.insert(QLatin1String("goto"));
keywords.insert(QLatin1String("if"));
keywords.insert(QLatin1String("implements"));
keywords.insert(QLatin1String("import"));
keywords.insert(QLatin1String("instanceof"));
keywords.insert(QLatin1String("int"));
keywords.insert(QLatin1String("interface"));
keywords.insert(QLatin1String("long"));
keywords.insert(QLatin1String("native"));
keywords.insert(QLatin1String("new"));
keywords.insert(QLatin1String("null"));
keywords.insert(QLatin1String("package"));
keywords.insert(QLatin1String("private"));
keywords.insert(QLatin1String("protected"));
keywords.insert(QLatin1String("public"));
keywords.insert(QLatin1String("return"));
keywords.insert(QLatin1String("short"));
keywords.insert(QLatin1String("static"));
keywords.insert(QLatin1String("strictfp"));
keywords.insert(QLatin1String("super"));
keywords.insert(QLatin1String("switch"));
keywords.insert(QLatin1String("synchronized"));
keywords.insert(QLatin1String("this"));
keywords.insert(QLatin1String("throw"));
keywords.insert(QLatin1String("throws"));
keywords.insert(QLatin1String("transient"));
keywords.insert(QLatin1String("true"));
keywords.insert(QLatin1String("try"));
keywords.insert(QLatin1String("void"));
keywords.insert(QLatin1String("volatile"));
keywords.insert(QLatin1String("while"));
}
return keywords;
}
bool FormWindow::unify(QObject *w, QString &s, bool changeIt)
{
typedef QSet<QString> StringSet;
QWidget *main = mainContainer();
if (!main)
return true;
StringSet existingNames = languageKeywords();
// build a set of existing names of other widget excluding self
if (!(w->isWidgetType() && isMainContainer(qobject_cast<QWidget*>(w))))
existingNames.insert(main->objectName());
const QDesignerMetaDataBaseInterface *metaDataBase = core()->metaDataBase();
const QWidgetList widgetChildren = qFindChildren<QWidget*>(main);
if (!widgetChildren.empty())
insertNames(metaDataBase, widgetChildren.constBegin(), widgetChildren.constEnd(), w, existingNames);
const QList<QLayout *> layoutChildren = qFindChildren<QLayout*>(main);
if (!layoutChildren.empty())
insertNames(metaDataBase, layoutChildren.constBegin(), layoutChildren.constEnd(), w, existingNames);
const QList<QAction *> actionChildren = qFindChildren<QAction*>(main);
if (!actionChildren.empty())
insertNames(metaDataBase, actionChildren.constBegin(), actionChildren.constEnd(), w, existingNames);
const QList<QButtonGroup *> buttonGroupChildren = qFindChildren<QButtonGroup*>(main);
if (!buttonGroupChildren.empty())
insertNames(metaDataBase, buttonGroupChildren.constBegin(), buttonGroupChildren.constEnd(), w, existingNames);
const StringSet::const_iterator enEnd = existingNames.constEnd();
if (existingNames.constFind(s) == enEnd)
return true;
else
if (!changeIt)
return false;
// split 'name_number'
qlonglong num = 0;
qlonglong factor = 1;
int idx = s.length()-1;
const ushort zeroUnicode = QLatin1Char('0').unicode();
for ( ; idx > 0 && s.at(idx).isDigit(); --idx) {
num += (s.at(idx).unicode() - zeroUnicode) * factor;
factor *= 10;
}
// Position index past '_'.
const QChar underscore = QLatin1Char('_');
if (idx >= 0 && s.at(idx) == underscore) {
idx++;
} else {
num = 1;
s += underscore;
idx = s.length();
}
// try 'name_n', 'name_n+1'
for (num++ ; ;num++) {
s.truncate(idx);
s += QString::number(num);
if (existingNames.constFind(s) == enEnd)
break;
}
return false;
}
/* already_in_form is true when we are moving a widget from one parent to another inside the same
* form. All this means is that InsertWidgetCommand::undo() must not unmanage it. */
void FormWindow::insertWidget(QWidget *w, const QRect &rect, QWidget *container, bool already_in_form)
{
clearSelection(false);
beginCommand(tr("Insert widget '%1'").arg(WidgetFactory::classNameOf(m_core, w))); // ### use the WidgetDatabaseItem
/* Reparenting into a QSplitter automatically adjusts child's geometry. We create the geometry
* command before we push the reparent command, so that the geometry command has the original
* geometry of the widget. */
QRect r = rect;
Q_ASSERT(r.isValid());
SetPropertyCommand *geom_cmd = new SetPropertyCommand(this);
geom_cmd->init(w, QLatin1String("geometry"), r); // ### use rc.size()
if (w->parentWidget() != container) {
ReparentWidgetCommand *cmd = new ReparentWidgetCommand(this);
cmd->init(w, container);
m_undoStack.push(cmd);
}
m_undoStack.push(geom_cmd);
InsertWidgetCommand *cmd = new InsertWidgetCommand(this);
cmd->init(w, already_in_form);
m_undoStack.push(cmd);
endCommand();
w->show();
}
QWidget *FormWindow::createWidget(DomUI *ui, const QRect &rc, QWidget *target)
{
QWidget *container = findContainer(target, false);
if (!container)
return 0;
if (isMainContainer(container)) {
if (QMainWindow *mw = qobject_cast<QMainWindow*>(container)) {
Q_ASSERT(mw->centralWidget() != 0);
container = mw->centralWidget();
}
}
QDesignerResource resource(this);
const FormBuilderClipboard clipboard = resource.paste(ui, container);
if (clipboard.m_widgets.size() != 1) // multiple-paste from DomUI not supported yet
return 0;
QWidget *widget = clipboard.m_widgets.first();
insertWidget(widget, rc, container);
return widget;
}
#ifndef QT_NO_DEBUG
static bool isDescendant(const QWidget *parent, const QWidget *child)
{
for (; child != 0; child = child->parentWidget()) {
if (child == parent)
return true;
}
return false;
}
#endif
void FormWindow::resizeWidget(QWidget *widget, const QRect &geometry)
{
Q_ASSERT(isDescendant(this, widget));
QRect r = geometry;
SetPropertyCommand *cmd = new SetPropertyCommand(this);
cmd->init(widget, QLatin1String("geometry"), r);
cmd->setText(tr("Resize"));
m_undoStack.push(cmd);
}
void FormWindow::raiseChildSelections(QWidget *w)
{
const QWidgetList l = qFindChildren<QWidget*>(w);
if (l.isEmpty())
return;
m_selection->raiseList(l);
}
QWidget *FormWindow::containerAt(const QPoint &pos, QWidget *notParentOf)
{
QWidget *container = 0;
int depth = -1;
const QWidgetList selected = selectedWidgets();
if (rect().contains(mapFromGlobal(pos))) {
container = mainContainer();
depth = widgetDepth(container);
}
QListIterator<QWidget*> it(m_widgets);
while (it.hasNext()) {
QWidget *wit = it.next();
if (qobject_cast<QLayoutWidget*>(wit) || qobject_cast<QSplitter*>(wit))
continue;
if (!wit->isVisibleTo(this))
continue;
if (selected.indexOf(wit) != -1)
continue;
if (!core()->widgetDataBase()->isContainer(wit) &&
wit != mainContainer())
continue;
// the rectangles of all ancestors of the container must contain the insert position
QWidget *w = wit;
while (w && !w->isWindow()) {
if (!w->rect().contains((w->mapFromGlobal(pos))))
break;
w = w->parentWidget();
}
if (!(w == 0 || w->isWindow()))
continue; // we did not get through the full while loop
int wd = widgetDepth(wit);
if (wd == depth && container) {
if (wit->parentWidget()->children().indexOf(wit) >
container->parentWidget()->children().indexOf(container))
wd++;
}
if (wd > depth && !isChildOf(wit, notParentOf)) {
depth = wd;
container = wit;
}
}
return container;
}
QWidgetList FormWindow::selectedWidgets() const
{
return m_selection->selectedWidgets();
}
void FormWindow::selectWidgets()
{
bool selectionChanged = false;
const QWidgetList l = qFindChildren<QWidget*>(mainContainer());
QListIterator <QWidget*> it(l);
const QRect selRect(mapToGlobal(m_currRect.topLeft()), m_currRect.size());
while (it.hasNext()) {
QWidget *w = it.next();
if (w->isVisibleTo(this) && isManaged(w)) {
const QPoint p = w->mapToGlobal(QPoint(0,0));
const QRect r(p, w->size());
if (r.intersects(selRect) && !r.contains(selRect) && trySelectWidget(w, true))
selectionChanged = true;
}
}
if (selectionChanged)
emitSelectionChanged();
}
bool FormWindow::handleKeyPressEvent(QWidget *widget, QWidget *, QKeyEvent *e)
{
if (qobject_cast<const FormWindow*>(widget) || qobject_cast<const QMenu*>(widget))
return false;
e->accept(); // we always accept!
switch (e->key()) {
default: break; // we don't care about the other keys
case Qt::Key_Delete:
case Qt::Key_Backspace:
if (e->modifiers() == Qt::NoModifier)
deleteWidgets();
break;
case Qt::Key_Tab:
if (e->modifiers() == Qt::NoModifier)
cursor()->movePosition(QDesignerFormWindowCursorInterface::Next);
break;
case Qt::Key_Backtab:
if (e->modifiers() == Qt::NoModifier)
cursor()->movePosition(QDesignerFormWindowCursorInterface::Prev);
break;
case Qt::Key_Left:
case Qt::Key_Right:
case Qt::Key_Up:
case Qt::Key_Down:
handleArrowKeyEvent(e->key(), e->modifiers());
break;
}
return true;
}
int FormWindow::getValue(const QRect &rect, int key, bool size) const
{
if (size) {
if (key == Qt::Key_Left || key == Qt::Key_Right)
return rect.width();
return rect.height();
}
if (key == Qt::Key_Left || key == Qt::Key_Right)
return rect.x();
return rect.y();
}
int FormWindow::calcValue(int val, bool forward, bool snap, int snapOffset) const
{
if (snap) {
const int rest = val % snapOffset;
if (rest) {
const int offset = forward ? snapOffset : 0;
const int newOffset = rest < 0 ? offset - snapOffset : offset;
return val + newOffset - rest;
}
return (forward ? val + snapOffset : val - snapOffset);
}
return (forward ? val + 1 : val - 1);
}
// ArrowKeyOperation: Stores a keyboard move or resize (Shift pressed)
// operation.
struct ArrowKeyOperation {
ArrowKeyOperation() : resize(false), distance(0), arrowKey(Qt::Key_Left) {}
QRect apply(const QRect &in) const;
bool resize; // Resize: Shift-Key->drag bottom/right corner, else just move
int distance;
int arrowKey;
};
} // namespace
QT_END_NAMESPACE
Q_DECLARE_METATYPE(qdesigner_internal::ArrowKeyOperation)
QT_BEGIN_NAMESPACE
namespace qdesigner_internal {
QRect ArrowKeyOperation::apply(const QRect &rect) const
{
QRect r = rect;
if (resize) {
if (arrowKey == Qt::Key_Left || arrowKey == Qt::Key_Right)
r.setWidth(r.width() + distance);
else
r.setHeight(r.height() + distance);
} else {
if (arrowKey == Qt::Key_Left || arrowKey == Qt::Key_Right)
r.moveLeft(r.x() + distance);
else
r.moveTop(r.y() + distance);
}
return r;
}
QDebug operator<<(QDebug in, const ArrowKeyOperation &op)
{
in.nospace() << "Resize=" << op.resize << " dist=" << op.distance << " Key=" << op.arrowKey << ' ';
return in;
}
// ArrowKeyPropertyHelper: Applies a struct ArrowKeyOperation
// (stored as new value) to a list of widgets using to calculate the
// changed geometry of the widget in setValue(). Thus, the 'newValue'
// of the property command is the relative move distance, which is the same
// for all widgets (although resulting in different geometries for the widgets).
// The command merging can then work as it would when applying the same text
// to all QLabels.
class ArrowKeyPropertyHelper : public PropertyHelper {
public:
ArrowKeyPropertyHelper(QObject* o, SpecialProperty sp,
QDesignerPropertySheetExtension *s, int i) :
PropertyHelper(o, sp, s, i) {}
virtual Value setValue(QDesignerFormWindowInterface *fw, const QVariant &value, bool changed, unsigned subPropertyMask);
};
PropertyHelper::Value ArrowKeyPropertyHelper::setValue(QDesignerFormWindowInterface *fw, const QVariant &value, bool changed, unsigned subPropertyMask)
{
// Apply operation to obtain the new geometry value.
QWidget *w = qobject_cast<QWidget*>(object());
const ArrowKeyOperation operation = qvariant_cast<ArrowKeyOperation>(value);
const QRect newGeom = operation.apply(w->geometry());
return PropertyHelper::setValue(fw, QVariant(newGeom), changed, subPropertyMask);
}
// ArrowKeyPropertyCommand: Helper factory overwritten to create
// ArrowKeyPropertyHelper and a merge operation that merges values of
// the same direction.
class ArrowKeyPropertyCommand: public SetPropertyCommand {
public:
explicit ArrowKeyPropertyCommand(QDesignerFormWindowInterface *fw,
QUndoCommand *p = 0);
void init(QWidgetList &l, const ArrowKeyOperation &op);
protected:
virtual PropertyHelper *createPropertyHelper(QObject *o, SpecialProperty sp,
QDesignerPropertySheetExtension *s, int i) const
{ return new ArrowKeyPropertyHelper(o, sp, s, i); }
virtual QVariant mergeValue(const QVariant &newValue);
};
ArrowKeyPropertyCommand::ArrowKeyPropertyCommand(QDesignerFormWindowInterface *fw,
QUndoCommand *p) :
SetPropertyCommand(fw, p)
{
static const int mid = qRegisterMetaType<qdesigner_internal::ArrowKeyOperation>();
Q_UNUSED(mid)
}
void ArrowKeyPropertyCommand::init(QWidgetList &l, const ArrowKeyOperation &op)
{
QObjectList ol;
foreach(QWidget *w, l)
ol.push_back(w);
SetPropertyCommand::init(ol, QLatin1String("geometry"), qVariantFromValue(op));
setText(op.resize ? FormWindow::tr("Key Resize") : FormWindow::tr("Key Move"));
}
QVariant ArrowKeyPropertyCommand::mergeValue(const QVariant &newMergeValue)
{
// Merge move operations of the same arrow key
if (!qVariantCanConvert<ArrowKeyOperation>(newMergeValue))
return QVariant();
ArrowKeyOperation mergedOperation = qvariant_cast<ArrowKeyOperation>(newValue());
const ArrowKeyOperation newMergeOperation = qvariant_cast<ArrowKeyOperation>(newMergeValue);
if (mergedOperation.resize != newMergeOperation.resize || mergedOperation.arrowKey != newMergeOperation.arrowKey)
return QVariant();
mergedOperation.distance += newMergeOperation.distance;
return qVariantFromValue(mergedOperation);
}
void FormWindow::handleArrowKeyEvent(int key, Qt::KeyboardModifiers modifiers)
{
const QDesignerFormWindowCursorInterface *c = cursor();
if (!c->hasSelection())
return;
QWidgetList selection;
// check if a laid out widget is selected
const int count = c->selectedWidgetCount();
for (int index = 0; index < count; ++index) {
QWidget *w = c->selectedWidget(index);
if (!LayoutInfo::isWidgetLaidout(m_core, w))
selection.append(w);
}
if (selection.isEmpty())
return;
QWidget *current = c->current();
if (!current || LayoutInfo::isWidgetLaidout(m_core, current)) {
current = selection.first();
}
const bool size = modifiers & Qt::ShiftModifier;
const bool snap = !(modifiers & Qt::ControlModifier);
const bool forward = (key == Qt::Key_Right || key == Qt::Key_Down);
const int snapPoint = (key == Qt::Key_Left || key == Qt::Key_Right) ? grid().x() : grid().y();
const int oldValue = getValue(current->geometry(), key, size);
const int newValue = calcValue(oldValue, forward, snap, snapPoint);
ArrowKeyOperation operation;
operation.resize = modifiers & Qt::ShiftModifier;
operation.distance = newValue - oldValue;
operation.arrowKey = key;
ArrowKeyPropertyCommand *cmd = new ArrowKeyPropertyCommand(this);
cmd->init(selection, operation);
m_undoStack.push(cmd);
}
bool FormWindow::handleKeyReleaseEvent(QWidget *, QWidget *, QKeyEvent *e)
{
e->accept();
return true;
}
void FormWindow::selectAll()
{
bool selectionChanged = false;
foreach (QWidget *widget, m_widgets) {
if (widget->isVisibleTo(this) && trySelectWidget(widget, true))
selectionChanged = true;
}
if (selectionChanged)
emitSelectionChanged();
}
void FormWindow::createLayout(int type, QWidget *container)
{
if (container) {
layoutContainer(container, type);
} else {
LayoutCommand *cmd = new LayoutCommand(this);
cmd->init(mainContainer(), selectedWidgets(), static_cast<LayoutInfo::Type>(type));
commandHistory()->push(cmd);
}
}
void FormWindow::morphLayout(QWidget *container, int newType)
{
MorphLayoutCommand *cmd = new MorphLayoutCommand(this);
if (cmd->init(container, newType)) {
commandHistory()->push(cmd);
} else {
qDebug() << "** WARNING Unable to morph layout.";
delete cmd;
}
}
void FormWindow::deleteWidgets()
{
QWidgetList selection = selectedWidgets();
simplifySelection(&selection);
deleteWidgetList(selection);
}
QString FormWindow::fileName() const
{
return m_fileName;
}
void FormWindow::setFileName(const QString &fileName)
{
if (m_fileName == fileName)
return;
m_fileName = fileName;
emit fileNameChanged(fileName);
}
QString FormWindow::contents() const
{
QBuffer b;
if (!mainContainer() || !b.open(QIODevice::WriteOnly))
return QString();
QDesignerResource resource(const_cast<FormWindow*>(this));
resource.save(&b, mainContainer());
return QString::fromUtf8(b.buffer());
}
void FormWindow::copy()
{
QBuffer b;
if (!b.open(QIODevice::WriteOnly))
return;
FormBuilderClipboard clipboard;
QDesignerResource resource(this);
resource.setSaveRelative(false);
clipboard.m_widgets = selectedWidgets();
simplifySelection(&clipboard.m_widgets);
resource.copy(&b, clipboard);
qApp->clipboard()->setText(QString::fromUtf8(b.buffer()), QClipboard::Clipboard);
}
void FormWindow::cut()
{
copy();
deleteWidgets();
}
// for cases like QMainWindow (central widget is an inner container) or QStackedWidget (page is an inner container)
QWidget *FormWindow::innerContainer(QWidget *outerContainer) const
{
if (m_core->widgetDataBase()->isContainer(outerContainer))
if (const QDesignerContainerExtension *container = qt_extension<QDesignerContainerExtension*>(m_core->extensionManager(), outerContainer)) {
const int currentIndex = container->currentIndex();
return currentIndex >= 0 ?
container->widget(currentIndex) :
static_cast<QWidget *>(0);
}
return outerContainer;
}
QWidget *FormWindow::containerForPaste() const
{
QWidget *w = mainContainer();
if (!w)
return 0;
do {
// Try to find a close parent, for example a non-laid-out
// QFrame/QGroupBox when a widget within it is selected.
QWidgetList selection = selectedWidgets();
if (selection.empty())
break;
simplifySelection(&selection);
QWidget *containerOfW = findContainer(selection.first(), /* exclude layouts */ true);
if (!containerOfW || containerOfW == mainContainer())
break;
// No layouts, must be container. No empty page-based containers.
containerOfW = innerContainer(containerOfW);
if (!containerOfW)
break;
if (LayoutInfo::layoutType(m_core, containerOfW) != LayoutInfo::NoLayout || !m_core->widgetDataBase()->isContainer(containerOfW))
break;
w = containerOfW;
} while (false);
// First check for layout (note that it does not cover QMainWindow
// and the like as the central widget has the layout).
w = innerContainer(w);
if (!w)
return 0;
if (LayoutInfo::layoutType(m_core, w) != LayoutInfo::NoLayout)
return 0;
// Go up via container extension (also includes step from QMainWindow to its central widget)
w = m_core->widgetFactory()->containerOfWidget(w);
if (w == 0 || LayoutInfo::layoutType(m_core, w) != LayoutInfo::NoLayout)
return 0;
if (debugFormWindow)
qDebug() <<"containerForPaste() " << w;
return w;
}
void FormWindow::paste()
{
paste(PasteAll);
}
// Construct DomUI from clipboard (paste) and determine number of widgets/actions.
static inline DomUI *domUIFromClipboard(int *widgetCount, int *actionCount)
{
*widgetCount = *actionCount = 0;
const QString clipboardText = qApp->clipboard()->text();
if (clipboardText.isEmpty() || clipboardText.indexOf(QLatin1Char('<')) == -1)
return 0;
QXmlStreamReader reader(clipboardText);
DomUI *ui = 0;
const QString uiElement = QLatin1String("ui");
while (!reader.atEnd()) {
if (reader.readNext() == QXmlStreamReader::StartElement) {
if (reader.name().compare(uiElement, Qt::CaseInsensitive) == 0 && !ui) {
ui = new DomUI();
ui->read(reader);
break;
} else {
reader.raiseError(QCoreApplication::translate("FormWindow", "Unexpected element <%1>").arg(reader.name().toString()));
}
}
}
if (reader.hasError()) {
delete ui;
ui = 0;
designerWarning(QCoreApplication::translate("FormWindow", "Error while pasting clipboard contents at line %1, column %2: %3").
arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString()));
return 0;
}
if (const DomWidget *topLevel = ui->elementWidget()) {
*widgetCount = topLevel->elementWidget().size();
*actionCount = topLevel->elementAction().size();
}
if (*widgetCount == 0 && *actionCount == 0) {
delete ui;
return 0;
}
return ui;
}
static inline QString pasteCommandDescription(int widgetCount, int actionCount)
{
if (widgetCount == 0)
return FormWindow::tr("Paste %n action(s)", 0, actionCount);
if (actionCount == 0)
return FormWindow::tr("Paste %n widget(s)", 0, widgetCount);
return FormWindow::tr("Paste (%1 widgets, %2 actions)").arg(widgetCount).arg(actionCount);
}
static void positionPastedWidgetsAtMousePosition(FormWindow *fw, const QPoint &contextMenuPosition, QWidget *parent, const QWidgetList &l)
{
// Try to position pasted widgets at mouse position (current mouse position for Ctrl-V or position of context menu)
// if it fits. If it is completely outside, force it to 0,0
// If it fails, the old coordinates relative to the previous parent will be used.
QPoint currentPos = contextMenuPosition.x() >=0 ? parent->mapFrom(fw, contextMenuPosition) : parent->mapFromGlobal(QCursor::pos());
const Grid &grid = fw->designerGrid();
QPoint cursorPos = grid.snapPoint(currentPos);
const QRect parentGeometry = QRect(QPoint(0, 0), parent->size());
const bool outside = !parentGeometry.contains(cursorPos);
if (outside)
cursorPos = grid.snapPoint(QPoint(0, 0));
// Determine area of pasted widgets
QRect pasteArea;
const QWidgetList::const_iterator lcend = l.constEnd();
for (QWidgetList::const_iterator it = l.constBegin(); it != lcend; ++it)
pasteArea =pasteArea.isNull() ? (*it)->geometry() : pasteArea.united((*it)->geometry());
// Mouse on some child? (try to position bottomRight on a free spot to
// get the stacked-offset effect of Designer 4.3, that is, offset by grid if Ctrl-V is pressed continuously
do {
const QPoint bottomRight = cursorPos + QPoint(pasteArea.width(), pasteArea.height()) - QPoint(1, 1);
if (bottomRight.y() > parentGeometry.bottom() || parent->childAt(bottomRight) == 0)
break;
cursorPos += QPoint(grid.deltaX(), grid.deltaY());
} while (true);
// Move.
const QPoint offset = cursorPos - pasteArea.topLeft();
for (QWidgetList::const_iterator it = l.constBegin(); it != lcend; ++it)
(*it)->move((*it)->pos() + offset);
}
void FormWindow::paste(PasteMode pasteMode)
{
// Avoid QDesignerResource constructing widgets that are not used as
// QDesignerResource manages the widgets it creates (creating havoc if one remains unused)
DomUI *ui = 0;
do {
int widgetCount;
int actionCount;
ui = domUIFromClipboard(&widgetCount, &actionCount);
if (!ui)
break;
// Check for actions
if (pasteMode == PasteActionsOnly)
if (widgetCount != 0 || actionCount == 0)
break;
// Check for widgets: need a container
QWidget *pasteContainer = widgetCount ? containerForPaste() : 0;
if (widgetCount && pasteContainer == 0) {
const QString message = tr("Cannot paste widgets. Designer could not find a container "
"without a layout to paste into.");
const QString infoMessage = tr("Break the layout of the "
"container you want to paste into, select this container "
"and then paste again.");
core()->dialogGui()->message(this, QDesignerDialogGuiInterface::FormEditorMessage, QMessageBox::Information,
tr("Paste error"), message, infoMessage, QMessageBox::Ok);
break;
}
QDesignerResource resource(this);
// Note that the widget factory must be able to locate the
// form window (us) via parent, otherwise, it will not able to construct QLayoutWidgets
// (It will then default to widgets) among other issues.
const FormBuilderClipboard clipboard = resource.paste(ui, pasteContainer, this);
clearSelection(false);
// Create command sequence
beginCommand(pasteCommandDescription(widgetCount, actionCount));
if (widgetCount) {
positionPastedWidgetsAtMousePosition(this, m_contextMenuPosition, pasteContainer, clipboard.m_widgets);
foreach (QWidget *w, clipboard.m_widgets) {
InsertWidgetCommand *cmd = new InsertWidgetCommand(this);
cmd->init(w);
m_undoStack.push(cmd);
selectWidget(w);
}
}
if (actionCount)
foreach (QAction *a, clipboard.m_actions) {
ensureUniqueObjectName(a);
AddActionCommand *cmd = new AddActionCommand(this);
cmd->init(a);
m_undoStack.push(cmd);
}
endCommand();
} while (false);
delete ui;
}
// Draw a dotted frame around containers
bool FormWindow::frameNeeded(QWidget *w) const
{
if (!core()->widgetDataBase()->isContainer(w))
return false;
if (qobject_cast<QGroupBox *>(w))
return false;
if (qobject_cast<QToolBox *>(w))
return false;
if (qobject_cast<QTabWidget *>(w))
return false;
if (qobject_cast<QStackedWidget *>(w))
return false;
if (qobject_cast<QDockWidget *>(w))
return false;
if (qobject_cast<QDesignerWidget *>(w))
return false;
if (qobject_cast<QMainWindow *>(w))
return false;
if (qobject_cast<QDialog *>(w))
return false;
if (qobject_cast<QLayoutWidget *>(w))
return false;
return true;
}
bool FormWindow::eventFilter(QObject *watched, QEvent *event)
{
const bool ret = FormWindowBase::eventFilter(watched, event);
if (event->type() != QEvent::Paint)
return ret;
Q_ASSERT(watched->isWidgetType());
QWidget *w = static_cast<QWidget *>(watched);
QPaintEvent *pe = static_cast<QPaintEvent*>(event);
const QRect widgetRect = w->rect();
const QRect paintRect = pe->rect();
// Does the paint rectangle touch the borders of the widget rectangle
if (paintRect.x() > widgetRect.x() && paintRect.y() > widgetRect.y() &&
paintRect.right() < widgetRect.right() && paintRect.bottom() < widgetRect.bottom())
return ret;
QPainter p(w);
const QPen pen(QColor(0, 0, 0, 32), 0, Qt::DotLine);
p.setPen(pen);
p.setBrush(QBrush(Qt::NoBrush));
p.drawRect(widgetRect.adjusted(0, 0, -1, -1));
return ret;
}
void FormWindow::manageWidget(QWidget *w)
{
if (isManaged(w))
return;
Q_ASSERT(qobject_cast<QMenu*>(w) == 0);
if (w->hasFocus())
setFocus();
core()->metaDataBase()->add(w);
m_insertedWidgets.insert(w);
m_widgets.append(w);
#ifndef QT_NO_CURSOR
setCursorToAll(Qt::ArrowCursor, w);
#endif
emit changed();
emit widgetManaged(w);
if (frameNeeded(w))
w->installEventFilter(this);
}
void FormWindow::unmanageWidget(QWidget *w)
{
if (!isManaged(w))
return;
m_selection->removeWidget(w);
emit aboutToUnmanageWidget(w);
if (w == m_currentWidget)
setCurrentWidget(mainContainer());
core()->metaDataBase()->remove(w);
m_insertedWidgets.remove(w);
m_widgets.removeAt(m_widgets.indexOf(w));
emit changed();
emit widgetUnmanaged(w);
if (frameNeeded(w))
w->removeEventFilter(this);
}
bool FormWindow::isManaged(QWidget *w) const
{
return m_insertedWidgets.contains(w);
}
void FormWindow::breakLayout(QWidget *w)
{
if (w == this)
w = mainContainer();
// Find the first-order managed child widgets
QWidgetList widgets;
const QObjectList children = w->children();
const QObjectList::const_iterator cend = children.constEnd();
const QDesignerMetaDataBaseInterface *mdb = core()->metaDataBase();
for (QObjectList::const_iterator it = children.constBegin(); it != cend; ++it)
if ( (*it)->isWidgetType()) {
QWidget *w = static_cast<QWidget*>(*it);
if (mdb->item(w))
widgets.push_back(w);
}
BreakLayoutCommand *cmd = new BreakLayoutCommand(this);
cmd->init(widgets, w);
commandHistory()->push(cmd);
clearSelection(false);
}
void FormWindow::beginCommand(const QString &description)
{
m_undoStack.beginMacro(description);
}
void FormWindow::endCommand()
{
m_undoStack.endMacro();
}
void FormWindow::raiseWidgets()
{
QWidgetList widgets = selectedWidgets();
simplifySelection(&widgets);
if (widgets.isEmpty())
return;
beginCommand(tr("Raise widgets"));
foreach (QWidget *widget, widgets) {
RaiseWidgetCommand *cmd = new RaiseWidgetCommand(this);
cmd->init(widget);
m_undoStack.push(cmd);
}
endCommand();
}
void FormWindow::lowerWidgets()
{
QWidgetList widgets = selectedWidgets();
simplifySelection(&widgets);
if (widgets.isEmpty())
return;
beginCommand(tr("Lower widgets"));
foreach (QWidget *widget, widgets) {
LowerWidgetCommand *cmd = new LowerWidgetCommand(this);
cmd->init(widget);
m_undoStack.push(cmd);
}
endCommand();
}
bool FormWindow::handleMouseButtonDblClickEvent(QWidget *w, QWidget *managedWidget, QMouseEvent *e)
{
if (debugFormWindow)
qDebug() << "handleMouseButtonDblClickEvent:" << w << ',' << managedWidget << "state=" << m_mouseState;
e->accept();
// Might be out of sync due cycling of the parent selection
// In that case, do nothing
if (isWidgetSelected(managedWidget))
emit activated(managedWidget);
m_mouseState = MouseDoubleClicked;
return true;
}
QMenu *FormWindow::initializePopupMenu(QWidget *managedWidget)
{
if (!isManaged(managedWidget) || currentTool())
return 0;
// Make sure the managedWidget is selected and current since
// the SetPropertyCommands must use the right reference
// object obtained from the property editor for the property group
// of a multiselection to be correct.
const bool selected = isWidgetSelected(managedWidget);
bool update = false;
if (selected == false) {
clearObjectInspectorSelection(m_core); // We might have a toolbar or non-widget selected in the object inspector.
clearSelection(false);
update = trySelectWidget(managedWidget, true);
raiseChildSelections(managedWidget); // raise selections and select widget
} else {
update = setCurrentWidget(managedWidget);
}
if (update) {
emitSelectionChanged();
QMetaObject::invokeMethod(core()->formWindowManager(), "slotUpdateActions");
}
QWidget *contextMenuWidget = 0;
if (isMainContainer(managedWidget)) { // press on a child widget
contextMenuWidget = mainContainer();
} else { // press on a child widget
// if widget is laid out, find the first non-laid out super-widget
QWidget *realWidget = managedWidget; // but store the original one
QMainWindow *mw = qobject_cast<QMainWindow*>(mainContainer());
if (mw && mw->centralWidget() == realWidget) {
contextMenuWidget = managedWidget;
} else {
contextMenuWidget = realWidget;
}
}
if (!contextMenuWidget)
return 0;
QMenu *contextMenu = createPopupMenu(contextMenuWidget);
if (!contextMenu)
return 0;
emit contextMenuRequested(contextMenu, contextMenuWidget);
return contextMenu;
}
bool FormWindow::handleContextMenu(QWidget *, QWidget *managedWidget, QContextMenuEvent *e)
{
QMenu *contextMenu = initializePopupMenu(managedWidget);
if (!contextMenu)
return false;
const QPoint globalPos = e->globalPos();
m_contextMenuPosition = mapFromGlobal (globalPos);
contextMenu->exec(globalPos);
delete contextMenu;
e->accept();
m_contextMenuPosition = QPoint(-1, -1);
return true;
}
void FormWindow::setContents(QIODevice *dev)
{
UpdateBlocker ub(this);
clearSelection();
m_selection->clearSelectionPool();
m_insertedWidgets.clear();
m_widgets.clear();
// The main container is cleared as otherwise
// the names of the newly loaded objects will be unified.
clearMainContainer();
emit changed();
QDesignerResource r(this);
QWidget *w = r.load(dev, formContainer());
setMainContainer(w);
emit changed();
}
void FormWindow::setContents(const QString &contents)
{
QByteArray data = contents.toUtf8();
QBuffer b(&data);
if (b.open(QIODevice::ReadOnly))
setContents(&b);
}
void FormWindow::layoutContainer(QWidget *w, int type)
{
if (w == this)
w = mainContainer();
w = core()->widgetFactory()->containerOfWidget(w);
const QObjectList l = w->children();
if (l.isEmpty())
return;
// find managed widget children
QWidgetList widgets;
const QObjectList::const_iterator ocend = l.constEnd();
for (QObjectList::const_iterator it = l.constBegin(); it != l.constEnd(); ++it)
if ( (*it)->isWidgetType() ) {
QWidget *widget = static_cast<QWidget*>(*it);
if (widget->isVisibleTo(this) && isManaged(widget))
widgets.append(widget);
}
LayoutCommand *cmd = new LayoutCommand(this);
cmd->init(mainContainer(), widgets, static_cast<LayoutInfo::Type>(type), w);
clearSelection(false);
commandHistory()->push(cmd);
}
bool FormWindow::hasInsertedChildren(QWidget *widget) const // ### move
{
if (QDesignerContainerExtension *container = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), widget)) {
const int index = container->currentIndex();
if (index < 0)
return false;
widget = container->widget(index);
}
const QWidgetList l = widgets(widget);
foreach (QWidget *child, l) {
if (isManaged(child) && !LayoutInfo::isWidgetLaidout(core(), child) && child->isVisibleTo(const_cast<FormWindow*>(this)))
return true;
}
return false;
}
// "Select Ancestor" sub menu code
void FormWindow::slotSelectWidget(QAction *a)
{
if (QWidget *w = qvariant_cast<QWidget*>(a->data()))
selectSingleWidget(w);
}
static inline QString objectNameOf(const QWidget *w)
{
if (const QLayoutWidget *lw = qobject_cast<const QLayoutWidget *>(w)) {
const QLayout *layout = lw->layout();
const QString rc = layout->objectName();
if (!rc.isEmpty())
return rc;
// Fall thru for 4.3 forms which have a name on the widget: Display the class name
return QString::fromUtf8(layout->metaObject()->className());
}
return w->objectName();
}
QAction *FormWindow::createSelectAncestorSubMenu(QWidget *w)
{
// Find the managed, unselected parents
QWidgetList parents;
QWidget *mc = mainContainer();
for (QWidget *p = w->parentWidget(); p && p != mc; p = p->parentWidget())
if (isManaged(p) && !isWidgetSelected(p))
parents.push_back(p);
if (parents.empty())
return 0;
// Create a submenu listing the managed, unselected parents
QMenu *menu = new QMenu;
QActionGroup *ag = new QActionGroup(menu);
QObject::connect(ag, SIGNAL(triggered(QAction*)), this, SLOT(slotSelectWidget(QAction*)));
const int size = parents.size();
for (int i = 0; i < size; i++) {
QWidget *w = parents.at(i);
QAction *a = ag->addAction(objectNameOf(w));
a->setData(qVariantFromValue(w));
menu->addAction(a);
}
QAction *ma = new QAction(tr("Select Ancestor"), 0);
ma->setMenu(menu);
return ma;
}
QMenu *FormWindow::createPopupMenu(QWidget *w)
{
QMenu *popup = createExtensionTaskMenu(this, w, true);
if (!popup)
popup = new QMenu;
// if w doesn't have a QDesignerTaskMenu as a child create one and make it a child.
// insert actions from QDesignerTaskMenu
QDesignerFormWindowManagerInterface *manager = core()->formWindowManager();
const bool isFormWindow = qobject_cast<const FormWindow*>(w);
// Check for special containers and obtain the page menu from them to add layout actions.
if (!isFormWindow) {
if (QStackedWidget *stackedWidget = qobject_cast<QStackedWidget*>(w)) {
QStackedWidgetEventFilter::addStackedWidgetContextMenuActions(stackedWidget, popup);
} else if (QTabWidget *tabWidget = qobject_cast<QTabWidget*>(w)) {
QTabWidgetEventFilter::addTabWidgetContextMenuActions(tabWidget, popup);
} else if (QToolBox *toolBox = qobject_cast<QToolBox*>(w)) {
QToolBoxHelper::addToolBoxContextMenuActions(toolBox, popup);
}
if (manager->actionLower()->isEnabled()) {
popup->addAction(manager->actionLower());
popup->addAction(manager->actionRaise());
popup->addSeparator();
}
popup->addAction(manager->actionCut());
popup->addAction(manager->actionCopy());
}
popup->addAction(manager->actionPaste());
if (QAction *selectAncestorAction = createSelectAncestorSubMenu(w))
popup->addAction(selectAncestorAction);
popup->addAction(manager->actionSelectAll());
if (!isFormWindow) {
popup->addAction(manager->actionDelete());
}
popup->addSeparator();
QMenu *layoutMenu = popup->addMenu(tr("Lay out"));
layoutMenu->addAction(manager->actionAdjustSize());
layoutMenu->addAction(manager->actionHorizontalLayout());
layoutMenu->addAction(manager->actionVerticalLayout());
if (!isFormWindow) {
layoutMenu->addAction(manager->actionSplitHorizontal());
layoutMenu->addAction(manager->actionSplitVertical());
}
layoutMenu->addAction(manager->actionGridLayout());
layoutMenu->addAction(manager->actionFormLayout());
layoutMenu->addAction(manager->actionBreakLayout());
layoutMenu->addAction(manager->actionSimplifyLayout());
return popup;
}
void FormWindow::resizeEvent(QResizeEvent *e)
{
m_geometryChangedTimer->start(10);
QWidget::resizeEvent(e);
}
/*!
Maps \a pos in \a w's coordinates to the form's coordinate system.
This is the equivalent to mapFromGlobal(w->mapToGlobal(pos)) but
avoids the two roundtrips to the X-Server on Unix/X11.
*/
QPoint FormWindow::mapToForm(const QWidget *w, const QPoint &pos) const
{
QPoint p = pos;
const QWidget* i = w;
while (i && !i->isWindow() && !isMainContainer(i)) {
p = i->mapToParent(p);
i = i->parentWidget();
}
return mapFromGlobal(w->mapToGlobal(pos));
}
bool FormWindow::canBeBuddy(QWidget *w) const // ### rename me.
{
if (QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), w)) {
const int index = sheet->indexOf(QLatin1String("focusPolicy"));
if (index != -1) {
bool ok = false;
const Qt::FocusPolicy q = static_cast<Qt::FocusPolicy>(Utils::valueOf(sheet->property(index), &ok));
return ok && q != Qt::NoFocus;
}
}
return false;
}
QWidget *FormWindow::findContainer(QWidget *w, bool excludeLayout) const
{
if (!isChildOf(w, this)
|| const_cast<const QWidget *>(w) == this)
return 0;
QDesignerWidgetFactoryInterface *widgetFactory = core()->widgetFactory();
QDesignerWidgetDataBaseInterface *widgetDataBase = core()->widgetDataBase();
QDesignerMetaDataBaseInterface *metaDataBase = core()->metaDataBase();
QWidget *container = widgetFactory->containerOfWidget(mainContainer()); // default parent for new widget is the formwindow
if (!isMainContainer(w)) { // press was not on formwindow, check if we can find another parent
while (w) {
if (qobject_cast<InvisibleWidget*>(w) || !metaDataBase->item(w)) {
w = w->parentWidget();
continue;
}
const bool isContainer = widgetDataBase->isContainer(w, true) || w == mainContainer();
if (!isContainer || (excludeLayout && qobject_cast<QLayoutWidget*>(w))) { // ### skip QSplitter
w = w->parentWidget();
} else {
container = w;
break;
}
}
}
return container;
}
void FormWindow::simplifySelection(QWidgetList *sel) const
{
if (sel->size() < 2)
return;
// Figure out which widgets should be removed from selection.
// We want to remove those whose parent widget is also in the
// selection (because the child widgets are contained by
// their parent, they shouldn't be in the selection --
// they are "implicitly" selected).
QWidget *mainC = mainContainer(); // Quick check for main container first
if (sel->contains(mainC)) {
sel->clear();
sel->push_back(mainC);
return;
}
typedef QVector<QWidget *> WidgetVector;
WidgetVector toBeRemoved;
toBeRemoved.reserve(sel->size());
const QWidgetList::const_iterator scend = sel->constEnd();
for (QWidgetList::const_iterator it = sel->constBegin(); it != scend; ++it) {
QWidget *child = *it;
for (QWidget *w = child; true ; ) { // Is any of the parents also selected?
QWidget *parent = w->parentWidget();
if (!parent || parent == mainC)
break;
if (sel->contains(parent)) {
toBeRemoved.append(child);
break;
}
w = parent;
}
}
// Now we can actually remove the widgets that were marked
// for removal in the previous pass.
if (!toBeRemoved.isEmpty()) {
const WidgetVector::const_iterator rcend = toBeRemoved.constEnd();
for (WidgetVector::const_iterator it = toBeRemoved.constBegin(); it != rcend; ++it)
sel->removeAll(*it);
}
}
FormWindow *FormWindow::findFormWindow(QWidget *w)
{
return qobject_cast<FormWindow*>(QDesignerFormWindowInterface::findFormWindow(w));
}
bool FormWindow::isDirty() const
{
return m_undoStack.isDirty();
}
void FormWindow::setDirty(bool dirty)
{
m_undoStack.setDirty(dirty);
}
QWidget *FormWindow::containerAt(const QPoint &pos)
{
QWidget *widget = widgetAt(pos);
return findContainer(widget, true);
}
static QWidget *childAt_SkipDropLine(QWidget *w, QPoint pos)
{
const QObjectList child_list = w->children();
for (int i = child_list.size() - 1; i >= 0; --i) {
QObject *child_obj = child_list[i];
if (qobject_cast<WidgetHandle*>(child_obj) != 0)
continue;
QWidget *child = qobject_cast<QWidget*>(child_obj);
if (!child || child->isWindow() || !child->isVisible() ||
!child->geometry().contains(pos) || child->testAttribute(Qt::WA_TransparentForMouseEvents))
continue;
const QPoint childPos = child->mapFromParent(pos);
if (QWidget *res = childAt_SkipDropLine(child, childPos))
return res;
if (child->testAttribute(Qt::WA_MouseNoMask) || child->mask().contains(pos)
|| child->mask().isEmpty())
return child;
}
return 0;
}
QWidget *FormWindow::widgetAt(const QPoint &pos)
{
QWidget *w = childAt(pos);
if (qobject_cast<const WidgetHandle*>(w) != 0)
w = childAt_SkipDropLine(this, pos);
return (w == 0 || w == formContainer()) ? this : w;
}
void FormWindow::highlightWidget(QWidget *widget, const QPoint &pos, HighlightMode mode)
{
Q_ASSERT(widget);
if (QMainWindow *mainWindow = qobject_cast<QMainWindow*> (widget)) {
widget = mainWindow->centralWidget();
}
QWidget *container = findContainer(widget, false);
if (container == 0 || core()->metaDataBase()->item(container) == 0)
return;
if (QDesignerActionProviderExtension *g = qt_extension<QDesignerActionProviderExtension*>(core()->extensionManager(), container)) {
if (mode == Restore) {
g->adjustIndicator(QPoint());
} else {
const QPoint pt = widget->mapTo(container, pos);
g->adjustIndicator(pt);
}
} else if (QDesignerLayoutDecorationExtension *g = qt_extension<QDesignerLayoutDecorationExtension*>(core()->extensionManager(), container)) {
if (mode == Restore) {
g->adjustIndicator(QPoint(), -1);
} else {
const QPoint pt = widget->mapTo(container, pos);
const int index = g->findItemAt(pt);
g->adjustIndicator(pt, index);
}
}
QMainWindow *mw = qobject_cast<QMainWindow*> (container);
if (container == mainContainer() || (mw && mw->centralWidget() && mw->centralWidget() == container))
return;
if (mode == Restore) {
const WidgetPaletteMap::iterator pit = m_palettesBeforeHighlight.find(container);
if (pit != m_palettesBeforeHighlight.end()) {
container->setPalette(pit.value().first);
container->setAutoFillBackground(pit.value().second);
m_palettesBeforeHighlight.erase(pit);
}
} else {
QPalette p = container->palette();
if (!m_palettesBeforeHighlight.contains(container)) {
PaletteAndFill paletteAndFill;
if (container->testAttribute(Qt::WA_SetPalette))
paletteAndFill.first = p;
paletteAndFill.second = container->autoFillBackground();
m_palettesBeforeHighlight.insert(container, paletteAndFill);
}
p.setColor(backgroundRole(), p.midlight().color());
container->setPalette(p);
container->setAutoFillBackground(true);
}
}
QWidgetList FormWindow::widgets(QWidget *widget) const
{
const QObjectList children = widget->children();
if (children.empty())
return QWidgetList();
QWidgetList rc;
const QObjectList::const_iterator cend = children.constEnd();
for (QObjectList::const_iterator it = children.constBegin(); it != cend; ++it)
if ((*it)->isWidgetType()) {
QWidget *w = qobject_cast<QWidget*>(*it);
if (isManaged(w))
rc.push_back(w);
}
return rc;
}
int FormWindow::toolCount() const
{
return m_widgetStack->count();
}
QDesignerFormWindowToolInterface *FormWindow::tool(int index) const
{
return m_widgetStack->tool(index);
}
void FormWindow::registerTool(QDesignerFormWindowToolInterface *tool)
{
Q_ASSERT(tool != 0);
m_widgetStack->addTool(tool);
if (m_mainContainer)
m_mainContainer->update();
}
void FormWindow::setCurrentTool(int index)
{
m_widgetStack->setCurrentTool(index);
}
int FormWindow::currentTool() const
{
return m_widgetStack->currentIndex();
}
bool FormWindow::handleEvent(QWidget *widget, QWidget *managedWidget, QEvent *event)
{
if (m_widgetStack == 0)
return false;
QDesignerFormWindowToolInterface *tool = m_widgetStack->currentTool();
if (tool == 0)
return false;
return tool->handleEvent(widget, managedWidget, event);
}
void FormWindow::initializeCoreTools()
{
m_widgetEditor = new WidgetEditorTool(this);
registerTool(m_widgetEditor);
}
void FormWindow::checkSelection()
{
m_checkSelectionTimer->start(0);
}
void FormWindow::checkSelectionNow()
{
m_checkSelectionTimer->stop();
foreach (QWidget *widget, selectedWidgets()) {
updateSelection(widget);
if (LayoutInfo::layoutType(core(), widget) != LayoutInfo::NoLayout)
updateChildSelections(widget);
}
}
QString FormWindow::author() const
{
return m_author;
}
QString FormWindow::comment() const
{
return m_comment;
}
void FormWindow::setAuthor(const QString &author)
{
m_author = author;
}
void FormWindow::setComment(const QString &comment)
{
m_comment = comment;
}
void FormWindow::editWidgets()
{
m_widgetEditor->action()->trigger();
}
QStringList FormWindow::resourceFiles() const
{
return m_resourceFiles;
}
void FormWindow::addResourceFile(const QString &path)
{
if (!m_resourceFiles.contains(path)) {
m_resourceFiles.append(path);
setDirty(true);
emit resourceFilesChanged();
}
}
void FormWindow::removeResourceFile(const QString &path)
{
if (m_resourceFiles.removeAll(path) > 0) {
setDirty(true);
emit resourceFilesChanged();
}
}
bool FormWindow::blockSelectionChanged(bool b)
{
const bool blocked = m_blockSelectionChanged;
m_blockSelectionChanged = b;
return blocked;
}
void FormWindow::editContents()
{
const QWidgetList sel = selectedWidgets();
if (sel.count() == 1) {
QWidget *widget = sel.first();
if (QAction *a = preferredEditAction(core(), widget))
a->trigger();
}
}
void FormWindow::dragWidgetWithinForm(QWidget *widget, const QRect &targetGeometry, QWidget *targetContainer)
{
const bool fromLayout = canDragWidgetInLayout(core(), widget);
const QDesignerLayoutDecorationExtension *targetDeco = qt_extension<QDesignerLayoutDecorationExtension*>(core()->extensionManager(), targetContainer);
const bool toLayout = targetDeco != 0;
if (fromLayout) {
// Drag from Layout: We need to delete the widget properly to store the layout state
// Do not simplify the layout when dragging onto a layout
// as this might invalidate the insertion position if it is the same layout
DeleteWidgetCommand *cmd = new DeleteWidgetCommand(this);
unsigned deleteFlags = DeleteWidgetCommand::DoNotUnmanage;
if (toLayout)
deleteFlags |= DeleteWidgetCommand::DoNotSimplifyLayout;
cmd->init(widget, deleteFlags);
commandHistory()->push(cmd);
}
if (toLayout) {
// Drag from form to layout: just insert. Do not manage
insertWidget(widget, targetGeometry, targetContainer, true);
} else {
// into container without layout
if (targetContainer != widget->parent()) { // different parent
ReparentWidgetCommand *cmd = new ReparentWidgetCommand(this);
cmd->init(widget, targetContainer );
commandHistory()->push(cmd);
}
resizeWidget(widget, targetGeometry);
selectWidget(widget, true);
widget->show();
}
}
static Qt::DockWidgetArea detectDropArea(QMainWindow *mainWindow, const QRect &area, const QPoint &drop)
{
QPoint offset = area.topLeft();
QRect rect = area;
rect.moveTopLeft(QPoint(0, 0));
QPoint point = drop - offset;
const int x = point.x();
const int y = point.y();
const int w = rect.width();
const int h = rect.height();
if (rect.contains(point)) {
bool topRight = false;
bool topLeft = false;
if (w * y < h * x) // top and right, oterwise bottom and left
topRight = true;
if (w * y < h * (w - x)) // top and left, otherwise bottom and right
topLeft = true;
if (topRight && topLeft)
return Qt::TopDockWidgetArea;
else if (topRight && !topLeft)
return Qt::RightDockWidgetArea;
else if (!topRight && topLeft)
return Qt::LeftDockWidgetArea;
return Qt::BottomDockWidgetArea;
}
if (x < 0) {
if (y < 0)
return mainWindow->corner(Qt::TopLeftCorner);
else if (y > h)
return mainWindow->corner(Qt::BottomLeftCorner);
else
return Qt::LeftDockWidgetArea;
} else if (x > w) {
if (y < 0)
return mainWindow->corner(Qt::TopRightCorner);
else if (y > h)
return mainWindow->corner(Qt::BottomRightCorner);
else
return Qt::RightDockWidgetArea;
} else {
if (y < 0)
return Qt::TopDockWidgetArea;
else
return Qt::BottomDockWidgetArea;
}
return Qt::LeftDockWidgetArea;
}
bool FormWindow::dropDockWidget(QDesignerDnDItemInterface *item, const QPoint &global_mouse_pos)
{
DomUI *dom_ui = item->domUi();
QMainWindow *mw = qobject_cast<QMainWindow *>(mainContainer());
if (!mw)
return false;
QDesignerResource resource(this);
const FormBuilderClipboard clipboard = resource.paste(dom_ui, mw);
if (clipboard.m_widgets.size() != 1) // multiple-paste from DomUI not supported yet
return false;
QWidget *centralWidget = mw->centralWidget();
QPoint localPos = centralWidget->mapFromGlobal(global_mouse_pos);
const QRect centralWidgetAreaRect = centralWidget->rect();
Qt::DockWidgetArea area = detectDropArea(mw, centralWidgetAreaRect, localPos);
beginCommand(tr("Drop widget"));
clearSelection(false);
highlightWidget(mw, QPoint(0, 0), FormWindow::Restore);
QWidget *widget = clipboard.m_widgets.first();
insertWidget(widget, QRect(0, 0, 1, 1), mw);
selectWidget(widget, true);
mw->setFocus(Qt::MouseFocusReason); // in case focus was in e.g. object inspector
core()->formWindowManager()->setActiveFormWindow(this);
mainContainer()->activateWindow();
QDesignerPropertySheetExtension *propertySheet = qobject_cast<QDesignerPropertySheetExtension*>(m_core->extensionManager()->extension(widget, Q_TYPEID(QDesignerPropertySheetExtension)));
if (propertySheet) {
const QString dockWidgetAreaName = QLatin1String("dockWidgetArea");
PropertySheetEnumValue e = qvariant_cast<PropertySheetEnumValue>(propertySheet->property(propertySheet->indexOf(dockWidgetAreaName)));
e.value = area;
QVariant v;
qVariantSetValue(v, e);
SetPropertyCommand *cmd = new SetPropertyCommand(this);
cmd->init(widget, dockWidgetAreaName, v);
m_undoStack.push(cmd);
}
endCommand();
return true;
}
bool FormWindow::dropWidgets(const QList<QDesignerDnDItemInterface*> &item_list, QWidget *target,
const QPoint &global_mouse_pos)
{
QWidget *parent = target;
if (parent == 0)
parent = mainContainer();
// You can only drop stuff onto the central widget of a QMainWindow
// ### generalize to use container extension
if (QMainWindow *main_win = qobject_cast<QMainWindow*>(target)) {
if (!main_win->centralWidget()) {
designerWarning(tr("A QMainWindow-based form does not contain a central widget."));
return false;
}
const QPoint main_win_pos = main_win->mapFromGlobal(global_mouse_pos);
const QRect central_wgt_geo = main_win->centralWidget()->geometry();
if (!central_wgt_geo.contains(main_win_pos))
return false;
}
QWidget *container = findContainer(parent, false);
if (container == 0)
return false;
beginCommand(tr("Drop widget"));
clearSelection(false);
highlightWidget(target, target->mapFromGlobal(global_mouse_pos), FormWindow::Restore);
QPoint offset;
QDesignerDnDItemInterface *current = 0;
QDesignerFormWindowCursorInterface *c = cursor();
foreach (QDesignerDnDItemInterface *item, item_list) {
QWidget *w = item->widget();
if (!current)
current = item;
if (c->current() == w) {
current = item;
break;
}
}
if (current) {
QRect geom = current->decoration()->geometry();
QPoint topLeft = container->mapFromGlobal(geom.topLeft());
offset = designerGrid().snapPoint(topLeft) - topLeft;
}
foreach (QDesignerDnDItemInterface *item, item_list) {
DomUI *dom_ui = item->domUi();
QRect geometry = item->decoration()->geometry();
Q_ASSERT(dom_ui != 0);
geometry.moveTopLeft(container->mapFromGlobal(geometry.topLeft()) + offset);
if (item->type() == QDesignerDnDItemInterface::CopyDrop) { // from widget box or CTRL + mouse move
QWidget *widget = createWidget(dom_ui, geometry, parent);
if (!widget) {
endCommand();
return false;
}
selectWidget(widget, true);
mainContainer()->setFocus(Qt::MouseFocusReason); // in case focus was in e.g. object inspector
} else { // same form move
QWidget *widget = item->widget();
Q_ASSERT(widget != 0);
QDesignerFormWindowInterface *dest = findFormWindow(widget);
if (dest == this) {
dragWidgetWithinForm(widget, geometry, container);
} else { // from other form
FormWindow *source = qobject_cast<FormWindow*>(item->source());
Q_ASSERT(source != 0);
source->deleteWidgetList(QWidgetList() << widget);
QWidget *new_widget = createWidget(dom_ui, geometry, parent);
selectWidget(new_widget, true);
}
}
}
core()->formWindowManager()->setActiveFormWindow(this);
mainContainer()->activateWindow();
endCommand();
return true;
}
QDir FormWindow::absoluteDir() const
{
if (fileName().isEmpty())
return QDir::current();
return QFileInfo(fileName()).absoluteDir();
}
void FormWindow::layoutDefault(int *margin, int *spacing)
{
*margin = m_defaultMargin;
*spacing = m_defaultSpacing;
}
void FormWindow::setLayoutDefault(int margin, int spacing)
{
m_defaultMargin = margin;
m_defaultSpacing = spacing;
}
void FormWindow::layoutFunction(QString *margin, QString *spacing)
{
*margin = m_marginFunction;
*spacing = m_spacingFunction;
}
void FormWindow::setLayoutFunction(const QString &margin, const QString &spacing)
{
m_marginFunction = margin;
m_spacingFunction = spacing;
}
QString FormWindow::pixmapFunction() const
{
return m_pixmapFunction;
}
void FormWindow::setPixmapFunction(const QString &pixmapFunction)
{
m_pixmapFunction = pixmapFunction;
}
QStringList FormWindow::includeHints() const
{
return m_includeHints;
}
void FormWindow::setIncludeHints(const QStringList &includeHints)
{
m_includeHints = includeHints;
}
QString FormWindow::exportMacro() const
{
return m_exportMacro;
}
void FormWindow::setExportMacro(const QString &exportMacro)
{
m_exportMacro = exportMacro;
}
QEditorFormBuilder *FormWindow::createFormBuilder()
{
return new QDesignerResource(this);
}
QWidget *FormWindow::formContainer() const
{
return m_widgetStack->formContainer();
}
QUndoStack *FormWindow::commandHistory() const
{
return const_cast<QDesignerUndoStack &>(m_undoStack).qundoStack();
}
} // namespace
QT_END_NAMESPACE