diff options
author | Michele Calgaro <[email protected]> | 2024-11-22 18:41:30 +0900 |
---|---|---|
committer | Michele Calgaro <[email protected]> | 2024-11-22 18:41:30 +0900 |
commit | ee0d99607c14cb63d3ebdb3a970b508949fa8219 (patch) | |
tree | 94ac1efedb94cb38bf6879ba0610fe75b554216b /src/libs/thumbbar | |
parent | 4adff739380e4ae9f30e443ee95644f184456869 (diff) | |
download | digikam-ee0d99607c14cb63d3ebdb3a970b508949fa8219.tar.gz digikam-ee0d99607c14cb63d3ebdb3a970b508949fa8219.zip |
Rename 'digikam' folder to 'src'
Signed-off-by: Michele Calgaro <[email protected]>
Diffstat (limited to 'src/libs/thumbbar')
-rw-r--r-- | src/libs/thumbbar/Makefile.am | 18 | ||||
-rw-r--r-- | src/libs/thumbbar/thumbbar.cpp | 1138 | ||||
-rw-r--r-- | src/libs/thumbbar/thumbbar.h | 239 | ||||
-rw-r--r-- | src/libs/thumbbar/thumbnailjob.cpp | 318 | ||||
-rw-r--r-- | src/libs/thumbbar/thumbnailjob.h | 88 |
5 files changed, 1801 insertions, 0 deletions
diff --git a/src/libs/thumbbar/Makefile.am b/src/libs/thumbbar/Makefile.am new file mode 100644 index 00000000..7e885514 --- /dev/null +++ b/src/libs/thumbbar/Makefile.am @@ -0,0 +1,18 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = libthumbbar.la + +libthumbbar_la_SOURCES = thumbbar.cpp thumbnailjob.cpp + +libthumbbar_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor + +INCLUDES = -I$(top_srcdir)/src/libs/dimg \ + -I$(top_srcdir)/src/libs/dmetadata \ + -I$(top_srcdir)/src/digikam \ + $(LIBKDCRAW_CFLAGS) \ + $(LIBKEXIV2_CFLAGS) \ + $(all_includes) + +digikaminclude_HEADERS = thumbbar.h + +digikamincludedir = $(includedir)/digikam diff --git a/src/libs/thumbbar/thumbbar.cpp b/src/libs/thumbbar/thumbbar.cpp new file mode 100644 index 00000000..f5b32767 --- /dev/null +++ b/src/libs/thumbbar/thumbbar.cpp @@ -0,0 +1,1138 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-22 + * Description : a bar widget to display image thumbnails + * + * Copyright (C) 2004-2005 by Renchi Raju <[email protected]> + * Copyright (C) 2005-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. + * + * ============================================================ */ + +// C Ansi includes. + +extern "C" +{ +#include <unistd.h> +} + +// C++ includes. + +#include <cmath> + +// TQt includes. + +#include <tqdir.h> +#include <tqpixmap.h> +#include <tqimage.h> +#include <tqtimer.h> +#include <tqpainter.h> +#include <tqdict.h> +#include <tqpoint.h> +#include <tqstylesheet.h> +#include <tqdatetime.h> +#include <tqguardedptr.h> + +// KDE includes. + +#include <kmdcodec.h> +#include <tdefileitem.h> +#include <tdeapplication.h> +#include <kiconloader.h> +#include <tdelocale.h> +#include <kmimetype.h> +#include <tdefileitem.h> +#include <tdeglobal.h> + +// LibKDcraw includes. + +#include <libkdcraw/version.h> +#include <libkdcraw/kdcraw.h> + +#if KDCRAW_VERSION < 0x000106 +#include <libkdcraw/dcrawbinary.h> +#endif + +// Local includes. + +#include "dmetadata.h" +#include "thumbnailjob.h" +#include "thumbnailsize.h" +#include "thumbbar.h" +#include "thumbbar.moc" + +namespace Digikam +{ + +class ThumbBarViewPriv +{ +public: + + ThumbBarViewPriv() : + margin(5) + { + dragging = false; + exifRotate = false; + clearing = false; + toolTip = 0; + firstItem = 0; + lastItem = 0; + currItem = 0; + count = 0; + thumbJob = 0; + tileSize = ThumbnailSize::Small; + + itemDict.setAutoDelete(false); + } + + bool clearing; + bool exifRotate; + bool dragging; + + const int margin; + int count; + int tileSize; + int orientation; + + TQTimer *timer; + + TQPoint dragStartPos; + + ThumbBarItem *firstItem; + ThumbBarItem *lastItem; + ThumbBarItem *currItem; + + TQDict<ThumbBarItem> itemDict; + TQGuardedPtr<ThumbnailJob> thumbJob; + + ThumbBarToolTipSettings toolTipSettings; + + ThumbBarToolTip *toolTip; +}; + +// ------------------------------------------------------------------------- + +class ThumbBarItemPriv +{ +public: + + ThumbBarItemPriv() + { + pos = 0; + pixmap = 0; + next = 0; + prev = 0; + view = 0; + } + + int pos; + + TQPixmap *pixmap; + + KURL url; + + ThumbBarItem *next; + ThumbBarItem *prev; + + ThumbBarView *view; +}; + +// ------------------------------------------------------------------------- + +ThumbBarView::ThumbBarView(TQWidget* parent, int orientation, bool exifRotate, + ThumbBarToolTipSettings settings) + : TQScrollView(parent) +{ + d = new ThumbBarViewPriv; + d->orientation = orientation; + d->exifRotate = exifRotate; + d->toolTipSettings = settings; + d->toolTip = new ThumbBarToolTip(this); + d->timer = new TQTimer(this); + + connect(d->timer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotUpdate())); + + viewport()->setBackgroundMode(TQt::NoBackground); + viewport()->setMouseTracking(true); + viewport()->setAcceptDrops(true); + + setFrameStyle(TQFrame::NoFrame); + setAcceptDrops(true); + + if (d->orientation ==TQt::Vertical) + { + setHScrollBarMode(TQScrollView::AlwaysOff); + } + else + { + setVScrollBarMode(TQScrollView::AlwaysOff); + } +} + +ThumbBarView::~ThumbBarView() +{ + if (!d->thumbJob.isNull()) + { + d->thumbJob->kill(); + d->thumbJob = 0; + } + + clear(false); + + delete d->timer; + delete d->toolTip; + delete d; +} + +void ThumbBarView::resizeEvent(TQResizeEvent* e) +{ + if (!e) return; + + TQScrollView::resizeEvent(e); + + if (d->orientation ==TQt::Vertical) + { + d->tileSize = width() - 2*d->margin - verticalScrollBar()->sizeHint().width(); + verticalScrollBar()->setLineStep(d->tileSize); + verticalScrollBar()->setPageStep(2*d->tileSize); + } + else + { + d->tileSize = height() - 2*d->margin - horizontalScrollBar()->sizeHint().height(); + horizontalScrollBar()->setLineStep(d->tileSize); + horizontalScrollBar()->setPageStep(2*d->tileSize); + } + + rearrangeItems(); + ensureItemVisible(currentItem()); +} + +void ThumbBarView::setExifRotate(bool exifRotate) +{ + if (d->exifRotate == exifRotate) + return; + + d->exifRotate = exifRotate; + TQString thumbCacheDir = TQDir::homeDirPath() + "/.thumbnails/"; + + for (ThumbBarItem *item = d->firstItem; item; item = item->d->next) + { + // Remove all current album item thumbs from disk cache. + + TQString uri = "file://" + TQDir::cleanDirPath(item->url().path(-1)); + KMD5 md5(TQFile::encodeName(uri).data()); + uri = md5.hexDigest(); + + TQString smallThumbPath = thumbCacheDir + "normal/" + uri + ".png"; + TQString bigThumbPath = thumbCacheDir + "large/" + uri + ".png"; + + ::unlink(TQFile::encodeName(smallThumbPath)); + ::unlink(TQFile::encodeName(bigThumbPath)); + + invalidateThumb(item); + } + + triggerUpdate(); +} + +bool ThumbBarView::getExifRotate() +{ + return d->exifRotate; +} + +int ThumbBarView::getOrientation() +{ + return d->orientation; +} + +int ThumbBarView::getTileSize() +{ + return d->tileSize; +} + +int ThumbBarView::getMargin() +{ + return d->margin; +} + +void ThumbBarView::setToolTipSettings(const ThumbBarToolTipSettings &settings) +{ + d->toolTipSettings = settings; +} + +ThumbBarToolTipSettings& ThumbBarView::getToolTipSettings() +{ + return d->toolTipSettings; +} + +int ThumbBarView::countItems() +{ + return d->count; +} + +KURL::List ThumbBarView::itemsURLs() +{ + KURL::List urlList; + if (!countItems()) + return urlList; + + for (ThumbBarItem *item = firstItem(); item; item = item->next()) + urlList.append(item->url()); + + return urlList; +} + +void ThumbBarView::clear(bool updateView) +{ + d->clearing = true; + + ThumbBarItem *item = d->firstItem; + while (item) + { + ThumbBarItem *tmp = item->d->next; + delete item; + item = tmp; + } + + d->firstItem = 0; + d->lastItem = 0; + d->count = 0; + d->currItem = 0; + + if (updateView) + slotUpdate(); + + d->clearing = false; + + emit signalItemSelected(0); +} + +void ThumbBarView::triggerUpdate() +{ + d->timer->start(0, true); +} + +ThumbBarItem* ThumbBarView::currentItem() const +{ + return d->currItem; +} + +ThumbBarItem* ThumbBarView::firstItem() const +{ + return d->firstItem; +} + +ThumbBarItem* ThumbBarView::lastItem() const +{ + return d->lastItem; +} + +ThumbBarItem* ThumbBarView::findItem(const TQPoint& pos) const +{ + int itemPos; + + if (d->orientation ==TQt::Vertical) + itemPos = pos.y(); + else + itemPos = pos.x(); + + for (ThumbBarItem *item = d->firstItem; item; item = item->d->next) + { + if (itemPos >= item->d->pos && itemPos <= (item->d->pos+d->tileSize+2*d->margin)) + { + return item; + } + } + + return 0; +} + +ThumbBarItem* ThumbBarView::findItemByURL(const KURL& url) const +{ + for (ThumbBarItem *item = d->firstItem; item; item = item->d->next) + { + if (item->url().equals(url)) + { + return item; + } + } + + return 0; +} + +void ThumbBarView::setSelected(ThumbBarItem* item) +{ + if (!item) return; + + ensureItemVisible(item); + emit signalURLSelected(item->url()); + emit signalItemSelected(item); + + if (d->currItem == item) return; + + if (d->currItem) + { + ThumbBarItem* item = d->currItem; + d->currItem = 0; + item->repaint(); + } + + d->currItem = item; + if (d->currItem) + item->repaint(); +} + +void ThumbBarView::ensureItemVisible(ThumbBarItem* item) +{ + if (item) + { + // We want the complete thumb visible and the next one. + // find the middle of the image and give a margin of 1,5 image + // When changed, watch regression for bug 104031 + if (d->orientation ==TQt::Vertical) + ensureVisible(0, (int)(item->d->pos + d->margin + d->tileSize*.5), + 0, (int)(d->tileSize*1.5 + 3*d->margin)); + else + ensureVisible((int)(item->d->pos + d->margin + d->tileSize*.5), 0, + (int)(d->tileSize*1.5 + 3*d->margin), 0); + } +} + +void ThumbBarView::refreshThumbs(const KURL::List& urls) +{ + for (KURL::List::const_iterator it = urls.begin() ; it != urls.end() ; ++it) + { + ThumbBarItem *item = findItemByURL(*it); + if (item) + { + invalidateThumb(item); + } + } +} + +void ThumbBarView::invalidateThumb(ThumbBarItem* item) +{ + if (!item) return; + + if (item->d->pixmap) + { + delete item->d->pixmap; + item->d->pixmap = 0; + } + + if (!d->thumbJob.isNull()) + { + d->thumbJob->kill(); + d->thumbJob = 0; + } + + d->thumbJob = new ThumbnailJob(item->url(), ThumbnailSize::Huge, true, d->exifRotate); + + connect(d->thumbJob, TQ_SIGNAL(signalThumbnail(const KURL&, const TQPixmap&)), + this, TQ_SLOT(slotGotThumbnail(const KURL&, const TQPixmap&))); + + connect(d->thumbJob, TQ_SIGNAL(signalFailed(const KURL&)), + this, TQ_SLOT(slotFailedThumbnail(const KURL&))); +} + +void ThumbBarView::viewportPaintEvent(TQPaintEvent* e) +{ + int cy, cx, ts, y1, y2, x1, x2; + TQPixmap bgPix, tile; + TQRect er(e->rect()); + + if (d->orientation ==TQt::Vertical) + { + cy = viewportToContents(er.topLeft()).y(); + + bgPix.resize(contentsRect().width(), er.height()); + + ts = d->tileSize + 2*d->margin; + tile.resize(visibleWidth(), ts); + + y1 = (cy/ts)*ts; + y2 = ((y1 + er.height())/ts +1)*ts; + } + else + { + cx = viewportToContents(er.topLeft()).x(); + + bgPix.resize(er.width(), contentsRect().height()); + + ts = d->tileSize + 2*d->margin; + tile.resize(ts, visibleHeight()); + + x1 = (cx/ts)*ts; + x2 = ((x1 + er.width())/ts +1)*ts; + } + + bgPix.fill(colorGroup().background()); + + for (ThumbBarItem *item = d->firstItem; item; item = item->d->next) + { + if (d->orientation ==TQt::Vertical) + { + if (y1 <= item->d->pos && item->d->pos <= y2) + { + if (item == d->currItem) + tile.fill(colorGroup().highlight()); + else + tile.fill(colorGroup().background()); + + TQPainter p(&tile); + p.setPen(TQt::white); + p.drawRect(0, 0, tile.width(), tile.height()); + p.end(); + + if (item->d->pixmap) + { + TQPixmap pix; + pix.convertFromImage(TQImage(item->d->pixmap->convertToImage()). + smoothScale(d->tileSize, d->tileSize, TQImage::ScaleMin)); + int x = (tile.width() - pix.width())/2; + int y = (tile.height() - pix.height())/2; + bitBlt(&tile, x, y, &pix); + } + + bitBlt(&bgPix, 0, item->d->pos - cy, &tile); + } + } + else + { + if (x1 <= item->d->pos && item->d->pos <= x2) + { + if (item == d->currItem) + tile.fill(colorGroup().highlight()); + else + tile.fill(colorGroup().background()); + + TQPainter p(&tile); + p.setPen(TQt::white); + p.drawRect(0, 0, tile.width(), tile.height()); + p.end(); + + if (item->d->pixmap) + { + TQPixmap pix; + pix.convertFromImage(TQImage(item->d->pixmap->convertToImage()). + smoothScale(d->tileSize, d->tileSize, TQImage::ScaleMin)); + int x = (tile.width() - pix.width())/2; + int y = (tile.height()- pix.height())/2; + bitBlt(&tile, x, y, &pix); + } + + bitBlt(&bgPix, item->d->pos - cx, 0, &tile); + } + } + } + + if (d->orientation ==TQt::Vertical) + bitBlt(viewport(), 0, er.y(), &bgPix); + else + bitBlt(viewport(), er.x(), 0, &bgPix); +} + +void ThumbBarView::contentsMousePressEvent(TQMouseEvent* e) +{ + ThumbBarItem* barItem = findItem(e->pos()); + d->dragging = true; + d->dragStartPos = e->pos(); + + if (!barItem || barItem == d->currItem) + return; + + if (d->currItem) + { + ThumbBarItem* item = d->currItem; + d->currItem = 0; + item->repaint(); + } + + d->currItem = barItem; + barItem->repaint(); +} + +void ThumbBarView::contentsMouseMoveEvent(TQMouseEvent *e) +{ + if (!e) return; + + if (d->dragging && (e->state() & TQt::LeftButton)) + { + if ( findItem(d->dragStartPos) && + (d->dragStartPos - e->pos()).manhattanLength() > TQApplication::startDragDistance() ) + { + startDrag(); + } + return; + } +} + +void ThumbBarView::contentsMouseReleaseEvent(TQMouseEvent *e) +{ + d->dragging = false; + ThumbBarItem *item = findItem(e->pos()); + if (item) + { + emit signalURLSelected(item->url()); + emit signalItemSelected(item); + } +} + +void ThumbBarView::contentsWheelEvent(TQWheelEvent *e) +{ + e->accept(); + + if (e->delta() < 0) + { + if (e->state() & TQt::ShiftButton) + { + if (d->orientation ==TQt::Vertical) + scrollBy(0, verticalScrollBar()->pageStep()); + else + scrollBy(horizontalScrollBar()->pageStep(), 0); + } + else + { + if (d->orientation ==TQt::Vertical) + scrollBy(0, verticalScrollBar()->lineStep()); + else + scrollBy(horizontalScrollBar()->lineStep(), 0); + } + } + + if (e->delta() > 0) + { + if (e->state() & TQt::ShiftButton) + { + if (d->orientation ==TQt::Vertical) + scrollBy(0, (-1)*verticalScrollBar()->pageStep()); + else + scrollBy((-1)*horizontalScrollBar()->pageStep(), 0); + } + else + { + if (d->orientation ==TQt::Vertical) + scrollBy(0, (-1)*verticalScrollBar()->lineStep()); + else + scrollBy((-1)*horizontalScrollBar()->lineStep(), 0); + } + } +} + +void ThumbBarView::startDrag() +{ +} + +void ThumbBarView::insertItem(ThumbBarItem* item) +{ + if (!item) return; + + if (!d->firstItem) + { + d->firstItem = item; + d->lastItem = item; + item->d->prev = 0; + item->d->next = 0; + } + else + { + d->lastItem->d->next = item; + item->d->prev = d->lastItem; + item->d->next = 0; + d->lastItem = item; + + } + + if (!d->currItem) + { + d->currItem = item; + emit signalURLSelected(item->url()); + emit signalItemSelected(item); + } + + d->itemDict.insert(item->url().url(), item); + + d->count++; + triggerUpdate(); + emit signalItemAdded(); +} + +void ThumbBarView::removeItem(ThumbBarItem* item) +{ + if (!item) return; + + d->count--; + + if (item == d->firstItem) + { + d->firstItem = d->currItem = d->firstItem->d->next; + if (d->firstItem) + d->firstItem->d->prev = 0; + else + d->firstItem = d->lastItem = d->currItem = 0; + } + else if (item == d->lastItem) + { + d->lastItem = d->currItem = d->lastItem->d->prev; + if ( d->lastItem ) + d->lastItem->d->next = 0; + else + d->firstItem = d->lastItem = d->currItem = 0; + } + else + { + ThumbBarItem *i = item; + if (i) + { + if (i->d->prev ) + { + i->d->prev->d->next = d->currItem = i->d->next; + } + if ( i->d->next ) + { + i->d->next->d->prev = d->currItem = i->d->prev; + } + } + } + + d->itemDict.remove(item->url().url()); + + if (!d->clearing) + { + triggerUpdate(); + } + + if (d->count == 0) + emit signalItemSelected(0); +} + +void ThumbBarView::rearrangeItems() +{ + KURL::List urlList; + + int pos = 0; + ThumbBarItem *item = d->firstItem; + + while (item) + { + item->d->pos = pos; + pos += d->tileSize + 2*d->margin; + if (!(item->d->pixmap)) + urlList.append(item->d->url); + item = item->d->next; + } + + if (d->orientation ==TQt::Vertical) + resizeContents(visibleWidth(), d->count*(d->tileSize+2*d->margin)); + else + resizeContents(d->count*(d->tileSize+2*d->margin), visibleHeight()); + + if (!urlList.isEmpty()) + { + if (!d->thumbJob.isNull()) + { + d->thumbJob->kill(); + d->thumbJob = 0; + } + + d->thumbJob = new ThumbnailJob(urlList, ThumbnailSize::Huge, true, d->exifRotate); + + connect(d->thumbJob, TQ_SIGNAL(signalThumbnail(const KURL&, const TQPixmap&)), + this, TQ_SLOT(slotGotThumbnail(const KURL&, const TQPixmap&))); + + connect(d->thumbJob, TQ_SIGNAL(signalFailed(const KURL&)), + this, TQ_SLOT(slotFailedThumbnail(const KURL&))); + } +} + +void ThumbBarView::repaintItem(ThumbBarItem* item) +{ + if (item) + { + if (d->orientation ==TQt::Vertical) + repaintContents(0, item->d->pos, visibleWidth(), d->tileSize+2*d->margin); + else + repaintContents(item->d->pos, 0, d->tileSize+2*d->margin, visibleHeight()); + } +} + +void ThumbBarView::slotUpdate() +{ + rearrangeItems(); + viewport()->update(); +} + +void ThumbBarView::slotGotThumbnail(const KURL& url, const TQPixmap& pix) +{ + if (!pix.isNull()) + { + ThumbBarItem* item = d->itemDict.find(url.url()); + if (!item) + return; + + if (item->d->pixmap) + { + delete item->d->pixmap; + item->d->pixmap = 0; + } + + item->d->pixmap = new TQPixmap(pix); + item->repaint(); + } +} + +void ThumbBarView::slotFailedThumbnail(const KURL& url) +{ + ThumbBarItem* item = d->itemDict.find(url.url()); + if (!item) + return; + + TDEIconLoader* iconLoader = TDEApplication::kApplication()->iconLoader(); + TQPixmap pix = iconLoader->loadIcon("image-x-generic", TDEIcon::NoGroup, ThumbnailSize::Huge); + + if (item->d->pixmap) + { + delete item->d->pixmap; + item->d->pixmap = 0; + } + + item->d->pixmap = new TQPixmap(pix); + item->repaint(); +} + +// ------------------------------------------------------------------------- + +ThumbBarItem::ThumbBarItem(ThumbBarView* view, const KURL& url) +{ + d = new ThumbBarItemPriv; + d->url = url; + d->view = view; + d->view->insertItem(this); +} + +ThumbBarItem::~ThumbBarItem() +{ + d->view->removeItem(this); + + if (d->pixmap) + delete d->pixmap; + + delete d; +} + +KURL ThumbBarItem::url() const +{ + return d->url; +} + +ThumbBarItem* ThumbBarItem::next() const +{ + return d->next; +} + +ThumbBarItem* ThumbBarItem::prev() const +{ + return d->prev; +} + +TQRect ThumbBarItem::rect() const +{ + if (d->view->d->orientation == ThumbBarView::Vertical) + { + return TQRect(0, d->pos, + d->view->visibleWidth(), + d->view->d->tileSize + 2*d->view->d->margin); + } + else + { + return TQRect(d->pos, 0, + d->view->d->tileSize + 2*d->view->d->margin, + d->view->visibleHeight()); + } +} + +int ThumbBarItem::position() const +{ + return d->pos; +} + +TQPixmap* ThumbBarItem::pixmap() const +{ + return d->pixmap; +} + +void ThumbBarItem::repaint() +{ + d->view->repaintItem(this); +} + +// ------------------------------------------------------------------------- + +ThumbBarToolTip::ThumbBarToolTip(ThumbBarView* parent) + : TQToolTip(parent->viewport()), m_maxStringLen(30), m_view(parent) +{ + m_headBeg = TQString("<tr bgcolor=\"orange\"><td colspan=\"2\">" + "<nobr><font size=\"-1\" color=\"black\"><b>"); + m_headEnd = TQString("</b></font></nobr></td></tr>"); + + m_cellBeg = TQString("<tr><td><nobr><font size=\"-1\" color=\"black\">"); + m_cellMid = TQString("</font></nobr></td>" + "<td><nobr><font size=\"-1\" color=\"black\">"); + m_cellEnd = TQString("</font></nobr></td></tr>"); + + m_cellSpecBeg = TQString("<tr><td><nobr><font size=\"-1\" color=\"black\">"); + m_cellSpecMid = TQString("</font></nobr></td>" + "<td><nobr><font size=\"-1\" color=\"steelblue\"><i>"); + m_cellSpecEnd = TQString("</i></font></nobr></td></tr>"); +} + +void ThumbBarToolTip::maybeTip(const TQPoint& pos) +{ + if ( !parentWidget() || !m_view) return; + + ThumbBarItem* item = m_view->findItem( m_view->viewportToContents(pos) ); + if (!item) return; + + if (!m_view->getToolTipSettings().showToolTips) return; + + TQString tipText = tipContent(item); + tipText.append(tipContentExtraData(item)); + tipText.append("</table>"); + + TQRect r(item->rect()); + r = TQRect( m_view->contentsToViewport(r.topLeft()), r.size() ); + + tip(r, tipText); +} + +TQString ThumbBarToolTip::tipContent(ThumbBarItem* item) +{ + ThumbBarToolTipSettings settings = m_view->getToolTipSettings(); + + TQString tipText, str; + TQString unavailable(i18n("unavailable")); + + tipText = "<table cellspacing=\"0\" cellpadding=\"0\" width=\"250\" border=\"0\">"; + + TQFileInfo fileInfo(item->url().path()); + + KFileItem fi(KFileItem::Unknown, KFileItem::Unknown, item->url()); + DMetadata metaData(item->url().path()); + + // -- File properties ---------------------------------------------- + + if (settings.showFileName || + settings.showFileDate || + settings.showFileSize || + settings.showImageType || + settings.showImageDim) + { + tipText += m_headBeg + i18n("File Properties") + m_headEnd; + + if (settings.showFileName) + { + tipText += m_cellBeg + i18n("Name:") + m_cellMid; + tipText += item->url().fileName() + m_cellEnd; + } + + if (settings.showFileDate) + { + TQDateTime modifiedDate = fileInfo.lastModified(); + str = TDEGlobal::locale()->formatDateTime(modifiedDate, true, true); + tipText += m_cellBeg + i18n("Modified:") + m_cellMid + str + m_cellEnd; + } + + if (settings.showFileSize) + { + tipText += m_cellBeg + i18n("Size:") + m_cellMid; + str = i18n("%1 (%2)").arg(TDEIO::convertSize(fi.size())) + .arg(TDEGlobal::locale()->formatNumber(fi.size(), 0)); + tipText += str + m_cellEnd; + } + + TQSize dims; +#if KDCRAW_VERSION < 0x000106 + TQString rawFilesExt(KDcrawIface::DcrawBinary::instance()->rawFiles()); +#else + TQString rawFilesExt(KDcrawIface::KDcraw::rawFiles()); +#endif + TQString ext = fileInfo.extension(false).upper(); + + if (!ext.isEmpty() && rawFilesExt.upper().contains(ext)) + { + str = i18n("RAW Image"); + dims = metaData.getImageDimensions(); + } + else + { + str = fi.mimeComment(); + + KFileMetaInfo meta = fi.metaInfo(); + if (meta.isValid()) + { + if (meta.containsGroup("Jpeg EXIF Data")) + dims = meta.group("Jpeg EXIF Data").item("Dimensions").value().toSize(); + else if (meta.containsGroup("General")) + dims = meta.group("General").item("Dimensions").value().toSize(); + else if (meta.containsGroup("Technical")) + dims = meta.group("Technical").item("Dimensions").value().toSize(); + } + } + + if (settings.showImageType) + { + tipText += m_cellBeg + i18n("Type:") + m_cellMid + str + m_cellEnd; + } + + if (settings.showImageDim) + { + TQString mpixels; + mpixels.setNum(dims.width()*dims.height()/1000000.0, 'f', 2); + str = (!dims.isValid()) ? i18n("Unknown") : i18n("%1x%2 (%3Mpx)") + .arg(dims.width()).arg(dims.height()).arg(mpixels); + tipText += m_cellBeg + i18n("Dimensions:") + m_cellMid + str + m_cellEnd; + } + } + + // -- Photograph Info ---------------------------------------------------- + + if (settings.showPhotoMake || + settings.showPhotoDate || + settings.showPhotoFocal || + settings.showPhotoExpo || + settings.showPhotoMode || + settings.showPhotoFlash || + settings.showPhotoWB) + { + PhotoInfoContainer photoInfo = metaData.getPhotographInformations(); + + if (!photoInfo.isEmpty()) + { + TQString metaStr; + tipText += m_headBeg + i18n("Photograph Properties") + m_headEnd; + + if (settings.showPhotoMake) + { + str = TQString("%1 / %2").arg(photoInfo.make.isEmpty() ? unavailable : photoInfo.make) + .arg(photoInfo.model.isEmpty() ? unavailable : photoInfo.model); + if (str.length() > m_maxStringLen) str = str.left(m_maxStringLen-3) + "..."; + metaStr += m_cellBeg + i18n("Make/Model:") + m_cellMid + TQStyleSheet::escape( str ) + m_cellEnd; + } + + if (settings.showPhotoDate) + { + if (photoInfo.dateTime.isValid()) + { + str = TDEGlobal::locale()->formatDateTime(photoInfo.dateTime, true, true); + if (str.length() > m_maxStringLen) str = str.left(m_maxStringLen-3) + "..."; + metaStr += m_cellBeg + i18n("Created:") + m_cellMid + TQStyleSheet::escape( str ) + m_cellEnd; + } + else + metaStr += m_cellBeg + i18n("Created:") + m_cellMid + TQStyleSheet::escape( unavailable ) + m_cellEnd; + } + + if (settings.showPhotoFocal) + { + str = photoInfo.aperture.isEmpty() ? unavailable : photoInfo.aperture; + + if (photoInfo.focalLength35mm.isEmpty()) + str += TQString(" / %1").arg(photoInfo.focalLength.isEmpty() ? unavailable : photoInfo.focalLength); + else + str += TQString(" / %1").arg(i18n("%1 (35mm: %2)").arg(photoInfo.focalLength).arg(photoInfo.focalLength35mm)); + + if (str.length() > m_maxStringLen) str = str.left(m_maxStringLen-3) + "..."; + metaStr += m_cellBeg + i18n("Aperture/Focal:") + m_cellMid + TQStyleSheet::escape( str ) + m_cellEnd; + } + + if (settings.showPhotoExpo) + { + str = TQString("%1 / %2").arg(photoInfo.exposureTime.isEmpty() ? unavailable : photoInfo.exposureTime) + .arg(photoInfo.sensitivity.isEmpty() ? unavailable : i18n("%1 ISO").arg(photoInfo.sensitivity)); + if (str.length() > m_maxStringLen) str = str.left(m_maxStringLen-3) + "..."; + metaStr += m_cellBeg + i18n("Exposure/Sensitivity:") + m_cellMid + TQStyleSheet::escape( str ) + m_cellEnd; + } + + if (settings.showPhotoMode) + { + + if (photoInfo.exposureMode.isEmpty() && photoInfo.exposureProgram.isEmpty()) + str = unavailable; + else if (!photoInfo.exposureMode.isEmpty() && photoInfo.exposureProgram.isEmpty()) + str = photoInfo.exposureMode; + else if (photoInfo.exposureMode.isEmpty() && !photoInfo.exposureProgram.isEmpty()) + str = photoInfo.exposureProgram; + else + str = TQString("%1 / %2").arg(photoInfo.exposureMode).arg(photoInfo.exposureProgram); + if (str.length() > m_maxStringLen) str = str.left(m_maxStringLen-3) + "..."; + metaStr += m_cellBeg + i18n("Mode/Program:") + m_cellMid + TQStyleSheet::escape( str ) + m_cellEnd; + } + + if (settings.showPhotoFlash) + { + str = photoInfo.flash.isEmpty() ? unavailable : photoInfo.flash; + if (str.length() > m_maxStringLen) str = str.left(m_maxStringLen-3) + "..."; + metaStr += m_cellBeg + i18n("Flash:") + m_cellMid + TQStyleSheet::escape( str ) + m_cellEnd; + } + + if (settings.showPhotoWB) + { + str = photoInfo.whiteBalance.isEmpty() ? unavailable : photoInfo.whiteBalance; + if (str.length() > m_maxStringLen) str = str.left(m_maxStringLen-3) + "..."; + metaStr += m_cellBeg + i18n("White Balance:") + m_cellMid + TQStyleSheet::escape( str ) + m_cellEnd; + } + + tipText += metaStr; + } + } + + return tipText; +} + +TQString ThumbBarToolTip::breakString(const TQString& input) +{ + TQString str = input.simplifyWhiteSpace(); + str = TQStyleSheet::escape(str); + const uint maxLen = m_maxStringLen; + + if (str.length() <= maxLen) + return str; + + TQString br; + + uint i = 0; + uint count = 0; + + while (i < str.length()) + { + if (count >= maxLen && str[i].isSpace()) + { + count = 0; + br.append("<br>"); + } + else + { + br.append(str[i]); + } + + i++; + count++; + } + + return br; +} + +} // NameSpace Digikam diff --git a/src/libs/thumbbar/thumbbar.h b/src/libs/thumbbar/thumbbar.h new file mode 100644 index 00000000..48ac574d --- /dev/null +++ b/src/libs/thumbbar/thumbbar.h @@ -0,0 +1,239 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-11-22 + * Description : a bar widget to display image thumbnails + * + * Copyright (C) 2004-2005 by Renchi Raju <[email protected]> + * Copyright (C) 2005-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. + * + * ============================================================ */ + +#ifndef THUMBBAR_H +#define THUMBBAR_H + +// TQt includes. + +#include <tqstring.h> +#include <tqscrollview.h> +#include <tqtooltip.h> + +// KDE includes. + +#include <kurl.h> + +// Local includes. + +#include "digikam_export.h" + +namespace Digikam +{ + +class ThumbBarItem; +class ThumbBarToolTip; +class ThumbBarViewPriv; +class ThumbBarItemPriv; + +class DIGIKAM_EXPORT ThumbBarToolTipSettings +{ +public: + + ThumbBarToolTipSettings() + { + showToolTips = true; + showFileName = true; + showFileDate = false; + showFileSize = false; + showImageType = false; + showImageDim = true; + showPhotoMake = true; + showPhotoDate = true; + showPhotoFocal = true; + showPhotoExpo = true; + showPhotoMode = true; + showPhotoFlash = false; + showPhotoWB = false; + }; + + bool showToolTips; + bool showFileName; + bool showFileDate; + bool showFileSize; + bool showImageType; + bool showImageDim; + bool showPhotoMake; + bool showPhotoDate; + bool showPhotoFocal; + bool showPhotoExpo; + bool showPhotoMode; + bool showPhotoFlash; + bool showPhotoWB; +}; + +// ------------------------------------------------------------------------- + +class DIGIKAM_EXPORT ThumbBarView : public TQScrollView +{ + TQ_OBJECT + + +public: + + enum Orientation + { + Horizontal=0, + Vertical + }; + +public: + + ThumbBarView(TQWidget* parent, int orientation=Vertical, bool exifRotate=true, + ThumbBarToolTipSettings settings=ThumbBarToolTipSettings()); + virtual ~ThumbBarView(); + + int countItems(); + KURL::List itemsURLs(); + + void clear(bool updateView=true); + void triggerUpdate(); + + void removeItem(ThumbBarItem* item); + + void setSelected(ThumbBarItem* item); + void ensureItemVisible(ThumbBarItem* item); + + void setExifRotate(bool exifRotate); + bool getExifRotate(); + + void setToolTipSettings(const ThumbBarToolTipSettings &settings); + ThumbBarToolTipSettings& getToolTipSettings(); + + ThumbBarItem* currentItem() const; + ThumbBarItem* firstItem() const; + ThumbBarItem* lastItem() const; + ThumbBarItem* findItem(const TQPoint& pos) const; + ThumbBarItem* findItemByURL(const KURL& url) const; + + void refreshThumbs(const KURL::List& urls); + void invalidateThumb(ThumbBarItem* item); + +signals: + + void signalItemSelected(ThumbBarItem*); + void signalURLSelected(const KURL&); + void signalItemAdded(); + +protected: + + int getOrientation(); + int getTileSize(); + int getMargin(); + + void resizeEvent(TQResizeEvent*); + void contentsMousePressEvent(TQMouseEvent*); + void contentsMouseMoveEvent(TQMouseEvent*); + void contentsMouseReleaseEvent(TQMouseEvent*); + void contentsWheelEvent(TQWheelEvent*); + + void insertItem(ThumbBarItem* item); + void rearrangeItems(); + void repaintItem(ThumbBarItem* item); + + virtual void viewportPaintEvent(TQPaintEvent*); + virtual void startDrag(); + +protected slots: + + void slotUpdate(); + +private slots: + + void slotGotThumbnail(const KURL&, const TQPixmap&); + void slotFailedThumbnail(const KURL&); + +private: + + ThumbBarViewPriv* d; + + friend class ThumbBarItem; +}; + +// ------------------------------------------------------------------------- + +class DIGIKAM_EXPORT ThumbBarItem +{ +public: + + ThumbBarItem(ThumbBarView *view, const KURL& url); + virtual ~ThumbBarItem(); + + KURL url() const; + + ThumbBarItem* next() const; + ThumbBarItem* prev() const; + int position() const; + TQRect rect() const; + TQPixmap* pixmap() const; + + void repaint(); + +private: + + ThumbBarItemPriv* d; + + friend class ThumbBarView; +}; + +// ------------------------------------------------------------------------- + +class DIGIKAM_EXPORT ThumbBarToolTip : public TQToolTip +{ + +public: + + ThumbBarToolTip(ThumbBarView *parent); + virtual ~ThumbBarToolTip(){}; + +protected: + + const uint m_maxStringLen; + + TQString m_headBeg; + TQString m_headEnd; + TQString m_cellBeg; + TQString m_cellMid; + TQString m_cellEnd; + TQString m_cellSpecBeg; + TQString m_cellSpecMid; + TQString m_cellSpecEnd; + + ThumbBarView *m_view; + +protected: + + TQString breakString(const TQString& input); + + virtual TQString tipContentExtraData(ThumbBarItem*){ return TQString(); }; + +private: + + void maybeTip(const TQPoint& pos); + TQString tipContent(ThumbBarItem* item); +}; + +} // NameSpace Digikam + +#endif /* THUMBBAR_H */ diff --git a/src/libs/thumbbar/thumbnailjob.cpp b/src/libs/thumbbar/thumbnailjob.cpp new file mode 100644 index 00000000..dcabb393 --- /dev/null +++ b/src/libs/thumbbar/thumbnailjob.cpp @@ -0,0 +1,318 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-10-14 + * Description : digiKam TDEIO thumbnails generator interface + * + * Copyright (C) 2003-2005 by Renchi Raju <[email protected]> + * Copyright (C) 2006-2007 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. + * + * ============================================================ */ + +// C Ansi includes. + +extern "C" +{ +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <fcntl.h> +#include <unistd.h> +} + +// TQt includes. + +#include <tqstring.h> +#include <tqdir.h> +#include <tqfileinfo.h> +#include <tqimage.h> +#include <tqpixmap.h> +#include <tqpainter.h> +#include <tqcolor.h> +#include <tqdatastream.h> + +// KDE includes. + +#include <tdeglobal.h> + +// Local includes. + +#include "ddebug.h" +#include "thumbnailjob.h" +#include "thumbnailjob.moc" + +namespace Digikam +{ + +class ThumbnailJobPriv +{ +public: + + bool highlight; + bool exifRotate; + bool running; + + int size; + + // Shared memory segment Id. The segment is allocated to a size + // of extent x extent x 4 (32 bit image) on first need. + int shmid; + + // And the data area + uchar *shmaddr; + + KURL curr_url; + KURL next_url; + KURL::List urlList; +}; + +ThumbnailJob::ThumbnailJob(const KURL& url, int size, + bool highlight, bool exifRotate) + : TDEIO::Job(false) +{ + d = new ThumbnailJobPriv; + + d->urlList.append(url); + + d->size = size; + d->highlight = highlight; + d->exifRotate = exifRotate; + d->curr_url = d->urlList.first(); + d->next_url = d->curr_url; + d->running = false; + d->shmid = -1; + d->shmaddr = 0; + + processNext(); +} + +ThumbnailJob::ThumbnailJob(const KURL::List& urlList, int size, + bool highlight, bool exifRotate) + : TDEIO::Job(false) +{ + d = new ThumbnailJobPriv; + + d->urlList = urlList; + d->size = size; + d->highlight = highlight; + d->running = false; + d->exifRotate = exifRotate; + d->curr_url = d->urlList.first(); + d->next_url = d->curr_url; + d->shmid = -1; + d->shmaddr = 0; + + processNext(); +} + +ThumbnailJob::~ThumbnailJob() +{ + if (d->shmaddr) + { + shmdt((char*)d->shmaddr); + shmctl(d->shmid, IPC_RMID, 0); + } + + delete d; +} + +void ThumbnailJob::addItem(const KURL& url) +{ + d->urlList.append(url); + + if (!d->running && subjobs.isEmpty()) + processNext(); +} + +void ThumbnailJob::addItems(const KURL::List& urlList) +{ + for (KURL::List::const_iterator it = urlList.begin(); + it != urlList.end(); ++it) + { + d->urlList.append(*it); + } + + if (!d->running && subjobs.isEmpty()) + processNext(); +} + +bool ThumbnailJob::setNextItemToLoad(const KURL& url) +{ + KURL::List::const_iterator it = d->urlList.find(url); + if (it != d->urlList.end()) + { + d->next_url = *it; + return true; + } + + return false; +} + +void ThumbnailJob::removeItem(const KURL& url) +{ + d->urlList.remove(url); +} + +void ThumbnailJob::processNext() +{ + if (d->urlList.isEmpty()) + { + d->running = false; + emit signalCompleted(); + return; + } + + KURL::List::iterator it = d->urlList.find(d->next_url); + if (it == d->urlList.end()) + { + it = d->urlList.begin(); + } + + d->curr_url = *it; + it = d->urlList.remove(it); + if (it != d->urlList.end()) + { + d->next_url = *it; + } + else + { + d->next_url = KURL(); + } + + KURL url(d->curr_url); + url.setProtocol("digikamthumbnail"); + + TDEIO::TransferJob *job = TDEIO::get(url, false, false); + job->addMetaData("size", TQString::number(d->size)); + createShmSeg(); + + if (d->shmid != -1) + job->addMetaData("shmid", TQString::number(d->shmid)); + + // Rotate thumbnail accordindly with Exif rotation tag if necessary. + if (d->exifRotate) + job->addMetaData("exif", "yes"); + + connect(job, TQ_SIGNAL(data(TDEIO::Job *, const TQByteArray &)), + this, TQ_SLOT(slotThumbData(TDEIO::Job *, const TQByteArray &))); + + addSubjob(job); + d->running = true; +} + +void ThumbnailJob::slotResult(TDEIO::Job *job) +{ + subjobs.remove(job); + Q_ASSERT( subjobs.isEmpty() ); + + if (job->error()) + { + emit signalFailed(d->curr_url); + } + + d->running = false; + processNext(); +} + +void ThumbnailJob::createShmSeg() +{ + if (d->shmid == -1) + { + if (d->shmaddr) + { + shmdt((char*)d->shmaddr); + shmctl(d->shmid, IPC_RMID, 0); + } + + d->shmid = shmget(IPC_PRIVATE, 256 * 256 * 4, IPC_CREAT|0600); + if (d->shmid != -1) + { + d->shmaddr = static_cast<uchar *>(shmat(d->shmid, 0, SHM_RDONLY)); + if (d->shmaddr == (uchar *)-1) + { + shmctl(d->shmid, IPC_RMID, 0); + d->shmaddr = 0; + d->shmid = -1; + } + } + else + d->shmaddr = 0; + } +} + +void ThumbnailJob::slotThumbData(TDEIO::Job*, const TQByteArray &data) +{ + if (data.isEmpty()) + return; + + TQImage thumb; + TQDataStream stream(data, IO_ReadOnly); + if (d->shmaddr) + { + int width, height, depth; + stream >> width >> height >> depth; + thumb = TQImage(d->shmaddr, width, height, depth, + 0, 0, TQImage::IgnoreEndian); + + // The buffer supplied to the TQImage constructor above must remain valid + // throughout the lifetime of the object. + // This is not true, the shared memory will be freed or reused. + // If we pass the object around, we must do a deep copy. + thumb = thumb.copy(); + } + else + { + stream >> thumb; + } + + if (thumb.isNull()) + { + DWarning() << k_funcinfo << "thumbnail is null" << endl; + emit signalFailed(d->curr_url); + return; + } + + emitThumbnail(thumb); +} + +void ThumbnailJob::emitThumbnail(TQImage& thumb) +{ + if (thumb.isNull()) + { + return; + } + + TQPixmap pix(thumb); + + int w = pix.width(); + int h = pix.height(); + + // highlight only when requested and when thumbnail + // width and height are greater than 10 + if (d->highlight && (w >= 10 && h >= 10)) + { + TQPainter p(&pix); + p.setPen(TQPen(TQColor(0,0,0),1)); + p.drawRect(0,0,w,h); + p.end(); + } + + emit signalThumbnail(d->curr_url, pix); +} + +} // namespace Digikam + + diff --git a/src/libs/thumbbar/thumbnailjob.h b/src/libs/thumbbar/thumbnailjob.h new file mode 100644 index 00000000..cc7080e3 --- /dev/null +++ b/src/libs/thumbbar/thumbnailjob.h @@ -0,0 +1,88 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-10-14 + * Description : digiKam TDEIO thumbnails generator interface + * + * Copyright (C) 2003-2005 by Renchi Raju <[email protected]> + * Copyright (C) 2006-2007 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. + * + * ============================================================ */ + +#ifndef THUMBNAILJOB_H +#define THUMBNAILJOB_H + +// TQt includes. + +#include <tqcstring.h> + +// KDE includes. + +#include <tdeio/job.h> +#include <kurl.h> + +class TQPixmap; +class TQImage; + +namespace Digikam +{ + +class ThumbnailJobPriv; + +class ThumbnailJob : public TDEIO::Job +{ + TQ_OBJECT + + +public: + + ThumbnailJob(const KURL& url, int size, + bool highlight=true, bool exifRotate=false); + ThumbnailJob(const KURL::List& urlList, int size, + bool highlight=true, bool exifRotate=false); + ~ThumbnailJob(); + + void addItem(const KURL& url); + void addItems(const KURL::List& urlList); + + bool setNextItemToLoad(const KURL& url); + void removeItem(const KURL& url); + +signals: + + void signalThumbnail(const KURL& url, const TQPixmap& pix); + void signalCompleted(); + void signalFailed(const KURL& url); + +private: + + void processNext(); + void emitThumbnail(TQImage& thumb); + void createShmSeg(); + +protected slots: + + void slotResult(TDEIO::Job *job); + void slotThumbData(TDEIO::Job *job, const TQByteArray &data); + +private: + + ThumbnailJobPriv *d; +}; + +} // namespace Digikam + +#endif /* THUMBNAILJOB_H */ |