/*****************************************************************************
 * resizablepopup.cpp - QStarDict, a StarDict clone written with using Qt    *
 * Copyright (C) 2007 Alexander Rodin                                        *
 *                                                                           *
 * This program is free software; you can redistribute it and/or modify      *
 * it under the terms of the GNU General Public License as published by      *
 * the Free Software Foundation; either version 2 of the License, or         *
 * (at your option) any later version.                                       *
 *                                                                           *
 * This program is distributed in the hope that it will be useful,           *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 * GNU General Public License for more details.                              *
 *                                                                           *
 * You should have received a copy of the GNU General Public License along   *
 * with this program; if not, write to the Free Software Foundation, Inc.,   *
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.               *
 *****************************************************************************/

#include "resizablepopup.h"

#include <QApplication>
#include <QCursor>
#include <QDesktopWidget>
#include <QMouseEvent>
#include <QTimerEvent>

namespace
{
const int CornerSize = 10;
}

namespace QStarDict
{

ResizablePopup::ResizablePopup(QWidget *parent)
    : QFrame(parent, Qt::Popup)
{
    m_isMoving = false;
    m_resizeDirection = ResizeDirection::None;
    m_timeoutBeforeHide = 0;
    m_timerCloseId = 0;
    m_timerResizeId = 0;
    m_isPopuped = false;
    setMouseTracking(true);
    setLineWidth(1);
    setMidLineWidth(2);
    setFrameStyle(QFrame::Box);
    setFrameShadow(QFrame::Raised);
}

void ResizablePopup::popup()
{
    if (m_defaultSize != size())
        resize(m_defaultSize);
    QPoint newPosition = QCursor::pos() - QPoint(30, 30);
    if (newPosition.x() < 0)
        newPosition.setX(0);
    else if (newPosition.x() + width() > QApplication::desktop()->width())
        newPosition.setX(QApplication::desktop()->width() - width());
    if (newPosition.y() < 0)
        newPosition.setY(0);
    else if (newPosition.y() + height() > QApplication::desktop()->height())
        newPosition.setY(QApplication::desktop()->height() - height());
    move(newPosition);
    show();
    m_isPopuped = true;
}

void ResizablePopup::enterEvent(QEvent*)
{
    if (m_timerCloseId)
    {
        killTimer(m_timerCloseId);
        m_timerCloseId = 0;
    }
}

void ResizablePopup::leaveEvent(QEvent*)
{
    if (geometry().contains(QCursor::pos()))
        return;
    if (m_resizeDirection != ResizeDirection::None)
        return;
    if (m_timeoutBeforeHide < 0)
        return;
    if (m_timeoutBeforeHide == 0)
    {
        m_isPopuped = false;
        // hide();
    }
    else if (! m_timerCloseId)
        m_timerCloseId = startTimer(m_timeoutBeforeHide);
}

void ResizablePopup::mouseMoveEvent(QMouseEvent *event)
{
    Qt::CursorShape cursorShape = Qt::ArrowCursor;
    if ((event->x() >= 0 && event->x() < CornerSize &&
            event->y() >= 0 && event->y() < CornerSize) ||
        (event->x() < width() && event->x() >= width() - CornerSize &&
            event->y() < height() && event->y() >= height() - CornerSize))
        cursorShape = Qt::SizeFDiagCursor;
    else if ((event->x() < width() && event->x() >= width() - CornerSize &&
                event->y() >= 0 && event->y() < CornerSize) ||
             (event->x() >= 0 && event->x() < CornerSize &&
                event->y() < height() && event->y() >= height() - CornerSize))
        cursorShape = Qt::SizeBDiagCursor;
    else if ((event->x() >= 0 && event->x() < frameWidth()) ||
             (event->x() < width() && event->x() >= width() - frameWidth()))
        cursorShape = Qt::SizeHorCursor;
    else if ((event->y() >= 0 && event->y() < frameWidth()) ||
             (event->y() < height() && event->y() >= height() - frameWidth()))
        cursorShape = Qt::SizeVerCursor;
    
    if (cursor().shape() != cursorShape)
        setCursor(cursorShape);
    else
        if (event->buttons().testFlag(Qt::LeftButton))
        {
            if (m_isMoving)
                move(pos() + (event->globalPos() - m_oldCursorPos));
            m_oldCursorPos = event->globalPos();
            return;
        }
    m_isMoving = false;
}

void ResizablePopup::mousePressEvent(QMouseEvent *event)
{
    if (! geometry().contains(event->globalPos()))
    {
        if (m_timerCloseId)
        {
            killTimer(m_timerCloseId);
            m_timerCloseId = 0;
        }
        m_isPopuped = false;
        hide();
        return;
    }

    if (event->buttons().testFlag(Qt::LeftButton))
    {
        if (event->x() < CornerSize && event->y() < CornerSize)
            m_resizeDirection = ResizeDirection::TopLeft;
        else if (event->x() >= width() - CornerSize && event->y() < CornerSize)
            m_resizeDirection = ResizeDirection::TopRight;
        else if (event->x() < CornerSize && event->y() >= height() - CornerSize)
            m_resizeDirection = ResizeDirection::BottomLeft;
        else if (event->x() >= width() - CornerSize && event->y() >= height() - CornerSize)
            m_resizeDirection = ResizeDirection::BottomRight;
        else if (event->x() < frameWidth())
            m_resizeDirection = ResizeDirection::Left;
        else if (event->x() >= width() - frameWidth())
            m_resizeDirection = ResizeDirection::Right;
        else if (event->y() < frameWidth())
            m_resizeDirection = ResizeDirection::Top;
        else if (event->y() >= height() - frameWidth())
            m_resizeDirection = ResizeDirection::Bottom;
        else
            m_resizeDirection = ResizeDirection::None;
        if (m_resizeDirection != ResizeDirection::None)
            m_timerResizeId = startTimer(8);
    }

    m_isMoving = true;
    m_oldCursorPos = event->globalPos();
}

void ResizablePopup::mouseReleaseEvent(QMouseEvent*)
{
    stopResize();
}

void ResizablePopup::mouseDoubleClickEvent(QMouseEvent*)
{
    m_isPopuped = false;
    if (m_timerCloseId)
    {
        killTimer(m_timerCloseId);
        m_timerCloseId = 0;
    }
    hide();
}

void ResizablePopup::timerEvent(QTimerEvent *event)
{
    if (event->timerId() == m_timerResizeId)
    {
        doResize();
    }
    else if (event->timerId() == m_timerCloseId)
    {
        m_isPopuped = false;
        hide();
        killTimer(m_timerCloseId);
        m_timerCloseId = 0;
        stopResize();
    }
}

void ResizablePopup::doResize()
{
    if (! QApplication::mouseButtons().testFlag(Qt::LeftButton))
        stopResize();
    
    if (m_resizeDirection != ResizeDirection::None)
    {
        QRect newGeometry = geometry();
        switch (m_resizeDirection)
        {
            case ResizeDirection::TopLeft:
                newGeometry.setTopLeft(QCursor::pos());
                break;
            case ResizeDirection::TopRight:
                newGeometry.setTopRight(QCursor::pos());
                break;
            case ResizeDirection::BottomLeft:
                newGeometry.setBottomLeft(QCursor::pos());
                break;
            case ResizeDirection::BottomRight:
                newGeometry.setBottomRight(QCursor::pos());
                break;
            case ResizeDirection::Left:
                newGeometry.setLeft(QCursor::pos().x());
                break;
            case ResizeDirection::Right:
                newGeometry.setRight(QCursor::pos().x());
                break;
            case ResizeDirection::Top:
                newGeometry.setTop(QCursor::pos().y());
                break;
            case ResizeDirection::Bottom:
                newGeometry.setBottom(QCursor::pos().y());
                break;
            default:
                ; // Nothing
        }
        if (newGeometry.width() < minimumSize().width())
        {
            newGeometry.setWidth(width());
            newGeometry.moveLeft(geometry().left());
        }
        if (newGeometry.height() < minimumSize().height())
        {
            newGeometry.setHeight(height());
            newGeometry.moveTop(geometry().top());
        }
        if (newGeometry != geometry())
            setGeometry(newGeometry);
    }
}

void ResizablePopup::stopResize()
{
    if (m_resizeDirection != ResizeDirection::None)
    {
        m_resizeDirection = ResizeDirection::None;
        killTimer(m_timerResizeId);
        m_timerResizeId = 0;
    }
}

bool ResizablePopup::event(QEvent *event)
{
    if (event->type() == QEvent::WindowUnblocked && m_isPopuped)
    {
        if (m_timerCloseId)
        {
            killTimer(m_timerCloseId);
            m_timerCloseId = 0;
        }
        show();
        return true;
    }
    else
        return QFrame::event(event);
}

}

// vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab cindent textwidth=120 formatoptions=tc

