/**
 * Moves the point or line if active.
 */
void AssociationLine::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    UMLScene* scene = m_associationWidget->umlScene();

    QPointF oldPos = event->scenePos();
    QPointF newPos(
        scene->snappedX(oldPos.x()),
        scene->snappedY(oldPos.y())
    );

    // Prevent the moving vertex from disappearing underneath a widget
    // (else there's no way to get it back.)
    UMLWidget *onW = scene->widgetAt(newPos);
    if (onW && onW->baseType() != WidgetBase::wt_Box) {  // boxes are transparent
        const qreal pX = newPos.x();
        const qreal pY = newPos.y();
        const qreal wX = onW->x();
        const qreal wY = onW->y();
        const qreal wWidth = onW->width();
        const qreal wHeight = onW->height();
        if (pX > wX && pX < wX + wWidth) {
            const qreal midX = wX + wWidth / 2.0;
            if (pX <= midX)
                newPos.setX(wX);
            else
                newPos.setX(wX + wWidth);
        }
        if (pY > wY && pY < wY + wHeight) {
            const qreal midY = wY + wHeight / 2.0;
            if (pY <= midY)
                newPos.setY(wY);
            else
                newPos.setY(wY + wHeight);
        }
    }

    if (m_activePointIndex != -1) {
        // Move a single point (snap behaviour)
        setPoint(m_activePointIndex, newPos);
    }
    else if (m_activeSegmentIndex != -1 && !isEndSegmentIndex(m_activeSegmentIndex)) {
        // Move a segment (between two points, snap behaviour not implemented)
        QPointF delta = event->scenePos() - event->lastScenePos();
        setPoint(m_activeSegmentIndex, m_points[m_activeSegmentIndex] + delta);
        setPoint(m_activeSegmentIndex + 1, m_points[m_activeSegmentIndex + 1] + delta);
    }
}
/**
 * Sets the second widget in the association using the current widget and
 * creates the association.
 * If the association between the two widgets using the current type of
 * association is illegitimate, an error is shown and the association cancelled.
 * Otherwise, the association is created and added to the scene, and the tool
 * is changed to the default tool.
 *
 * @todo Why change to the default tool? Shouldn't it better to stay on
 *       association and let the user change with a right click? The tool to
 *       create widgets doesn't change to default after creating a widget
 */
void ToolBarStateAssociation::setSecondWidget()
{
    Uml::AssociationType::Enum type = getAssociationType();
    UMLWidget* widgetA = m_firstWidget;
    UMLWidget* widgetB = currentWidget();
    WidgetBase::WidgetType at = widgetA->baseType();
    bool valid = true;
    if (type == Uml::AssociationType::Generalization) {
        type = AssocRules::isGeneralisationOrRealisation(widgetA, widgetB);
    }
    if (widgetA == widgetB) {
        valid = AssocRules::allowSelf(type, at);
        if (valid && type == Uml::AssociationType::Association) {
            type = Uml::AssociationType::Association_Self;
        }
    } else {
        valid = AssocRules::allowAssociation(type, widgetA, widgetB);
    }
    if (valid) {
        AssociationWidget *temp = AssociationWidget::create(m_pUMLScene, widgetA, type, widgetB);
        FloatingTextWidget *wt = temp->textWidgetByRole(Uml::TextRole::Coll_Message);
        if (wt)
            wt->showOperationDialog();
        if (addAssociationInViewAndDoc(temp)) {
            if (type == Uml::AssociationType::Containment) {
                UMLObject *newContainer = widgetA->umlObject();
                UMLObject *objToBeMoved = widgetB->umlObject();
                if (newContainer && objToBeMoved) {
                    Model_Utils::treeViewMoveObjectTo(newContainer, objToBeMoved);
                }
            }
            UMLApp::app()->document()->setModified();
        }
    } else {
        //TODO improve error feedback: tell the user what are the valid type of associations for
        //the second widget using the first widget
        KMessageBox::error(0, i18n("Incorrect use of associations."), i18n("Association Error"));
    }

    cleanAssociation();
}