diff options
Diffstat (limited to 'src/utilities/lighttable/lighttablepreview.cpp')
| -rw-r--r-- | src/utilities/lighttable/lighttablepreview.cpp | 777 | 
1 files changed, 777 insertions, 0 deletions
| diff --git a/src/utilities/lighttable/lighttablepreview.cpp b/src/utilities/lighttable/lighttablepreview.cpp new file mode 100644 index 00000000..100427ec --- /dev/null +++ b/src/utilities/lighttable/lighttablepreview.cpp @@ -0,0 +1,777 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date        : 2006-21-12 + * Description : digiKam light table preview item. + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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 <tqpainter.h> +#include <tqcursor.h> +#include <tqstring.h> +#include <tqvaluevector.h> +#include <tqfileinfo.h> +#include <tqtoolbutton.h> +#include <tqtooltip.h> +#include <tqpixmap.h> +#include <tqdrawutil.h> + +// KDE includes. + +#include <kdialogbase.h> +#include <tdelocale.h> +#include <kservice.h> +#include <krun.h> +#include <ktrader.h> +#include <kmimetype.h> +#include <kcursor.h> +#include <kdatetbl.h> +#include <kiconloader.h> +#include <kprocess.h> +#include <tdeapplication.h> + +// Local includes. + +#include "dimg.h" +#include "ddebug.h" +#include "albumdb.h" +#include "constants.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "dragobjects.h" +#include "dmetadata.h" +#include "dpopupmenu.h" +#include "metadatahub.h" +#include "paniconwidget.h" +#include "previewloadthread.h" +#include "loadingdescription.h" +#include "tagspopupmenu.h" +#include "ratingpopupmenu.h" +#include "themeengine.h" +#include "lighttablepreview.h" +#include "lighttablepreview.moc" + +namespace Digikam +{ + +class LightTablePreviewPriv +{ +public: + +    LightTablePreviewPriv() +    { +        panIconPopup         = 0; +        panIconWidget        = 0; +        cornerButton         = 0; +        previewThread        = 0; +        previewPreloadThread = 0; +        imageInfo            = 0; +        hasPrev              = false; +        hasNext              = false; +        selected             = false; +        dragAndDropEnabled   = true; +        loadFullImageSize    = false; +        currentFitWindowZoom = 0; +        previewSize          = 1024; +    } + +    bool               hasPrev; +    bool               hasNext; +    bool               selected; +    bool               dragAndDropEnabled; +    bool               loadFullImageSize; + +    int                previewSize; + +    double             currentFitWindowZoom; + +    TQString            path; +    TQString            nextPath; +    TQString            previousPath; + +    TQToolButton       *cornerButton; + +    TDEPopupFrame       *panIconPopup; + +    PanIconWidget     *panIconWidget; + +    DImg               preview; + +    ImageInfo         *imageInfo; + +    PreviewLoadThread *previewThread; +    PreviewLoadThread *previewPreloadThread; +}; +     +LightTablePreview::LightTablePreview(TQWidget *parent) +                 : PreviewWidget(parent) +{ +    d = new LightTablePreviewPriv; + +    // get preview size from screen size, but limit from VGA to WTQXGA +    d->previewSize = TQMAX(TDEApplication::desktop()->height(), +                          TDEApplication::desktop()->width()); +    if (d->previewSize < 640) +        d->previewSize = 640; +    if (d->previewSize > 2560) +        d->previewSize = 2560; +     +    viewport()->setAcceptDrops(true); +    setAcceptDrops(true);  + +    slotThemeChanged(); +    setSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding); + +    d->cornerButton = new TQToolButton(this); +    d->cornerButton->setIconSet(SmallIcon("move")); +    d->cornerButton->hide(); +    TQToolTip::add(d->cornerButton, i18n("Pan the image")); +    setCornerWidget(d->cornerButton); + +    setLineWidth(5); +    setSelected(false); + +    // ------------------------------------------------------------ + +    connect(d->cornerButton, TQ_SIGNAL(pressed()), +            this, TQ_SLOT(slotCornerButtonPressed())); + +    connect(this, TQ_SIGNAL(signalRightButtonClicked()), +            this, TQ_SLOT(slotContextMenu())); + +    connect(ThemeEngine::instance(), TQ_SIGNAL(signalThemeChanged()), +            this, TQ_SLOT(slotThemeChanged())); + +    // ------------------------------------------------------------ + +    slotReset();  +} + +LightTablePreview::~LightTablePreview() +{ +    delete d->previewThread; +    delete d->previewPreloadThread; +    delete d; +} + +void LightTablePreview::setLoadFullImageSize(bool b) +{ +    d->loadFullImageSize = b; +    reload(); +} + +void LightTablePreview::setDragAndDropEnabled(bool b) +{ +    d->dragAndDropEnabled = b; +} + +void LightTablePreview::setDragAndDropMessage() +{ +    if (d->dragAndDropEnabled) +    { +        TQPixmap pix(visibleWidth(), visibleHeight()); +        pix.fill(ThemeEngine::instance()->baseColor()); +        TQPainter p(&pix); +        p.setPen(TQPen(ThemeEngine::instance()->textRegColor())); +        p.drawText(0, 0, pix.width(), pix.height(), +                   TQt::AlignCenter|TQt::WordBreak,  +                   i18n("Drag and drop an image here")); +        p.end(); +        setImage(pix.convertToImage()); +    } +} + +void LightTablePreview::setImage(const DImg& image) +{    +    d->preview = image; + +    updateZoomAndSize(true); + +    viewport()->setUpdatesEnabled(true); +    viewport()->update(); +} + +DImg& LightTablePreview::getImage() const +{ +    return d->preview; +} + +TQSize LightTablePreview::getImageSize() +{ +    return d->preview.size(); +} + +void LightTablePreview::reload() +{ +    // cache is cleaned from AlbumIconView::refreshItems +    setImagePath(d->path); +} + +void LightTablePreview::setPreviousNextPaths(const TQString& previous, const TQString &next) +{ +    d->nextPath     = next; +    d->previousPath = previous; +} + +void LightTablePreview::setImagePath(const TQString& path) +{ +    setCursor( KCursor::waitCursor() ); + +    d->path         = path; +    d->nextPath     = TQString(); +    d->previousPath = TQString(); + +    if (d->path.isEmpty()) +    { +        slotReset(); +        unsetCursor(); +        return; +    } + +    if (!d->previewThread) +    { +        d->previewThread = new PreviewLoadThread(); +        connect(d->previewThread, TQ_SIGNAL(signalImageLoaded(const LoadingDescription &, const DImg &)), +                this, TQ_SLOT(slotGotImagePreview(const LoadingDescription &, const DImg&))); +    } +    if (!d->previewPreloadThread) +    { +        d->previewPreloadThread = new PreviewLoadThread(); +        connect(d->previewPreloadThread, TQ_SIGNAL(signalImageLoaded(const LoadingDescription &, const DImg &)), +                this, TQ_SLOT(slotNextPreload())); +    } + +    if (d->loadFullImageSize) +        d->previewThread->loadHighQuality(LoadingDescription(path, 0, AlbumSettings::instance()->getExifRotate())); +    else +        d->previewThread->load(LoadingDescription(path, d->previewSize, AlbumSettings::instance()->getExifRotate())); +} + +void LightTablePreview::slotGotImagePreview(const LoadingDescription &description, const DImg& preview) +{ +    if (description.filePath != d->path) +        return;    + +    if (preview.isNull()) +    { +        TQPixmap pix(visibleWidth(), visibleHeight()); +        pix.fill(ThemeEngine::instance()->baseColor()); +        TQPainter p(&pix); +        TQFileInfo info(d->path); +        p.setPen(TQPen(ThemeEngine::instance()->textRegColor())); +        p.drawText(0, 0, pix.width(), pix.height(), +                   TQt::AlignCenter|TQt::WordBreak,  +                   i18n("Unable to display preview for\n\"%1\"") +                   .arg(info.fileName())); +        p.end(); +        setImage(DImg(pix.convertToImage())); + +        emit signalPreviewLoaded(false); +    } +    else +    { +        DImg img(preview); +        if (AlbumSettings::instance()->getExifRotate()) +            d->previewThread->exifRotate(img, description.filePath); +        setImage(img); +        emit signalPreviewLoaded(true); +    } + +    unsetCursor(); +    slotNextPreload(); +} + +void LightTablePreview::slotNextPreload() +{ +    TQString loadPath; +    if (!d->nextPath.isNull()) +    { +        loadPath    = d->nextPath; +        d->nextPath = TQString(); +    } +    else if (!d->previousPath.isNull()) +    { +        loadPath        = d->previousPath; +        d->previousPath = TQString(); +    } +    else +        return; + +    d->previewPreloadThread->load(LoadingDescription(loadPath, d->previewSize, +                                  AlbumSettings::instance()->getExifRotate())); +} + +void LightTablePreview::setImageInfo(ImageInfo* info, ImageInfo *previous, ImageInfo *next) +{ +    d->imageInfo = info; +    d->hasPrev   = previous; +    d->hasNext   = next; + +    if (d->imageInfo) +        setImagePath(info->filePath()); +    else +    { +        setImagePath(); +        setSelected(false); +    } + +    setPreviousNextPaths(previous ? previous->filePath() : TQString(), +                         next     ? next->filePath()     : TQString()); +} + +ImageInfo* LightTablePreview::getImageInfo() const +{ +    return d->imageInfo; +} + +void LightTablePreview::slotContextMenu() +{ +    RatingPopupMenu *ratingMenu     = 0; +    TagsPopupMenu   *assignTagsMenu = 0; +    TagsPopupMenu   *removeTagsMenu = 0; + +    if (!d->imageInfo) +        return; + +    //-- Open With Actions ------------------------------------ + +    KURL url(d->imageInfo->kurl().path()); +    KMimeType::Ptr mimePtr = KMimeType::findByURL(url, 0, true, true); + +    TQValueVector<KService::Ptr> serviceVector; +    TDETrader::OfferList offers = TDETrader::self()->query(mimePtr->name(), "Type == 'Application'"); + +    TQPopupMenu openWithMenu; + +    TDETrader::OfferList::Iterator iter; +    KService::Ptr ptr; +    int index = 100; + +    for( iter = offers.begin(); iter != offers.end(); ++iter ) +    { +        ptr = *iter; +        openWithMenu.insertItem( ptr->pixmap(TDEIcon::Small), ptr->name(), index++); +        serviceVector.push_back(ptr); +    } + +    DPopupMenu popmenu(this); +     +    //-- Zoom actions ----------------------------------------------- + +    popmenu.insertItem(SmallIcon("viewmag"), i18n("Zoom in"), 17); +    popmenu.insertItem(SmallIcon("zoom-out"), i18n("Zoom out"), 18); +    popmenu.insertItem(SmallIcon("view_fit_window"), i18n("Fit to &Window"), 19); + +    //-- Edit actions ----------------------------------------------- + +    popmenu.insertSeparator(); +    popmenu.insertItem(SmallIcon("slideshow"), i18n("SlideShow"), 16); +    popmenu.insertItem(SmallIcon("editimage"), i18n("Edit..."), 12); +    popmenu.insertItem(i18n("Open With"), &openWithMenu, 13); + +    //-- Trash action ------------------------------------------- + +    popmenu.insertSeparator(); +    popmenu.insertItem(SmallIcon("edittrash"), i18n("Move to Trash"), 14); + +    // Bulk assignment/removal of tags -------------------------- + +    TQ_LLONG id = d->imageInfo->id(); +    TQValueList<TQ_LLONG> idList; +    idList.append(id); + +    assignTagsMenu = new TagsPopupMenu(idList, 1000, TagsPopupMenu::ASSIGN); +    removeTagsMenu = new TagsPopupMenu(idList, 2000, TagsPopupMenu::REMOVE); + +    popmenu.insertSeparator(); + +    popmenu.insertItem(i18n("Assign Tag"), assignTagsMenu); +    int i = popmenu.insertItem(i18n("Remove Tag"), removeTagsMenu); + +    connect(assignTagsMenu, TQ_SIGNAL(signalTagActivated(int)), +            this, TQ_SLOT(slotAssignTag(int))); + +    connect(removeTagsMenu, TQ_SIGNAL(signalTagActivated(int)), +            this, TQ_SLOT(slotRemoveTag(int))); + +    AlbumDB* db = AlbumManager::instance()->albumDB(); +    if (!db->hasTags( idList )) +        popmenu.setItemEnabled(i, false); + +    popmenu.insertSeparator(); + +    // Assign Star Rating ------------------------------------------- + +    ratingMenu = new RatingPopupMenu(); +     +    connect(ratingMenu, TQ_SIGNAL(activated(int)), +            this, TQ_SLOT(slotAssignRating(int))); + +    popmenu.insertItem(i18n("Assign Rating"), ratingMenu); + +    // -------------------------------------------------------- + +    int idm = popmenu.exec(TQCursor::pos()); + +    switch(idm)  +    { +        case 12:     // Edit... +        { +            emit signalEditItem(d->imageInfo); +            break; +        } + +        case 14:     // Move to trash +        { +            emit signalDeleteItem(d->imageInfo); +            break; +        } + +        case 16:     // SlideShow +        { +            emit signalSlideShow(); +            break; +        } + +        case 17:     // Zoom in +        { +            slotIncreaseZoom(); +            break; +        } + +        case 18:     // Zoom out +        { +            slotDecreaseZoom(); +            break; +        } + +        case 19:     // Fit to window +        { +            fitToWindow(); +            break; +        } + +        default: +            break; +    } + +    // Open With... +    if (idm >= 100 && idm < 1000)  +    { +        KService::Ptr imageServicePtr = serviceVector[idm-100]; +        KRun::run(*imageServicePtr, url); +    } + +    serviceVector.clear(); +    delete assignTagsMenu; +    delete removeTagsMenu; +    delete ratingMenu; +} + +void LightTablePreview::slotAssignTag(int tagID) +{ +    if (d->imageInfo) +    { +        MetadataHub hub; +        hub.load(d->imageInfo); +        hub.setTag(tagID, true); +        hub.write(d->imageInfo, MetadataHub::PartialWrite); +        hub.write(d->imageInfo->filePath(), MetadataHub::FullWriteIfChanged); +    } +} + +void LightTablePreview::slotRemoveTag(int tagID) +{ +    if (d->imageInfo) +    { +        MetadataHub hub; +        hub.load(d->imageInfo); +        hub.setTag(tagID, false); +        hub.write(d->imageInfo, MetadataHub::PartialWrite); +        hub.write(d->imageInfo->filePath(), MetadataHub::FullWriteIfChanged); +    } +} + +void LightTablePreview::slotAssignRating(int rating) +{ +    rating = TQMIN(RatingMax, TQMAX(RatingMin, rating)); +    if (d->imageInfo) +    { +        MetadataHub hub; +        hub.load(d->imageInfo); +        hub.setRating(rating); +        hub.write(d->imageInfo, MetadataHub::PartialWrite); +        hub.write(d->imageInfo->filePath(), MetadataHub::FullWriteIfChanged); +    } +} + +void LightTablePreview::slotThemeChanged() +{ +    setBackgroundColor(ThemeEngine::instance()->baseColor()); +    frameChanged(); +} + +void LightTablePreview::slotCornerButtonPressed() +{     +    if (d->panIconPopup) +    { +        d->panIconPopup->hide(); +        delete d->panIconPopup; +        d->panIconPopup = 0; +    } + +    d->panIconPopup    = new TDEPopupFrame(this); +    PanIconWidget *pan = new PanIconWidget(d->panIconPopup); +    pan->setImage(180, 120, getImage()); +    d->panIconPopup->setMainWidget(pan); + +    TQRect r((int)(contentsX()    / zoomFactor()), (int)(contentsY()     / zoomFactor()), +            (int)(visibleWidth() / zoomFactor()), (int)(visibleHeight() / zoomFactor())); +    pan->setRegionSelection(r); +    pan->setMouseFocus(); + +    connect(pan, TQ_SIGNAL(signalSelectionMoved(const TQRect&, bool)), +            this, TQ_SLOT(slotPanIconSelectionMoved(const TQRect&, bool))); +     +    connect(pan, TQ_SIGNAL(signalHiden()), +            this, TQ_SLOT(slotPanIconHiden())); +     +    TQPoint g = mapToGlobal(viewport()->pos()); +    g.setX(g.x()+ viewport()->size().width()); +    g.setY(g.y()+ viewport()->size().height()); +    d->panIconPopup->popup(TQPoint(g.x() - d->panIconPopup->width(),  +                                  g.y() - d->panIconPopup->height())); + +    pan->setCursorToLocalRegionSelectionCenter(); +} + +void LightTablePreview::slotPanIconHiden() +{ +    d->cornerButton->blockSignals(true); +    d->cornerButton->animateClick(); +    d->cornerButton->blockSignals(false); +} + +void LightTablePreview::slotPanIconSelectionMoved(const TQRect& r, bool b) +{ +    setContentsPos((int)(r.x()*zoomFactor()), (int)(r.y()*zoomFactor())); + +    if (b) +    { +        d->panIconPopup->hide(); +        delete d->panIconPopup; +        d->panIconPopup = 0; +        slotPanIconHiden(); +    } +} + +void LightTablePreview::zoomFactorChanged(double zoom) +{ +    updateScrollBars(); + +    if (horizontalScrollBar()->isVisible() || verticalScrollBar()->isVisible()) +        d->cornerButton->show(); +    else +        d->cornerButton->hide();         + +    PreviewWidget::zoomFactorChanged(zoom); +} + +void LightTablePreview::resizeEvent(TQResizeEvent* e) +{ +    if (!e) return; + +    TQScrollView::resizeEvent(e); + +    if (!d->imageInfo) +    { +        d->cornerButton->hide(); +        setDragAndDropMessage(); +    } + +    updateZoomAndSize(false); +} + +void LightTablePreview::updateZoomAndSize(bool alwaysFitToWindow) +{ +    // Set zoom for fit-in-window as minimum, but dont scale up images +    // that are smaller than the available space, only scale down. +    double zoom = calcAutoZoomFactor(ZoomInOnly); +    setZoomMin(zoom); +    setZoomMax(zoom*12.0); + +    // Is currently the zoom factor set to fit to window? Then set it again to fit the new size. +    if (zoomFactor() < zoom || alwaysFitToWindow || zoomFactor() == d->currentFitWindowZoom) +    { +        setZoomFactor(zoom); +    } + +    // store which zoom factor means it is fit to window +    d->currentFitWindowZoom = zoom; + +    updateContentsSize(); +} + +int LightTablePreview::previewWidth() +{ +    return d->preview.width(); +} + +int LightTablePreview::previewHeight() +{ +    return d->preview.height(); +} + +bool LightTablePreview::previewIsNull() +{ +    return d->preview.isNull(); +} + +void LightTablePreview::resetPreview() +{ +    d->preview   = DImg(); +    d->path      = TQString();  +    d->imageInfo = 0; + +    setDragAndDropMessage(); +    updateZoomAndSize(true); +    viewport()->setUpdatesEnabled(true); +    viewport()->update(); +    emit signalPreviewLoaded(false); +} + +void LightTablePreview::paintPreview(TQPixmap *pix, int sx, int sy, int sw, int sh) +{ +    DImg img     = d->preview.smoothScaleSection(sx, sy, sw, sh, tileSize(), tileSize());     +    TQPixmap pix2 = img.convertToPixmap(); +    bitBlt(pix, 0, 0, &pix2, 0, 0); +} + +void LightTablePreview::contentsDragMoveEvent(TQDragMoveEvent *e) +{ +    if (d->dragAndDropEnabled) +    { +        int             albumID; +        TQValueList<int> albumIDs; +        TQValueList<int> imageIDs; +        KURL::List      urls; +        KURL::List      kioURLs;         +     +        if (ItemDrag::decode(e, urls, kioURLs, albumIDs, imageIDs) || +            AlbumDrag::decode(e, urls, albumID) || +            TagDrag::canDecode(e)) +        { +            e->accept(); +            return; +        } +    } + +    e->ignore(); +} + +void LightTablePreview::contentsDropEvent(TQDropEvent *e) +{ +    if (d->dragAndDropEnabled) +    { +        int             albumID; +        TQValueList<int> albumIDs; +        TQValueList<int> imageIDs; +        KURL::List      urls; +        KURL::List      kioURLs;   +        ImageInfoList   list; +     +        if (ItemDrag::decode(e, urls, kioURLs, albumIDs, imageIDs)) +        { +            for (TQValueList<int>::const_iterator it = imageIDs.begin(); +                 it != imageIDs.end(); ++it) +            { +                list.append(new ImageInfo(*it)); +            } + +            emit signalDroppedItems(list); +            e->accept(); +            return; +        } +        else if (AlbumDrag::decode(e, urls, albumID)) +        { +            TQValueList<TQ_LLONG> itemIDs = AlbumManager::instance()->albumDB()->getItemIDsInAlbum(albumID); +     +            for (TQValueList<TQ_LLONG>::const_iterator it = itemIDs.begin(); +                it != itemIDs.end(); ++it) +            { +                list.append(new ImageInfo(*it)); +            } + +            emit signalDroppedItems(list); +            e->accept(); +            return; +        } +        else if(TagDrag::canDecode(e)) +        { +            TQByteArray ba = e->encodedData("digikam/tag-id"); +            TQDataStream ds(ba, IO_ReadOnly); +            int tagID; +            ds >> tagID; +     +            AlbumManager* man           = AlbumManager::instance(); +            TQValueList<TQ_LLONG> itemIDs = man->albumDB()->getItemIDsInTag(tagID, true); +            ImageInfoList imageInfoList; +     +            for (TQValueList<TQ_LLONG>::const_iterator it = itemIDs.begin(); +                it != itemIDs.end(); ++it) +            { +                list.append(new ImageInfo(*it)); +            } +         +            emit signalDroppedItems(list); +            e->accept(); +            return; +        }    +    } + +    e->ignore(); +} + +void LightTablePreview::setSelected(bool sel) +{ +    if (d->selected != sel) +    { +        d->selected = sel; +        frameChanged(); +    } +} + +bool LightTablePreview::isSelected() +{ +    return d->selected; +} + +void LightTablePreview::drawFrame(TQPainter *p) +{ +    if (d->selected) +    { +        qDrawPlainRect(p, frameRect(), ThemeEngine::instance()->thumbSelColor(), lineWidth()); +        qDrawPlainRect(p, frameRect(), ThemeEngine::instance()->textSelColor(), 2); +    } +    else  +        qDrawPlainRect(p, frameRect(), ThemeEngine::instance()->baseColor(), lineWidth()); +} + +}  // NameSpace Digikam | 
