/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** 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.
**
** 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.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/

#include "qdeclarativeviewobserver.h"
#include "qdeclarativeviewobserver_p.h"
#include "qdeclarativeobserverservice.h"
#include "editor/liveselectiontool.h"
#include "editor/zoomtool.h"
#include "editor/colorpickertool.h"
#include "editor/livelayeritem.h"
#include "editor/boundingrecthighlighter.h"
#include "editor/subcomponenteditortool.h"
#include "editor/qmltoolbar.h"

#include "qt_private/qdeclarativedebughelper_p.h"

#include <QtDeclarative/QDeclarativeItem>
#include <QtDeclarative/QDeclarativeEngine>
#include <QtDeclarative/QDeclarativeContext>
#include <QtDeclarative/QDeclarativeExpression>
#include <QtGui/QWidget>
#include <QtGui/QVBoxLayout>
#include <QtGui/QMouseEvent>
#include <QtGui/QGraphicsObject>
#include <QtGui/QApplication>
#include <QtCore/QSettings>

static inline void initEditorResource() { Q_INIT_RESOURCE(editor); }

namespace QmlJSDebugger {

const char * const KEY_TOOLBOX_GEOMETRY = "toolBox/geometry";

const int SceneChangeUpdateInterval = 5000;


class ToolBox : public QWidget
{
    Q_OBJECT

public:
    ToolBox(QWidget *parent = 0);
    ~ToolBox();

    QmlToolBar *toolBar() const { return m_toolBar; }

private:
    QSettings m_settings;
    QmlToolBar *m_toolBar;
};

ToolBox::ToolBox(QWidget *parent)
    : QWidget(parent, Qt::Tool)
    , m_settings(QLatin1String("Nokia"), QLatin1String("QmlObserver"), this)
    , m_toolBar(new QmlToolBar)
{
    setWindowFlags((windowFlags() & ~Qt::WindowCloseButtonHint) | Qt::CustomizeWindowHint);
    setWindowTitle(tr("Qt Quick Toolbox"));

    QVBoxLayout *verticalLayout = new QVBoxLayout;
    verticalLayout->setMargin(0);
    verticalLayout->addWidget(m_toolBar);
    setLayout(verticalLayout);

    restoreGeometry(m_settings.value(QLatin1String(KEY_TOOLBOX_GEOMETRY)).toByteArray());
}

ToolBox::~ToolBox()
{
    m_settings.setValue(QLatin1String(KEY_TOOLBOX_GEOMETRY), saveGeometry());
}


QDeclarativeViewObserverPrivate::QDeclarativeViewObserverPrivate(QDeclarativeViewObserver *q) :
    q(q),
    designModeBehavior(false),
    showAppOnTop(false),
    animationPaused(false),
    slowDownFactor(1.0f),
    toolBox(0)
{
}

QDeclarativeViewObserverPrivate::~QDeclarativeViewObserverPrivate()
{
}

QDeclarativeViewObserver::QDeclarativeViewObserver(QDeclarativeView *view, QObject *parent) :
    QObject(parent), data(new QDeclarativeViewObserverPrivate(this))
{
    initEditorResource();

    data->view = view;
    data->manipulatorLayer = new LiveLayerItem(view->scene());
    data->selectionTool = new LiveSelectionTool(this);
    data->zoomTool = new ZoomTool(this);
    data->colorPickerTool = new ColorPickerTool(this);
    data->boundingRectHighlighter = new BoundingRectHighlighter(this);
    data->subcomponentEditorTool = new SubcomponentEditorTool(this);
    data->currentTool = data->selectionTool;

    // to capture ChildRemoved event when viewport changes
    data->view->installEventFilter(this);

    data->setViewport(data->view->viewport());

    data->debugService = QDeclarativeObserverService::instance();

    // tool box is disabled
    //connect(data->debugService, SIGNAL(debuggingClientChanged(bool)),
    //        data.data(), SLOT(_q_setToolBoxVisible(bool)));

    connect(data->debugService, SIGNAL(designModeBehaviorChanged(bool)),
            SLOT(setDesignModeBehavior(bool)));
    connect(data->debugService, SIGNAL(showAppOnTopChanged(bool)),
            SLOT(setShowAppOnTop(bool)));
    connect(data->debugService, SIGNAL(reloadRequested()), data.data(), SLOT(_q_reloadView()));
    connect(data->debugService, SIGNAL(currentObjectsChanged(QList<QObject*>)),
            data.data(), SLOT(_q_onCurrentObjectsChanged(QList<QObject*>)));
    connect(data->debugService, SIGNAL(animationSpeedChangeRequested(qreal)),
            SLOT(animationSpeedChangeRequested(qreal)));
    connect(data->debugService, SIGNAL(executionPauseChangeRequested(bool)),
            SLOT(animationPausedChangeRequested(bool)));
    connect(data->debugService, SIGNAL(colorPickerToolRequested()),
            data.data(), SLOT(_q_changeToColorPickerTool()));
    connect(data->debugService, SIGNAL(selectMarqueeToolRequested()),
            data.data(), SLOT(_q_changeToMarqueeSelectTool()));
    connect(data->debugService, SIGNAL(selectToolRequested()), data.data(), SLOT(_q_changeToSingleSelectTool()));
    connect(data->debugService, SIGNAL(zoomToolRequested()), data.data(), SLOT(_q_changeToZoomTool()));
    connect(data->debugService,
            SIGNAL(objectCreationRequested(QString,QObject*,QStringList,QString)),
            data.data(), SLOT(_q_createQmlObject(QString,QObject*,QStringList,QString)));
    connect(data->debugService,
            SIGNAL(objectReparentRequested(QObject *, QObject *)),
            data.data(), SLOT(_q_reparentQmlObject(QObject *, QObject *)));
    connect(data->debugService, SIGNAL(contextPathIndexChanged(int)),
            data.data(), SLOT(_q_changeContextPathIndex(int)));
    connect(data->debugService, SIGNAL(clearComponentCacheRequested()),
            data.data(), SLOT(_q_clearComponentCache()));
    connect(data->view, SIGNAL(statusChanged(QDeclarativeView::Status)),
            data.data(), SLOT(_q_onStatusChanged(QDeclarativeView::Status)));

    connect(data->colorPickerTool, SIGNAL(selectedColorChanged(QColor)),
            SIGNAL(selectedColorChanged(QColor)));
    connect(data->colorPickerTool, SIGNAL(selectedColorChanged(QColor)),
            data->debugService, SLOT(selectedColorChanged(QColor)));

    connect(data->subcomponentEditorTool, SIGNAL(cleared()), SIGNAL(inspectorContextCleared()));
    connect(data->subcomponentEditorTool, SIGNAL(contextPushed(QString)),
            SIGNAL(inspectorContextPushed(QString)));
    connect(data->subcomponentEditorTool, SIGNAL(contextPopped()),
            SIGNAL(inspectorContextPopped()));
    connect(data->subcomponentEditorTool, SIGNAL(contextPathChanged(QStringList)),
            data->debugService, SLOT(contextPathUpdated(QStringList)));

    data->_q_changeToSingleSelectTool();
}

QDeclarativeViewObserver::~QDeclarativeViewObserver()
{
}

void QDeclarativeViewObserver::setObserverContext(int contextIndex)
{
    if (data->subcomponentEditorTool->contextIndex() != contextIndex) {
        QGraphicsObject *object = data->subcomponentEditorTool->setContext(contextIndex);
        if (object)
            setSelectedItems(QList<QGraphicsItem*>() << object);
    }
}

void QDeclarativeViewObserverPrivate::_q_setToolBoxVisible(bool visible)
{
#if !defined(Q_OS_SYMBIAN) && !defined(Q_WS_MAEMO_5) && !defined(Q_WS_SIMULATOR)
    if (!toolBox && visible)
        createToolBox();
    if (toolBox)
        toolBox->setVisible(visible);
#else
    Q_UNUSED(visible)
#endif
}

void QDeclarativeViewObserverPrivate::_q_reloadView()
{
    subcomponentEditorTool->clear();
    clearHighlight();
    emit q->reloadRequested();
}

void QDeclarativeViewObserverPrivate::setViewport(QWidget *widget)
{
    if (viewport.data() == widget)
        return;

    if (viewport)
        viewport.data()->removeEventFilter(q);

    viewport = widget;
    if (viewport) {
        // make sure we get mouse move events
        viewport.data()->setMouseTracking(true);
        viewport.data()->installEventFilter(q);
    }
}

void QDeclarativeViewObserverPrivate::clearEditorItems()
{
    clearHighlight();
    setSelectedItems(QList<QGraphicsItem*>());
}

bool QDeclarativeViewObserver::eventFilter(QObject *obj, QEvent *event)
{
    if (obj == data->view) {
        // Event from view
        if (event->type() == QEvent::ChildRemoved) {
            // Might mean that viewport has changed
            if (data->view->viewport() != data->viewport.data())
                data->setViewport(data->view->viewport());
        }
        return QObject::eventFilter(obj, event);
    }

    // Event from viewport
    switch (event->type()) {
    case QEvent::Leave: {
        if (leaveEvent(event))
            return true;
        break;
    }
    case QEvent::MouseButtonPress: {
        if (mousePressEvent(static_cast<QMouseEvent*>(event)))
            return true;
        break;
    }
    case QEvent::MouseMove: {
        if (mouseMoveEvent(static_cast<QMouseEvent*>(event)))
            return true;
        break;
    }
    case QEvent::MouseButtonRelease: {
        if (mouseReleaseEvent(static_cast<QMouseEvent*>(event)))
            return true;
        break;
    }
    case QEvent::KeyPress: {
        if (keyPressEvent(static_cast<QKeyEvent*>(event)))
            return true;
        break;
    }
    case QEvent::KeyRelease: {
        if (keyReleaseEvent(static_cast<QKeyEvent*>(event)))
            return true;
        break;
    }
    case QEvent::MouseButtonDblClick: {
        if (mouseDoubleClickEvent(static_cast<QMouseEvent*>(event)))
            return true;
        break;
    }
    case QEvent::Wheel: {
        if (wheelEvent(static_cast<QWheelEvent*>(event)))
            return true;
        break;
    }
    default: {
        break;
    }
    } //switch

    // standard event processing
    return QObject::eventFilter(obj, event);
}

bool QDeclarativeViewObserver::leaveEvent(QEvent * /*event*/)
{
    if (!data->designModeBehavior)
        return false;
    data->clearHighlight();
    return true;
}

bool QDeclarativeViewObserver::mousePressEvent(QMouseEvent *event)
{
    if (!data->designModeBehavior)
        return false;
    data->cursorPos = event->pos();
    data->currentTool->mousePressEvent(event);
    return true;
}

bool QDeclarativeViewObserver::mouseMoveEvent(QMouseEvent *event)
{
    if (!data->designModeBehavior) {
        data->clearEditorItems();
        return false;
    }
    data->cursorPos = event->pos();

    QList<QGraphicsItem*> selItems = data->selectableItems(event->pos());
    if (!selItems.isEmpty()) {
        declarativeView()->setToolTip(AbstractLiveEditTool::titleForItem(selItems.first()));
    } else {
        declarativeView()->setToolTip(QString());
    }
    if (event->buttons()) {
        data->subcomponentEditorTool->mouseMoveEvent(event);
        data->currentTool->mouseMoveEvent(event);
    } else {
        data->subcomponentEditorTool->hoverMoveEvent(event);
        data->currentTool->hoverMoveEvent(event);
    }
    return true;
}

bool QDeclarativeViewObserver::mouseReleaseEvent(QMouseEvent *event)
{
    if (!data->designModeBehavior)
        return false;
    data->subcomponentEditorTool->mouseReleaseEvent(event);

    data->cursorPos = event->pos();
    data->currentTool->mouseReleaseEvent(event);
    return true;
}

bool QDeclarativeViewObserver::keyPressEvent(QKeyEvent *event)
{
    if (!data->designModeBehavior)
        return false;

    data->currentTool->keyPressEvent(event);
    return true;
}

bool QDeclarativeViewObserver::keyReleaseEvent(QKeyEvent *event)
{
    if (!data->designModeBehavior)
        return false;

    switch(event->key()) {
    case Qt::Key_V:
        data->_q_changeToSingleSelectTool();
        break;
// disabled because multiselection does not do anything useful without design mode
//    case Qt::Key_M:
//        data->_q_changeToMarqueeSelectTool();
//        break;
    case Qt::Key_I:
        data->_q_changeToColorPickerTool();
        break;
    case Qt::Key_Z:
        data->_q_changeToZoomTool();
        break;
    case Qt::Key_Enter:
    case Qt::Key_Return:
        if (!data->selectedItems().isEmpty())
            data->subcomponentEditorTool->setCurrentItem(data->selectedItems().first());
        break;
    case Qt::Key_Space:
        setAnimationPaused(!data->animationPaused);
        break;
    default:
        break;
    }

    data->currentTool->keyReleaseEvent(event);
    return true;
}

void QDeclarativeViewObserverPrivate::_q_createQmlObject(const QString &qml, QObject *parent,
                                                         const QStringList &importList,
                                                         const QString &filename)
{
    if (!parent)
        return;

    QString imports;
    foreach (const QString &s, importList) {
        imports += s + "\n";
    }

    QDeclarativeContext *parentContext = view->engine()->contextForObject(parent);
    QDeclarativeComponent component(view->engine(), q);
    QByteArray constructedQml = QString(imports + qml).toLatin1();

    component.setData(constructedQml, filename);
    QObject *newObject = component.create(parentContext);
    if (newObject) {
        newObject->setParent(parent);
        QDeclarativeItem *parentItem = qobject_cast<QDeclarativeItem*>(parent);
        QDeclarativeItem *newItem    = qobject_cast<QDeclarativeItem*>(newObject);
        if (parentItem && newItem)
            newItem->setParentItem(parentItem);
    }
}

void QDeclarativeViewObserverPrivate::_q_reparentQmlObject(QObject *object, QObject *newParent)
{
    if (!newParent)
        return;

    object->setParent(newParent);
    QDeclarativeItem *newParentItem = qobject_cast<QDeclarativeItem*>(newParent);
    QDeclarativeItem *item    = qobject_cast<QDeclarativeItem*>(object);
    if (newParentItem && item)
        item->setParentItem(newParentItem);
}

void QDeclarativeViewObserverPrivate::_q_clearComponentCache()
{
    view->engine()->clearComponentCache();
}

void QDeclarativeViewObserverPrivate::_q_removeFromSelection(QObject *obj)
{
    QList<QGraphicsItem*> items = selectedItems();
    if (QGraphicsItem *item = qobject_cast<QGraphicsObject*>(obj))
        items.removeOne(item);
    setSelectedItems(items);
}

QGraphicsItem *QDeclarativeViewObserverPrivate::currentRootItem() const
{
    return subcomponentEditorTool->currentRootItem();
}

bool QDeclarativeViewObserver::mouseDoubleClickEvent(QMouseEvent *event)
{
    if (!data->designModeBehavior)
        return false;

    if (data->currentToolMode != Constants::SelectionToolMode
            && data->currentToolMode != Constants::MarqueeSelectionToolMode)
        return true;

    QGraphicsItem *itemToEnter = 0;
    QList<QGraphicsItem*> itemList = data->view->items(event->pos());
    data->filterForSelection(itemList);

    if (data->selectedItems().isEmpty() && !itemList.isEmpty()) {
        itemToEnter = itemList.first();
    } else if (!data->selectedItems().isEmpty() && !itemList.isEmpty()) {
        itemToEnter = itemList.first();
    }

    if (itemToEnter)
        itemToEnter = data->subcomponentEditorTool->firstChildOfContext(itemToEnter);

    data->subcomponentEditorTool->setCurrentItem(itemToEnter);
    data->subcomponentEditorTool->mouseDoubleClickEvent(event);

    if ((event->buttons() & Qt::LeftButton) && itemToEnter) {
        if (QGraphicsObject *objectToEnter = itemToEnter->toGraphicsObject())
            setSelectedItems(QList<QGraphicsItem*>() << objectToEnter);
    }

    return true;
}

bool QDeclarativeViewObserver::wheelEvent(QWheelEvent *event)
{
    if (!data->designModeBehavior)
        return false;
    data->currentTool->wheelEvent(event);
    return true;
}

void QDeclarativeViewObserverPrivate::enterContext(QGraphicsItem *itemToEnter)
{
    QGraphicsItem *itemUnderCurrentContext = itemToEnter;
    if (itemUnderCurrentContext)
        itemUnderCurrentContext = subcomponentEditorTool->firstChildOfContext(itemToEnter);

    if (itemUnderCurrentContext)
        subcomponentEditorTool->setCurrentItem(itemToEnter);
}

void QDeclarativeViewObserver::setDesignModeBehavior(bool value)
{
    emit designModeBehaviorChanged(value);

    if (data->toolBox)
        data->toolBox->toolBar()->setDesignModeBehavior(value);
    data->debugService->setDesignModeBehavior(value);

    data->designModeBehavior = value;
    if (data->subcomponentEditorTool) {
        data->subcomponentEditorTool->clear();
        data->clearHighlight();
        data->setSelectedItems(QList<QGraphicsItem*>());

        if (data->view->rootObject())
            data->subcomponentEditorTool->pushContext(data->view->rootObject());
    }

    if (!data->designModeBehavior)
        data->clearEditorItems();
}

bool QDeclarativeViewObserver::designModeBehavior()
{
    return data->designModeBehavior;
}

bool QDeclarativeViewObserver::showAppOnTop() const
{
    return data->showAppOnTop;
}

void QDeclarativeViewObserver::setShowAppOnTop(bool appOnTop)
{
    if (data->view) {
        QWidget *window = data->view->window();
        Qt::WindowFlags flags = window->windowFlags();
        if (appOnTop)
            flags |= Qt::WindowStaysOnTopHint;
        else
            flags &= ~Qt::WindowStaysOnTopHint;

        window->setWindowFlags(flags);
        window->show();
    }

    data->showAppOnTop = appOnTop;
    data->debugService->setShowAppOnTop(appOnTop);

    emit showAppOnTopChanged(appOnTop);
}

void QDeclarativeViewObserverPrivate::changeTool(Constants::DesignTool tool,
                                                 Constants::ToolFlags /*flags*/)
{
    switch(tool) {
    case Constants::SelectionToolMode:
        _q_changeToSingleSelectTool();
        break;
    case Constants::NoTool:
    default:
        currentTool = 0;
        break;
    }
}

void QDeclarativeViewObserverPrivate::setSelectedItemsForTools(QList<QGraphicsItem *> items)
{
    foreach (QWeakPointer<QGraphicsObject> obj, currentSelection) {
        if (QGraphicsItem *item = obj.data()) {
            if (!items.contains(item)) {
                QObject::disconnect(obj.data(), SIGNAL(destroyed(QObject*)),
                                    this, SLOT(_q_removeFromSelection(QObject*)));
                currentSelection.removeOne(obj);
            }
        }
    }

    foreach (QGraphicsItem *item, items) {
        if (item) {
            if (QGraphicsObject *obj = item->toGraphicsObject()) {
                QObject::connect(obj, SIGNAL(destroyed(QObject*)),
                                 this, SLOT(_q_removeFromSelection(QObject*)));
                currentSelection.append(obj);
            }
        }
    }

    currentTool->updateSelectedItems();
}

void QDeclarativeViewObserverPrivate::setSelectedItems(QList<QGraphicsItem *> items)
{
    QList<QWeakPointer<QGraphicsObject> > oldList = currentSelection;
    setSelectedItemsForTools(items);
    if (oldList != currentSelection) {
        QList<QObject*> objectList;
        foreach (QWeakPointer<QGraphicsObject> graphicsObject, currentSelection) {
            if (graphicsObject)
                objectList << graphicsObject.data();
        }

        debugService->setCurrentObjects(objectList);
    }
}

QList<QGraphicsItem *> QDeclarativeViewObserverPrivate::selectedItems()
{
    QList<QGraphicsItem *> selection;
    foreach (const QWeakPointer<QGraphicsObject> &selectedObject, currentSelection) {
        if (selectedObject.data())
            selection << selectedObject.data();
    }

    return selection;
}

void QDeclarativeViewObserver::setSelectedItems(QList<QGraphicsItem *> items)
{
    data->setSelectedItems(items);
}

QList<QGraphicsItem *> QDeclarativeViewObserver::selectedItems()
{
    return data->selectedItems();
}

QDeclarativeView *QDeclarativeViewObserver::declarativeView()
{
    return data->view;
}

void QDeclarativeViewObserverPrivate::clearHighlight()
{
    boundingRectHighlighter->clear();
}

void QDeclarativeViewObserverPrivate::highlight(QGraphicsObject * item, ContextFlags flags)
{
    highlight(QList<QGraphicsObject*>() << item, flags);
}

void QDeclarativeViewObserverPrivate::highlight(QList<QGraphicsObject *> items, ContextFlags flags)
{
    if (items.isEmpty())
        return;

    QList<QGraphicsObject*> objectList;
    foreach (QGraphicsItem *item, items) {
        QGraphicsItem *child = item;
        if (flags & ContextSensitive)
            child = subcomponentEditorTool->firstChildOfContext(item);

        if (child) {
            QGraphicsObject *childObject = child->toGraphicsObject();
            if (childObject)
                objectList << childObject;
        }
    }

    boundingRectHighlighter->highlight(objectList);
}

bool QDeclarativeViewObserverPrivate::mouseInsideContextItem() const
{
    return subcomponentEditorTool->containsCursor(cursorPos.toPoint());
}

QList<QGraphicsItem*> QDeclarativeViewObserverPrivate::selectableItems(
    const QPointF &scenePos) const
{
    QList<QGraphicsItem*> itemlist = view->scene()->items(scenePos);
    return filterForCurrentContext(itemlist);
}

QList<QGraphicsItem*> QDeclarativeViewObserverPrivate::selectableItems(const QPoint &pos) const
{
    QList<QGraphicsItem*> itemlist = view->items(pos);
    return filterForCurrentContext(itemlist);
}

QList<QGraphicsItem*> QDeclarativeViewObserverPrivate::selectableItems(
    const QRectF &sceneRect, Qt::ItemSelectionMode selectionMode) const
{
    QList<QGraphicsItem*> itemlist = view->scene()->items(sceneRect, selectionMode);

    return filterForCurrentContext(itemlist);
}

void QDeclarativeViewObserverPrivate::_q_changeToSingleSelectTool()
{
    currentToolMode = Constants::SelectionToolMode;
    selectionTool->setRubberbandSelectionMode(false);

    changeToSelectTool();

    emit q->selectToolActivated();
    debugService->setCurrentTool(Constants::SelectionToolMode);
}

void QDeclarativeViewObserverPrivate::changeToSelectTool()
{
    if (currentTool == selectionTool)
        return;

    currentTool->clear();
    currentTool = selectionTool;
    currentTool->clear();
    currentTool->updateSelectedItems();
}

void QDeclarativeViewObserverPrivate::_q_changeToMarqueeSelectTool()
{
    changeToSelectTool();
    currentToolMode = Constants::MarqueeSelectionToolMode;
    selectionTool->setRubberbandSelectionMode(true);

    emit q->marqueeSelectToolActivated();
    debugService->setCurrentTool(Constants::MarqueeSelectionToolMode);
}

void QDeclarativeViewObserverPrivate::_q_changeToZoomTool()
{
    currentToolMode = Constants::ZoomMode;
    currentTool->clear();
    currentTool = zoomTool;
    currentTool->clear();

    emit q->zoomToolActivated();
    debugService->setCurrentTool(Constants::ZoomMode);
}

void QDeclarativeViewObserverPrivate::_q_changeToColorPickerTool()
{
    if (currentTool == colorPickerTool)
        return;

    currentToolMode = Constants::ColorPickerMode;
    currentTool->clear();
    currentTool = colorPickerTool;
    currentTool->clear();

    emit q->colorPickerActivated();
    debugService->setCurrentTool(Constants::ColorPickerMode);
}

void QDeclarativeViewObserverPrivate::_q_changeContextPathIndex(int index)
{
    subcomponentEditorTool->setContext(index);
}

void QDeclarativeViewObserver::setAnimationSpeed(qreal slowDownFactor)
{
    Q_ASSERT(slowDownFactor > 0);
    if (data->slowDownFactor == slowDownFactor)
        return;

    animationSpeedChangeRequested(slowDownFactor);
    data->debugService->setAnimationSpeed(slowDownFactor);
}

void QDeclarativeViewObserver::setAnimationPaused(bool paused)
{
    if (data->animationPaused == paused)
        return;

    animationPausedChangeRequested(paused);
    data->debugService->setAnimationPaused(paused);
}

void QDeclarativeViewObserver::animationSpeedChangeRequested(qreal factor)
{
    if (data->slowDownFactor != factor) {
        data->slowDownFactor = factor;
        emit animationSpeedChanged(factor);
    }

    const float effectiveFactor = data->animationPaused ? 0 : factor;
    QDeclarativeDebugHelper::setAnimationSlowDownFactor(effectiveFactor);
}

void QDeclarativeViewObserver::animationPausedChangeRequested(bool paused)
{
    if (data->animationPaused != paused) {
        data->animationPaused = paused;
        emit animationPausedChanged(paused);
    }

    const float effectiveFactor = paused ? 0 : data->slowDownFactor;
    QDeclarativeDebugHelper::setAnimationSlowDownFactor(effectiveFactor);
}


void QDeclarativeViewObserverPrivate::_q_applyChangesFromClient()
{
}


QList<QGraphicsItem*> QDeclarativeViewObserverPrivate::filterForSelection(
    QList<QGraphicsItem*> &itemlist) const
{
    foreach (QGraphicsItem *item, itemlist) {
        if (isEditorItem(item) || !subcomponentEditorTool->isChildOfContext(item))
            itemlist.removeOne(item);
    }

    return itemlist;
}

QList<QGraphicsItem*> QDeclarativeViewObserverPrivate::filterForCurrentContext(
    QList<QGraphicsItem*> &itemlist) const
{
    foreach (QGraphicsItem *item, itemlist) {

        if (isEditorItem(item) || !subcomponentEditorTool->isDirectChildOfContext(item)) {

            // if we're a child, but not directly, replace with the parent that is directly in context.
            if (QGraphicsItem *contextParent = subcomponentEditorTool->firstChildOfContext(item)) {
                if (contextParent != item) {
                    if (itemlist.contains(contextParent)) {
                        itemlist.removeOne(item);
                    } else {
                        itemlist.replace(itemlist.indexOf(item), contextParent);
                    }
                }
            } else {
                itemlist.removeOne(item);
            }
        }
    }

    return itemlist;
}

bool QDeclarativeViewObserverPrivate::isEditorItem(QGraphicsItem *item) const
{
    return (item->type() == Constants::EditorItemType
            || item->type() == Constants::ResizeHandleItemType
            || item->data(Constants::EditorItemDataKey).toBool());
}

void QDeclarativeViewObserverPrivate::_q_onStatusChanged(QDeclarativeView::Status status)
{
    if (status == QDeclarativeView::Ready) {
        if (view->rootObject()) {
            if (subcomponentEditorTool->contextIndex() != -1)
                subcomponentEditorTool->clear();
            subcomponentEditorTool->pushContext(view->rootObject());
        }
        debugService->reloaded();
    }
}

void QDeclarativeViewObserverPrivate::_q_onCurrentObjectsChanged(QList<QObject*> objects)
{
    QList<QGraphicsItem*> items;
    QList<QGraphicsObject*> gfxObjects;
    foreach (QObject *obj, objects) {
        QDeclarativeItem* declarativeItem = qobject_cast<QDeclarativeItem*>(obj);
        if (declarativeItem) {
            items << declarativeItem;
            if (QGraphicsObject *gfxObj = declarativeItem->toGraphicsObject())
                gfxObjects << gfxObj;
        }
    }
    if (designModeBehavior) {
        setSelectedItemsForTools(items);
        clearHighlight();
        highlight(gfxObjects, QDeclarativeViewObserverPrivate::IgnoreContext);
    }
}

QString QDeclarativeViewObserver::idStringForObject(QObject *obj)
{
    return QDeclarativeObserverService::instance()->idStringForObject(obj);
}

// adjusts bounding boxes on edges of screen to be visible
QRectF QDeclarativeViewObserver::adjustToScreenBoundaries(const QRectF &boundingRectInSceneSpace)
{
    int marginFromEdge = 1;
    QRectF boundingRect(boundingRectInSceneSpace);
    if (qAbs(boundingRect.left()) - 1 < 2)
        boundingRect.setLeft(marginFromEdge);

    QRect rect = data->view->rect();

    if (boundingRect.right() >= rect.right())
        boundingRect.setRight(rect.right() - marginFromEdge);

    if (qAbs(boundingRect.top()) - 1 < 2)
        boundingRect.setTop(marginFromEdge);

    if (boundingRect.bottom() >= rect.bottom())
        boundingRect.setBottom(rect.bottom() - marginFromEdge);

    return boundingRect;
}

void QDeclarativeViewObserverPrivate::createToolBox()
{
    toolBox = new ToolBox(q->declarativeView());

    QmlToolBar *toolBar = toolBox->toolBar();

    QObject::connect(q, SIGNAL(selectedColorChanged(QColor)),
                     toolBar, SLOT(setColorBoxColor(QColor)));

    QObject::connect(q, SIGNAL(designModeBehaviorChanged(bool)),
                     toolBar, SLOT(setDesignModeBehavior(bool)));

    QObject::connect(toolBar, SIGNAL(designModeBehaviorChanged(bool)),
                     q, SLOT(setDesignModeBehavior(bool)));
    QObject::connect(toolBar, SIGNAL(animationSpeedChanged(qreal)), q, SLOT(setAnimationSpeed(qreal)));
    QObject::connect(toolBar, SIGNAL(animationPausedChanged(bool)), q, SLOT(setAnimationPaused(bool)));
    QObject::connect(toolBar, SIGNAL(colorPickerSelected()), this, SLOT(_q_changeToColorPickerTool()));
    QObject::connect(toolBar, SIGNAL(zoomToolSelected()), this, SLOT(_q_changeToZoomTool()));
    QObject::connect(toolBar, SIGNAL(selectToolSelected()), this, SLOT(_q_changeToSingleSelectTool()));
    QObject::connect(toolBar, SIGNAL(marqueeSelectToolSelected()),
                     this, SLOT(_q_changeToMarqueeSelectTool()));

    QObject::connect(toolBar, SIGNAL(applyChangesFromQmlFileSelected()),
                     this, SLOT(_q_applyChangesFromClient()));

    QObject::connect(q, SIGNAL(animationSpeedChanged(qreal)), toolBar, SLOT(setAnimationSpeed(qreal)));
    QObject::connect(q, SIGNAL(animationPausedChanged(bool)), toolBar, SLOT(setAnimationPaused(bool)));

    QObject::connect(q, SIGNAL(selectToolActivated()), toolBar, SLOT(activateSelectTool()));

    // disabled features
    //connect(d->m_toolBar, SIGNAL(applyChangesToQmlFileSelected()), SLOT(applyChangesToClient()));
    //connect(q, SIGNAL(resizeToolActivated()), d->m_toolBar, SLOT(activateSelectTool()));
    //connect(q, SIGNAL(moveToolActivated()),   d->m_toolBar, SLOT(activateSelectTool()));

    QObject::connect(q, SIGNAL(colorPickerActivated()), toolBar, SLOT(activateColorPicker()));
    QObject::connect(q, SIGNAL(zoomToolActivated()), toolBar, SLOT(activateZoom()));
    QObject::connect(q, SIGNAL(marqueeSelectToolActivated()),
                     toolBar, SLOT(activateMarqueeSelectTool()));
}

} // namespace QmlJSDebugger

#include "qdeclarativeviewobserver.moc"
