| /**************************************************************************** |
| ** |
| ** 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 "buddyeditor.h" |
| |
| #include <QtDesigner/QDesignerFormWindowInterface> |
| #include <QtDesigner/QDesignerPropertySheetExtension> |
| #include <QtDesigner/QDesignerFormEditorInterface> |
| #include <QtDesigner/QExtensionManager> |
| |
| #include <qdesigner_command_p.h> |
| #include <qdesigner_propertycommand_p.h> |
| #include <qdesigner_utils_p.h> |
| #include <qlayout_widget_p.h> |
| #include <connectionedit_p.h> |
| |
| #include <QtCore/qdebug.h> |
| #include <QtGui/QLabel> |
| #include <QtGui/QMenu> |
| #include <QtGui/QAction> |
| #include <QtGui/QApplication> |
| |
| QT_BEGIN_NAMESPACE |
| |
| static const char *buddyPropertyC = "buddy"; |
| |
| static bool canBeBuddy(QWidget *w, QDesignerFormWindowInterface *form) |
| { |
| if (qobject_cast<const QLayoutWidget*>(w) || qobject_cast<const QLabel*>(w)) |
| return false; |
| if (w == form->mainContainer() || w->isHidden() ) |
| return false; |
| |
| QExtensionManager *ext = form->core()->extensionManager(); |
| if (QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(ext, w)) { |
| const int index = sheet->indexOf(QLatin1String("focusPolicy")); |
| if (index != -1) { |
| bool ok = false; |
| const Qt::FocusPolicy q = static_cast<Qt::FocusPolicy>(qdesigner_internal::Utils::valueOf(sheet->property(index), &ok)); |
| return ok && q != Qt::NoFocus; |
| } |
| } |
| return false; |
| } |
| |
| static QString buddy(QLabel *label, QDesignerFormEditorInterface *core) |
| { |
| QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), label); |
| if (sheet == 0) |
| return QString(); |
| const int prop_idx = sheet->indexOf(QLatin1String(buddyPropertyC)); |
| if (prop_idx == -1) |
| return QString(); |
| return sheet->property(prop_idx).toString(); |
| } |
| |
| typedef QList<QLabel*> LabelList; |
| |
| namespace qdesigner_internal { |
| |
| /******************************************************************************* |
| ** BuddyEditor |
| */ |
| |
| BuddyEditor::BuddyEditor(QDesignerFormWindowInterface *form, QWidget *parent) : |
| ConnectionEdit(parent, form), |
| m_formWindow(form), |
| m_updating(false) |
| { |
| } |
| |
| |
| QWidget *BuddyEditor::widgetAt(const QPoint &pos) const |
| { |
| QWidget *w = ConnectionEdit::widgetAt(pos); |
| |
| while (w != 0 && !m_formWindow->isManaged(w)) |
| w = w->parentWidget(); |
| if (!w) |
| return w; |
| |
| if (state() == Editing) { |
| QLabel *label = qobject_cast<QLabel*>(w); |
| if (label == 0) |
| return 0; |
| const int cnt = connectionCount(); |
| for (int i = 0; i < cnt; ++i) { |
| Connection *con = connection(i); |
| if (con->widget(EndPoint::Source) == w) |
| return 0; |
| } |
| } else { |
| if (!canBeBuddy(w, m_formWindow)) |
| return 0; |
| } |
| |
| return w; |
| } |
| |
| Connection *BuddyEditor::createConnection(QWidget *source, QWidget *destination) |
| { |
| return new Connection(this, source, destination); |
| } |
| |
| QDesignerFormWindowInterface *BuddyEditor::formWindow() const |
| { |
| return m_formWindow; |
| } |
| |
| void BuddyEditor::updateBackground() |
| { |
| if (m_updating || background() == 0) |
| return; |
| ConnectionEdit::updateBackground(); |
| |
| m_updating = true; |
| QList<Connection *> newList; |
| const LabelList label_list = qFindChildren<QLabel*>(background()); |
| foreach (QLabel *label, label_list) { |
| const QString buddy_name = buddy(label, m_formWindow->core()); |
| if (buddy_name.isEmpty()) |
| continue; |
| |
| const QList<QWidget *> targets = qFindChildren<QWidget*>(background(), buddy_name); |
| if (targets.isEmpty()) |
| continue; |
| |
| QWidget *target = 0; |
| |
| QListIterator<QWidget *> it(targets); |
| while (it.hasNext()) { |
| QWidget *widget = it.next(); |
| if (widget && !widget->isHidden()) { |
| target = widget; |
| break; |
| } |
| } |
| |
| if (target == 0) |
| continue; |
| |
| Connection *con = new Connection(this); |
| con->setEndPoint(EndPoint::Source, label, widgetRect(label).center()); |
| con->setEndPoint(EndPoint::Target, target, widgetRect(target).center()); |
| newList.append(con); |
| } |
| |
| QList<Connection *> toRemove; |
| |
| const int c = connectionCount(); |
| for (int i = 0; i < c; i++) { |
| Connection *con = connection(i); |
| QObject *source = con->object(EndPoint::Source); |
| QObject *target = con->object(EndPoint::Target); |
| bool found = false; |
| QListIterator<Connection *> it(newList); |
| while (it.hasNext()) { |
| Connection *newConn = it.next(); |
| if (newConn->object(EndPoint::Source) == source && newConn->object(EndPoint::Target) == target) { |
| found = true; |
| break; |
| } |
| } |
| if (found == false) |
| toRemove.append(con); |
| } |
| if (!toRemove.isEmpty()) { |
| DeleteConnectionsCommand command(this, toRemove); |
| command.redo(); |
| foreach (Connection *con, toRemove) |
| delete takeConnection(con); |
| } |
| |
| QListIterator<Connection *> it(newList); |
| while (it.hasNext()) { |
| Connection *newConn = it.next(); |
| |
| bool found = false; |
| const int c = connectionCount(); |
| for (int i = 0; i < c; i++) { |
| Connection *con = connection(i); |
| if (con->object(EndPoint::Source) == newConn->object(EndPoint::Source) && |
| con->object(EndPoint::Target) == newConn->object(EndPoint::Target)) { |
| found = true; |
| break; |
| } |
| } |
| if (found == false) { |
| AddConnectionCommand command(this, newConn); |
| command.redo(); |
| } else { |
| delete newConn; |
| } |
| } |
| m_updating = false; |
| } |
| |
| void BuddyEditor::setBackground(QWidget *background) |
| { |
| clear(); |
| ConnectionEdit::setBackground(background); |
| |
| const LabelList label_list = qFindChildren<QLabel*>(background); |
| foreach (QLabel *label, label_list) { |
| const QString buddy_name = buddy(label, m_formWindow->core()); |
| if (buddy_name.isEmpty()) |
| continue; |
| QWidget *target = qFindChild<QWidget*>(background, buddy_name); |
| if (target == 0) |
| continue; |
| |
| Connection *con = new Connection(this); |
| con->setEndPoint(EndPoint::Source, label, widgetRect(label).center()); |
| con->setEndPoint(EndPoint::Target, target, widgetRect(target).center()); |
| addConnection(con); |
| } |
| } |
| |
| static QUndoCommand *createBuddyCommand(QDesignerFormWindowInterface *fw, QLabel *label, QWidget *buddy) |
| { |
| SetPropertyCommand *command = new SetPropertyCommand(fw); |
| command->init(label, QLatin1String(buddyPropertyC), buddy->objectName()); |
| command->setText(BuddyEditor::tr("Add buddy")); |
| return command; |
| } |
| |
| void BuddyEditor::endConnection(QWidget *target, const QPoint &pos) |
| { |
| Connection *tmp_con = newlyAddedConnection(); |
| Q_ASSERT(tmp_con != 0); |
| |
| tmp_con->setEndPoint(EndPoint::Target, target, pos); |
| |
| QWidget *source = tmp_con->widget(EndPoint::Source); |
| Q_ASSERT(source != 0); |
| Q_ASSERT(target != 0); |
| setEnabled(false); |
| Connection *new_con = createConnection(source, target); |
| setEnabled(true); |
| if (new_con != 0) { |
| new_con->setEndPoint(EndPoint::Source, source, tmp_con->endPointPos(EndPoint::Source)); |
| new_con->setEndPoint(EndPoint::Target, target, tmp_con->endPointPos(EndPoint::Target)); |
| |
| selectNone(); |
| addConnection(new_con); |
| QLabel *source = qobject_cast<QLabel*>(new_con->widget(EndPoint::Source)); |
| QWidget *target = new_con->widget(EndPoint::Target); |
| if (source) { |
| undoStack()->push(createBuddyCommand(m_formWindow, source, target)); |
| } else { |
| qDebug("BuddyEditor::endConnection(): not a label"); |
| } |
| setSelected(new_con, true); |
| } |
| |
| clearNewlyAddedConnection(); |
| findObjectsUnderMouse(mapFromGlobal(QCursor::pos())); |
| } |
| |
| void BuddyEditor::widgetRemoved(QWidget *widget) |
| { |
| QList<QWidget*> child_list = qFindChildren<QWidget*>(widget); |
| child_list.prepend(widget); |
| |
| ConnectionSet remove_set; |
| foreach (QWidget *w, child_list) { |
| const ConnectionList &cl = connectionList(); |
| foreach (Connection *con, cl) { |
| if (con->widget(EndPoint::Source) == w || con->widget(EndPoint::Target) == w) |
| remove_set.insert(con, con); |
| } |
| } |
| |
| if (!remove_set.isEmpty()) { |
| undoStack()->beginMacro(tr("Remove buddies")); |
| foreach (Connection *con, remove_set) { |
| setSelected(con, false); |
| con->update(); |
| QWidget *source = con->widget(EndPoint::Source); |
| if (qobject_cast<QLabel*>(source) == 0) { |
| qDebug("BuddyConnection::widgetRemoved(): not a label"); |
| } else { |
| ResetPropertyCommand *command = new ResetPropertyCommand(formWindow()); |
| command->init(source, QLatin1String(buddyPropertyC)); |
| undoStack()->push(command); |
| } |
| delete takeConnection(con); |
| } |
| undoStack()->endMacro(); |
| } |
| } |
| |
| void BuddyEditor::deleteSelected() |
| { |
| const ConnectionSet selectedConnections = selection(); // want copy for unselect |
| if (selectedConnections.isEmpty()) |
| return; |
| |
| undoStack()->beginMacro(tr("Remove %n buddies", 0, selectedConnections.size())); |
| foreach (Connection *con, selectedConnections) { |
| setSelected(con, false); |
| con->update(); |
| QWidget *source = con->widget(EndPoint::Source); |
| if (qobject_cast<QLabel*>(source) == 0) { |
| qDebug("BuddyConnection::deleteSelected(): not a label"); |
| } else { |
| ResetPropertyCommand *command = new ResetPropertyCommand(formWindow()); |
| command->init(source, QLatin1String(buddyPropertyC)); |
| undoStack()->push(command); |
| } |
| delete takeConnection(con); |
| } |
| undoStack()->endMacro(); |
| } |
| |
| void BuddyEditor::autoBuddy() |
| { |
| // Any labels? |
| LabelList labelList = qFindChildren<QLabel*>(background()); |
| if (labelList.empty()) |
| return; |
| // Find already used buddies |
| QWidgetList usedBuddies; |
| const ConnectionList &beforeConnections = connectionList(); |
| foreach (const Connection *c, beforeConnections) |
| usedBuddies.push_back(c->widget(EndPoint::Target)); |
| // Find potential new buddies, keep lists in sync |
| QWidgetList buddies; |
| for (LabelList::iterator it = labelList.begin(); it != labelList.end(); ) { |
| QLabel *label = *it; |
| QWidget *newBuddy = 0; |
| if (m_formWindow->isManaged(label)) { |
| const QString buddy_name = buddy(label, m_formWindow->core()); |
| if (buddy_name.isEmpty()) |
| newBuddy = findBuddy(label, usedBuddies); |
| } |
| if (newBuddy) { |
| buddies.push_back(newBuddy); |
| usedBuddies.push_back(newBuddy); |
| ++it; |
| } else { |
| it = labelList.erase(it); |
| } |
| } |
| // Add the list in one go. |
| if (labelList.empty()) |
| return; |
| const int count = labelList.size(); |
| Q_ASSERT(count == buddies.size()); |
| undoStack()->beginMacro(tr("Add %n buddies", 0, count)); |
| for (int i = 0; i < count; i++) |
| undoStack()->push(createBuddyCommand(m_formWindow, labelList.at(i), buddies.at(i))); |
| undoStack()->endMacro(); |
| // Now select all new ones |
| const ConnectionList &connections = connectionList(); |
| foreach (Connection *con, connections) |
| setSelected(con, buddies.contains(con->widget(EndPoint::Target))); |
| } |
| |
| // Geometrically find a potential buddy for label by checking neighbouring children of parent |
| QWidget *BuddyEditor::findBuddy(QLabel *l, const QWidgetList &existingBuddies) const |
| { |
| enum { DeltaX = 5 }; |
| const QWidget *parent = l->parentWidget(); |
| // Try to find next managed neighbour on horizontal line |
| const QRect geom = l->geometry(); |
| const int y = geom.center().y(); |
| QWidget *neighbour = 0; |
| switch (l->layoutDirection()) { |
| case Qt::LayoutDirectionAuto: |
| case Qt::LeftToRight: { // Walk right to find next managed neighbour |
| const int xEnd = parent->size().width(); |
| for (int x = geom.right() + 1; x < xEnd; x += DeltaX) |
| if (QWidget *c = parent->childAt (x, y)) |
| if (m_formWindow->isManaged(c)) { |
| neighbour = c; |
| break; |
| } |
| } |
| break; |
| case Qt::RightToLeft: // Walk left to find next managed neighbour |
| for (int x = geom.x() - 1; x >= 0; x -= DeltaX) |
| if (QWidget *c = parent->childAt (x, y)) |
| if (m_formWindow->isManaged(c)) { |
| neighbour = c; |
| break; |
| } |
| break; |
| } |
| if (neighbour && !existingBuddies.contains(neighbour) && canBeBuddy(neighbour, m_formWindow)) |
| return neighbour; |
| |
| return 0; |
| } |
| |
| void BuddyEditor::createContextMenu(QMenu &menu) |
| { |
| QAction *autoAction = menu.addAction(tr("Set automatically")); |
| connect(autoAction, SIGNAL(triggered()), this, SLOT(autoBuddy())); |
| menu.addSeparator(); |
| ConnectionEdit::createContextMenu(menu); |
| } |
| |
| } |
| |
| QT_END_NAMESPACE |