#include <qfont.h>

#include "browser.h"
#include "menuid.h"
#include "rightclick.h"
#include "uimanager.h"
#include "imageheaders.h"
//#include "mplayer.h"
#include "fileop.h"
#include "ifapp.h"
#include "imageutils.h"
#include "imagedata.h"
#include "../misc/exif.h"

#include <qlayout.h>
#include <qfileinfo.h>
#include <qdir.h>
#include <qpainter.h>
#include <qimage.h>
#include <qapplication.h>
#include <qmessagebox.h>
#include <qcursor.h>
#include <qclipboard.h>
#include <qdragobject.h>
#include <klocale.h>
#include <kmdcodec.h>
#include <kurl.h>
#include <kfileitem.h>
#include <kiconloader.h>
#include <kmimetype.h>
#include <kmimemagic.h>
#include <kmessagebox.h>
#include <kdirwatch.h>
#include <kstandarddirs.h>

#include <unistd.h>
#include <sys/types.h>
#include <fnmatch.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <utime.h>
#include <pwd.h>
#include <grp.h>

#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

#define INVALID_POS QCOORD_MAX

extern GC rubberBandGC;
extern GC viewFillGC;

QRect oldSelectRect;

inline bool intersects(int x, int y, int w, int h, const QRect &r)
{
    return(QMAX(x, r.x()) <= QMIN(x+w-1, r.right()) &&
           QMAX(y, r.y()) <= QMIN(y+h-1, r.bottom()));
}

inline bool contains(int x, int y, int w, int h, int px, int py)
{
    return(px >= x && px <= x+w-1 &&
           py >= y && py <= y+h-1);
}

PixieBrowser::PixieBrowser(UIManager *manager, QWidget *parent,
                           const char *name)
    : QWidget(parent, name, WResizeNoErase | WRepaintNoErase)
{
    mgr = manager;
    itemList = NULL;
    itemCount = 0;
    curIdx = -1;
    curPreviewingIdx = -1;
    inSelection = selectStarted = false;
    dndStarted = dndSent = false;
    stopProcessing = false;
    inGenerateThumbs = false;
    inDirLoad = false;
    invalidDir = false;
    hasNameFilter = false;
    hasInitialLoad = false;
    currentSort = 0;
    oldestFile = newestFile = 0;
    kioCurrentThumb = kioThumbCount = 0;
    thumbJob = NULL;
    imagesOnTop = false;
    sortCatagories = false;
    arrayCount = 0;
    visibleRects = NULL;
    iconDict.setAutoDelete(true);
    catDict.setAutoDelete(true);

    QHBoxLayout *layout = new QHBoxLayout(this);

    setBackgroundMode(QWidget::NoBackground);
    view = new QWidget(this, "PixieViewport", WResizeNoErase | WRepaintNoErase);
    view->setBackgroundMode(QWidget::NoBackground);
    view->installEventFilter(this);
    view->setMouseTracking(true);
    view->setAcceptDrops(true);
    layout->addWidget(view, 1);
    sb = new QScrollBar(Qt::Vertical, this);
    connect(sb, SIGNAL(valueChanged(int)), this,
            SLOT(slotScrollBarChanged(int)));
    layout->addWidget(sb);

    fm = new QFontMetrics(view->fontMetrics());
    textHeight = fm->lineSpacing()*2;

    selectBrush.setColor(Qt::blue);
    selectBrush.setStyle(Dense4Pattern);
    tmpIconPix = new QPixmap;
    curFramePix = NULL;
    curGraphicsPix = NULL;
    curTextPix = NULL;
    curSelTextPix = NULL;
    curPreviewingPix = NULL;

    dirWatch = new KDirWatch;
    connect(dirWatch, SIGNAL(dirty(const QString &)), this,
            SLOT(slotDirChanged(const QString &)));

    resize(600, 450);
    view->setFocusPolicy(StrongFocus);

    tips = new BrowserTip(view, NULL);
}

PixieBrowser::~PixieBrowser()
{
    stopProcessing = true;
    if(thumbJob)
        thumbJob->kill();

    clear();
    if(visibleRects)
        free(visibleRects);

    delete fm;
    delete dirWatch;
    delete tmpIconPix;
    if(curFramePix)
        delete curFramePix;
    if(curGraphicsPix)
        delete curGraphicsPix;
    if(curTextPix)
        delete curTextPix;
    if(curSelTextPix)
        delete curSelTextPix;
    if(curPreviewingPix)
        delete curPreviewingPix;

    delete tips;
}

void PixieBrowser::clear()
{
    if(!itemList || !itemCount)
        return;

    int i;
    clearSelection(false);
    for(i=0; i < itemCount; ++i){
        if(itemList[i].filename)
            free(itemList[i].filename);
        if(itemList[i].formattedText)
            free(itemList[i].formattedText);
        if(itemList[i].tooltip)
            free(itemList[i].tooltip);
        if(itemList[i].thumbnailData)
            free(itemList[i].thumbnailData);
        if(itemList[i].mimetype)
            free(itemList[i].mimetype);
        if(itemList[i].status)
            free(itemList[i].status);
        if(itemList[i].pixmap)
            delete itemList[i].pixmap;
    }
    free(itemList);
    itemList = NULL;
    itemCount = 0;
    curIdx = -1;

    firstVisibleIdx = -1;
    visibleRectCount = 0;
    if(visibleRects){
        free(visibleRects);
        visibleRects = NULL;
    }

    inSelection = false;
    selectStarted = false;
    dndStarted = false;
    dndSent = false;
    sb->setValue(0);
    sb->setEnabled(false);
    view->repaint(false);

}

void PixieBrowser::selectAll()
{
    if(!itemList || !itemCount)
        return;
    selectList.clear();
    int i;
    for(i=0; i < itemCount; ++i){
        itemList[i].selected = true;
        selectList.append(i);
    }
    view->repaint(false);
}

void PixieBrowser::clearSelection(bool redraw)
{
    if(!itemList || !itemCount)
        return;
    // TODO - test if this gets last item!
    QValueList<int>::iterator it;
    bool redrawNeeded = false;
    for(it = selectList.begin(); it != selectList.end(); ++it){
        if((*it) < itemCount){
            itemList[(*it)].selected = false;
            redrawNeeded = true;
        }
    }
    selectList.clear();
    if(redrawNeeded && redraw)
        view->repaint(false);
}

bool PixieBrowser::allocateArray(int c)
{

    clear();
    itemList = (Thumbnail *)malloc(sizeof(Thumbnail)*c);
    if(!itemList){
        itemCount = 0;
        return(false);
    }
    int i;
    for(i=0; i < c; ++i){
        itemList[i].filename = NULL;
        itemList[i].formattedText = NULL;
        itemList[i].thumbnailData = NULL;
        itemList[i].tooltip = NULL;
        itemList[i].mimetype = NULL;
        itemList[i].status = NULL;
        itemList[i].pixmap = NULL;
        itemList[i].thumbnailed = itemList[i].isDir = itemList[i].selected = false;
        itemList[i].textDirty = true;
        itemList[i].isImage = false;
        itemList[i].imageHasThumb = false;
        itemList[i].extensionChecked = false;
        itemList[i].mimetypeChecked = false;
    }
    itemCount = c;
    return(true);
}

QValueList<int>* PixieBrowser::selection()
{
    if(itemList &&itemCount)
        sortSelectionByView();
    return(&selectList);
}

bool PixieBrowser::selectionStringList(QStringList &list)
{
    list.clear();
    if(selectList.count() == 0)
        return(false);

    sortSelectionByView();
    QValueList<int>::iterator it;
    for(it = selectList.begin(); it != selectList.end(); ++it){
        if((*it) < itemCount)
            list.append(absFileStr + "/" + itemList[(*it)].filename);
    }
    return(true);
}

bool PixieBrowser::selectionHasImages()
{
    if(selectList.count() == 0)
        return(false);
    QValueList<int>::iterator it;
    int idx;
    for(it = selectList.begin(); it != selectList.end(); ++it){
        idx = *it;
        if(isImage(&itemList[idx], absFileStr + "/" +
                   itemList[idx].filename, false))
            return(true);
    }
    return(false);
}

bool PixieBrowser::selectionHasFolders()
{
    if(selectList.count() == 0)
        return(false);
    QValueList<int>::iterator it;
    int idx;
    for(it = selectList.begin(); it != selectList.end(); ++it){
        idx = *it;
        if(S_ISDIR(itemList[idx].status->st_mode))
            return(true);
    }
    return(false);
}

void PixieBrowser::sortSelectionByView()
{
    if(selectList.count() == 0)
        return;
    qHeapSort(selectList);
}

int PixieBrowser::count()
{
    return(itemCount);
}

int PixieBrowser::currentIndex()
{
    return(curIdx);
}

Thumbnail* PixieBrowser::currentItem()
{
    if(itemCount && curIdx != -1)
        return(&itemList[curIdx]);
    else
        return(NULL);
}

bool PixieBrowser::setCurrentItem(int idx)
{
    if(itemCount && idx < itemCount){
        clearSelection(true);
        curIdx = idx;
        return(true);
    }
    return(false);
}

Thumbnail* PixieBrowser::allItems()
{
    return(itemList);
}

int PixieBrowser::findItem(const char *filename)
{
    if(!itemCount)
        return(-1);
    int i;
    for(i=0; i < itemCount; ++i){
        if(qstrcmp(itemList[i].filename, filename) == 0)
            return(i);
    }
    return(-1);
}

bool PixieBrowser::eventFilter(QObject *obj, QEvent *ev)
{
    if(obj == view){
        if(ev->type() == QEvent::Paint){
            viewportPaintEvent((QPaintEvent *)ev);
            return(true);
        }
        else if(ev->type() == 6){
            // for some reason QEvent::KeyPress gives compile error. Conflict?
            viewportKeyEvent((QKeyEvent *)ev);
        }
        else if(ev->type() == QEvent::MouseButtonPress){
            viewportMousePressEvent((QMouseEvent *)ev);
        }
        else if(ev->type() == QEvent::MouseButtonRelease){
            viewportMouseReleaseEvent((QMouseEvent *)ev);
        }
        else if(ev->type() == QEvent::MouseButtonDblClick){
            viewportMouseDoubleClickEvent((QMouseEvent *)ev);
        }
        else if(ev->type() == QEvent::MouseMove){
            viewportMouseMoveEvent((QMouseEvent *)ev);
        }
        else if(ev->type() == QEvent::Resize){
            viewportResizeEvent((QResizeEvent *)ev);
        }
        else if(ev->type() == QEvent::Drop){
            viewportDropEvent((QDropEvent *)ev);
        }
        else if(ev->type() == QEvent::DragMove){
            viewportDragMoveEvent((QDragMoveEvent *)ev);
        }
        else if(ev->type() == QEvent::Wheel){
            viewportWheelEvent((QWheelEvent *)ev);
        }
    }
    return(false);
}

void PixieBrowser::viewportWheelEvent(QWheelEvent *ev)
{
    ev->accept();
    if(ev->orientation() == Vertical)
        QApplication::sendEvent(sb, ev);
}

void PixieBrowser::viewportKeyEvent(QKeyEvent *ev)
{
    if(!itemCount){
        qWarning("Got keypress with no items!");
        ev->ignore();
        return;
    }

    if(ev->key() == Qt::Key_Down || ev->key() == Qt::Key_Right){
        int idx;
        if(curIdx < firstVisibleIdx || curIdx > firstVisibleIdx +
           visibleRectCount)
            curIdx = firstVisibleIdx;
        if(ev->key() == Qt::Key_Down)
            idx = curIdx + columns;
        else 
            idx = ++curIdx;;

        if(idx >= itemCount){
            qWarning("Already at bottom of view");
            qApp->beep();
        }
        else{
            int itemY = (idx/columns)*totalItemHeight;
            curIdx = idx;

            if(itemY+totalItemHeight <= sb->value()+view->height())
                view->repaint(false);
            else{
                qWarning("Scrolling down");
                sb->setValue(sb->value()+(itemY-sb->value()));
            }
        }
    }
    else if(ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Left){
        int idx;
        if(curIdx < firstVisibleIdx || curIdx > firstVisibleIdx +
           visibleRectCount)
            curIdx = firstVisibleIdx;
        if(ev->key() == Qt::Key_Up)
            idx = curIdx - columns;
        else
            idx = --curIdx;
        if(idx < 0){
            qWarning("Already at top of view");
            qApp->beep();
        }
        else{
            int itemY = (idx/columns)*totalItemHeight;
            curIdx = idx;
            if(itemY < sb->value())
                sb->setValue(itemY);
            else
                view->repaint(false);
        }

    }
    else if(ev->key() == Qt::Key_PageUp){
        // FIXME: Make entire item visible
        if(sb->value() - sb->pageStep() >= 0)
            sb->setValue(sb->value()-sb->pageStep());
        else
            sb->setValue(0);
        curIdx = firstVisibleIdx;
        view->repaint(false);
    }
    else if(ev->key() == Qt::Key_PageDown){
        if(sb->value() + sb->pageStep() <= sb->maxValue())
            sb->setValue(sb->value()+sb->pageStep());
        else
            sb->setValue(sb->maxValue());
        curIdx = firstVisibleIdx + visibleRectCount-1;
        if(curIdx >= itemCount)
            curIdx = itemCount-1;
        view->repaint(false);
    }
    else if(ev->key() == Qt::Key_Home){
        curIdx = 0;
        if(sb->value() != 0)
            sb->setValue(0);
        else
            view->repaint(false);
    }
    else if(ev->key() == Qt::Key_End){
        curIdx = itemCount-1;
        if(sb->value() != sb->maxValue())
            sb->setValue(sb->maxValue());
        else
            view->repaint(false);
    }
    else if(ev->key() == Qt::Key_Space){
        if(curIdx >= itemCount)
            return;
        if(!isImage(&itemList[curIdx], absFileStr + "/" +
                    itemList[curIdx].filename, false))
            qWarning("Space clicked on non-image file!");
        else{
            clearSelection(false);
            itemList[curIdx].selected = true;
            selectList.append(curIdx);
            view->repaint(true);
            emit clicked(&itemList[curIdx]);
        }
    }
    else if(ev->key() == Qt::Key_Return || ev->key() == Qt::Key_Enter){
        if(curIdx >= itemCount)
            return;
        clearSelection(false);
        itemList[curIdx].selected = true;
        selectList.append(curIdx);
        view->repaint(true);
        emit doubleClicked(&itemList[curIdx]);
    }
    ev->ignore();
}

void PixieBrowser::viewportDragMoveEvent(QDragMoveEvent *ev)
{
    QStringList fileList;
    if(!QUriDrag::decodeLocalFiles(ev, fileList)){
        qWarning("Pixie: Can't decode drop.");
        return;
    }
    if(!fileList.count())
        return;

    QStringList::Iterator it;
    QFileInfo fi;
    bool valid = true;
    int idx = itemAt(ev->pos().x(), ev->pos().y());

    if(idx != -1 && !itemList[idx].isDir){
        ev->ignore(itemRect(idx));
        valid = false;
    }
    else{
        for(it = fileList.begin(); it != fileList.end(); ++it){
            fi.setFile(*it);
            if(idx == -1){
                if(fi.dirPath(true) == absFileStr){
                    ev->ignore();
                    valid = false;
                }
            }
            else{
                if(qstrcmp(QFile::encodeName(fi.fileName()),
                           itemList[idx].filename) == 0){
                    ev->ignore(itemRect(idx));
                    valid = false;
                }
            }
        }
    }
    if(valid){
        if(idx == -1)
            ev->accept();
        else
            ev->accept(itemRect(idx));
    }
}

void PixieBrowser::viewportDropEvent(QDropEvent *ev)
{
    QStringList fileList;
    if(!QUriDrag::decodeLocalFiles(ev, fileList)){
        qWarning("Pixie: Can't decode drop.");
        return;
    }
    if(!fileList.count())
        return;

    // check if valid - shouldn't have to since same check is in dragmove
    QStringList::Iterator it;
    QFileInfo fi;
    bool valid = true;
    int idx = itemAt(ev->pos().x(), ev->pos().y());
    if(idx != -1 && !itemList[idx].isDir)
        valid = false;
    else{
        for(it = fileList.begin(); it != fileList.end(); ++it){
            fi.setFile(*it);
            if(idx == -1){
                if(fi.dirPath(true) == absFileStr)
                    valid = false;
            }
            else{
                if(qstrcmp(QFile::encodeName(fi.fileName()),
                           itemList[idx].filename) == 0)
                    valid = false;
            }
        }
    }
    if(!valid)
        return;

    int op;
    QPopupMenu opPopup;
    opPopup.insertItem(i18n("&Copy Here"), 1);
    opPopup.insertItem(i18n("&Move Here"), 2);
    opPopup.insertItem(i18n("&Link Here"), 3);
    op = opPopup.exec(view->mapToGlobal(ev->pos()));
    switch(op){
    case 1:
        ev->setAction(QDropEvent::Copy);
        break;
    case 2:
        ev->setAction(QDropEvent::Move);
        break;
    case 3:
        ev->setAction(QDropEvent::Link);
        break;
    default:
        return;
    }
    QString destStr(idx == -1 ? absFileStr : absFileStr+ "/" +
                    itemList[idx].filename);
    KIFFileTransfer::transferFiles(fileList, destStr, ev->action());
}

void PixieBrowser::viewportResizeEvent(QResizeEvent *ev)
{
    recalcColumns(ev->size().width(), ev->size().height());
    QWidget::resizeEvent(ev);
}

void PixieBrowser::recalcRects()
{
    // I originally managed visible rects in a QValueList but decided a
    // array would be faster than iterating over a list all the time.
    if(itemCount == 0){
        firstVisibleIdx = -1;
        visibleRectCount = 0;
        if(visibleRects){
            free(visibleRects);
            visibleRects = NULL;
        }
        return;
    }
    int start = sb->value()/totalItemHeight;
    int offset = sb->value()-(start*totalItemHeight);
    int idx = start*columns;
    int tmp, x, y, i, n=0;

    tmp = ((int)ceil(((float)(view->height() + offset))/totalItemHeight)) *
        columns;
    if(idx + tmp >= itemCount)
        tmp = itemCount-idx;
    firstVisibleIdx = idx;

    if(!visibleRects)
        visibleRects = (PixieRect *)malloc(sizeof(PixieRect)*tmp);
    else if(tmp > visibleRectCount || visibleRectCount-tmp > 64){
            // to avoid memory fragmentation only reallocate if it's worth it
            free(visibleRects);
            visibleRects = (PixieRect *)malloc(sizeof(PixieRect)*tmp);
    }
    visibleRectCount = tmp;

    for(y=0-offset; y < view->height() && idx < itemCount; y+= totalItemHeight){
        for(x=i=0; x < view->width() && idx < itemCount && i < columns;
            x+=totalItemWidth, ++i){
            visibleRects[n].x = x+4;
            visibleRects[n].y = y+4;
            visibleRects[n].w = iSize+2;
            visibleRects[n].h = iSize+2+textHeight;
            ++n;
            ++idx;
        }
    }
}

bool PixieBrowser::viewToIconRect(const QRect &viewRect, QRect &destRect)
{
    destRect = viewRect;
    if(!visibleRects)
        return(false);

    int i;
    for(i=0; i < visibleRectCount; ++i){
        if(intersects(visibleRects[i].x, visibleRects[i].y,
                      visibleRects[i].w, visibleRects[i].h, viewRect))
            destRect = destRect.unite(QRect(visibleRects[i].x,
                                            visibleRects[i].y,
                                            visibleRects[i].w,
                                            visibleRects[i].h));
    }
    return(destRect.isValid());
}

QRect PixieBrowser::itemRect(int item)
{
    QRect r;
    if(!visibleRects)
        return(r); // invalid rect
    int i, idx = firstVisibleIdx;
    for(i=0; i < visibleRectCount; ++i){
        if(idx == item)
            return(QRect(visibleRects[i].x, visibleRects[i].y,
                         visibleRects[i].w, visibleRects[i].h));
        ++idx;
    }
    return(r);
}

int PixieBrowser::itemAt(int x, int y)
{
    if(!visibleRects)
        return(-1);
    int i, idx = firstVisibleIdx;
    for(i=0; i < visibleRectCount; ++i){
        if(contains(visibleRects[i].x, visibleRects[i].y,
                    visibleRects[i].w, visibleRects[i].h, x, y))
            return(idx);
        ++idx;
    }
    return(-1);
}

bool PixieBrowser::isItemVisible(int i)
{
    return(visibleRects && i >= firstVisibleIdx &&
           i <= firstVisibleIdx+visibleRectCount);
}

void PixieBrowser::viewportPaintEvent(QPaintEvent *ev)
{
    QPainter p;
    if(!hasInitialLoad){
        p.begin(view);
        p.fillRect(ev->rect(), Qt::white);
        p.end();
        return;
    }
    if(inDirLoad){
        p.begin(view);
        QFont fnt(p.font());
        p.setPen(Qt::black);
        fnt.setBold(true);
        p.setFont(fnt);
        p.fillRect(ev->rect(), Qt::white);
        if(invalidDir)
            p.drawText(20, 20, i18n("Invalid folder."));
        else if(inDirLoad)
            p.drawText(20, 20, i18n("Loading folder..."));
        p.end();
        return;
    }
    else if(!count()){
        p.begin(view);
        QFont fnt(p.font());
        fnt.setBold(true);
        p.setFont(fnt);
        p.fillRect(ev->rect(), Qt::white);
        p.setPen(Qt::black);
        p.drawText(20, 20, i18n("Empty folder."));
        p.end();
        return;
    }

    int i;
    int idx = firstVisibleIdx;
    QRegion bgRegion(ev->rect());
    // do background fill before the icons - uses an extra
    // loop but looks much nicer if the thumbs take a sec to load
    for(i=0; i < visibleRectCount; ++i){
        if(intersects(visibleRects[i].x, visibleRects[i].y,
                      visibleRects[i].w, visibleRects[i].h, ev->rect()))
            bgRegion -= QRect(visibleRects[i].x, visibleRects[i].y,
                              visibleRects[i].w, visibleRects[i].h);
    }
    QMemArray<QRect>rects = bgRegion.rects();
    XRectangle *xRects = (XRectangle *)malloc(sizeof(XRectangle)*
                                              rects.size());
    for(i=0; i < (int)rects.size(); ++i){
        xRects[i].x = rects[i].x();
        xRects[i].y = rects[i].y();
        xRects[i].width = rects[i].width();
        xRects[i].height = rects[i].height();
    }
    // Use Xlib because QPainter doesn't allow you to specify multiple
    // rects in fillRect(). Xlib lets you do it all in one call :)
    XFillRectangles(x11Display(), view->winId(), viewFillGC, xRects,
                    rects.size());
    free(xRects);

    // now do the actual painting
    if(tmpIconPix->width() != iSize+2 || tmpIconPix->height() != iSize+2+textHeight)
        tmpIconPix->resize(iSize+2, iSize+2+textHeight);
    p.begin(tmpIconPix);
    for(i=0; i < visibleRectCount; ++i){
        if(intersects(visibleRects[i].x, visibleRects[i].y,
                      visibleRects[i].w, visibleRects[i].h, ev->rect())){
            paintItem(&p, idx, visibleRects[i].x, visibleRects[i].y);
            bitBlt(view, visibleRects[i].x, visibleRects[i].y, tmpIconPix,
                   0, 0, iSize+2, iSize+2+textHeight, Qt::CopyROP, true);
        }
        ++idx;
    }
    p.end();
}

void PixieBrowser::paintItem(QPainter *p, int idx, int x, int y)
{
    Thumbnail *i =  itemList+idx;
    bool isBeingPreviewed = /*inGenerateThumbs && curPreviewingIdx == idx*/ 0;

    if(!i->thumbnailed)
        updateThumbnail(idx);

    if(i->isImage && !i->imageHasThumb && !isBeingPreviewed)
        bitBlt(tmpIconPix, 0, 0, curGraphicsPix, 0, 0, iSize+2, iSize+2,
               Qt::CopyROP, true);
    else
        bitBlt(tmpIconPix, 0, 0, curFramePix, 0, 0, iSize+2, iSize+2,
               Qt::CopyROP, true);

    if(isBeingPreviewed){
        bitBlt(tmpIconPix, 1, 1, curPreviewingPix, 0, 0,
               iSize, iSize, Qt::CopyROP, false);
    }
    else if(i->pixmap && (!i->isImage || i->imageHasThumb)){
        int dx = ((iSize+2)-i->pixmap->width())/2;
        int dy = ((iSize+2)-i->pixmap->height())/2;
        bitBlt(tmpIconPix,  dx, dy, i->pixmap, 0, 0,
               i->pixmap->width(), i->pixmap->height(), Qt::CopyROP, false);
    }

    bool selected = i->selected;
    if(inSelection && selectStarted){
        QRect selRect(selectStartPos, currentSelPos);
        selRect = selRect.normalize(); 
        QRect itemRect(x, y+sb->value(), iSize+2, iSize+2+textHeight);
        if(selRect.intersects(itemRect))
            selected = true;
        else
            selected = i->selected;
    }

    // text area box
    QPixmap *pix = selected ? curSelTextPix : curTextPix;
    bitBlt(tmpIconPix, 0, iSize+2, pix, 0, 0, iSize+2, textHeight,
           Qt::CopyROP, true);

    if(i->textDirty)
        calcTextWrapping(i);

    if(selected)
        p->fillRect(0, 0, iSize+2, iSize, selectBrush);

    if(idx == curIdx){
        p->setPen(QColor(0, 0, 64));
        p->drawRect(0, 0, tmpIconPix->width(), tmpIconPix->height());
        p->setPen(QPen(QColor(200, 200, 255), 1, DotLine));
        p->drawRect(0, 0, tmpIconPix->width(), tmpIconPix->height());
    }
    p->setPen(Qt::black);
    p->drawText(0, iSize+2, iSize+2, textHeight,
                Qt::AlignHCenter | AlignTop, i->formattedText ?
                i->formattedText : i->filename);
}

bool PixieBrowser::paintThumbnail(int i, QPainter *painter)
{
    if(!isItemVisible(i))
        return(false);
    int idx = i-firstVisibleIdx;
    if(painter)
        paintItem(painter, i, visibleRects[idx].x, visibleRects[idx].y);
    else{
        if(tmpIconPix->width() != iSize+2 || tmpIconPix->height() != iSize+2+textHeight)
            tmpIconPix->resize(iSize+2, iSize+2+textHeight);
        QPainter p;
        p.begin(tmpIconPix);
        paintItem(&p, i, visibleRects[idx].x, visibleRects[idx].y);
        p.end();
    }
    bitBlt(view, visibleRects[idx].x, visibleRects[idx].y, tmpIconPix,
           0, 0, iSize+2, iSize+2+textHeight, Qt::CopyROP, true);
    return(true);
}

void PixieBrowser::resetFrames()
{
    if(curFramePix && curFramePix->width() == iSize + 2 &&
       curGraphicsPix && curGraphicsPix->width() == iSize + 2)
        return;
    qWarning("Creating icon frames");
    iconDict.clear();

    curFrameImage = uic_findImage("imagetile.png").smoothScale(iSize+2,
                                                               iSize+2);
    curGraphicsImage = uic_findImage("imageicon.png").smoothScale(iSize+2,
                                                                  iSize+2);
    if(!curFramePix)
        curFramePix = new QPixmap(curFrameImage);
    else
        curFramePix->convertFromImage(curFrameImage);

    if(!curGraphicsPix)
        curGraphicsPix = new QPixmap(curGraphicsImage);
    else
        curGraphicsPix->convertFromImage(curGraphicsImage);
    if(!curTextPix)
        curTextPix = new QPixmap;
    if(!curSelTextPix)
        curSelTextPix = new QPixmap;
    curTextPix->convertFromImage(uic_findImage("textbox.png").
                                 smoothScale(iSize+2, textHeight));
    curSelTextPix->convertFromImage(uic_findImage("textbox-select.png").
                                    smoothScale(iSize+2, textHeight));
    int tw = fm->width(i18n("Previewing..."))+4;
    if(tw < iSize)
        tw = iSize;

    /*
    if(curPreviewingPix)
        curPreviewingPix->resize(tw, tw);
    else
        curPreviewingPix = new QPixmap(tw, tw);
    QPainter p(curPreviewingPix);
    p.fillRect(0, 0, tw, tw, QColor(191, 227, 255));
    p.setPen(QColor(0, 95, 168));
    p.drawRect(0, 0, tw, tw);
    p.setPen(Qt::black);
    p.drawText(2, 2, tw-4, tw-4, Qt::AlignCenter, i18n("Previewing..."));
    p.end();
    if(tw > iSize){
        QImage img(curPreviewingPix->convertToImage());
        img = img.smoothScale(iSize, iSize);
        curPreviewingPix->convertFromImage(img);
    }*/
}

void PixieBrowser::viewportMousePressEvent(QMouseEvent *ev)
{
    if(!itemCount)
        return;
    int idx = itemAt(ev->x(), ev->y());
    bool ctrlDown = ev->state() & ControlButton;
    bool shiftDown = ev->state() & ShiftButton;
    bool hasOldSelection = selectList.count() != 0;
    if(ev->button() == LeftButton){
        // handle selection
        if(idx != -1){
            if(shiftDown){
                // if shift is down select all items between this one and
                // the previous selected item. First try searching forward
                // and only if no selection is found search backward.
                int i;
                bool hasSelection = false;

                if(!itemList[idx].selected){
                    itemList[idx].selected = true;
                    selectList.append(idx);
                }
                // first search forward
                for(i=idx+1; i < itemCount; ++i){
                    if(itemList[i].selected){
                        hasSelection = true;
                        break;
                    }
                }
                if(hasSelection){
                    for(i=idx+1; i < itemCount && !itemList[i].selected; ++i){
                            itemList[i].selected = true;
                            selectList.append(i);
                    }
                }
                else{
                    for(i=idx-1; i >= 0; --i){
                        if(itemList[i].selected){
                            hasSelection = true;
                            break;
                        }
                    }
                    if(hasSelection){
                        for(i=idx-1; i >= 0  && !itemList[i].selected; --i){
                            itemList[i].selected = true;
                            selectList.append(i);
                        }
                    }
                    else
                        qWarning("Shift key down but no previous selection!");
                }
                view->repaint(false);
                return;
            }
            else if(itemList[idx].selected){
                // if we are on a button and it is already selected start a
                // potential dnd, else add it to selection and emit clicked
                if(!ctrlDown){
                    dndStarted = true;
                    dndSent = false;
                    selectStartPos = QPoint(ev->x(), ev->y()+sb->value());
                }
            }
            else{
                if(hasOldSelection && !ctrlDown)
                    clearSelection(false);
                dndStarted = false;
                itemList[idx].selected = true;
                selectList.append(idx);
            }
            curIdx = idx;
            emit clicked(&itemList[idx]);
            inSelection = false;
            selectStarted = false;
            // TODO - only repaint full view if hasOldSelection && !ctrlbtn
            view->repaint(false);
        }
        else{
            // setup rubberbanding
            if(hasOldSelection && !ctrlDown){
                clearSelection(false);
                view->repaint(false);
            }
            selectStartPos = QPoint(ev->x(), ev->y()+sb->value());
            currentSelPos = QPoint(INVALID_POS, INVALID_POS);
            inSelection = true;
            selectStarted = false;
            dndStarted = false;
            oldSelectRect = QRect();
        }

    }
    else if(ev->button() == RightButton){
        //emit rightButtonClicked(idx == -1 ? NULL : &itemList[idx],
        //                        ev->globalPos());
        if(hasOldSelection && !ctrlDown && !itemList[idx].selected)
            clearSelection(false);
        if(idx != -1){
            if(!itemList[idx].selected){
                itemList[idx].selected = true;
                selectList.append(idx);
                curIdx = idx;
            }
            view->repaint(false);
            /*
            iconRightClickMenu(absFileStr, mgr, this, &itemList[idx],
            ev->globalPos());*/
            EditMenu *mnu = new EditMenu(mgr);
            mnu->execItem(&itemList[idx], ev->globalPos());
            delete mnu;
        }
        else
            viewRightClickMenu(absFileStr, mgr, this, ev->globalPos());
    }
}

void PixieBrowser::viewportMouseReleaseEvent(QMouseEvent *ev)
{
    if(!itemCount)
        return;
    if(dndStarted){
        inSelection = selectStarted = false;
        dndStarted = dndSent = false;
    }
    else if(inSelection){
        inSelection = false;
        disconnect(&scrollOutsideViewTimer, SIGNAL(timeout()),
               this, SLOT(slotScrollOutsideView()));
        scrollOutsideViewTimer.stop();
        if(selectStarted){
            selectStarted = false;
            QPoint pos(ev->x(), ev->y());
            if(pos.x() < 0)
                pos.setX(0);
            if(pos.x() > view->width())
                pos.setX(view->width());
            if(pos.y() < 0)
                pos.setY(0);
            if(pos.y() > view->height())
                pos.setY(view->height());
            pos.setY(pos.y()+sb->value());
            QRect selRect(selectStartPos, pos);
            selRect = selRect.normalize();

            // Pain in the butt because we need to account for spacing
            // between items :P
            int start_row = (selRect.y()/totalItemHeight)*totalItemHeight;
            int rows = (int)ceil(((float)selRect.height())/totalItemHeight);
            int curr_row;
            int x, y, idx, i;
            QRect r;
            idx = (selRect.y()/totalItemHeight)*columns;
            for(y=start_row, curr_row=0; curr_row <= rows;
                y+=totalItemHeight, ++curr_row){
                for(x=i=0; x < view->width() && idx < itemCount && i < columns;
                    x+=totalItemWidth, ++i, ++idx){
                    r.setRect(x+4, y+4, iSize+2, iSize+2+textHeight);
                    if(r.intersects(selRect)){
                        itemList[idx].selected = true;
                        if(selectList.findIndex(idx) == -1)
                            selectList.append(idx);
                    }
                }
            }

        }
        if(selectList.count() != 0)
            sortSelectionByView();
        view->repaint(false);
    }
}

void PixieBrowser::viewportMouseDoubleClickEvent(QMouseEvent *ev)
{
    if(!itemCount)
        return;
    inSelection = selectStarted = false;
    dndStarted = dndSent = false;
    int idx = itemAt(ev->x(), ev->y());
    if(idx != -1){
        curIdx = idx;
        emit doubleClicked(&itemList[idx]);
    }
}

void PixieBrowser::viewportMouseMoveEvent(QMouseEvent *ev)
{
    if(!itemCount)
        return;
    if(dndStarted){
        if(!dndSent){
            if((selectStartPos-QPoint(ev->pos().x(), ev->pos().y()-sb->value())).
               manhattanLength() > QApplication::startDragDistance()){
                dndSent = true;
                QStrList uriList;
                QValueList<int>::iterator it;
                sortSelectionByView();
                int idx;
                Thumbnail *item = 0;
                for(it = selectList.begin(); it != selectList.end(); ++it){
                    idx = (*it);
                    if(idx < itemCount){
                        item = &itemList[idx];
                        uriList.append(QUriDrag::localFileToUri(currentDir+"/"+
                                                                itemList[idx].filename));
                    }
                }
                QUriDrag *uri = new QUriDrag(uriList, this);
                if(uriList.count() == 1 && item->pixmap){
                    if(item->isImage)
                        uri->setPixmap(*item->pixmap);
                    else{
                        // we can't just use the mimetype pixmap cached in
                        // the thumbnail because it's alphablended w/ the
                        // tile :P
                        KURL url(absFileStr+"/"+item->filename);
                        uri->setPixmap(KMimeType::pixmapForURL(url,
                                                               item->status->st_mode,
                                                               KIcon::Desktop,
                                                               64));
                    }
                }
                else
                    uri->setPixmap(KGlobal::iconLoader()->
                                   loadIcon("kmultiple", KIcon::NoGroup,
                                            KIcon::SizeLarge));
                uri->drag();
            }
        }
    }
    else if(inSelection){
        if(!selectStarted){
            if((selectStartPos-QPoint(ev->pos().x(), ev->pos().y()-sb->value())).
               manhattanLength() > QApplication::startDragDistance()){
                selectStarted = true;
            }
        }
        if(selectStarted){
            QPoint pos(ev->x(), ev->y());
            if(ev->x() > view->width()) // only scrolls vertically
                pos.setX(view->width());
            else if(ev->x() < 0)
                pos.setX(0);
            if(ev->y() > view->height()){
                if(!scrollOutsideViewTimer.isActive())
                    slotScrollOutsideView();
                return;
            }
            else if(ev->y() < 0){
                if(sb->value() == 0)
                    pos.setY(0);
                else{
                    if(!scrollOutsideViewTimer.isActive())
                        slotScrollOutsideView();
                }
                return;
            }
            disconnect(&scrollOutsideViewTimer, SIGNAL(timeout()),
                       this, SLOT(slotScrollOutsideView()));
            scrollOutsideViewTimer.stop();
            Display *dpy = QPaintDevice::x11Display();
            QRect r1, r2;
            if(currentSelPos.x() != INVALID_POS){
                r1.setCoords(selectStartPos.x(), selectStartPos.y()-sb->value(),
                             currentSelPos.x(), currentSelPos.y()-sb->value());
                r1 = r1.normalize();
                // we shouldn't have to do this, must have a logic error...
                XDrawRectangle(dpy, view->winId(), rubberBandGC,
                               r1.x(), r1.y(), r1.width(), r1.height());
            }
            r2.setCoords(selectStartPos.x(), selectStartPos.y()-sb->value(),
                        pos.x(), pos.y());

            r2 = r2.normalize();

            currentSelPos.setX(pos.x());
            currentSelPos.setY(pos.y()+sb->value());

            QRect destRect;
            if(r1.isValid()){
                r1 = r1.unite(r2);
                r1 = r1.intersect(view->rect());
                viewToIconRect(r1, destRect);
                QPaintEvent pe(destRect, false);
                viewportPaintEvent(&pe);
            }
            else{
                r2 = r2.intersect(view->rect());
                viewToIconRect(r2, destRect);
                QPaintEvent pe(destRect, false);
                viewportPaintEvent(&pe);
            }
            XDrawRectangle(dpy, view->winId(), rubberBandGC,
                           r2.x(), r2.y(), r2.width(), r2.height());
        }
    }
}

void PixieBrowser::slotScrollOutsideView()
{
    disconnect(&scrollOutsideViewTimer, SIGNAL(timeout()),
               this, SLOT(slotScrollOutsideView()));
    scrollOutsideViewTimer.stop();
    Window root, child;
    int root_x, root_y, win_x, win_y;
    unsigned int mousestate;
    // Mouse pointer may have been released while events were blocked...
    XQueryPointer(qt_xdisplay(), qt_xrootwin(), &root, &child,
                  &root_x, &root_y, &win_x, &win_y, &mousestate);
    if(!(mousestate & Button1Mask))
        return;

    int step;
    Display *dpy = QPaintDevice::x11Display();
    QPoint pos(QCursor::pos());
    pos = view->mapFromGlobal(pos);
    if(pos.y() < 0 && sb->value() != 0){
        step = sb->value()-sb->lineStep();
        if(step < 0)
            step = 0;
        sb->setValue(step);
        pos.setY(0);
    }
    else if(pos.y() > view->height()){
        step = sb->value()+sb->lineStep();
        if(step > sb->maxValue())
            step = sb->maxValue();
        sb->setValue(step);
        pos.setY(view->height());
    }
    else
        view->repaint(false);

    if(pos.x() < 0)
        pos.setX(0);
    else if(pos.x() > view->width())
        pos.setX(view->width());

    QRect r;
    r.setCoords(selectStartPos.x(), selectStartPos.y()-sb->value(),
                pos.x(), pos.y());
    r = r.normalize();
    XDrawRectangle(dpy, view->winId(), rubberBandGC,
                   r.x(), r.y(), r.width(), r.height());
    currentSelPos.setX(pos.x());
    currentSelPos.setY(pos.y()+sb->value());

    connect(&scrollOutsideViewTimer, SIGNAL(timeout()), this,
            SLOT(slotScrollOutsideView()));
    scrollOutsideViewTimer.start(100);
}

void PixieBrowser::slotScrollBarChanged(int)
{
    recalcRects();
    view->repaint(false);
}

void PixieBrowser::slotDirChanged(const QString &path)
{
    qWarning("In slotDirChanged");
    emit updateMe();
    emit dirChanged(path);
}

bool PixieBrowser::updateThumbnail(int idx)
{
    Thumbnail *i =  itemList+idx;
    if(i->thumbnailed)
        return(true);
    i->thumbnailed = true;
    if(i->textDirty)
        calcTextWrapping(i);

    if(i->pixmap)
        return(true);

    resetFrames();
    QFileInfo fi(currentDir+"/"+i->filename);
    bool hasPixieThumb = false;
    bool hasKonqThumb = false;

    if(!fi.isDir()){
        hasPixieThumb = QFile::exists(pixieThumbPath + fi.fileName())&&
            QFileInfo(pixieThumbPath+fi.fileName()).lastModified() >= fi.lastModified();
        hasKonqThumb = !hasPixieThumb && hasKonqThumbnails &&
            QFile::exists(konqThumbPath + fi.fileName()) &&
            QFileInfo(konqThumbPath + fi.fileName()).lastModified() >=
            fi.lastModified();
    }
    QString fn, textStr;
    QImage img;
    if(hasPixieThumb || hasKonqThumb){
        if(mgr->usePixieThumbs()){
            fn = hasPixieThumb ?
                QFile::encodeName(pixieThumbPath + fi.fileName()) :
                QFile::encodeName(konqThumbPath + fi.fileName());
        }
        else{
            fn = hasKonqThumb ?
                QFile::encodeName(konqThumbPath + fi.fileName()) :
                QFile::encodeName(pixieThumbPath + fi.fileName());
        }
        if(img.load(fn, "PNG")){
            if(i->pixmap)
                delete i->pixmap;
            i->pixmap = new QPixmap(img.width(), img.height());
            convertImageToPixmapBlend(img, curFrameImage,
                                      ((iSize+2)-img.width())/2,
                                      ((iSize+2)-img.height())/2,
                                      *i->pixmap);
            i->imageHasThumb = true;
            textStr = img.text("PixiePlus Size");
            if(!textStr.isEmpty()){
                if(i->thumbnailData)
                    free(i->thumbnailData);
                i->thumbnailData = (char *)malloc(strlen(textStr.ascii())+1);
                qstrcpy(i->thumbnailData, textStr.ascii());
            }
            return(true);
        }
        else
            qWarning("Invalid image for thumbnail %s!", i->filename);
    }
    // okay, no thumbnail - determine the icon
    if(!i->extensionChecked){
        i->isImage = isImageType(i->filename);
        i->extensionChecked = true;
    }
    if(i->isImage){
        i->pixmap = new QPixmap(iSize >= 64 ?
                                DesktopIcon("image", 64) :
                                DesktopIcon("image", 48));
    }
    else
        processThumbnailMimeType(i, fi.absFilePath(), iSize);
    return(true);
}

QString PixieBrowser::calcKonqThumbPath(const QString &path, int iconSize)
{
    QString sizeStr;
    if(iconSize == 48)
        sizeStr = "small";
    else if(iconSize == 64)
        sizeStr = "med";
    else if(iconSize == 90)
        sizeStr = "large";
    else if(iconSize == 112)
        sizeStr = "xxl";

    QString url("file:");
    url += QDir::cleanDirPath(path);
    KMD5 md5(QFile::encodeName(url));
    QCString hash = md5.hexDigest();
    QString thumbPath(QDir::homeDirPath() + "/.kde/share/thumbnails/" +
                      QString::fromLatin1(hash.data(), 4) + "/" +
                      QString::fromLatin1(hash.data()+4, 4) + "/" +
                      QString::fromLatin1(hash.data()+8) + "/" +
                      sizeStr + "/");
    return(thumbPath);
}

void PixieBrowser::calcTextWrapping(Thumbnail *item)
{
    if(!item->textDirty)
        return;
    item->textDirty = false;
    int w = iSize;
    QString str(item->filename);
    if(fm->width(str) <= w)
        return;

    QString tmpStr;
    int i = 0;
    while(fm->width(tmpStr + str[i]) < w)
        tmpStr += str[i++];

    QString buffer;
    if(fm->width(str)-fm->width(tmpStr) < w){
        while(fm->width(buffer + str[i]) < w && i < ((int)str.length()))
            buffer += str[i++];
    }
    else{
        while(fm->width(buffer + str[i]) < w && i < ((int)str.length()))
            buffer += str[i++];
        buffer.remove(0, 3);
        buffer += "...";
    }
    tmpStr = tmpStr + "\n" + buffer;
    item->formattedText = (char *)malloc(strlen(tmpStr.latin1())+1);
    strcpy(item->formattedText, tmpStr.latin1());
}

void PixieBrowser::calcTooltip(Thumbnail *item)
{
    if(item->tooltip)
        return;
    QString tipStr;
    QString fn(item->filename);
    bool isDir = S_ISDIR(item->status->st_mode);

    tipStr += "<QT><B>"+ fn + "</B><BR>";

    // size
    QString sizeStr;
    if(!isDir){
        float size = item->status->st_size;
        if(size >= 1024){
            size /= 1024.0;
            if(size >= 1024){
                size /= 1024.0;
                sizeStr += i18n("File size: %1Mb<BR>").arg(size, 0, 'f', 2);
            }
            else{
                sizeStr += i18n("File size: %1Kb<BR>").arg(size, 0, 'f', 2);

            }
        }
        else{
                sizeStr += i18n("File size: %1b<BR>").arg((int)size);
        }
        tipStr += sizeStr;
    }

    // owner and group
    struct passwd *pentry = getpwuid(item->status->st_uid);
    if(pentry)
        tipStr += i18n("Owner: %1").arg(pentry->pw_name) + ", ";
    struct group *gentry = getgrgid(item->status->st_gid);
    if(gentry)
        tipStr += i18n("Group: %1").arg(gentry->gr_name) + "<BR>";

    QString pathStr(absFileStr + '/' + item->filename);
    tipStr += i18n("Your rights: ");
    bool anyRights = false;
    if(access(QFile::encodeName(pathStr), R_OK) == 0){
        tipStr += i18n("Read ");
        anyRights = true;
    }
    if(access(QFile::encodeName(pathStr), W_OK) == 0){
        tipStr += i18n("Write ");
        anyRights = true;
    }
    if(access(QFile::encodeName(pathStr), X_OK) == 0){
        tipStr += i18n("Execute ");
        anyRights = true;
    }
    if(!anyRights)
        tipStr += i18n("None");
    tipStr += "<BR>";

    // access and modified time
    QDateTime t;
    t.setTime_t(item->status->st_atime);
    tipStr += i18n("Last accessed: %1<BR>").arg(t.toString()) + "<BR>";
    t.setTime_t(item->status->st_mtime);
    tipStr += i18n("Last modified: %1<BR>").arg(t.toString()) + "<BR>";

    // get image specific information
    QString imageStr, cameraStr, commentStr;
    appendTooltipData(QFile::encodeName(pathStr), imageStr,
                      cameraStr, commentStr, true);

    // Append them all together
    if(!imageStr.isEmpty()){
        tipStr += "<B>";
        tipStr += i18n("Image info");
        tipStr += "</B><BR>";
        tipStr += imageStr;
    }
    else if(item->thumbnailData){
        tipStr += "<B>";
        tipStr += i18n("Image info");
        tipStr += "</B><BR>";
        tipStr += i18n("Image size: ");
        tipStr += item->thumbnailData;
        tipStr += "<BR>";
    }

    // Display catagories after image info
    // catagories
    tipStr += /*"<HR><B>"*/ "<B>" + i18n("Categories") + "</B><BR>";
    QStringList itemList(itemCatagories(item));
    if(itemList.count() == 0)
        tipStr += i18n("None");
    else{
        QStringList::Iterator it;
        for(it = itemList.begin(); it != itemList.end(); ++it){
            tipStr += "\n\t";
            tipStr += (*it);
        }
    }
    tipStr += "<BR>";


    if(!cameraStr.isEmpty()){
        tipStr += "<B>";
        tipStr += i18n("Camera/Scanner info");
        tipStr += "</B><BR>";
        tipStr += cameraStr;
    }
    if(!commentStr.isEmpty()){
        tipStr += "<B>";
        tipStr += i18n("Comment");
        tipStr += "</B><BR>";
        tipStr += commentStr;
    }

    tipStr += "</QT>";
    item->tooltip = (char *)malloc(tipStr.length()+1);
    strcpy(item->tooltip, tipStr.ascii());
}

int sortLongIntegers(const void *long1, const void *long2)
{
    return((*((long*)long1))-(*((long*)long2)));
}


void PixieBrowser::loadPath(const QString &newPathStr, int iconSize,
                            int sortType, int showOnlyCatagory,
                            bool catagoriesFirst, bool iconOnly,
                            bool imagesFirst, const QString &nameFilter)
{
    if(newPathStr != currentDir){
        if(!currentDir.isNull())
            dirWatch->removeDir(currentDir);
        dirWatch->addDir(newPathStr);
    }
    dirWatch->stopDirScan(newPathStr);
    qWarning("Show only catagory: %d", showOnlyCatagory);

    if(thumbJob){
        qWarning("Killing old thumbnail generator");
        thumbJob->kill();
        thumbJob = NULL;
        stopProcessing = true;
        emit enableStopButton(false);
        inGenerateThumbs = false;
    }
    int reloadPosition = -1;
    if(sb->value() != 0 && currentDir == newPathStr)
        reloadPosition = sb->value();

    currentDir = newPathStr;
    currentSort = sortType;
    QFileInfo fi(newPathStr);
    absFileStr = fi.absFilePath();
    iSize = iconSize;
    totalItemWidth = iSize+2+8; // 4 pixels per side border + 1 pix frame
    totalItemHeight = iSize+2+textHeight+8;
    oldestFile = newestFile = 0;

    inDirLoad = true;
    invalidDir = false;
    clear();
    QPainter p(view);
    p.fillRect(view->rect(), Qt::white);
    p.setPen(Qt::black);
    QFont fnt(p.font());
    p.setPen(Qt::black);
    fnt.setBold(true);
    p.setFont(fnt);
    p.drawText(20, 20, i18n("Loading folder..."));
    p.end();

    //view->repaint(false);
    qApp->processEvents();
    hasNameFilter = !nameFilter.isEmpty();
    const char *nameFilterStr = hasNameFilter ? nameFilter.latin1() : NULL;

    QString dirPrefix;
    switch(iSize){
    case 112:
        dirPrefix = "/xxl/";
        break;
    case 90:
        dirPrefix = "/large/";
        break;
    case 64:
        dirPrefix = "/med/";
        break;
    case 48:
    default:
        dirPrefix = "/small/";
        break;
    }
    konqThumbPath = calcKonqThumbPath(currentDir, iconSize);
    pixieThumbPath = currentDir + "/.pics" + dirPrefix;
    hasKonqThumbnails = QFile::exists(konqThumbPath);

    bool processEntry;
    bool hasParentDir = false;
    struct dirinfo *listitem, *listhead = NULL;
    struct dirent *direntry;
    DIR *dirHandle;
    int n, i=0;
    struct inodeinfo *inodeHead = NULL;
    struct inodeinfo *inodeItem = NULL;
    long *inodeList = NULL;
    bool useInodeList;
    int inodeCount = 0;;

    loadCatagories();
    sortCatagories = catagoriesFirst && catDict.count() != 0;
    useInodeList = catDict.count() != 0;

    // read all items into a singly linked list
    dirHandle = opendir(QFile::encodeName(currentDir));
    if(!dirHandle){
        qWarning("Invalid folder given to update!");
        inDirLoad = false;
        invalidDir = true;
        curIdx = -1;
        view->repaint(false);
        return;
    }
    direntry = readdir(dirHandle);
    while(direntry){
        if(direntry->d_name[0] != '.'){
            processEntry = true;
            listitem = (struct dirinfo *)malloc(sizeof(struct dirinfo));
            listitem->extensionChecked = false;
            listitem->isImage = false;
            listitem->browser = this;

            if(stat(QFile::encodeName(currentDir+"/"+direntry->d_name), &listitem->status) == -1){
                qWarning("Unable to stat: %s", direntry->d_name);
                free(listitem);
                processEntry = false;
            }
            else{
                if(useInodeList){
                    ++inodeCount;
                    inodeItem = (struct inodeinfo *)
                        malloc(sizeof(struct inodeinfo));
                    inodeItem->inode = listitem->status.st_ino;
                    inodeItem->next = inodeHead;
                    inodeHead = inodeItem;
                }
                if(processEntry && iconOnly){
                    if(S_ISDIR(listitem->status.st_mode))
                        ;
                    else{
                        listitem->isImage = isImageType(direntry->d_name);
                        listitem->extensionChecked = true;
                        if(!listitem->isImage){
                            free(listitem);
                            processEntry = false;
                        }
                    }
                }
                if(processEntry && hasNameFilter){
                    if(fnmatch(nameFilterStr, direntry->d_name, 0) != 0){
                        free(listitem);
                        processEntry = false;
                    }
                }
                if(processEntry && showOnlyCatagory != -1 &&
                   !S_ISDIR(listitem->status.st_mode)){
                    processEntry = false;
                    CatInfo *data = catDict.find((long)listitem->
                                                 status.st_ino);
                    if(data){
                        for(int j=0; j < 8; ++j){
                            if(data->catagories[j] == showOnlyCatagory){
                                processEntry = true;
                                break;
                            }
                        }
                    }
                    if(!processEntry)
                        free(listitem);
                }
            }
            if(processEntry){
                if(!i)
                    oldestFile = newestFile = listitem->status.st_mtime;
                else{
                    if(listitem->status.st_mtime < oldestFile)
                        oldestFile = listitem->status.st_mtime;
                    if(listitem->status.st_mtime > newestFile)
                        newestFile = listitem->status.st_mtime;
                }
                ++i;
                listitem->name = (char *)malloc(strlen(direntry->d_name)+1);
                strcpy(listitem->name, direntry->d_name);
                listitem->next = listhead;
                listhead = listitem;
            }
        }
        else if(qstrcmp(direntry->d_name, "..") == 0)
            hasParentDir = true;

        direntry = readdir(dirHandle);
    }
    closedir(dirHandle);
    emit enableUpDir(hasParentDir);
    hasInitialLoad = true;

    if(i == 0){
        inDirLoad = false;
        qWarning("No items in folder!");
        view->repaint(false);
        dirWatch->restartDirScan(currentDir);
        if(useInodeList && inodeHead && inodeCount){
            inodeItem = inodeHead;
            struct inodeinfo *tempinfo;
            for(n=0; n < inodeCount; ++n){
                tempinfo = inodeItem;
                inodeItem = inodeItem->next;
                free(tempinfo);
            }
        }
        return;
    }

    // create an index to all the items
    listarray = (struct dirinfo **)malloc((sizeof(struct dirinfo *))*i);
    listitem = listhead;
    for(n=0; n < i; ++n){
        listarray[n] = listitem;
        listitem = listitem->next;
    }
    if(useInodeList){
        inodeList = (long *)malloc(sizeof(long)*inodeCount);
        inodeItem = inodeHead;
        struct inodeinfo *tempinfo;
        for(n=0; n < inodeCount; ++n){
            inodeList[n] = inodeItem->inode;
            tempinfo = inodeItem;
            inodeItem = inodeItem->next;
            free(tempinfo);
        }
    }

    // sort
    imagesOnTop = imagesFirst;
    arrayCount = i;
    switch(sortType){
    case AscendingByNameID:
        qsort(listarray, i, sizeof(struct dirinfo *), sortNameAscending);
        break;
    case DescendingByNameID:
        qsort(listarray, i, sizeof(struct dirinfo *), sortNameDescending);
        break;
    case AscendingBySizeID:
        qsort(listarray, i, sizeof(struct dirinfo *), sortSizeAscending);
        break;
    case AscendingSameSizesFirstID:
        qsort(listarray, i, sizeof(struct dirinfo *),
              sortSizeAscendingSameFirst);
        break;
    case DescendingBySizeID:
        qsort(listarray, i, sizeof(struct dirinfo *), sortSizeDescending);
        break;
    case AscendingByDateID:
        qsort(listarray, i, sizeof(struct dirinfo *), sortDateAscending);
        break;
    case DescendingByDateID:
        qsort(listarray, i, sizeof(struct dirinfo *), sortDateDescending);
        break;
    default:
        qWarning("Unknown sort type!");
        break;
    }
    if(useInodeList)
        qsort(inodeList, inodeCount, sizeof(long), sortLongIntegers);

    allocateArray(i);
    recalcColumns(view->width(), view->height());

    for(n=0; n < i /*&& !stopProcessing*/; ++n){
        listitem = listarray[n];
        itemList[n].status = (struct stat *)malloc(sizeof(struct stat));
        memcpy(itemList[n].status, &listitem->status, sizeof(struct stat));
        itemList[n].filename = (char *)malloc(strlen(listitem->name)+1);
        strcpy(itemList[n].filename, listitem->name);
        itemList[n].isDir = S_ISDIR(listitem->status.st_mode);
        itemList[n].extensionChecked = listitem->extensionChecked;
        itemList[n].isImage = listitem->isImage;
        free(listitem->name);
        free(listitem);
    }
    free(listarray);

    if(useInodeList){
        // check for obselete catagory items - inodeList is a sorted
        // array of loaded inodes
        long inode;
        bool needsSave = false;
        QIntDictIterator<CatInfo> it(catDict);
        while(it.current()){
            inode = it.currentKey();
            if(!bsearch(&inode, inodeList, inodeCount, sizeof(long),
                        sortLongIntegers)){
                qWarning("Removing obselete inode: %ld", inode);
                catDict.remove(inode);
                needsSave = true;
            }
            else
                ++it;
        }
        free(inodeList);
        if(needsSave)
            kifapp()->catagoryManager()->
                saveFolderCatagories(currentDir, &catDict);
    }
    inDirLoad = false;
    view->setFocus();

    if(i)
        curIdx = 0;
    else
        curIdx = -1;

    if(reloadPosition != -1){
        if(reloadPosition <= sb->maxValue())
            sb->setValue(reloadPosition);
        else
            sb->setValue(sb->maxValue());
    }
    else
        view->repaint(false);
    dirWatch->restartDirScan(currentDir);
}

void PixieBrowser::slotGenerateThumbnails()
{
    if(inGenerateThumbs || thumbJob){
        QMessageBox::warning(this, i18n("Thumbnail Error"),
                             i18n("Already generating thumbnails."));
        return;
    }

    if(!itemCount){
        qWarning("No items to thumbnail!");
        return;
    }

    dirWatch->stopDirScan(currentDir);

    stopProcessing = false;
    inGenerateThumbs = true;
    curPreviewingIdx = -1;
    emit setStatusBarText(i18n("Previewing images...."));
    emit enableStopButton(true);
    mgr->blockUI(true);
    qApp->processEvents();

    QString thumbStr;
    switch(iSize){
    case 112:
        thumbStr = "xxl/";
        break;
    case 90:
        thumbStr = "large/";
        break;
    case 64:
        thumbStr = "med/";
        break;
    case 48:
    default:
        thumbStr = "small/";
        break;
    }

    if(tmpIconPix->width() != iSize+2 || tmpIconPix->height() != iSize+2+textHeight)
        tmpIconPix->resize(iSize+2, iSize+2+textHeight);

    // calc thumbnail paths
    QString path(currentDir);
    // Pixie
    struct stat fileInfo;
    bool canSavePixieThumb = true;
    if(stat(QFile::encodeName(path+"/.pics"), &fileInfo) == -1){
        if(mkdir(QFile::encodeName(path+"/.pics"), 0777) == -1){
            canSavePixieThumb = false;
        }
    }
    if(canSavePixieThumb &&
       stat(QFile::encodeName(path+"/.pics/"+thumbStr), &fileInfo) == -1){
        if(mkdir(QFile::encodeName(path+"/.pics/"+thumbStr), 0777) == -1){
            canSavePixieThumb = false;
        }
    }
    if(access(QFile::encodeName(path+"/.pics/"+thumbStr), W_OK) != 0)
        canSavePixieThumb = false;

    // Konq - make dirs even if we use internal previews if KIO thumbnails
    // are requested
    bool canSaveKonqThumb = true;
    if(!mgr->usePixieThumbs() || mgr->kioThumbnailTypes()->count() != 0){
        QString urlStr("file:");
        urlStr += QDir::cleanDirPath(currentDir);
        KMD5 md5(QFile::encodeName(urlStr));
        QCString hash = md5.hexDigest();
        QString testKonqPath = QDir::homeDirPath() + "/.kde/share/thumbnails/";
        if(stat(QFile::encodeName(testKonqPath), &fileInfo) == -1){
            if(mkdir(QFile::encodeName(testKonqPath), 0777) == -1){
                canSaveKonqThumb = false;
            }
        }
        testKonqPath += QString::fromLatin1(hash.data(), 4) + "/";
        if(canSaveKonqThumb &&
           stat(QFile::encodeName(testKonqPath), &fileInfo) == -1){
            if(mkdir(QFile::encodeName(testKonqPath), 0777) == -1){
                canSaveKonqThumb = false;
            }
        }
        testKonqPath += QString::fromLatin1(hash.data()+4, 4) + "/";
        if(canSaveKonqThumb &&
           stat(QFile::encodeName(testKonqPath), &fileInfo) == -1){
            if(mkdir(QFile::encodeName(testKonqPath), 0777) == -1){
                canSaveKonqThumb = false;
            }
        }
        testKonqPath += QString::fromLatin1(hash.data()+8) + "/";
        if(canSaveKonqThumb &&
           stat(QFile::encodeName(testKonqPath), &fileInfo) == -1){
            if(mkdir(QFile::encodeName(testKonqPath), 0777) == -1){
                canSaveKonqThumb = false;
            }
        }
        testKonqPath += thumbStr;
        if(canSaveKonqThumb &&
           stat(QFile::encodeName(testKonqPath), &fileInfo) == -1){
            if(mkdir(QFile::encodeName(testKonqPath), 0777) == -1){
                canSaveKonqThumb = false;
            }
        }
        if(access(QFile::encodeName(testKonqPath), W_OK) != 0)
            canSaveKonqThumb = false;
    }

    bool saveThumb;
    if(mgr->usePixieThumbs() &&  canSavePixieThumb){
        thumbStr = pixieThumbPath;
        saveThumb = true;
    }
    else{
        thumbStr = konqThumbPath;
        saveThumb = canSaveKonqThumb;
    }

    if(!saveThumb)
        qWarning("Could not save thumbnails!"); // shouldn't happen

    int i, percent, itemY;
    QString pathStr, sizeStr;
    QValueList<int> videoList;
    KFileItemList kioPreviewList;
    QStringList *kioHandlers = mgr->kioThumbnailTypes();
    const char *ext;
    bool useComment, process;

    QImage image;
    bool pixieThumb, konqThumb;
    QPainter p;
    p.begin(tmpIconPix);

    qWarning("KIO handlers: %d", kioHandlers->count());
    if(kioHandlers->count()){
        QStringList::Iterator it;
        for(it=kioHandlers->begin(); it != kioHandlers->end(); ++it)
            qWarning("Handler: %s", (*it).latin1());
    }
    for(i=0, percent=0; i < itemCount && !stopProcessing; ++i){
        pathStr = QDir::cleanDirPath(currentDir + "/" + itemList[i].filename);
        pixieThumb = QFile::exists(pixieThumbPath + itemList[i].filename) &&
            ((time_t)QFileInfo(pixieThumbPath+itemList[i].filename).lastModified().toTime_t()) >=
            itemList[i].status->st_mtime;
        konqThumb = hasKonqThumbnails && QFile::exists(konqThumbPath + itemList[i].filename) &&
            ((time_t)QFileInfo(konqThumbPath + itemList[i].filename).lastModified().toTime_t()) >=
            itemList[i].status->st_mtime;
        if(!pixieThumb && !konqThumb){
            if(!itemList[i].isDir && isImage(&itemList[i], pathStr, false)){
                ext = extension(itemList[i].filename);
                process = true;
                // check for embedded thumbnail if requested
                if(mgr->useEmbeddedJPEGPreview() &&
                   (qstricmp(ext, "jpg") == 0 || qstricmp(ext, "jpeg") == 0)){
                    MyExifData exifInfo; // recreate each time, it gets confused
                    if(exifInfo.scan(pathStr)){
                        image = exifInfo.getThumbnail();
                        if(image.isNull()){
                            qWarning("Unable to get embedded EXIF thumbnail for %s",
                                     itemList[i].filename);
                            process = loadImage(image, pathStr);
                        }
                        else
                            qWarning("Got EXIF thumbnail for %s",
                                     itemList[i].filename);
                    }
                    else
                        process = loadImage(image, pathStr);
                }
                else if(mgr->useEmbeddedTIFFPreview() && 
                        (qstricmp(ext, "tif") == 0 || qstricmp(ext, "tiff") == 0)){
                    if(!checkTIFFThumbnail(QFile::encodeName(pathStr), image))
                        process = loadImage(image, pathStr);
                }
                else
                    process = loadImage(image, pathStr);

                if(process){
                    itemY = (i/columns)*totalItemHeight;
                    curPreviewingIdx = i;
                    if(itemY+totalItemHeight > sb->value()+view->height() ||
                       itemY < sb->value()){
                        sb->setValue(itemY);
                        qApp->processEvents();
                    }
                    if(qstricmp(ext, "png") != 0 &&
                       qstricmp(ext, "jpg") != 0 &&
                       qstricmp(ext, "jpeg") != 0 &&
                       qstricmp(ext, "gif") != 0 &&
                       qstricmp(ext, "bmp") != 0 &&
                       qstricmp(ext, "tif") != 0 &&
                       qstricmp(ext, "tiff") != 0)
                        useComment = true;
                    else
                        useComment = false;
                    if(useComment){
                        sizeStr = sizeStr.sprintf("%dx%d", image.width(),
                                                  image.height());
                    }
                    if(image.width() > iSize || image.height() > iSize){
                        if(image.width() > image.height()){
                            float percent = (((float)iSize)/image.width());
                            int h = (int)(image.height()*percent);
                            image = image.smoothScale(iSize, h);
                        }
                        else{
                            float percent = (((float)iSize)/image.height());
                            int w = (int)(image.width()*percent);
                            image= image.smoothScale(w, iSize);
                        }
                    }
                    if(mgr->lowQualityPreview && image.depth() > 8)
                        image = image.convertDepth(8, Qt::AvoidDither |
                                                   Qt::DiffuseAlphaDither);
                    if(useComment)
                        image.setText("PixiePlus Size", 0, sizeStr);
                    image.save(thumbStr+itemList[i].filename, "PNG");
                    if(itemList[i].pixmap)
                        delete itemList[i].pixmap;
                    itemList[i].pixmap = new QPixmap(image.width(),
                                                     image.height());
                    convertImageToPixmapBlend(image, curFrameImage,
                                              ((iSize+2)-image.width())/2,
                                              ((iSize+2)-image.height())/2,
                                              *itemList[i].pixmap);
                    itemList[i].thumbnailed = true;
                    itemList[i].imageHasThumb = true;
                    if(itemList[i].thumbnailData)
                        free(itemList[i].thumbnailData);
                    itemList[i].thumbnailData = NULL;
                    if(useComment){
                        itemList[i].thumbnailData = (char *)
                            malloc(strlen(sizeStr.ascii())+1);
                        qstrcpy(itemList[i].thumbnailData, sizeStr.ascii());
                        if(itemList[i].tooltip){
                            free(itemList[i].tooltip);
                            itemList[i].tooltip = NULL;
                        }
                    }
                    curPreviewingIdx = -1;
                    paintThumbnail(i, &p);
		    emit setStatusBarText(i18n("Created Thumbnail for %1.").arg(itemList[i].filename));
                }
                else
                    qWarning("Unable to load %s!", itemList[i].filename);
            }
            else if(/*!itemList[i].isDir && mgr->useMPlayerVideo() &&
                    isVideoType(itemList[i].filename)*/false){
                videoList.append(i);
            }
            else if(!itemList[i].isDir && itemList[i].mimetype &&
                    kioHandlers->count() != 0){
                KURL url;
                url.setPath(currentDir + "/" + itemList[i].filename);
                KFileItem *fileItem = new KFileItem(url,
                                                    itemList[i].mimetype,
                                                    itemList[i].status->
                                                    st_mode);
                kioPreviewList.append(fileItem);
            }
        }
        if((int)(((float)i/itemCount)*100) > percent){
            percent = (int)(((float)i/itemCount)*100);
            emit updateProgress(percent);
        }
        kifapp()->processEvents();
    }

    // do video thumbnails
    if(!stopProcessing && videoList.count() && saveThumb){
        /*
        QStringList fileList;
        QValueList<int>::iterator it;
        for(it=videoList.begin(); it != videoList.end(); ++it){
            i = (*it);
            fileList.append(itemList[i].filename);
        }
        qWarning("running mplayer dialog");
        emit setStatusBarText(i18n("Previewing movies..."));
        qApp->processEvents();
        // create by new, even tho we delete it right away :P
        MPlayerDialog *dlg = new MPlayerDialog(fileList, currentDir,
                                               thumbStr, iSize, view);
        dlg->run();
        delete dlg;
        // update the items
        for(it=videoList.begin(); it != videoList.end(); ++it){
            i = (*it);
            if(image.load(thumbStr+itemList[i].filename, "PNG")){
                if(itemList[i].pixmap)
                    delete itemList[i].pixmap;
                itemList[i].pixmap = new QPixmap(image.width(),
                                                 image.height());
                convertImageToPixmapBlend(image, curFrameImage,
                                          ((iSize+2)-image.width())/2,
                                          ((iSize+2)-image.height())/2,
                                          *itemList[i].pixmap);
                itemList[i].thumbnailed = true;
                paintThumbnail(i, &p);
            }
            else
                qWarning("Could not load video thumbnail %s",
                         itemList[i].filename);
        }*/
    }

    p.end();
    // do KIO
    if(!stopProcessing && kioPreviewList.count() != 0){
        emit updateProgress(0);
        emit setStatusBarText(i18n("Running KDE non-image previews...."));
        kioCurrentThumb = 0;
        kioThumbCount = kioPreviewList.count();
        thumbJob = new KIO::PreviewJob(kioPreviewList, iSize, iSize,
                                       iSize, 70, true, true, kioHandlers);
        connect(thumbJob, SIGNAL(gotPreview(const KFileItem *,
                                            const QPixmap &)),
                this, SLOT(slotThumbJobPreview(const KFileItem *,
                                               const QPixmap &)));
        connect(thumbJob, SIGNAL(failed(const KFileItem *)), this,
                SLOT(slotThumbJobFailed(const KFileItem *)));
        connect(thumbJob, SIGNAL(result(KIO::Job *)), this,
                SLOT(slotThumbJobResult(KIO::Job *)));
        connect(thumbJob, SIGNAL(percent(KIO::Job *, unsigned long)), this,
                SLOT(slotThumbJobProgress(KIO::Job *, unsigned long)));
    }
    else{
        emit updateProgress(100);
        emit setStatusBarText(i18n("PixiePlus thumbnails complete."));
        emit enableStopButton(false);
        emit enableFolderChanges(true);
        inGenerateThumbs = false;
        dirWatch->restartDirScan(currentDir);
        mgr->blockUI(false);
    }
}

void PixieBrowser::stop()
{
    stopProcessing = true;
    if(inGenerateThumbs && thumbJob){
        emit updateProgress(100);
        emit setStatusBarText(i18n("KDE thumbnail stopped."));
        qWarning("Killing KIO thumbnail generation");
        thumbJob->kill(false);
        thumbJob = NULL;
        dirWatch->restartDirScan(currentDir);
    }
    inGenerateThumbs = false;
}

void PixieBrowser::recalcColumns(int vw, int vh)
{
    int h;
    if(!count()){
        columns = h = 0;
    }
    else{
        columns = vw/totalItemWidth;
        h = (int)ceil(((float)count())/columns);
        h *= totalItemHeight;
    }
    if(h-vh > 0){
        if(!sb->isEnabled())
            sb->setEnabled(true);
        sb->setRange(0, h-vh);
        sb->setLineStep(16);
        sb->setPageStep(vh);
    }
    else{
        sb->setRange(0, 0);
        if(sb->isEnabled())
            sb->setEnabled(false);
    }
    recalcRects();
}

void PixieBrowser::copy()
{
    QValueList<int>::iterator it;
    QStrList fileList;
    int idx;

    if(!selectList.count())
        return;
    sortSelectionByView();
    for(it = selectList.begin(); it != selectList.end(); ++it){
        idx = *it;
        fileList.append(QFile::encodeName(currentDir + "/" +
                                          itemList[idx].filename));
    }
    if(fileList.isEmpty())
        return;
    QUriDrag *obj = new QUriDrag(fileList);
    QClipboard *cb = QApplication::clipboard();
#if QT_VERSION & 0x00FF00
    cb->setData(obj, QClipboard::Clipboard);
#else
    cb->setData(obj);
#endif

}

void PixieBrowser::paste()
{
    QClipboard *cb = QApplication::clipboard();
    QStringList fileList;

#if QT_VERSION & 0x00FF00
    if(QUriDrag::decodeLocalFiles(cb->data(QClipboard::Clipboard), fileList)){
#else
    if(QUriDrag::decodeLocalFiles(cb->data(), fileList)){
#endif
        if(!fileList.count())
            return;
        KIFFileTransfer::transferFiles(fileList, currentDir,
                                       QDropEvent::Copy);
    }
    else
        KMessageBox::sorry(this, i18n("The clipboard is empty."),
                           i18n("Pixie Clipboard Error"));
}

// Tooltips
void BrowserTip::maybeTip(const QPoint &p)
{
    PixieBrowser *browser = (PixieBrowser *)parentWidget()->parent();
    if(!browser->count())
        return;
    int i, idx = browser->firstVisibleIdx;
    PixieRect r;
    for(i=0; i < browser->visibleRectCount; ++i){
        r = browser->visibleRects[i];
        if(contains(r.x, r.y, r.w, r.h, p.x(), p.y()) &&
           idx < browser->itemCount){
            if(!browser->itemList[idx].tooltip)
                browser->calcTooltip(&browser->itemList[idx]);
            tip(QRect(r.x, r.y, r.w, r.h),
                browser->itemList[idx].tooltip);
            return;
        }
        ++idx;
    }
}

void PixieBrowser::clearTips()
{
    int i;
    for(i=0; i < itemCount; ++i){
        if(itemList[i].tooltip){
            free(itemList[i].tooltip);
            itemList[i].tooltip = NULL;
        }
    }
}

// Catagories
bool PixieBrowser::loadCatagories()
{
    catError = false;
    supportCatagories = kifapp()->catagoryManager()->
        loadFolderCatagories(currentDir, &catDict);
    return(supportCatagories);
}

bool PixieBrowser::saveCatagories()
{
    dirWatch->stopDirScan(currentDir);
    clearTips();
    bool result = kifapp()->catagoryManager()->
        saveFolderCatagories(currentDir, &catDict);
    dirWatch->restartDirScan(currentDir);
    return(result);
}

QStringList PixieBrowser::itemCatagories(Thumbnail *item)
{
    QStringList list;
    if(!item)
        return(list);
    CatInfo *info = catDict.find((long)item->status->st_ino);
    QString **catarray = kifapp()->catagoryManager()->array();
    int i;

    if(info){
        for(i=0; i < 8 && info->catagories[i]; ++i){
            int j = info->catagories[i];
            if(catarray[j] != 0)
                list.append(*catarray[j]);
            else
                qWarning("Invalid catagory index %d, value: %d", i, j);
        }
    }
    list.sort();
    return(list);
}

void PixieBrowser::addCatagory(Thumbnail *item, int id)
{
    CatInfo *info;
    long inode = (long)item->status->st_ino;
    int i;

    info = catDict.find(inode);
    if(!info){
        qWarning("Adding new catagory %d for %s", id, item->filename);
        info = new CatInfo;
        info->catagories[0] = id;
        for(i=1; i < 8; ++i)
            info->catagories[i] = 0;
        catDict.insert(inode, info);
    }
    else{
        unsigned char *data = info->catagories;
        for(i=0; i < 8 && data[i] != 0 && data[i] != id; ++i)
            ;
        if(i == 8){
            // TODO: messagebox
            qWarning("Maximum allowed catagories!");
            return;
        }
        else if(data[i] == id){
            qWarning("Id already set!");
            return;
        }
        else{
            data[i] = id;
        }
    }
}

void PixieBrowser::removeCatagory(Thumbnail *item, int id)
{
    qWarning("In removeCatagory");
    CatInfo *info;
    long inode = (long)item->status->st_ino;
    int i;

    info = catDict.find(inode);
    if(!info){
        // TODO messagebox
        qWarning("Tried to remove catagory that wasn't set");
        return;
    }
    unsigned char *data = info->catagories;
    for(i=0; i < 8 && data[i] != id; ++i)
        ;
    if(i < 8 && data[i] == id){
        // justify left
        while(i < 7){
            data[i] = data[i+1];
            ++i;
        }
        data[7] = 0;
    }
    else
        qWarning("Id's are mismatched: id: %d, i: %d, data[i]: %d!",
                 id, i, data[i]);
    if(data[0] == 0){
        qWarning("No more catagories, removing entry");
        catDict.remove(inode);
    }
    qWarning("Leaving removeCatagory");
}

// KIO thumbnail generation slots
void PixieBrowser::slotThumbJobResult(KIO::Job *job)
{
    if(job == thumbJob){
        qWarning("KIO job finished");
        thumbJob = NULL;
        emit updateProgress(100);
        kioThumbCount = kioCurrentThumb = 0; // shouldn't matter
        stopProcessing = true;
        emit enableStopButton(false);
        emit enableFolderChanges(true);
        emit setStatusBarText(i18n("KDE thumbnails complete."));
        dirWatch->restartDirScan(currentDir);
        inGenerateThumbs = false;
        mgr->blockUI(false);
    }
}

void PixieBrowser::slotThumbJobProgress(KIO::Job *, unsigned long)
{
    ;
}

void PixieBrowser::slotThumbJobPreview(const KFileItem *item,
                                       const QPixmap &preview)
{
    int percent;
    ++kioCurrentThumb;
    percent = (int)(((float)kioCurrentThumb/kioThumbCount)*100);
    if(percent == 100)
        percent = 99; // Don't do 100% until done
    emit updateProgress(percent);
    QString previewItemStr(item->url().fileName());
    qWarning("Got KDE preview for %s", previewItemStr.latin1());
    int idx = findItem(QFile::encodeName(previewItemStr));
    if(idx != -1 && idx < itemCount){
        if(itemList[idx].pixmap)
            delete itemList[idx].pixmap;
        itemList[idx].pixmap = new QPixmap(preview);
        itemList[idx].thumbnailed = true;
        if(isItemVisible(idx))
            paintThumbnail(idx);
    }
    else
        qWarning("Could not find item!");
    if(!QFile::exists(konqThumbPath + "/" + previewItemStr)){
        // We always save thumbnails if possible. KDE skips some types
        // and will always regenerate, (ie: text), and uses a maximum
        // size limit
        qWarning("Thumbnail was not saved, saving...");
        preview.save(konqThumbPath + "/" + previewItemStr, "PNG");
    }
    else
        qWarning("Thumbnail was saved");
}

void PixieBrowser::slotThumbJobFailed(const KFileItem */*item*/)
{
    ++kioCurrentThumb;
    int percent = (int)(((float)kioCurrentThumb/kioThumbCount)*100);
    emit updateProgress(percent);
}

// Thumbnail conversion
void PixieBrowser::convertToPixieThumbs()
{
    QString thumbStr;
    int i, c = count();

    switch(iSize){
    case 112:
        thumbStr = "xxl/";
        break;
    case 64:
        thumbStr = "med/";
        break;
    case 48:
        thumbStr = "small/";
        break;
    case 90:
    default:
        thumbStr = "large/";
        break;
    }

    if(!hasKonqThumbnails || !c){
        KMessageBox::sorry(this, i18n("No Konqueror previews in this folder."),
                           i18n("Thumbnail Error"));
        return;
    }
    dirWatch->stopDirScan(currentDir);

    struct stat fileInfo;
    bool canSavePixieThumb = true;
    if(stat(QFile::encodeName(currentDir+"/.pics"), &fileInfo) == -1){
        if(mkdir(QFile::encodeName(currentDir+"/.pics"), 0777) == -1){
            canSavePixieThumb = false;
        }
    }
    if(canSavePixieThumb &&
       stat(QFile::encodeName(currentDir+"/.pics/"+thumbStr), &fileInfo) == -1){
        if(mkdir(QFile::encodeName(currentDir+"/.pics/"+thumbStr), 0777) == -1){
            canSavePixieThumb = false;
        }
    }
    if(access(QFile::encodeName(currentDir+"/.pics/"+thumbStr), W_OK) != 0)
        canSavePixieThumb = false;

    if(!canSavePixieThumb){
        KMessageBox::sorry(this, i18n("Cannot save PixiePlus previews for this folder."),
                           i18n("Thumbnail Error"));
        dirWatch->restartDirScan(currentDir);
        return;
    }

    emit setStatusBarText(i18n("Converting thumbnails...."));
    kifapp()->processEvents();
    QFileInfo fi;
    for(i=0; i < c; ++i){
        if(QFile::exists(konqThumbPath + itemList[i].filename)){
            fi.setFile(konqThumbPath + itemList[i].filename);
            KURL url("file:"+fi.absFilePath());
            QString mimeStr =  KMimeType::findByURL(url, true, true)->name();
            // move only images and text
            if(mimeStr.left(6) == "image/" || (mimeStr.left(5) == "text/" &&
                                               mimeStr != "text/html")){

                if(QFile::exists(pixieThumbPath + itemList[i].filename)){
                    qWarning("%s has both thumbnails", itemList[i].filename);
                    if(QFileInfo(konqThumbPath+itemList[i].filename).lastModified() >
                       QFileInfo(pixieThumbPath+itemList[i].filename).lastModified())
                        KIFFileTransfer::move(konqThumbPath + itemList[i].filename,
                                              pixieThumbPath, false);

                }
                else
                    KIFFileTransfer::move(konqThumbPath + itemList[i].filename,
                                          pixieThumbPath, false);
            }
            else
                qWarning("Ignoring non image or text file %s",
                         itemList[i].filename);
        }
    }
    dirWatch->restartDirScan(currentDir);
    emit setStatusBarText(i18n("Finished converting thumbnails."));
}

void PixieBrowser::convertToKonqThumbs()
{
    QString thumbStr;
    int i, c = count();

    switch(iSize){
    case 112:
        thumbStr = "xxl/";
        break;
    case 64:
        thumbStr = "med/";
        break;
    case 48:
        thumbStr = "small/";
        break;
    case 90:
    default:
        thumbStr = "large/";
        break;
    }

    QString pixiePath = currentDir+"/.pics/"+thumbStr;
    if(!QFile::exists(pixiePath) || !c){
        KMessageBox::sorry(this, i18n("No PixiePlus previews in this folder."),
                           i18n("Thumbnail Error"));
        return;
    }

    dirWatch->stopDirScan(currentDir);

    // test or create konq thumbnail dir
    bool canSaveKonqThumb = true;
    struct stat fileInfo;
    KURL url;
    url.setPath(QDir::cleanDirPath(currentDir));
    KMD5 md5(QFile::encodeName(url.url()));
    QCString hash = md5.hexDigest();
    QString testKonqPath = KGlobal::dirs()->localkdedir() +
        "/share/thumbnails/";
    if(stat(QFile::encodeName(testKonqPath), &fileInfo) == -1){
        if(mkdir(QFile::encodeName(testKonqPath), 0777) == -1){
            canSaveKonqThumb = false;
        }
    }
    testKonqPath += QString::fromLatin1(hash.data(), 4) + "/";
    if(canSaveKonqThumb &&
       stat(QFile::encodeName(testKonqPath), &fileInfo) == -1){
        if(mkdir(QFile::encodeName(testKonqPath), 0777) == -1){
            canSaveKonqThumb = false;
        }
    }
    testKonqPath += QString::fromLatin1(hash.data()+4, 4) + "/";
    if(canSaveKonqThumb &&
       stat(QFile::encodeName(testKonqPath), &fileInfo) == -1){
        if(mkdir(QFile::encodeName(testKonqPath), 0777) == -1){
            canSaveKonqThumb = false;
        }
    }
    testKonqPath += QString::fromLatin1(hash.data()+8) + "/";
    if(canSaveKonqThumb &&
       stat(QFile::encodeName(testKonqPath), &fileInfo) == -1){
        if(mkdir(QFile::encodeName(testKonqPath), 0777) == -1){
            canSaveKonqThumb = false;
        }
    }
    testKonqPath += thumbStr;
    if(canSaveKonqThumb &&
       stat(QFile::encodeName(testKonqPath), &fileInfo) == -1){
        if(mkdir(QFile::encodeName(testKonqPath), 0777) == -1){
            canSaveKonqThumb = false;
        }
    }
    if(access(QFile::encodeName(testKonqPath), W_OK) != 0)
        canSaveKonqThumb = false;
    if(!canSaveKonqThumb){
        KMessageBox::sorry(this, i18n("Cannot save Konqueror previews for this folder."),
                           i18n("Thumbnail Error"));
        dirWatch->restartDirScan(currentDir);
        return;
    }

    // move them
    emit setStatusBarText(i18n("Converting thumbnails...."));
    kifapp()->processEvents();
    for(i=0; i < c; ++i){
        if(QFile::exists(pixiePath + itemList[i].filename)){
            if(QFile::exists(testKonqPath + itemList[i].filename)){
                qWarning("%s has both thumbnails", itemList[i].filename);
                if(QFileInfo(pixiePath+itemList[i].filename).lastModified() >
                   QFileInfo(testKonqPath+itemList[i].filename).lastModified())
                    KIFFileTransfer::move(pixiePath + itemList[i].filename,
                                          testKonqPath, false);

            }
            else
                KIFFileTransfer::move(pixiePath + itemList[i].filename,
                                      testKonqPath, false);
        }
    }
    dirWatch->restartDirScan(currentDir);
    emit setStatusBarText(i18n("Finished converting thumbnails."));
}


// Misc
bool PixieBrowser::isImage(Thumbnail *i, const QString &absPath,
                           bool useExtensionOnly)
{
    if(i->isImage)
        return(true);
    if(!i->extensionChecked){
        i->isImage = isImageType(i->filename);
        i->extensionChecked = true;
        if(i->isImage)
            return(true);
    }
    if(!i->mimetypeChecked && !useExtensionOnly && !absPath.isEmpty())
        processThumbnailMimeType(i, absPath, iSize);
    return(i->isImage);
}

void PixieBrowser::processThumbnailMimeType(Thumbnail *i,
                                            const QString &path,
                                            int iconSize)
{
    if(i->isImage || i->mimetype || (i->mimetypeChecked && iconSize == -1))
        return;

    i->mimetypeChecked = true;

    KMimeType::Ptr mimeType =
        KMimeType::findByPath(path, i->status->st_mode);
    if(iconSize != -1 && !i->pixmap){
        i->pixmap = new QPixmap;
        QString iconStr = mimeType->icon(path, true);
        if(!iconStr)
            iconStr = "unknown";
        QPixmap *cachedPix = iconDict.find(iconStr);
        if(cachedPix)
            *i->pixmap = *cachedPix;
        else{
            QImage img(KGlobal::iconLoader()->
                       iconPath(iconStr, iconSize >= 64 ? -64 : -48));
            if(1){ /*FIXME - make configurable*/
                int scaleSize = iSize - 4;
                if(scaleSize > 82)
                    scaleSize = 82;
                img = img.smoothScale(scaleSize, scaleSize);
            }

            cachedPix = new QPixmap(img.width(), img.height());
            convertImageToPixmapBlend(img, curFrameImage,
                                      ((iSize+2)-img.width())/2,
                                      ((iSize+2)-img.height())/2,
                                      *cachedPix);
            iconDict.insert(iconStr, cachedPix);
            *i->pixmap = *cachedPix;
        }
    }
    if(mimeType->name().left(6) == "image/")
        i->isImage = true;
    else{
        i->mimetype = (char *)malloc(strlen(mimeType->name().latin1())+1);
        qstrcpy(i->mimetype, mimeType->name().latin1());
    }
}

QImage PixieBrowser::uicImage(const QString &name)
{
    return(uic_findImage(name));
}


#include "browser.moc"
