/***************************************************************************
 *   Copyright (C) 2006 by Peter Penz (peter.penz@gmx.at)                  *
 *   Copyright (C) 2006 by Aaron J. Seigo (<aseigo@kde.org>)               *
 *                                                                         *
 *   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 "urlnavigatorbutton.h"
#include <tqcursor.h>
#include <tqfontmetrics.h>
#include <tqpainter.h>
#include <tqtimer.h>
#include <tqtooltip.h>

#include <kglobalsettings.h>
#include <kiconloader.h>
#include <kio/jobclasses.h>
#include <klocale.h>
#include <kpopupmenu.h>
#include <kurl.h>
#include <kurldrag.h>
#include <assert.h>

#include "urlnavigator.h"
#include "dolphinview.h"
#include "dolphin.h"

URLNavigatorButton::URLNavigatorButton(int index, URLNavigator* parent) :
    URLButton(parent),
    m_index(-1),
    m_listJob(0)
{
    setAcceptDrops(true);
    setMinimumWidth(arrowWidth());
    setIndex(index);
    connect(this, TQT_SIGNAL(clicked()), this, TQT_SLOT(updateNavigatorURL()));

    m_popupDelay = new TQTimer(this);
    connect(m_popupDelay, TQT_SIGNAL(timeout()), this, TQT_SLOT(startListJob()));
    connect(this, TQT_SIGNAL(pressed()), this, TQT_SLOT(startPopupDelay()));
}

URLNavigatorButton::~URLNavigatorButton()
{
}

void URLNavigatorButton::setIndex(int index)
{
    if (index < 0) {
        index = 0;
    }

    m_index = index;
    TQString path(urlNavigator()->url().prettyURL());
    setText(path.section('/', index, index));

    // Check whether the button indicates the full path of the URL. If
    // this is the case, the button is marked as 'active'.
    ++index;
    TQFont adjustedFont(font());
    if (path.section('/', index, index).isEmpty()) {
        setDisplayHintEnabled(ActivatedHint, true);
        adjustedFont.setBold(true);
    }
    else {
        setDisplayHintEnabled(ActivatedHint, false);
        adjustedFont.setBold(false);
    }

    setFont(adjustedFont);
    update();
}

int URLNavigatorButton::index() const
{
    return m_index;
}

void URLNavigatorButton::drawButton(TQPainter* painter)
{
    const int buttonWidth  = width();
    const int buttonHeight = height();

    TQColor backgroundColor;
    TQColor foregroundColor;
    const bool isHighlighted = isDisplayHintEnabled(EnteredHint) ||
                               isDisplayHintEnabled(DraggedHint) ||
                               isDisplayHintEnabled(PopupActiveHint);
    if (isHighlighted) {
        backgroundColor = KGlobalSettings::highlightColor();
        foregroundColor = KGlobalSettings::highlightedTextColor();
    }
    else {
        backgroundColor = colorGroup().background();
        foregroundColor = KGlobalSettings::buttonTextColor();
    }

    // dimm the colors if the parent view does not have the focus
    const DolphinView* parentView = urlNavigator()->dolphinView();
    const Dolphin& dolphin = Dolphin::mainWin();

    const bool isActive = (dolphin.activeView() == parentView);
    if (!isActive) {
        TQColor dimmColor(colorGroup().background());
        foregroundColor = mixColors(foregroundColor, dimmColor);
        if (isHighlighted) {
            backgroundColor = mixColors(backgroundColor, dimmColor);
        }
    }

    // draw button background
    painter->setPen(NoPen);
    painter->setBrush(backgroundColor);
    painter->drawRect(0, 0, buttonWidth, buttonHeight);

    int textWidth = buttonWidth;
    if (isDisplayHintEnabled(ActivatedHint) && isActive || isHighlighted) {
        painter->setPen(foregroundColor);
    }
    else {
        // dimm the foreground color by mixing it with the background
        foregroundColor = mixColors(foregroundColor, backgroundColor);
        painter->setPen(foregroundColor);
    }

    if (!isDisplayHintEnabled(ActivatedHint)) {
        // draw arrow
        const int border = 2;  // horizontal border
        const int middleY = height() / 2;
        const int width = arrowWidth();
        const int startX = (buttonWidth - width) - (2 * border);
        const int startTopY = middleY - (width - 1);
        const int startBottomY = middleY + (width - 1);
        for (int i = 0; i < width; ++i) {
            painter->drawLine(startX, startTopY + i, startX + i, startTopY + i);
            painter->drawLine(startX, startBottomY - i, startX + i, startBottomY - i);
        }

        textWidth = startX - border;
    }

    const bool clipped = isTextClipped();
    const int align = clipped ? TQt::AlignVCenter : TQt::AlignCenter;
    painter->drawText(TQRect(0, 0, textWidth, buttonHeight), align, text());

    if (clipped) {
        // Blend the right area of the text with the background, as the
        // text is clipped.
        // TODO: use alpha blending in TQt4 instead of drawing the text that often
        const int blendSteps = 16;

        TQColor blendColor(backgroundColor);
        const int redInc   = (foregroundColor.red()   - backgroundColor.red())   / blendSteps;
        const int greenInc = (foregroundColor.green() - backgroundColor.green()) / blendSteps;
        const int blueInc  = (foregroundColor.blue()  - backgroundColor.blue())  / blendSteps;
        for (int i = 0; i < blendSteps; ++i) {
            painter->setClipRect(TQRect(textWidth - i, 0, 1, buttonHeight));
            painter->setPen(blendColor);
            painter->drawText(TQRect(0, 0, textWidth, buttonHeight), align, text());

            blendColor.setRgb(blendColor.red()   + redInc,
                              blendColor.green() + greenInc,
                              blendColor.blue()  + blueInc);
        }
    }
}

void URLNavigatorButton::enterEvent(TQEvent* event)
{
    URLButton::enterEvent(event);

    // if the text is clipped due to a small window width, the text should
    // be shown as tooltip
    if (isTextClipped()) {
        TQToolTip::add(this, text());
    }
}

void URLNavigatorButton::leaveEvent(TQEvent* event)
{
    URLButton::leaveEvent(event);
    TQToolTip::remove(this);
}

void URLNavigatorButton::dropEvent(TQDropEvent* event)
{
    KURL::List urls;
    if (KURLDrag::decode(event, urls) && !urls.isEmpty()) {
        setDisplayHintEnabled(DraggedHint, true);

        TQString path(urlNavigator()->url().prettyURL());
        path = path.section('/', 0, m_index);

        Dolphin::mainWin().dropURLs(urls, KURL(path));

        setDisplayHintEnabled(DraggedHint, false);
        update();
    }
}

void URLNavigatorButton::dragEnterEvent(TQDragEnterEvent* event)
{
    event->accept(KURLDrag::canDecode(event));

    setDisplayHintEnabled(DraggedHint, true);
    update();
}

void URLNavigatorButton::dragLeaveEvent(TQDragLeaveEvent* event)
{
    URLButton::dragLeaveEvent(event);

    setDisplayHintEnabled(DraggedHint, false);
    update();
}


void URLNavigatorButton::updateNavigatorURL()
{
    URLNavigator* navigator = urlNavigator();
    assert(navigator != 0);
    navigator->setURL(navigator->url(m_index));
}

void URLNavigatorButton::startPopupDelay()
{
    if (m_popupDelay->isActive() || m_listJob) {
        return;
    }

    m_popupDelay->start(300, true);
}

void URLNavigatorButton::stopPopupDelay()
{
    m_popupDelay->stop();
    if (m_listJob) {
        m_listJob->kill();
        m_listJob = 0;
    }
}

void URLNavigatorButton::startListJob()
{
    if (m_listJob) {
        return;
    }

    KURL url = urlNavigator()->url(m_index);
    m_listJob = KIO::listDir(url, false, false);
    m_subdirs.clear(); // just to be ++safe

    connect(m_listJob, TQT_SIGNAL(entries(KIO::Job*, const KIO::UDSEntryList &)),
            this, TQT_SLOT(entriesList(KIO::Job*, const KIO::UDSEntryList&)));
    connect(m_listJob, TQT_SIGNAL(result(KIO::Job*)), this, TQT_SLOT(listJobFinished(KIO::Job*)));
}

void URLNavigatorButton::entriesList(KIO::Job* job, const KIO::UDSEntryList& entries)
{
    if (job != m_listJob) {
        return;
    }

    KIO::UDSEntryList::const_iterator it = entries.constBegin();
    KIO::UDSEntryList::const_iterator itEnd = entries.constEnd();
    while (it != itEnd) {
        TQString name;
        bool isDir = false;
        KIO::UDSEntry entry = *it;
        KIO::UDSEntry::const_iterator atomIt = entry.constBegin();
        KIO::UDSEntry::const_iterator atomEndIt = entry.constEnd();

        while (atomIt != atomEndIt) {
            switch ((*atomIt).m_uds) {
                case KIO::UDS_NAME:
                    name = (*atomIt).m_str;
                    break;
                case KIO::UDS_FILE_TYPE:
                    isDir = S_ISDIR((*atomIt).m_long);
                    break;
                default:
                    break;
            }
            ++atomIt;
         }

        if (isDir) {
            m_subdirs.append(name);
        }

        ++it;
    }

    m_subdirs.sort();
}

void URLNavigatorButton::listJobFinished(KIO::Job* job)
{
    if (job != m_listJob) {
        return;
    }

    if (job->error() || m_subdirs.isEmpty()) {
        // clear listing
        return;
    }

    setDisplayHintEnabled(PopupActiveHint, true);
    update(); // ensure the button is drawn highlighted
    TQPopupMenu* dirsMenu = new TQPopupMenu(this);
    //setPopup(dirsMenu);
    TQStringList::const_iterator it = m_subdirs.constBegin();
    TQStringList::const_iterator itEnd = m_subdirs.constEnd();
    int i = 0;
    while (it != itEnd) {
        dirsMenu->insertItem(*it, i);
        ++i;
        ++it;
    }

    int result = dirsMenu->exec(urlNavigator()->mapToGlobal(geometry().bottomLeft()));

    if (result >= 0) {
        KURL url = urlNavigator()->url(m_index);
        url.addPath(*m_subdirs.at(result));
        urlNavigator()->setURL(url);
    }

    m_listJob = 0;
    m_subdirs.clear();
    delete dirsMenu;
    setDisplayHintEnabled(PopupActiveHint, false);
}

int URLNavigatorButton::arrowWidth() const
{
    int width = (height() / 2) - 7;
    if (width < 4) {
        width = 4;
    }
    return width;
}

bool URLNavigatorButton::isTextClipped() const
{
    int availableWidth = width();
    if (!isDisplayHintEnabled(ActivatedHint)) {
        availableWidth -= arrowWidth() + 1;
    }

    TQFontMetrics fontMetrics(font());
    return fontMetrics.width(text()) >= availableWidth;
}


void URLNavigatorButton::mousePressEvent(TQMouseEvent * event)
{
    if (event->button() == Qt::LeftButton)
        dragPos = event->pos();
    URLButton::mousePressEvent(event);
}

void URLNavigatorButton::mouseMoveEvent(TQMouseEvent * event)
{
    if (event->state() & Qt::LeftButton) {
        int distance = (event->pos() - dragPos).manhattanLength();
        if (distance > TQApplication::startDragDistance()*2)//don't start on small move (for submenu usability)
            startDrag();
    }
    URLButton::mouseMoveEvent(event);
}

void URLNavigatorButton::startDrag()
{
    KURL url = urlNavigator()->url(m_index);
    KURL::List lst;
    lst.append( url );
    KURLDrag *drag = new KURLDrag(lst, this);
    drag->drag();
}

#include "urlnavigatorbutton.moc"