/**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the Qt Designer of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "buddyeditor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE static const char *buddyPropertyC = "buddy"; static bool canBeBuddy(QWidget *w, QDesignerFormWindowInterface *form) { if (qobject_cast(w) || qobject_cast(w)) return false; if (w == form->mainContainer() || w->isHidden() ) return false; QExtensionManager *ext = form->core()->extensionManager(); if (QDesignerPropertySheetExtension *sheet = qt_extension(ext, w)) { const int index = sheet->indexOf(QLatin1String("focusPolicy")); if (index != -1) { bool ok = false; const Qt::FocusPolicy q = static_cast(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(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 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(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 newList; const LabelList label_list = qFindChildren(background()); foreach (QLabel *label, label_list) { const QString buddy_name = buddy(label, m_formWindow->core()); if (buddy_name.isEmpty()) continue; const QList targets = qFindChildren(background(), buddy_name); if (targets.isEmpty()) continue; QWidget *target = 0; QListIterator 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 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 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 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(background); foreach (QLabel *label, label_list) { const QString buddy_name = buddy(label, m_formWindow->core()); if (buddy_name.isEmpty()) continue; QWidget *target = qFindChild(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(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 child_list = qFindChildren(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(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(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(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