/***************************************************************************
                          sq_widgetstack.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 <tqlabel.h>
#include <tqbuttongroup.h>
#include <tqpushbutton.h>
#include <tqtoolbutton.h>
#include <tqtimer.h>
#include <tqapplication.h>

#include <tdeaction.h>
#include <kcombobox.h>
#include <kstandarddirs.h>
#include <tdestdaccel.h>
#include <tdelocale.h>
#include <tdeio/job.h>
#include <tdefiledialog.h>
#include <tdemessagebox.h>
#include <kstringhandler.h>

#include "ksquirrel.h"
#include "sq_config.h"
#include "sq_fileiconview.h"
#include "sq_filethumbview.h"
#include "sq_filedetailview.h"
#include "sq_widgetstack.h"
#include "sq_externaltool.h"
#include "sq_treeview.h"
#include "sq_hloptions.h"
#include "sq_archivehandler.h"
#include "sq_glview.h"
#include "sq_glwidget.h"
#include "sq_libraryhandler.h"
#include "sq_diroperator.h"
#include "sq_bookmarkowner.h"
#include "sq_thumbnailsize.h"
#include "sq_progressbox.h"
#include "sq_thumbnailloadjob.h"
#include "sq_pixmapcache.h"
#include "sq_selectdeselectgroup.h"
#include "sq_navigatordropmenu.h"

SQ_WidgetStack * SQ_WidgetStack::m_instance = 0;

SQ_WidgetStack::SQ_WidgetStack(TQWidget *parent, const int id) : TQObject(parent)
{
    m_instance = this;

    SQ_DirOperator::ViewT m_type = static_cast<SQ_DirOperator::ViewT>(id);

    KURL url;

    // Check Options for path
    SQ_Config::instance()->setGroup("Fileview");

    if(SQ_HLOptions::instance()->dir.isEmpty())
    {
        switch(SQ_Config::instance()->readNumEntry("set path", 0))
        {
            case 0: url = KURL::fromPathOrURL(SQ_Config::instance()->readEntry("last visited", "/")); break;
            case 1: url = KURL::fromPathOrURL(TQDir::current().path()); break;
            case 2: url = KURL::fromPathOrURL(SQ_Config::instance()->readEntry("custom directory", "/")); break;

            default: url = KURL::fromPathOrURL("/");
        }
    }
    else // path from command line
        url = SQ_HLOptions::instance()->dir;

    dirop = new SQ_DirOperator(url, static_cast<SQ_DirOperator::ViewT>(id), parent);

    raiseWidget(m_type, false);

    connect(KSquirrel::app(), TQ_SIGNAL(thumbSizeChanged(const TQString&)), dirop, TQ_SLOT(slotSetThumbSize(const TQString&)));
    connect(dirop, TQ_SIGNAL(runSeparately(KFileItem *)), this, TQ_SLOT(slotRunSeparately()));

    KSquirrel::app()->historyCombo()->setEditText(url.isLocalFile() ? url.path() : url.prettyURL());

    timerShowProgress = new TQTimer(this);
    connect(timerShowProgress, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotDelayedShowProgress()));
}

SQ_WidgetStack::~SQ_WidgetStack()
{}

/*
 *  Get current url of visible diroperator.
 */
KURL SQ_WidgetStack::url() const
{
    return dirop->url();
}

void SQ_WidgetStack::init()
{
    SQ_Config::instance()->setGroup("Fileview");

    fileaction = static_cast<SQ_WidgetStack::FileAction>(SQ_Config::instance()->readNumEntry("last_action", SQ_WidgetStack::Unknown));
    KURL uu = KURL::fromPathOrURL(SQ_Config::instance()->readEntry("last_url"));

    connect(SQ_NavigatorDropMenu::instance(), TQ_SIGNAL(done(const KURL &, int)),
            this, TQ_SLOT(slotTreeMenuDone(const KURL &, int)));

    emitNewLastURL(uu);
}

void SQ_WidgetStack::slotTreeMenuDone(const KURL &url, int a)
{
    if(a == SQ_NavigatorDropMenu::Copy)
        fileaction = SQ_WidgetStack::Copy;
    else if(a == SQ_NavigatorDropMenu::Move)
        fileaction = SQ_WidgetStack::Cut;
    else if(a == SQ_NavigatorDropMenu::Link)
        fileaction = SQ_WidgetStack::Link;

    lastURL = url;

    emitNewLastURL(lastURL);
}

void SQ_WidgetStack::setURL(const KURL &newurl, bool parseTree)
{
    KURL url = newurl;
    url.adjustPath(+1);

    // update bookmarks' url
    if(SQ_BookmarkOwner::instance())
        SQ_BookmarkOwner::instance()->setURL(url);

    // update history combobox
    KSquirrel::app()->historyCombo()->setEditText(url.isLocalFile() ? url.path() : url.prettyURL());
    KSquirrel::app()->historyCombo()->addToHistory(url.isLocalFile() ? url.path() : url.prettyURL());

    // set url for file tree
    if(SQ_TreeView::instance() && parseTree)
    {
        SQ_Config::instance()->setGroup("Fileview");
        int sync_type = SQ_Config::instance()->readNumEntry("sync type", 0);

        if(sync_type != 2)
            SQ_TreeView::instance()->emitNewURL(url);
    }
}

/*
 *  Select next/prevous supported image in filemanager.
 *  Do nothing, if no more supported images found in given
 *  direction.
 */
int SQ_WidgetStack::moveTo(Direction direction, KFileItem *it, bool useSupported)
{
    KFileView *local;
    KFileItem *item;

    // current fileview
    local = dirop->view();

    if(it)
        item = it;
    else
        item = (direction == SQ_WidgetStack::Next)?
                    local->nextItem(local->currentFileItem()):
                    local->prevItem(local->currentFileItem());

    if(!item)
        return moveFailed;

    if(useSupported)
    {
        while(true)
        {
            if(item->isFile())
                // supported image type ?
//                if(SQ_LibraryHandler::instance()->libraryForFile(item->url().path()))
                if(SQ_LibraryHandler::instance()->maybeSupported(item->url()) != SQ_LibraryHandler::No)
                    break;

            item = (direction == SQ_WidgetStack::Next)?
                    (local->nextItem(item)):
                    (local->prevItem(item));

            if(!item)
                return moveFailed;
        }
    }

    // set current file, select it
    dirop->setCurrentItem(item);

    return moveSuccess;
}

/*
 *  Set filter.
 */
void SQ_WidgetStack::setNameFilter(const TQString &f)
{
    dirop->setNameFilter(f);
    dirop->actionCollection()->action("reload")->activate();
}

/*
 *  Get current filter.
 */
TQString SQ_WidgetStack::nameFilter() const
{
    return dirop->nameFilter();
}

/*
 *  Change view type. See SQ_DirOperator::ViewT for more.
 */
void SQ_WidgetStack::raiseWidget(SQ_DirOperator::ViewT id, bool doUpdate)
{
    if(doUpdate) dirop->stopThumbnailUpdate();

    dirop->removeCdUpItem();
    dirop->prepareView(id);

    switch(id)
    {
        case SQ_DirOperator::TypeList:
        case SQ_DirOperator::TypeIcons:
        {
            SQ_FileIconView *iv = dynamic_cast<SQ_FileIconView *>(dirop->preparedView());

            dirop->setPreparedView();
            iv->setSelectionMode(KFile::Extended);
            action("short view")->activate();

            if(id == SQ_DirOperator::TypeList)
                iv->actionCollection()->action("small columns")->activate();
            else
                iv->actionCollection()->action("large rows")->activate();
        }
        break;

        case SQ_DirOperator::TypeDetailed:
        {
            SQ_FileDetailView *dv = dynamic_cast<SQ_FileDetailView *>(dirop->preparedView());

            dirop->setPreparedView();
            dv->setSelectionMode(KFile::Extended);
        }
        break;

        case SQ_DirOperator::TypeThumbs:
        {
            SQ_FileThumbView *tv = dynamic_cast<SQ_FileThumbView *>(dirop->preparedView());
            tv->actionCollection()->action("large rows")->activate();
            dirop->setPreparedView();
            action("short view")->activate();
            tv->setSelectionMode(KFile::Extended);
            updateGrid(true);
            if(doUpdate) dirop->startOrNotThumbnailUpdate();
        }
        break;
    }

    // enable/disable menu with thumbnail sizes
    KSquirrel::app()->enableThumbsMenu(id == SQ_DirOperator::TypeThumbs);
    dirop->enableThumbnailActions(id == SQ_DirOperator::TypeThumbs);
    dirop->selectOld();
}

/*
 *  Selected items in currently visible diroperator.
 */
const KFileItemList* SQ_WidgetStack::selectedItems() const
{
    return dirop->view()->selectedItems();
}

/*
 *  All items in currently visible diroperator.
 */
const KFileItemList* SQ_WidgetStack::items() const
{
    return dirop->view()->items();
}

void SQ_WidgetStack::emitNextSelected()
{
    if(moveTo(SQ_WidgetStack::Next) == SQ_WidgetStack::moveSuccess)
    {
        KFileItem *fi = dirop->view()->currentFileItem();
        SQ_GLWidget::window()->setExpectedURL(fi->url());
        dirop->execute(fi);
    }
}

void SQ_WidgetStack::emitPreviousSelected()
{
    if(moveTo(SQ_WidgetStack::Previous) == SQ_WidgetStack::moveSuccess)
    {
        KFileItem *fi = dirop->view()->currentFileItem();
        SQ_GLWidget::window()->setExpectedURL(fi->url());
        dirop->execute(fi);
    }
}

void SQ_WidgetStack::slotSelectFirstFile()
{
    moveToFirstLast(SQ_WidgetStack::Next, false);
}

void SQ_WidgetStack::slotSelectLastFile()
{
    moveToFirstLast(SQ_WidgetStack::Previous, false);
}

// Go to first file
void SQ_WidgetStack::firstFile()
{
    moveToFirstLast(SQ_WidgetStack::Next);
}

// Go to last file
void SQ_WidgetStack::lastFile()
{
    moveToFirstLast(SQ_WidgetStack::Previous);
}

void SQ_WidgetStack::moveToFirstLast(Direction direct, bool exec)
{
    KFileView *local_view = dirop->view();
    KFileItemList *list = const_cast<KFileItemList*>(local_view->items());

    if(list)
    {
        KFileItem *item = (direct == SQ_WidgetStack::Next) ? list->first() : list->last();

        if(!item || moveTo(direct, item) == moveFailed)
            return;

        item = local_view->currentFileItem();

        if(item)
        {
            dirop->setCurrentItem(item);
            SQ_GLWidget::window()->setExpectedURL(item->url());
            if(exec) dirop->execute(item);
        }
    }
}

/*
 *  Update grid for thumbnail view. New grid is calcalated from item
 *  margins (from Options).
 */
void SQ_WidgetStack::updateGrid(bool arrange)
{
    if(dirop->viewType() != SQ_DirOperator::TypeThumbs)
        return;

    SQ_Config::instance()->setGroup("Thumbnails");
    int newgrid = SQ_ThumbnailSize::instance()->extended() ?
                    SQ_ThumbnailSize::instance()->extendedSize().width()
                    : SQ_ThumbnailSize::instance()->pixelSize();

    newgrid += (SQ_Config::instance()->readNumEntry("margin", 2) + 2);

    SQ_FileThumbView *tv = dynamic_cast<SQ_FileThumbView *>(dirop->view());

    // different grid ?
    if(tv->gridX() != newgrid)
    {
        tv->setGridX(newgrid);

        if(arrange)
            tv->arrangeItemsInGrid();
    }
}

void SQ_WidgetStack::thumbnailsUpdateEnded()
{
    if(dirop->viewType() != SQ_DirOperator::TypeThumbs)
        return;

    SQ_FileThumbView *tv = dynamic_cast<SQ_FileThumbView *>(dirop->view());

    if(!tv) return;

    timerShowProgress->stop();
    tv->progressBox()->hide();
    tv->progressBox()->flush();
    tv->progressBox()->startButtonPixmap();
}

void SQ_WidgetStack::thumbnailUpdateStart(int count)
{
    SQ_FileThumbView *tv = dynamic_cast<SQ_FileThumbView *>(dirop->view());

    if(!tv) return;

    tv->progressBox()->setTotalSteps(count);
    tv->progressBox()->stopButtonPixmap();

    timerShowProgress->start(1000, true);
}

void SQ_WidgetStack::slotDelayedShowProgress()
{
    SQ_FileThumbView *tv = dynamic_cast<SQ_FileThumbView *>(dirop->view());

    if(tv)
        tv->progressBox()->show();
}

void SQ_WidgetStack::thumbnailProcess()
{
    SQ_FileThumbView *tv = dynamic_cast<SQ_FileThumbView *>(dirop->view());

    if(tv)
        tv->progressBox()->advance();
}

void SQ_WidgetStack::setURLForCurrent(const TQString &path, bool parseTree)
{
    KURL url = KURL::fromPathOrURL(path);

    setURLForCurrent(url, parseTree);
}

void SQ_WidgetStack::setURLForCurrent(const KURL &url, bool parseTree)
{
    setURL(url, parseTree);
    dirop->setURL(url, true);
}

void SQ_WidgetStack::updateView()
{
    dirop->actionCollection()->action("reload")->activate();
}

void SQ_WidgetStack::slotRunSeparately()
{
    const KFileItemList *selected = selectedItems();
    KFileItemListIterator it(*selected);

    KFileItem *item;

    while((item = it.current()))
    {
        if(item)
            item->run();

        ++it;
    }
}

// recreate selected thumbnails
void SQ_WidgetStack::slotRecreateThumbnail()
{
    // oops, not a thumbnail view
    if(dirop->viewType() != SQ_DirOperator::TypeThumbs)
        return;

    SQ_FileThumbView *tv = dynamic_cast<SQ_FileThumbView *>(dirop->view());

    if(tv->updateRunning())
        return;

    tv->progressBox()->show();

    TQTimer::singleShot(1, this, TQ_SLOT(slotDelayedRecreateThumbnail()));
}

void SQ_WidgetStack::slotDelayedRecreateThumbnail()
{
    SQ_Thumbnail thumb;

    KFileItemList *list = const_cast<KFileItemList *>(selectedItems());
    SQ_FileThumbView *tv = dynamic_cast<SQ_FileThumbView *>(dirop->view());

    if(!list || !tv)
        return;

    KFileItemListIterator it(*list);
    KFileItem *item;

    while((item = it.current()))
    {
        SQ_PixmapCache::instance()->removeEntryFull(item->url());
        ++it;
    }

    tv->doStartThumbnailUpdate(*list);
}

/*
 *  Save currently selected items' paths, if any.
 *
 *  Return true, if at least one url was saved.
 */
bool SQ_WidgetStack::prepare()
{
    // clear old files
    files.clear();

    KFileItemList *items = const_cast<KFileItemList *>(selectedItems());
    KFileItem *i;

    if(!items || items->isEmpty())
    {
        KMessageBox::information(dirop, i18n("No files to copy or move"));
        return false;
    }

    i = items->first();

    while(i)
    {
        files.append(i->url());
        i = items->next();
    }

    return true;
}

/*
 *  Slots for file actions: copy, move, cut...
 */
void SQ_WidgetStack::slotFileCopy()
{
    fileaction = (prepare()) ? SQ_WidgetStack::Copy : SQ_WidgetStack::Unknown;
}

void SQ_WidgetStack::slotFileCut()
{
    fileaction = (prepare()) ? SQ_WidgetStack::Cut : SQ_WidgetStack::Unknown;
}

// Paste files
void SQ_WidgetStack::slotFilePaste()
{
    // No files to copy ?
    if(files.isEmpty() || fileaction == SQ_WidgetStack::Unknown)
    {
        KMessageBox::information(dirop, i18n("No files to copy or move"));
        return;
    }

    // get current url
    KURL _url = url();
    emitNewLastURL(_url);
    TDEIO::Job *job;

    // now copy or move files to current url
    job = (fileaction == SQ_WidgetStack::Copy) ? TDEIO::copy(files, _url) : TDEIO::move(files, _url);
    connect(job, TQ_SIGNAL(result(TDEIO::Job *)), this, TQ_SLOT(slotJobResult(TDEIO::Job *)));
}

void SQ_WidgetStack::slotJobResult(TDEIO::Job *job)
{
    if(job && job->error())
        job->showErrorDialog(KSquirrel::app());
}

void SQ_WidgetStack::emitNewLastURL(const KURL &u)
{
    lastURL = u;
    TQString text;

    TQString targ = KStringHandler::csqueeze(u.isLocalFile() ? u.path() : u.prettyURL(), 25);

    if(fileaction == SQ_WidgetStack::Copy)
        text = i18n("Repeat (copy to %1)").arg(targ);
    else if(fileaction == SQ_WidgetStack::Cut)
        text = i18n("Repeat (move to %1)").arg(targ);
    else if(fileaction == SQ_WidgetStack::Link)
        text = i18n("Repeat (link to %1)").arg(targ);

    if(!text.isEmpty())
        emit newLastURL(text);
}

void SQ_WidgetStack::repeat()
{
    if(fileaction == SQ_WidgetStack::Copy || fileaction == SQ_WidgetStack::Cut)
        prepare();
    else
        return;

    // No files to copy ?
    if(lastURL.isEmpty() || files.isEmpty())
        return;

    TDEIO::Job *job = (fileaction == SQ_WidgetStack::Copy) ? TDEIO::copy(files, lastURL) : TDEIO::move(files, lastURL);
    connect(job, TQ_SIGNAL(result(TDEIO::Job *)), this, TQ_SLOT(slotJobResult(TDEIO::Job *)));
}

// Create links
void SQ_WidgetStack::slotFileLinkTo()
{
    if(!prepare())
        return;

    // select a directory
    KURL url = KFileDialog::getExistingURL(lastURL.url(), dirop);

    if(url.isEmpty())
        return;

    fileaction = SQ_WidgetStack::Link;
    emitNewLastURL(url);

    // create symlinks
    TDEIO::link(files, url);
}

// Copy to...
void SQ_WidgetStack::slotFileCopyTo()
{
    if(!prepare())
        return;

    // select a directory
    KURL url = KFileDialog::getExistingURL(lastURL.url(), dirop);

    if(url.isEmpty())
        return;

    fileaction = SQ_WidgetStack::Copy;
    emitNewLastURL(url);

    // copy files to selected directory
    TDEIO::copy(files, url);
}

// Move to...
void SQ_WidgetStack::slotFileMoveTo()
{
    if(!prepare())
        return;

    // select a directory
    KURL url = KFileDialog::getExistingURL(lastURL.url(), dirop);

    if(url.isEmpty())
        return;

    fileaction = SQ_WidgetStack::Cut;
    emitNewLastURL(url);

    // move files to selected directory
    TDEIO::move(files, url);
}

/*
 *  User wants to select to deselect some files.
 */
void SQ_WidgetStack::slotSelectGroup()
{
    selectDeselectGroup(true);
}

void SQ_WidgetStack::slotDeselectGroup()
{
    selectDeselectGroup(false);
}

/*
 *  Execute SQ_SelectDeselectGroup dialog, and select or
 *  deselect files after it has been closed.
 */
void SQ_WidgetStack::selectDeselectGroup(bool select)
{
    TQString mask;

    KFileView *local = dirop->view();

    SQ_SelectDeselectGroup sd(KSquirrel::app());

    sd.setCaption(select ? i18n("Select a group of files") : i18n("Deselect a group of files"));
    sd.pushMask->setText(select ? i18n("Select !") : i18n("Deselect !"));

    if(sd.exec(mask) == TQDialog::Accepted)
    {
        if(select)
            local->clearSelection();

        KFileItemList *sd_files = const_cast<KFileItemList *>(items());
        KFileItem *i;
        TQRegExp exp(mask);
        exp.setWildcard(true);

        if(!sd_files)
            return;

        i = sd_files->first();

        // go through all files
        while(i)
        {
            // name matched
            if(exp.exactMatch(i->name()))
                local->setSelected(i, select);

            // next file
            i = sd_files->next();
        }
    }
}

/*
 *  Deselect all files.
 */
void SQ_WidgetStack::slotDeselectAll()
{
    dirop->view()->clearSelection();
}

/*
 *  Select all files.
 */
void SQ_WidgetStack::slotSelectAll()
{
    KFileItemList *sd_files = const_cast<KFileItemList *>(items());
    KFileView *local = dirop->view();
    KFileItem *i;

    if(!sd_files)
        return;

    i = sd_files->first();

    // go through files and select them all
    while(i)
    {
        local->setSelected(i, true);
        i = sd_files->next();
    }
}

TDEAction* SQ_WidgetStack::action(const TQString &name)
{
    return dirop->actionCollection()->action(name.utf8());
}

bool SQ_WidgetStack::updateRunning() const
{
    if(dirop->viewType() != SQ_DirOperator::TypeThumbs)
        return false;

    SQ_FileThumbView *tv = dynamic_cast<SQ_FileThumbView *>(dirop->view());

    if(!tv)
        return false;

    return tv->updateRunning();
}

void SQ_WidgetStack::saveState()
{
    SQ_Config::instance()->writeEntry("last visited", dirop->url().prettyURL());
    SQ_Config::instance()->writeEntry("last_action", static_cast<int>(fileaction));
    SQ_Config::instance()->writeEntry("last_url", lastURL.prettyURL());

    dirop->saveConfig();
}

#include "sq_widgetstack.moc"
