/***************************************************************************
                          sq_fileiconview.cpp  -  description
                             -------------------
    begin                : Mon Mar 15 2004
    copyright            : (C) 2004 by Baryshev Dmitry
    email                : ksquirrel.iv@gmail.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <tqpainter.h>
#include <tqhbox.h>
#include <tqtoolbar.h>
#include <tqtoolbutton.h>
#include <tqlabel.h>
#include <tqtimer.h>
#include <tqapplication.h>

#include <kstandarddirs.h>
#include <kstringhandler.h>
#include <tdeglobalsettings.h>
#include <tdelocale.h>
#include <tdeapplication.h>
#include <tdeglobal.h>

#include "ksquirrel.h"
#include "sq_iconloader.h"
#include "sq_config.h"
#include "sq_dir.h"
#include "sq_filethumbview.h"
#include "sq_libraryhandler.h"
#include "sq_thumbnailloadjob.h"
#include "sq_thumbnailsize.h"
#include "sq_widgetstack.h"
#include "sq_diroperator.h"
#include "sq_progress.h"
#include "sq_progressbox.h"
#include "sq_filethumbviewitem.h"
#include "sq_dragprovider.h"

SQ_FileThumbView::SQ_FileThumbView(TQWidget *parent, const char *name) : SQ_FileIconViewBase(parent, name), isPending(false)
{
    // create progress bar
    m_progressBox = new SQ_ProgressBox(this);

    timerScroll = new TQTimer(this);
    connect(timerScroll, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotDelayedContentsMoving()));

    timerAdd = new TQTimer(this);
    connect(timerAdd, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotDelayedAddItems()));

    // setup cache limit
    SQ_Config::instance()->setGroup("Thumbnails");
    m_lazy = SQ_Config::instance()->readBoolEntry("lazy", true);
    lazyDelay = SQ_Config::instance()->readNumEntry("lazy_delay", 500);
    if(lazyDelay <= 0) lazyDelay = 500;
    m_rows = SQ_Config::instance()->readNumEntry("rows", 2);
    if(m_rows <= 0  || m_rows > 5) m_rows = 2;

    setResizeMode(TQIconView::Adjust);

    // load "pending" pixmaps
    pending = SQ_IconLoader::instance()->loadIcon("clock", TDEIcon::Desktop, 32);

    connect(this, TQ_SIGNAL(contentsMoving(int, int)), this, TQ_SLOT(slotContentsMoving(int, int)));

    rebuildCachedPixmaps();
}

SQ_FileThumbView::~SQ_FileThumbView()
{}

/*
 *  Reimplement insertItem() to enable/disable inserting
 *  directories (depends on current settings) and inserting thumbnails.
 */
void SQ_FileThumbView::insertItem(KFileItem *i)
{
    SQ_Config::instance()->setGroup("Fileview");

    // directores disabled ?
    if(i->isDir() && SQ_Config::instance()->readBoolEntry("disable_dirs", false))
        return;

    SQ_Config::instance()->setGroup("Thumbnails");

    SQ_FileThumbViewItem *item;

    setUpdatesEnabled(false);
    if(SQ_Config::instance()->readBoolEntry("mark", false) && SQ_LibraryHandler::instance()->libraryForFile(i->url().path()))
    {
        item = new SQ_FileThumbViewItem(this, i->text(), pendingCache, i);
    }
    else
    {
        TQPixmap mimeall = i->pixmap(SQ_ThumbnailSize::smallest());
        TQPixmap thumbnail(pixelSize.width(), pixelSize.height());
        TQPainter painter(&thumbnail);
        painter.setBrush(colorGroup().base());
        painter.setPen(colorGroup().highlight());
        painter.drawRect(0, 0, pixelSize.width(), pixelSize.height());
        painter.drawPixmap((pixelSize.width()-mimeall.width())/2, (pixelSize.height()-mimeall.height())/2, mimeall);
        item = new SQ_FileThumbViewItem(this, i->text(), thumbnail, i);
    }

    initItemMy(item, i);

    i->setExtraData(this, item);
    setUpdatesEnabled(true);
}

/*
 *  One thumbnail is loaded. Let's update KFileItem's pixmap.
 */
void SQ_FileThumbView::setThumbnailPixmap(const KFileItem* fileItem, const SQ_Thumbnail &t)
{
    KFileIconViewItem *iconItem = viewItem(fileItem);

    if(!iconItem) return;

    SQ_FileThumbViewItem *item = static_cast<SQ_FileThumbViewItem*>(iconItem);

    if(!item) return;

    TQPixmap newpix;

    // Extended thumbnail also have mime icon and dimensions
    if(SQ_ThumbnailSize::instance()->extended())
    {
        TQSize sz = pixelSize;//SQ_ThumbnailSize::instance()->extendedSize();
        int W = sz.width(), H = sz.height();

        // erase original pixmap
        newpix.resize(W, H);

        TQPainter painter;
        painter.begin(&newpix);

        painter.setPen(colorGroup().highlight());
        painter.setBrush(colorGroup().base());

        // draw bounding rect
        painter.drawRect(0, 0, W, H);

        painter.drawImage((W - t.thumbnail.width())/2, (W - t.thumbnail.height())/2, t.thumbnail);
        painter.drawPixmap(W-t.mime.width()-5, H-t.mime.height()-4, t.mime);
        painter.drawLine(3, W-1, W-4, W-1);

        TQFont f = painter.font();
        f.setPixelSize(10);
        painter.setFont(f);

        int rest = H-W-2;

        painter.setPen(colorGroup().text());

        if(t.w && t.h) painter.drawText(4, W+rest/2-12, 100, 12, 0, TQString::fromLatin1("%1x%2").arg(t.w).arg(t.h));

        painter.drawText(4, W+rest/2+1, 100, 12, 0, TDEIO::convertSize(fileItem->size()));
        painter.end();
    }
    else
    {
        newpix.resize(pixelSize.width(), pixelSize.height());

        TQPainter painter;
        painter.begin(&newpix);

        painter.setPen(colorGroup().highlight());
        painter.setBrush(colorGroup().base());

        // draw bounding rect
        painter.drawRect(0, 0, pixelSize.width(), pixelSize.height());

        // draw pixmap
        painter.drawImage((pixelSize.width() - t.thumbnail.width())/2, (pixelSize.height() - t.thumbnail.height())/2, t.thumbnail);
        painter.end();
    }

    item->setPixmap(newpix);
    item->setListed(true);

    // update item
    item->repaint();
}

void SQ_FileThumbView::startThumbnailUpdate()
{
    stopThumbnailUpdate();
    doStartThumbnailUpdate(itemsToUpdate());
}

KFileItemList SQ_FileThumbView::itemsToUpdate(bool fromAll)
{
    // non-lazy mode - simply return all items
    if(!m_lazy)
        return *items();

    // hehe, lazy mode
    KFileItemList list;

    TQRect rect(contentsX(), contentsY(), viewport()->width(), viewport()->height());
    TQIconViewItem *first = fromAll ? firstItem() : findFirstVisibleItem(rect);
    TQIconViewItem *last  = fromAll ? lastItem()  : findLastVisibleItem(rect);

    if(first && last)
    {
        SQ_FileThumbViewItem *tfi;

        TQIconViewItem *f = first;
        int yy;

        if(m_rows)
        {
            // one row more up and down
            for(int i = 0;i < m_rows;++i)
            {
                if(!last) break;

                last = last->nextItem();

                if(last)
                {
                    yy = last->y();

                    while((last = last->nextItem()) && last->y() == yy)
                    {}
                }
            }

            for(int i = 0;i < m_rows;++i)
            {
                f = first->prevItem();

                if(f)
                {
                    yy = f->y();

                    while((f = first->prevItem()) && f->y() == yy)
                        first = f;
                }
            }
        }
        else
            last = last->nextItem();

        for(TQIconViewItem *item = first;(item && item != last);item = item->nextItem())
        {
            tfi = dynamic_cast<SQ_FileThumbViewItem *>(item);

            if(tfi && !tfi->listed())
                list.append(tfi->fileInfo());
        }
    }

    return list;
}

void SQ_FileThumbView::slotContentsMoving(int, int)
{
    if(isVisible())
        timerScroll->start(lazyDelay, true);
    else
        waitForShowEvent();
}

void SQ_FileThumbView::slotDelayedContentsMoving()
{
    // restart generator in lazy mode
    if(m_lazy)
    {
        stopThumbnailUpdate();
        doStartThumbnailUpdate(itemsToUpdate());
    }
    // make visible items first items in the job
    else
    {
        // force itemsToUpdate() return only visible items
        // that need update
        m_lazy = true;
        KFileItemList visibleItems = itemsToUpdate();

        if(updateRunning())
            thumbJob->pop(visibleItems);

        // restore lazy mode
        m_lazy = false;
    }
}

/*
 *  Create job, connect signals and start updating
 */
void SQ_FileThumbView::doStartThumbnailUpdate(const KFileItemList &list)
{
    if(list.isEmpty())
        return;

    // update progress bar
    SQ_WidgetStack::instance()->thumbnailUpdateStart(list.count());

    // create new job
    thumbJob = new SQ_ThumbnailLoadJob(list, this);

    connect(thumbJob, TQ_SIGNAL(thumbnailLoaded(const KFileItem*, const SQ_Thumbnail &)),
            this, TQ_SLOT(setThumbnailPixmap(const KFileItem*, const SQ_Thumbnail&)));

    connect(thumbJob, TQ_SIGNAL(done()), SQ_WidgetStack::instance(), TQ_SLOT(thumbnailsUpdateEnded()));

    // start!
    thumbJob->start();
}

// Stop thumbnail update: delete job, reset progress bar
void SQ_FileThumbView::stopThumbnailUpdate()
{
    timerScroll->stop();
    timerAdd->stop();
    newItems.clear();

    if(!thumbJob.isNull())
    {
        thumbJob->kill();
        SQ_WidgetStack::instance()->thumbnailsUpdateEnded();
    }
}

// Start/stop thumbnail job
void SQ_FileThumbView::slotThumbnailUpdateToggle()
{
    if(!thumbJob.isNull())
        stopThumbnailUpdate();
    else
        startThumbnailUpdate();
}

/*
 *  Append new items to thumbnail generating job.
 */
void SQ_FileThumbView::addItemsToJob(const KFileItemList &items, bool append)
{
    newItemsAppend = append;

    KFileItemListIterator it(items);
    KFileItem *fi;

    while((fi = it.current()))
    {
        newItems.append(fi);
        ++it;
    }

    // don't confuse user with multiple updates
    timerAdd->start(500, true);
}

void SQ_FileThumbView::slotDelayedAddItems()
{
    KFileItemList _newItems = newItems;
    newItems.clear();

    if(m_lazy)
    {
        KFileItemList visItems = itemsToUpdate();
        KFileItemListIterator it(_newItems);
        KFileItem *fi;

        while((fi = it.current()))
        {
            if(visItems.findRef(fi) == -1)
                _newItems.removeRef(fi); // also does ++it
            else
                ++it;
        }
    }

    // job is not running
    if(thumbJob.isNull())
        doStartThumbnailUpdate(_newItems);
    // add new items to running job
    else
    {
        m_progressBox->addSteps(_newItems.count());

        if(newItemsAppend)
            thumbJob->appendItems(_newItems);
        else
            thumbJob->prependItems(_newItems);
    }
}

/*
 *  Clear current view and insert "..".
 */
void SQ_FileThumbView::clearView()
{
    // stop job
    stopThumbnailUpdate();
    slotRemoveToolTip();

    pixelSize = SQ_ThumbnailSize::instance()->extended() ?
            SQ_ThumbnailSize::instance()->extendedSize() : TQSize(SQ_ThumbnailSize::instance()->pixelSize()+2,SQ_ThumbnailSize::instance()->pixelSize()+2);

    // clear
    KFileIconView::clearView();

    // insert ".."
    insertCdUpItem(SQ_WidgetStack::instance()->url());
}

/*
 *  Is thumbnail job running ?
 */
bool SQ_FileThumbView::updateRunning() const
{
    return !thumbJob.isNull();
}

/*
 *  Insert ".." item.
 */
void SQ_FileThumbView::insertCdUpItem(const KURL &base)
{
    static const TQString &dirup = TDEGlobal::staticQString("..");

    KFileItem *fi = new KFileItem(base.upURL(), "inode/directory", S_IFDIR);

    // insert new item
    SQ_FileThumbViewItem *item = new SQ_FileThumbViewItem(this, dirup, directoryCache, fi);

    // item ".." won't be selectable
    item->setSelectable(false);

    fi->setExtraData(this, item);
}

/*
 *  All files are listed. Do something important.
 */
void SQ_FileThumbView::listingCompleted()
{
    arrangeItemsInGrid();
}

void SQ_FileThumbView::rebuildPendingPixmap(bool dir)
{
    TQPixmap pixmapDir;

    if(dir)
        pixmapDir = SQ_IconLoader::instance()->loadIcon("folder", TDEIcon::Desktop, 48);

    TQPixmap *p = dir ? &directoryCache : &pendingCache;
    TQPixmap *w = dir ? &pixmapDir : &pending;

    p->resize(pixelSize.width(), pixelSize.height());

    TQPainter painter(p);
    painter.setBrush(colorGroup().base());
    painter.setPen(colorGroup().highlight());
    painter.drawRect(0, 0, pixelSize.width(), pixelSize.height());
    painter.drawPixmap((pixelSize.width() - w->width())/2, (pixelSize.height() - w->height())/2, *w);
}

void SQ_FileThumbView::rebuildCachedPixmaps()
{
    pixelSize = SQ_ThumbnailSize::instance()->extended() ?
            SQ_ThumbnailSize::instance()->extendedSize() : TQSize(SQ_ThumbnailSize::instance()->pixelSize()+2,SQ_ThumbnailSize::instance()->pixelSize()+2);

    // rebuild "pending" thumbnail
    rebuildPendingPixmap();

    // rebuild directory pixmap
    rebuildPendingPixmap(true);
}

void SQ_FileThumbView::itemRemoved(KFileItem *i)
{
    thumbJob->itemRemoved(i);
}

void SQ_FileThumbView::itemsRemoved(const KFileItemList &l)
{
    thumbJob->itemsRemoved(l);
}

void SQ_FileThumbView::showEvent(TQShowEvent *e)
{
    KFileIconView::showEvent(e);

    if(isPending)
    {
        isPending = false;
        startThumbnailUpdate();
    }
}

void SQ_FileThumbView::startDrag()
{
    SQ_Config::instance()->setGroup("Fileview");

    if(SQ_Config::instance()->readBoolEntry("drag", true))
    {
        SQ_DragProvider::instance()->setParams(this, *KFileView::selectedItems(), SQ_DragProvider::Thumbnails);
        SQ_DragProvider::instance()->start();
    }
    else
        KFileIconView::startDrag();
}

void SQ_FileThumbView::setLazy(bool l, int delay)
{
    m_lazy = l;
    lazyDelay = delay <= 0 ? 500 : delay;

    // non-lazy mode requires to update all thumbnails
    if(!m_lazy)
    {
        m_lazy = true;

        stopThumbnailUpdate();
        doStartThumbnailUpdate(itemsToUpdate(true));

        // restore lazy mode
        m_lazy = false;
    }
}

void SQ_FileThumbView::resizeEvent(TQResizeEvent *e)
{
    KFileIconView::resizeEvent(e);

    if(isVisible())
        timerScroll->start(lazyDelay, true);
    else
        waitForShowEvent();
}

#include "sq_filethumbview.moc"
