/* ============================================================
 *
 * This file is a part of digiKam project
 * http://www.digikam.org
 *
 * Date        : 2005-05-06
 * Description : Albums folder view.
 *
 * Copyright (C) 2005-2006 by Joern Ahrens <joern.ahrens@kdemail.net>
 * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com>
 * Copyright (C) 2009 by Andi Clemens <andi dot clemens at gmx dot net>
 *
 * 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, 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.
 *
 * ============================================================ */

// TQt includes.

#include <tqpixmap.h>
#include <tqguardedptr.h>
#include <tqdir.h>
#include <kpopupmenu.h>
#include <tqcursor.h>
#include <tqdatastream.h>
#include <tqvaluelist.h>
#include <tqdatetime.h>

// KDE includes.

#include <klocale.h>
#include <kglobal.h>
#include <kcalendarsystem.h>
#include <kiconloader.h>
#include <kapplication.h>
#include <kmessagebox.h>
#include <kaction.h>
#include <kfiledialog.h>

#include <kdeversion.h>
#if KDE_IS_VERSION(3,2,0)
#include <kinputdialog.h>
#else
#include <klineeditdlg.h>
#endif

// Local includes.

#include "ddebug.h"
#include "digikamapp.h"
#include "albumlister.h"
#include "album.h"
#include "albumdb.h"
#include "albumpropsedit.h"
#include "albummanager.h"
#include "albumsettings.h"
#include "thumbnailjob.h"
#include "thumbnailsize.h"
#include "folderitem.h"
#include "cameraui.h"
#include "dio.h"
#include "dragobjects.h"
#include "albumthumbnailloader.h"
#include "deletedialog.h"
#include "albumfolderview.h"
#include "albumfolderview.moc"

// X11 C Ansi includes.

extern "C"
{
#include <X11/Xlib.h>
}

namespace Digikam
{

class AlbumFolderViewItem : public FolderItem
{

public:

    AlbumFolderViewItem(TQListView *parent, PAlbum *album);
    AlbumFolderViewItem(TQListViewItem *parent, PAlbum *album);

    // special group item (collection/dates)
    AlbumFolderViewItem(TQListViewItem* parent, const TQString& name,
                        int year, int month);

    PAlbum* album() const;
    int     id() const;
    bool    isGroupItem() const;
    int     compare(TQListViewItem *i, int col, bool ascending) const;
    void    refresh();
    void    setOpen(bool o);
    void    setCount(int count);
    int     count();

private:

    bool    m_groupItem;

    int     m_count;
    int     m_year;
    int     m_month;

    PAlbum *m_album;
};

AlbumFolderViewItem::AlbumFolderViewItem(TQListView *parent, PAlbum *album)
                   : FolderItem(parent, album->title())
{
    setDragEnabled(true);
    m_album     = album;
    m_groupItem = false;
    m_count     = 0;
}

AlbumFolderViewItem::AlbumFolderViewItem(TQListViewItem *parent, PAlbum *album)
                   : FolderItem(parent, album->title())
{
    setDragEnabled(true);
    m_album     = album;
    m_groupItem = false;
    m_count     = 0;
}

// special group item (collection/dates)
AlbumFolderViewItem::AlbumFolderViewItem(TQListViewItem* parent, const TQString& name,
                                         int year, int month)
                   : FolderItem(parent, name, true)
{
    m_album     = 0;
    m_year      = year;
    m_month     = month;
    m_groupItem = true;
    m_count     = 0;
}

void AlbumFolderViewItem::refresh()
{
    if (!m_album) return;

    if (AlbumSettings::instance()->getShowFolderTreeViewItemsCount() &&
        dynamic_cast<AlbumFolderViewItem*>(parent()))
    {
        if (isOpen())
            setText(0, TQString("%1 (%2)").arg(m_album->title()).arg(m_count));
        else
        {
            int countRecursive = m_count;
            AlbumIterator it(m_album);
            while ( it.current() )
            {
                AlbumFolderViewItem *item = (AlbumFolderViewItem*)it.current()->extraData(listView());
                if (item)
                    countRecursive += item->count();
                ++it;
            }
            setText(0, TQString("%1 (%2)").arg(m_album->title()).arg(countRecursive));
        }
    }
    else
    {
        setText(0, m_album->title());
    }
}

void AlbumFolderViewItem::setOpen(bool o)
{
    TQListViewItem::setOpen(o);
    refresh();
}

PAlbum* AlbumFolderViewItem::album() const
{
    return m_album;
}

int AlbumFolderViewItem::id() const
{
    if (m_groupItem)
    {
        if (m_year != 0 && m_month != 0)
        {
            return (m_year*(-100) + m_month*(-1));
        }
        else
        {
            return ( - (AlbumSettings::instance()->getAlbumCollectionNames()
                        .findIndex(text(0)) ) );
        }
    }
    else
    {
        return m_album ? m_album->id() : 0;
    }
}

bool AlbumFolderViewItem::isGroupItem() const
{
    return m_groupItem;
}

int AlbumFolderViewItem::compare(TQListViewItem *i, int col, bool ascending) const
{
    if (!m_groupItem || m_year == 0 || m_month == 0)
        return TQListViewItem::compare(i, col, ascending);

    AlbumFolderViewItem* thatItem = dynamic_cast<AlbumFolderViewItem*>(i);
    if (!thatItem)
        return 0;

    int myWeight  = m_year*100 + m_month;
    int hisWeight = thatItem->m_year*100 + thatItem->m_month;

    if (myWeight == hisWeight)
        return 0;
    else if (myWeight > hisWeight)
        return 1;
    else
        return -1;
}

void AlbumFolderViewItem::setCount(int count)
{
    m_count = count;
    refresh();
}

int AlbumFolderViewItem::count()
{
    return m_count;
}

// -----------------------------------------------------------------------------

class AlbumFolderViewPriv
{
public:

    AlbumFolderViewPriv()
    {
        albumMan     = 0;
        iconThumbJob = 0;
    }

    AlbumManager                     *albumMan;
    ThumbnailJob                     *iconThumbJob;
    TQValueList<AlbumFolderViewItem*>  groupItems;
};

AlbumFolderView::AlbumFolderView(TQWidget *parent)
               : FolderView(parent, "AlbumFolderView")
{
    d = new AlbumFolderViewPriv();
    d->albumMan     = AlbumManager::instance();
    d->iconThumbJob = 0;

    addColumn(i18n("My Albums"));
    setResizeMode(TQListView::LastColumn);
    setRootIsDecorated(false);
    setAllColumnsShowFocus(true);

    setAcceptDrops(true);
    viewport()->setAcceptDrops(true);

    connect(d->albumMan, TQT_SIGNAL(signalAlbumAdded(Album*)),
            this, TQT_SLOT(slotAlbumAdded(Album*)));

    connect(d->albumMan, TQT_SIGNAL(signalAlbumDeleted(Album*)),
            this, TQT_SLOT(slotAlbumDeleted(Album*)));

    connect(d->albumMan, TQT_SIGNAL(signalAlbumsCleared()),
            this, TQT_SLOT(slotAlbumsCleared()));

    connect(d->albumMan, TQT_SIGNAL(signalAlbumIconChanged(Album*)),
            this, TQT_SLOT(slotAlbumIconChanged(Album*)));

    connect(d->albumMan, TQT_SIGNAL(signalAlbumRenamed(Album*)),
            this, TQT_SLOT(slotAlbumRenamed(Album*)));

    connect(d->albumMan, TQT_SIGNAL(signalPAlbumsDirty(const TQMap<int, int>&)),
            this, TQT_SLOT(slotRefresh(const TQMap<int, int>&)));

    AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance();

    connect(loader, TQT_SIGNAL(signalThumbnail(Album *, const TQPixmap&)),
            this, TQT_SLOT(slotGotThumbnailFromIcon(Album *, const TQPixmap&)));

    connect(loader, TQT_SIGNAL(signalFailed(Album *)),
            this, TQT_SLOT(slotThumbnailLost(Album *)));

    connect(loader, TQT_SIGNAL(signalReloadThumbnails()),
            this, TQT_SLOT(slotReloadThumbnails()));

    connect(this, TQT_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint&, int)),
            this, TQT_SLOT(slotContextMenu(TQListViewItem*, const TQPoint&, int)));

    connect(this, TQT_SIGNAL(selectionChanged()),
            this, TQT_SLOT(slotSelectionChanged()));
}

AlbumFolderView::~AlbumFolderView()
{
    if (d->iconThumbJob)
        d->iconThumbJob->kill();

    saveViewState();
    delete d;
}

void AlbumFolderView::slotTextFolderFilterChanged(const TQString& filter)
{
    if (filter.isEmpty())
    {
        collapseView();
        return;
    }

    TQString search = filter.lower();

    bool atleastOneMatch = false;

    AlbumList pList = d->albumMan->allPAlbums();
    for (AlbumList::iterator it = pList.begin(); it != pList.end(); ++it)
    {
        PAlbum* palbum  = (PAlbum*)(*it);

        // don't touch the root Album
        if (palbum->isRoot())
            continue;

        bool match = palbum->title().lower().contains(search);
        bool doesExpand = false;
        if (!match)
        {
            // check if any of the parents match the search
            Album* parent = palbum->parent();
            while (parent && !parent->isRoot())
            {
                if (parent->title().lower().contains(search))
                {
                    match = true;
                    break;
                }

                parent = parent->parent();
            }
        }

        if (!match)
        {
            // check if any of the children match the search
            AlbumIterator it(palbum);
            while (it.current())
            {
                if ((*it)->title().lower().contains(search))
                {
                    match = true;
                    doesExpand = true;
                    break;
                }
                ++it;
            }
        }

        AlbumFolderViewItem* viewItem = (AlbumFolderViewItem*) palbum->extraData(this);

        if (match)
        {
            atleastOneMatch = true;

            if (viewItem)
            {
                viewItem->setVisible(true);
                viewItem->setOpen(doesExpand);
        }
        }
        else
        {
            if (viewItem)
            {
                viewItem->setVisible(false);
                viewItem->setOpen(false);
            }
        }
    }

    emit signalTextFolderFilterMatch(atleastOneMatch);
}

void AlbumFolderView::slotAlbumAdded(Album *album)
{
    if(!album)
        return;

    PAlbum *palbum = dynamic_cast<PAlbum*>(album);
    if(!palbum)
        return;

    bool failed;
    AlbumFolderViewItem* parent = findParent(palbum, failed);
    if (failed)
    {
        DWarning() << k_funcinfo << " Failed to find Album parent "
                   << palbum->url() << endl;
        return;
    }

    AlbumFolderViewItem *item;
    if (!parent)
    {
        // root album
        item = new AlbumFolderViewItem(this, palbum);
        palbum->setExtraData(this, item);
        item->setOpen(true);
    }
    else
    {
        item = new AlbumFolderViewItem(parent, palbum);
        palbum->setExtraData(this, item);
    }

    setAlbumThumbnail(palbum);
}

void AlbumFolderView::slotAlbumDeleted(Album *album)
{
    if(!album)
        return;

    PAlbum* palbum = dynamic_cast<PAlbum*>(album);
    if(!palbum)
        return;

    if(!palbum->icon().isEmpty() && d->iconThumbJob)
        d->iconThumbJob->removeItem(palbum->icon());

    AlbumFolderViewItem* item = (AlbumFolderViewItem*) palbum->extraData(this);
    if(item)
    {
        AlbumFolderViewItem *itemParent = dynamic_cast<AlbumFolderViewItem*>(item->parent());

        if(itemParent)
            itemParent->takeItem(item);
        else
            takeItem(item);

        delete item;
        clearEmptyGroupItems();
    }
}

void AlbumFolderView::slotAlbumRenamed(Album *album)
{
    PAlbum* palbum = dynamic_cast<PAlbum*>(album);
    if(!palbum)
        return;

    AlbumFolderViewItem* item = (AlbumFolderViewItem*) palbum->extraData(this);
    if(item)
        item->refresh();
    if (item->parent())
        item->parent()->sort();
}

void AlbumFolderView::slotAlbumsCleared()
{
    d->groupItems.clear();
    clear();
}

void AlbumFolderView::setAlbumThumbnail(PAlbum *album)
{
    if(!album)
        return;

    AlbumFolderViewItem* item = (AlbumFolderViewItem*) album->extraData(this);

    if(!item)
        return;

    // Either, getThumbnail returns true and loads an icon asynchronously.
    // Then, for the time being, we set the standard icon.
    // Or, no icon is associated with the album, then we set the standard icon anyway.
    AlbumThumbnailLoader *loader = AlbumThumbnailLoader::instance();
    item->setPixmap(0, loader->getStandardAlbumIcon(album));
    loader->getAlbumThumbnail(album);
}

void AlbumFolderView::setCurrentAlbum(Album *album)
{
    if(!album) return;

    AlbumFolderViewItem* item = (AlbumFolderViewItem*) album->extraData(this);
    if(!item) return;

    setCurrentItem(item);
    ensureItemVisible(item);
}

void AlbumFolderView::slotGotThumbnailFromIcon(Album *album,
                                               const TQPixmap& thumbnail)
{
    if(!album || album->type() != Album::PHYSICAL)
        return;

    AlbumFolderViewItem* item = (AlbumFolderViewItem*)album->extraData(this);

    if(!item)
        return;

    item->setPixmap(0, thumbnail);
}

void AlbumFolderView::slotThumbnailLost(Album *)
{
    // we already set the standard icon before loading
}

void AlbumFolderView::slotReloadThumbnails()
{
    AlbumList tList = d->albumMan->allPAlbums();
    for (AlbumList::iterator it = tList.begin(); it != tList.end(); ++it)
    {
        PAlbum* album  = (PAlbum*)(*it);
        setAlbumThumbnail(album);
    }
}

void AlbumFolderView::slotAlbumIconChanged(Album* album)
{
    if(!album || album->type() != Album::PHYSICAL)
        return;

    setAlbumThumbnail((PAlbum*)album);
}

void AlbumFolderView::slotSelectionChanged()
{
    if (!active())
        return;

    TQListViewItem* selItem = 0;
    TQListViewItemIterator it(this);
    while(it.current())
    {
        if(it.current()->isSelected())
        {
            selItem = it.current();
            break;
        }
        ++it;
    }

    if(!selItem)
    {
        d->albumMan->setCurrentAlbum(0);
        return;
    }

    AlbumFolderViewItem *albumitem = dynamic_cast<AlbumFolderViewItem*>(selItem);
    if(!albumitem)
    {
        d->albumMan->setCurrentAlbum(0);
        return;
    }

    d->albumMan->setCurrentAlbum(albumitem->album());
}

void AlbumFolderView::slotContextMenu(TQListViewItem *listitem, const TQPoint &, int)
{
    KActionMenu menuImport(i18n("Import"));
    KActionMenu menuExport(i18n("Export"));
    KActionMenu menuKIPIBatch(i18n("Batch Process"));

    KPopupMenu popmenu(this);
    popmenu.insertTitle(SmallIcon("digikam"), i18n("My Albums"));
    popmenu.insertItem(SmallIcon("albumfolder-new"), i18n("New Album..."), 10);

    AlbumFolderViewItem *item = dynamic_cast<AlbumFolderViewItem*>(listitem);
    if (item && !item->album())
    {
        // if collection/date return
        return;
    }

    // Root folder only shows "New Album..."
    if(item && item->parent())
    {
        popmenu.insertItem(SmallIcon("pencil"), i18n("Rename..."), 14);
        popmenu.insertItem(SmallIcon("albumfolder-properties"), i18n("Album Properties..."), 11);
        popmenu.insertItem(SmallIcon("reload_page"), i18n("Reset Album Icon"), 13);
        popmenu.insertSeparator();

        // Add KIPI Albums plugins Actions
        KAction *action;
        const TQPtrList<KAction>& albumActions = DigikamApp::getinstance()->menuAlbumActions();
        if(!albumActions.isEmpty())
        {
            TQPtrListIterator<KAction> it(albumActions);
            while((action = it.current()))
            {
                action->plug(&popmenu);
                ++it;
            }
        }

        // Add All Import Actions
        const TQPtrList<KAction> importActions = DigikamApp::getinstance()->menuImportActions();
        if(!importActions.isEmpty())
        {
            TQPtrListIterator<KAction> it3(importActions);
            while((action = it3.current()))
            {
                menuImport.insert(action);
                ++it3;
            }
            menuImport.plug(&popmenu);
        }

        // Add All Export Actions
        const TQPtrList<KAction> exportActions = DigikamApp::getinstance()->menuExportActions();
        if(!exportActions.isEmpty())
        {
            TQPtrListIterator<KAction> it4(exportActions);
            while((action = it4.current()))
            {
                menuExport.insert(action);
                ++it4;
            }
            menuExport.plug(&popmenu);
        }

        // Add KIPI Batch processes plugins Actions
        const TQPtrList<KAction>& batchActions = DigikamApp::getinstance()->menuBatchActions();
        if(!batchActions.isEmpty())
        {
            TQPtrListIterator<KAction> it2(batchActions);
            while((action = it2.current()))
            {
                menuKIPIBatch.insert(action);
                ++it2;
            }
            menuKIPIBatch.plug(&popmenu);
        }

        if(!albumActions.isEmpty() || !batchActions.isEmpty() ||
           !importActions.isEmpty())
        {
            popmenu.insertSeparator(-1);
        }

        if(AlbumSettings::instance()->getUseTrash())
        {
            popmenu.insertItem(SmallIcon("edittrash"),
                               i18n("Move Album to Trash"), 12);
        }
        else
        {
            popmenu.insertItem(SmallIcon("editdelete"),
                               i18n("Delete Album"), 12);
        }
    }

    switch(popmenu.exec((TQCursor::pos())))
    {
        case 10:
        {
            albumNew(item);
            break;
        }
        case 11:
        {
            albumEdit(item);
            break;
        }
        case 12:
        {
            albumDelete(item);
            break;
        }
        case 13:
        {
            TQString err;
            d->albumMan->updatePAlbumIcon(item->album(), 0, err);
            break;
        }
        case 14:
        {
            albumRename(item);
            break;
        }
        default:
            break;
    }
}

void AlbumFolderView::albumNew()
{
    AlbumFolderViewItem *item = dynamic_cast<AlbumFolderViewItem*>(selectedItem());
    if (!item)
    {
        item = dynamic_cast<AlbumFolderViewItem*>(firstChild());
    }

    if (!item)
        return;

    albumNew(item);
}

void AlbumFolderView::albumNew(AlbumFolderViewItem *item)
{
    AlbumSettings* settings = AlbumSettings::instance();
    if(!settings)
    {
        DWarning() << "AlbumFolderView: Couldn't get Album Settings" << endl;
        return;
    }

    TQDir libraryDir(settings->getAlbumLibraryPath());
    if(!libraryDir.exists())
    {
        KMessageBox::error(0,
                           i18n("The album library has not been set correctly.\n"
                                "Select \"Configure Digikam\" from the Settings "
                                "menu and choose a folder to use for the album "
                                "library."));
        return;
    }

    PAlbum *parent;

    if(!item)
        parent = d->albumMan->findPAlbum(0);
    else
        parent = item->album();

    if (!parent)
        return;

    TQString     title;
    TQString     comments;
    TQString     collection;
    TQDate       date;
    TQStringList albumCollections;

    if(!AlbumPropsEdit::createNew(parent, title, comments, date, collection,
                                  albumCollections))
        return;

    TQStringList oldAlbumCollections(AlbumSettings::instance()->getAlbumCollectionNames());
    if(albumCollections != oldAlbumCollections)
    {
        AlbumSettings::instance()->setAlbumCollectionNames(albumCollections);
        resort();
    }

    TQString errMsg;
    PAlbum* album = d->albumMan->createPAlbum(parent, title, comments,
                                              date, collection, errMsg);
    if (!album)
    {
        KMessageBox::error(0, errMsg);
        return;
    }

    // by this time the signalAlbumAdded has been fired and the appropriate
    // AlbumFolderViewItem has been created. Now make this folderviewitem visible
    AlbumFolderViewItem* newItem = (AlbumFolderViewItem*)album->extraData(this);
    if (newItem)
    {
        if(item)
            item->setOpen(true);

        ensureItemVisible(newItem);
    }
}

void AlbumFolderView::albumDelete()
{
    AlbumFolderViewItem *item = dynamic_cast<AlbumFolderViewItem*>(selectedItem());
    if(!item)
        return;

    albumDelete(item);
}

void AlbumFolderView::albumDelete(AlbumFolderViewItem *item)
{
    PAlbum *album = item->album();

    if(!album || album->isRoot())
        return;

    // find subalbums
    KURL::List childrenList;
    addAlbumChildrenToList(childrenList, album);

    DeleteDialog dialog(this);

    // All subalbums will be presented in the list as well
    if (!dialog.confirmDeleteList(childrenList,
                                  childrenList.size() == 1 ?
                                  DeleteDialogMode::Albums : DeleteDialogMode::Subalbums,
                                  DeleteDialogMode::UserPreference))
        return;

    bool useTrash = !dialog.shouldDelete();

    // Currently trash kioslave can handle only full paths.
    // pass full folder path to the trashing job
    KURL u;
    u.setProtocol("file");
    u.setPath(album->folderPath());
    KIO::Job* job = DIO::del(u, useTrash);
    connect(job, TQT_SIGNAL(result(KIO::Job *)),
            this, TQT_SLOT(slotDIOResult(KIO::Job *)));
}

void AlbumFolderView::addAlbumChildrenToList(KURL::List &list, Album *album)
{
    // simple recursive helper function
    if (album)
    {
        list.append(album->kurl());
        AlbumIterator it(album);
        while(it.current())
        {
            addAlbumChildrenToList(list, *it);
            ++it;
        }
    }
}

void AlbumFolderView::slotDIOResult(KIO::Job* job)
{
    if (job->error())
        job->showErrorDialog(this);
}

void AlbumFolderView::albumRename()
{
    AlbumFolderViewItem *item = dynamic_cast<AlbumFolderViewItem*>(selectedItem());
    if(!item)
        return;

    albumRename(item);
}

void AlbumFolderView::albumRename(AlbumFolderViewItem* item)
{
    PAlbum *album = item->album();

    if (!album)
        return;

    TQString oldTitle(album->title());
    bool    ok;

#if KDE_IS_VERSION(3,2,0)
    TQString title = KInputDialog::getText(i18n("Rename Album (%1)").arg(oldTitle),
                                          i18n("Enter new album name:"),
                                          oldTitle, &ok, this);
#else
    TQString title = KLineEditDlg::getText(i18n("Rename Item (%1)").arg(oldTitle),
                                          i18n("Enter new album name:"),
                                          oldTitle, &ok, this);
#endif

    if (!ok)
        return;

    if(title != oldTitle)
    {
        TQString errMsg;
        if (!d->albumMan->renamePAlbum(album, title, errMsg))
            KMessageBox::error(0, errMsg);
    }

    emit signalAlbumModified();
}

void AlbumFolderView::albumEdit()
{
    AlbumFolderViewItem *item = dynamic_cast<AlbumFolderViewItem*>(selectedItem());
    if(!item)
        return;

    albumEdit(item);
}

void AlbumFolderView::albumEdit(AlbumFolderViewItem* item)
{
    PAlbum *album = item->album();

    if (!album)
        return;

    TQString     oldTitle(album->title());
    TQString     oldComments(album->caption());
    TQString     oldCollection(album->collection());
    TQDate       oldDate(album->date());
    TQStringList oldAlbumCollections(AlbumSettings::instance()->getAlbumCollectionNames());

    TQString     title, comments, collection;
    TQDate       date;
    TQStringList albumCollections;

    if(AlbumPropsEdit::editProps(album, title, comments, date,
                                 collection, albumCollections))
    {
        if(comments != oldComments)
            album->setCaption(comments);

        if(date != oldDate && date.isValid())
            album->setDate(date);

        if(collection != oldCollection)
            album->setCollection(collection);

        AlbumSettings::instance()->setAlbumCollectionNames(albumCollections);
        resort();

        // Do this last : so that if anything else changed we can
        // successfuly save to the db with the old name

        if(title != oldTitle)
        {
            TQString errMsg;
            if (!d->albumMan->renamePAlbum(album, title, errMsg))
                KMessageBox::error(0, errMsg);
        }

        emit signalAlbumModified();
    }
}

TQDragObject* AlbumFolderView::dragObject()
{
    AlbumFolderViewItem *item = dynamic_cast<AlbumFolderViewItem*>(dragItem());
    if(!item)
        return 0;

    PAlbum *album = item->album();
    if(album->isRoot())
        return 0;

    AlbumDrag *a = new AlbumDrag(album->kurl(), album->id(), this);
    if(!a)
        return 0;
    a->setPixmap(*item->pixmap(0));

    return a;
}

bool AlbumFolderView::acceptDrop(const TQDropEvent *e) const
{
    TQPoint vp = contentsToViewport(e->pos());
    AlbumFolderViewItem *itemDrop = dynamic_cast<AlbumFolderViewItem*>(itemAt(vp));
    AlbumFolderViewItem *itemDrag = dynamic_cast<AlbumFolderViewItem*>(dragItem());

    if(AlbumDrag::canDecode(e))
    {
        switch(AlbumSettings::instance()->getAlbumSortOrder())
        {
            case(AlbumSettings::ByFolder):
            {
                // Allow dragging at the root, to move the album at the root
                if(!itemDrop)
                    return true;

                // Dragging an item on itself makes no sense
                if(itemDrag == itemDrop)
                    return false;

                // Dragging a parent on its child makes no sense
                if(itemDrag && itemDrag->album()->isAncestorOf(itemDrop->album()))
                    return false;

                return true;
            }
            case (AlbumSettings::ByCollection):
            {
                if (!itemDrop)
                    return false;

                // Only allow dragging onto Collection
                if (itemDrop->isGroupItem())
                    return true;

                return false;
            }
            default:
            {
                return false;
            }
        }
    }

    if(itemDrop  && !itemDrop->parent())
    {
        // Do not allow drop images on album root
        return false;
    }

    if (itemDrop && itemDrop->isGroupItem())
    {
        // do not allow drop on a group item
        return false;
    }

    if(ItemDrag::canDecode(e))
    {
        return true;
    }

    if (CameraItemListDrag::canDecode(e))
    {
        return true;
    }

    if(TQUriDrag::canDecode(e))
    {
        return true;
    }

    return false;
}

void AlbumFolderView::contentsDropEvent(TQDropEvent *e)
{
    FolderView::contentsDropEvent(e);

    if(!acceptDrop(e))
        return;

    TQPoint vp                     = contentsToViewport(e->pos());
    AlbumFolderViewItem *itemDrop = dynamic_cast<AlbumFolderViewItem*>(itemAt(vp));

    if(AlbumDrag::canDecode(e))
    {
        AlbumFolderViewItem *itemDrag = dynamic_cast<AlbumFolderViewItem*>(dragItem());
        if(!itemDrag)
            return;

        if (AlbumSettings::instance()->getAlbumSortOrder()
            == AlbumSettings::ByFolder)
        {
            // TODO: Copy?
            KPopupMenu popMenu(this);
            popMenu.insertTitle(SmallIcon("digikam"), i18n("My Albums"));
            popMenu.insertItem(SmallIcon("goto"), i18n("&Move Here"), 10);
            popMenu.insertSeparator(-1);
            popMenu.insertItem(SmallIcon("cancel"), i18n("C&ancel"), 20);
            popMenu.setMouseTracking(true);
            int id = popMenu.exec(TQCursor::pos());

            if(id == 10)
            {
                PAlbum *album = itemDrag->album();
                PAlbum *destAlbum;
                if(!itemDrop)
                {
                    // move dragItem to the root
                    destAlbum = d->albumMan->findPAlbum(0);
                }
                else
                {
                    // move dragItem below dropItem
                    destAlbum = itemDrop->album();
                }
                KIO::Job* job = DIO::move(album->kurl(), destAlbum->kurl());
                connect(job, TQT_SIGNAL(result(KIO::Job*)),
                        this, TQT_SLOT(slotDIOResult(KIO::Job*)));
            }
        }
        else if (AlbumSettings::instance()->getAlbumSortOrder()
                 == AlbumSettings::ByCollection)
        {
            if (!itemDrop)
                return;

            if (itemDrop->isGroupItem())
            {
                PAlbum *album = itemDrag->album();
                if (!album)
                    return;

                album->setCollection(itemDrop->text(0));
                resort();
            }
        }

        return;
    }

    if (ItemDrag::canDecode(e))
    {
        if (!itemDrop)
            return;

        PAlbum *destAlbum = itemDrop->album();

        KURL::List      urls;
        KURL::List      kioURLs;
        TQValueList<int> albumIDs;
        TQValueList<int> imageIDs;

        if (!ItemDrag::decode(e, urls, kioURLs, albumIDs, imageIDs))
            return;

        if (urls.isEmpty() || kioURLs.isEmpty() || albumIDs.isEmpty() || imageIDs.isEmpty())
            return;

        // Check if items dropped come from outside current album.
        // This can be the case with reccursive content album mode.
        KURL::List extUrls;
        ImageInfoList extImgInfList;
        for (TQValueList<int>::iterator it = imageIDs.begin(); it != imageIDs.end(); ++it)
        {
            ImageInfo *info = new ImageInfo(*it);
            if (info->albumID() != destAlbum->id())
            {
                extUrls.append(info->kurlForKIO());
                extImgInfList.append(info);
            }
        }

        int id = 0;
        char keys_return[32];
        XQueryKeymap(x11Display(), keys_return);
        int key_1 = XKeysymToKeycode(x11Display(), 0xFFE3);
        int key_2 = XKeysymToKeycode(x11Display(), 0xFFE4);
        int key_3 = XKeysymToKeycode(x11Display(), 0xFFE1);
        int key_4 = XKeysymToKeycode(x11Display(), 0xFFE2);

        if(extUrls.isEmpty())
        {
            // Setting the dropped image as the album thumbnail
            // If the ctrl key is pressed, when dropping the image, the
            // thumbnail is set without a popup menu
            if (((keys_return[key_1 / 8]) && (1 << (key_1 % 8))) ||
                ((keys_return[key_2 / 8]) && (1 << (key_2 % 8))))
            {
                id = 12;
            }
            else
            {
                KPopupMenu popMenu(this);
                popMenu.insertTitle(SmallIcon("digikam"), i18n("My Albums"));
                popMenu.insertItem(i18n("Set as Album Thumbnail"), 12);
                popMenu.insertSeparator(-1);
                popMenu.insertItem( SmallIcon("cancel"), i18n("C&ancel") );
                popMenu.setMouseTracking(true);
                id = popMenu.exec(TQCursor::pos());
            }

            if(id == 12)
            {
                TQString errMsg;
                d->albumMan->updatePAlbumIcon(destAlbum, imageIDs.first(), errMsg);
            }
            return;
        }

        // If shift key is pressed while dragging, move the drag object without
        // displaying popup menu -> move
        if (((keys_return[key_3 / 8]) && (1 << (key_3 % 8))) ||
            ((keys_return[key_4 / 8]) && (1 << (key_4 % 8))))
        {
            id = 10;
        }
        // If ctrl key is pressed while dragging, copy the drag object without
        // displaying popup menu -> copy
        else if (((keys_return[key_1 / 8]) && (1 << (key_1 % 8))) ||
                 ((keys_return[key_2 / 8]) && (1 << (key_2 % 8))))
        {
            id = 11;
        }
        else
        {
            KPopupMenu popMenu(this);
            popMenu.insertTitle(SmallIcon("digikam"), i18n("My Albums"));
            popMenu.insertItem( SmallIcon("goto"), i18n("&Move Here"), 10 );
            popMenu.insertItem( SmallIcon("editcopy"), i18n("&Copy Here"), 11 );
            if (imageIDs.count() == 1)
                popMenu.insertItem(i18n("Set as Album Thumbnail"), 12);
            popMenu.insertSeparator(-1);
            popMenu.insertItem( SmallIcon("cancel"), i18n("C&ancel") );
            popMenu.setMouseTracking(true);
            id = popMenu.exec(TQCursor::pos());
        }

        switch(id)
        {
            case 10:
            {
                KIO::Job* job = DIO::move(extUrls, destAlbum->kurl());
                connect(job, TQT_SIGNAL(result(KIO::Job*)),
                        this, TQT_SLOT(slotDIOResult(KIO::Job*)));

                // In recurssive album contents mode, we need to force AlbumLister to take a care about
                // moved items. This will have no incidence in normal mode.
                ImageInfo* item;
                for (ImageInfoListIterator it(extImgInfList); (item = it.current()) ; ++it)
                {
                    AlbumLister::instance()->invalidateItem(item);
                }
                break;
            }
            case 11:
            {
                KIO::Job* job = DIO::copy(extUrls, destAlbum->kurl());
                connect(job, TQT_SIGNAL(result(KIO::Job*)),
                        this, TQT_SLOT(slotDIOResult(KIO::Job*)));
                break;
            }
            case 12:
            {
                TQString errMsg;
                d->albumMan->updatePAlbumIcon(destAlbum, imageIDs.first(), errMsg);
            }
            default:
                break;
        }

        return;
    }

    // -- DnD from Camera GUI ------------------------------------------------

    if(CameraItemListDrag::canDecode(e))
    {
        Album *album = dynamic_cast<Album*>(itemDrop->album());
        if (!album) return;

        CameraUI *ui = dynamic_cast<CameraUI*>(e->source());
        if (ui)
        {
            KPopupMenu popMenu(this);
            popMenu.insertTitle(SmallIcon("digikam"), i18n("My Albums"));
            popMenu.insertItem(SmallIcon("down"), i18n("Download from camera"),           10);
            popMenu.insertItem(SmallIcon("down"), i18n("Download && Delete from camera"), 11);
            popMenu.insertSeparator(-1);
            popMenu.insertItem(SmallIcon("cancel"), i18n("&Cancel"));
            popMenu.setMouseTracking(true);
            int id = popMenu.exec(TQCursor::pos());
            switch(id)
            {
                case 10:    // Download from camera
                {
                    ui->slotDownload(true, false, album);
                    break;
                }
                case 11:    // Download and Delete from camera
                {
                    ui->slotDownload(true, true, album);
                    break;
                }
                default:
                    break;
            }
        }
    }

    // -- DnD from an external source ----------------------------------------

    if(TQUriDrag::canDecode(e))
    {
        PAlbum* destAlbum = 0;

        if (itemDrop)
            destAlbum = itemDrop->album();
        else
            destAlbum = d->albumMan->findPAlbum(0);

        // B.K.O #119205: do not handle root album.
        if (destAlbum->isRoot())
            return;

        KURL destURL(destAlbum->kurl());

        KURL::List srcURLs;
        KURLDrag::decode(e, srcURLs);

        char keys_return[32];
        XQueryKeymap(x11Display(), keys_return);
        int id = 0;

        int key_1 = XKeysymToKeycode(x11Display(), 0xFFE3);
        int key_2 = XKeysymToKeycode(x11Display(), 0xFFE4);
        int key_3 = XKeysymToKeycode(x11Display(), 0xFFE1);
        int key_4 = XKeysymToKeycode(x11Display(), 0xFFE2);
        // If shift key is pressed while dropping, move the drag object without
        // displaying popup menu -> move
        if(((keys_return[key_3 / 8]) && (1 << (key_3 % 8))) ||
           ((keys_return[key_4 / 8]) && (1 << (key_4 % 8))))
        {
            id = 10;
        }
        // If ctrl key is pressed while dropping, copy the drag object without
        // displaying popup menu -> copy
        else if(((keys_return[key_1 / 8]) && (1 << (key_1 % 8))) ||
                ((keys_return[key_2 / 8]) && (1 << (key_2 % 8))))
        {
            id = 11;
        }
        else
        {
            KPopupMenu popMenu(this);
            popMenu.insertTitle(SmallIcon("digikam"), i18n("My Albums"));
            popMenu.insertItem( SmallIcon("goto"), i18n("&Move Here"), 10 );
            popMenu.insertItem( SmallIcon("editcopy"), i18n("&Copy Here"), 11 );
            popMenu.insertSeparator(-1);
            popMenu.insertItem( SmallIcon("cancel"), i18n("C&ancel") );
            popMenu.setMouseTracking(true);
            id = popMenu.exec(TQCursor::pos());
        }

        switch(id)
        {
            case 10:
            {
                KIO::Job* job = DIO::move(srcURLs, destAlbum->kurl());
                connect(job, TQT_SIGNAL(result(KIO::Job*)),
                        this, TQT_SLOT(slotDIOResult(KIO::Job*)));
                break;
            }
            case 11:
            {
                KIO::Job* job = DIO::copy(srcURLs, destAlbum->kurl());
                connect(job, TQT_SIGNAL(result(KIO::Job*)),
                        this, TQT_SLOT(slotDIOResult(KIO::Job*)));
                break;
            }
            default:
                break;
        }

        return;
    }
}

void AlbumFolderView::albumImportFolder()
{
    AlbumSettings* settings = AlbumSettings::instance();
    TQDir libraryDir(settings->getAlbumLibraryPath());
    if(!libraryDir.exists())
    {
        KMessageBox::error(0,
                           i18n("The album library has not been set correctly.\n"
                                "Select \"Configure digiKam\" from the Settings "
                                "menu and choose a folder to use for the album "
                                "library."));
        return;
    }

    PAlbum* parent = 0;
    if(selectedItem())
    {
        AlbumFolderViewItem *folderItem = dynamic_cast<AlbumFolderViewItem*>(selectedItem());
        Album *album = folderItem->album();
        if (album && album->type() == Album::PHYSICAL)
        {
            parent = dynamic_cast<PAlbum*>(album);
        }
    }
    if(!parent)
        parent = dynamic_cast<PAlbum*>(d->albumMan->findPAlbum(0));

    TQString libraryPath = parent->folderPath();

    KFileDialog dlg(TQString(), "inode/directory", this, "importFolder", true);
    dlg.setCaption(i18n("Select folders to import"));
    dlg.setMode(KFile::Directory |  KFile::Files);
    if(dlg.exec() != TQDialog::Accepted)
        return;

    KURL::List urls = dlg.selectedURLs();
    if(urls.empty())
        return;

    KIO::Job* job = DIO::copy(urls, parent->kurl());
    connect(job, TQT_SIGNAL(result(KIO::Job *)),
            this, TQT_SLOT(slotDIOResult(KIO::Job *)));
}

void AlbumFolderView::selectItem(int id)
{
    PAlbum* album = d->albumMan->findPAlbum(id);
    if(!album)
        return;

    AlbumFolderViewItem *item = (AlbumFolderViewItem*)album->extraData(this);
    if(item)
    {
        setSelected(item, true);
        ensureItemVisible(item);
    }
}

AlbumFolderViewItem* AlbumFolderView::findParent(PAlbum* album, bool& failed)
{
    if (album->isRoot())
    {
        failed = false;
        return 0;
    }

    switch(AlbumSettings::instance()->getAlbumSortOrder())
    {
        case(AlbumSettings::ByFolder):
        {
            return findParentByFolder(album, failed);
        }
        case(AlbumSettings::ByCollection):
        {
            return findParentByCollection(album, failed);
        }
        case(AlbumSettings::ByDate):
        {
            return findParentByDate(album, failed);
        }
    }

    failed = true;
    return 0;
}

AlbumFolderViewItem* AlbumFolderView::findParentByFolder(PAlbum* album, bool& failed)
{
    AlbumFolderViewItem* parent =
        (AlbumFolderViewItem*) album->parent()->extraData(this);
    if (!parent)
    {
        failed = true;
        return 0;
    }

    failed = false;
    return parent;
}

AlbumFolderViewItem* AlbumFolderView::findParentByCollection(PAlbum* album, bool& failed)
{
    TQStringList collectionList = AlbumSettings::instance()->getAlbumCollectionNames();
    TQString collection = album->collection();

    if (collection.isEmpty() || !collectionList.contains(collection))
        collection = i18n("Uncategorized Albums");

    AlbumFolderViewItem* parent = 0;

    for (TQValueList<AlbumFolderViewItem*>::iterator it=d->groupItems.begin();
         it != d->groupItems.end(); ++it)
    {
        AlbumFolderViewItem* groupItem = *it;
        if (groupItem->text(0) == collection)
        {
            parent = groupItem;
            break;
        }
    }

    // Need to create a new parent item
    if (!parent)
    {
        parent = new AlbumFolderViewItem(firstChild(), collection, 0, 0);
        d->groupItems.append(parent);
    }

    failed = false;
    return parent;
}

AlbumFolderViewItem* AlbumFolderView::findParentByDate(PAlbum* album, bool& failed)
{
    TQDate date = album->date();

    TQString timeString = TQString::number(date.year()) + ", " +
                         KGlobal::locale()->calendar()->monthName(date, false);

    AlbumFolderViewItem* parent = 0;

    for (TQValueList<AlbumFolderViewItem*>::iterator it=d->groupItems.begin();
         it != d->groupItems.end(); ++it)
    {
        AlbumFolderViewItem* groupItem = *it;
        if (groupItem->text(0) == timeString)
        {
            parent = groupItem;
            break;
        }
    }

    // Need to create a new parent item
    if (!parent)
    {
        parent = new AlbumFolderViewItem(firstChild(), timeString,
                                         date.year(), date.month());
        d->groupItems.append(parent);
    }

    failed = false;
    return parent;
}

void AlbumFolderView::resort()
{
    AlbumFolderViewItem* prevSelectedItem = dynamic_cast<AlbumFolderViewItem*>(selectedItem());
    if (prevSelectedItem && prevSelectedItem->isGroupItem())
        prevSelectedItem = 0;

    AlbumList pList(d->albumMan->allPAlbums());
    for (AlbumList::iterator it = pList.begin(); it != pList.end(); ++it)
    {
        PAlbum *album = (PAlbum*)(*it);
        if (!album->isRoot() && album->extraData(this))
        {
            reparentItem(static_cast<AlbumFolderViewItem*>(album->extraData(this)));
        }
    }

    // Clear any groupitems which have been left empty
    clearEmptyGroupItems();

    if (prevSelectedItem)
    {
        ensureItemVisible(prevSelectedItem);
        setSelected(prevSelectedItem, true);
    }
}

void AlbumFolderView::reparentItem(AlbumFolderViewItem* folderItem)
{
    if (!folderItem)
        return;

    PAlbum* album = folderItem->album();
    if (!album || album->isRoot())
        return;

    AlbumFolderViewItem* oldParent = dynamic_cast<AlbumFolderViewItem*>(folderItem->parent());

    bool failed;
    AlbumFolderViewItem* newParent = findParent(album, failed);
    if (failed)
        return;

    if (oldParent == newParent)
        return;

    if (oldParent)
        oldParent->removeItem(folderItem);
    else
        removeItem(folderItem);

    // insert into new parent
    if (newParent)
        newParent->insertItem(folderItem);
    else
        insertItem(folderItem);
}

void AlbumFolderView::clearEmptyGroupItems()
{
    TQValueList<AlbumFolderViewItem*> deleteItems;

    for (TQValueList<AlbumFolderViewItem*>::iterator it=d->groupItems.begin();
         it != d->groupItems.end(); ++it)
    {
        AlbumFolderViewItem* groupItem = *it;

        if (!groupItem->firstChild())
        {
            deleteItems.append(groupItem);
        }
    }

    for (TQValueList<AlbumFolderViewItem*>::iterator it=deleteItems.begin();
         it != deleteItems.end(); ++it)
    {
        d->groupItems.remove(*it);
        delete *it;
    }
}

void AlbumFolderView::refresh()
{
    TQListViewItemIterator it(this);

    while (it.current())
    {
        AlbumFolderViewItem* item = dynamic_cast<AlbumFolderViewItem*>(*it);
        if (item)
            item->refresh();
        ++it;
    }
}

void AlbumFolderView::slotRefresh(const TQMap<int, int>& albumsStatMap)
{
    TQListViewItemIterator it(this);

    while (it.current())
    {
        AlbumFolderViewItem* item = dynamic_cast<AlbumFolderViewItem*>(*it);
        if (item)
        {
            if (item->album())
            {
                int id = item->id();
                TQMap<int, int>::const_iterator it2 = albumsStatMap.find(id);
                if ( it2 != albumsStatMap.end() )
                    item->setCount(it2.data());
            }
        }
        ++it;
    }

    refresh();
}

}  // namespace Digikam
