summaryrefslogtreecommitdiffstats
path: root/src/libs/thumbbar
diff options
context:
space:
mode:
authorMichele Calgaro <[email protected]>2024-11-22 18:41:30 +0900
committerMichele Calgaro <[email protected]>2024-11-22 18:41:30 +0900
commitee0d99607c14cb63d3ebdb3a970b508949fa8219 (patch)
tree94ac1efedb94cb38bf6879ba0610fe75b554216b /src/libs/thumbbar
parent4adff739380e4ae9f30e443ee95644f184456869 (diff)
downloaddigikam-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.am18
-rw-r--r--src/libs/thumbbar/thumbbar.cpp1138
-rw-r--r--src/libs/thumbbar/thumbbar.h239
-rw-r--r--src/libs/thumbbar/thumbnailjob.cpp318
-rw-r--r--src/libs/thumbbar/thumbnailjob.h88
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 */