diff options
Diffstat (limited to 'src/utilities/imageeditor/canvas/canvas.cpp')
-rw-r--r-- | src/utilities/imageeditor/canvas/canvas.cpp | 1421 |
1 files changed, 1421 insertions, 0 deletions
diff --git a/src/utilities/imageeditor/canvas/canvas.cpp b/src/utilities/imageeditor/canvas/canvas.cpp new file mode 100644 index 00000000..89758c3d --- /dev/null +++ b/src/utilities/imageeditor/canvas/canvas.cpp @@ -0,0 +1,1421 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2003-01-09 + * Description : image editor canvas management class + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2004-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++ includes. + +#include <cstdio> +#include <cmath> + +// TQt includes. + +#include <tqtooltip.h> +#include <tqfile.h> +#include <tqstring.h> +#include <tqevent.h> +#include <tqpoint.h> +#include <tqpainter.h> +#include <tqpen.h> +#include <tqpixmap.h> +#include <tqstyle.h> +#include <tqapplication.h> +#include <tqcursor.h> +#include <tqimage.h> +#include <tqregion.h> +#include <tqtimer.h> +#include <tqcache.h> +#include <tqcolor.h> +#include <tqdragobject.h> +#include <tqclipboard.h> +#include <tqtoolbutton.h> + +// KDE includes. + +#include <kcursor.h> +#include <tdelocale.h> +#include <kiconloader.h> +#include <kdatetbl.h> +#include <tdeglobalsettings.h> + +// Local includes. + +#include "ddebug.h" +#include "imagehistogram.h" +#include "imagepaniconwidget.h" +#include "dimginterface.h" +#include "iccsettingscontainer.h" +#include "exposurecontainer.h" +#include "iofilesettingscontainer.h" +#include "loadingcacheinterface.h" +#include "canvas.h" +#include "canvas.moc" + +namespace Digikam +{ + +class CanvasPrivate +{ + +public: + + CanvasPrivate() : + tileSize(128), minZoom(0.1), maxZoom(12.0), zoomMultiplier(1.2) + { + rubber = 0; + pressedMoved = false; + pressedMoving = false; + ltActive = false; + rtActive = false; + lbActive = false; + rbActive = false; + midButtonPressed = false; + midButtonX = 0; + midButtonY = 0; + panIconPopup = 0; + panIconWidget = 0; + cornerButton = 0; + parent = 0; + im = 0; + rubber = 0; + autoZoom = false; + fullScreen = false; + zoom = 1.0; + tileTmpPix = new TQPixmap(tileSize, tileSize); + + tileCache.setMaxCost((10*1024*1024)/(tileSize*tileSize*4)); + tileCache.setAutoDelete(true); + } + + bool autoZoom; + bool fullScreen; + bool pressedMoved; + bool pressedMoving; + bool ltActive; + bool rtActive; + bool lbActive; + bool rbActive; + bool midButtonPressed; + + const int tileSize; + int midButtonX; + int midButtonY; + + double zoom; + const double minZoom; + const double maxZoom; + const double zoomMultiplier; + + TQToolButton *cornerButton; + + TQRect *rubber; + TQRect pixmapRect; + + TQCache<TQPixmap> tileCache; + + TQPixmap* tileTmpPix; + TQPixmap qcheck; + + TQColor bgColor; + + TQWidget *parent; + + TDEPopupFrame *panIconPopup; + + DImgInterface *im; + + ImagePanIconWidget *panIconWidget; +}; + +Canvas::Canvas(TQWidget *parent) + : TQScrollView(parent) +{ + d = new CanvasPrivate; + d->im = new DImgInterface(); + d->parent = parent; + d->bgColor.setRgb(0, 0, 0); + + d->qcheck.resize(16, 16); + TQPainter p(&d->qcheck); + p.fillRect(0, 0, 8, 8, TQColor(144, 144, 144)); + p.fillRect(8, 8, 8, 8, TQColor(144, 144, 144)); + p.fillRect(0, 8, 8, 8, TQColor(100, 100, 100)); + p.fillRect(8, 0, 8, 8, TQColor(100, 100, 100)); + p.end(); + + d->cornerButton = new TQToolButton(this); + d->cornerButton->setIconSet(SmallIcon("move")); + d->cornerButton->hide(); + TQToolTip::add(d->cornerButton, i18n("Pan the image to a region")); + setCornerWidget(d->cornerButton); + + viewport()->setBackgroundMode(TQt::NoBackground); + viewport()->setMouseTracking(false); + setFrameStyle( TQFrame::NoFrame ); + + // ------------------------------------------------------------ + + connect(this, TQ_SIGNAL(signalZoomChanged(double)), + this, TQ_SLOT(slotZoomChanged(double))); + + connect(d->cornerButton, TQ_SIGNAL(pressed()), + this, TQ_SLOT(slotCornerButtonPressed())); + + connect(d->im, TQ_SIGNAL(signalModified()), + this, TQ_SLOT(slotModified())); + + connect(d->im, TQ_SIGNAL(signalUndoStateChanged(bool, bool, bool)), + this, TQ_SIGNAL(signalUndoStateChanged(bool, bool, bool))); + + connect(d->im, TQ_SIGNAL(signalLoadingStarted(const TQString&)), + this, TQ_SIGNAL(signalLoadingStarted(const TQString&))); + + connect(d->im, TQ_SIGNAL(signalImageLoaded(const TQString&, bool)), + this, TQ_SLOT(slotImageLoaded(const TQString&, bool))); + + connect(d->im, TQ_SIGNAL(signalImageSaved(const TQString&, bool)), + this, TQ_SLOT(slotImageSaved(const TQString&, bool))); + + connect(d->im, TQ_SIGNAL(signalLoadingProgress(const TQString&, float)), + this, TQ_SIGNAL(signalLoadingProgress(const TQString&, float))); + + connect(d->im, TQ_SIGNAL(signalSavingProgress(const TQString&, float)), + this, TQ_SIGNAL(signalSavingProgress(const TQString&, float))); + + connect(this, TQ_SIGNAL(signalSelected(bool)), + this, TQ_SLOT(slotSelected())); +} + +Canvas::~Canvas() +{ + delete d->tileTmpPix; + delete d->im; + + if (d->rubber) + delete d->rubber; + + delete d; +} + +void Canvas::resetImage() +{ + reset(); + viewport()->setUpdatesEnabled(false); + d->im->resetImage(); +} + +void Canvas::reset() +{ + if (d->rubber) + { + delete d->rubber; + d->rubber = 0; + if (d->im->imageValid()) + emit signalSelected(false); + } + + d->tileCache.clear(); +} + +void Canvas::load(const TQString& filename, IOFileSettingsContainer *IOFileSettings) +{ + reset(); + + viewport()->setUpdatesEnabled(false); + + d->im->load( filename, IOFileSettings, d->parent ); + + emit signalPrepareToLoad(); +} + +void Canvas::slotImageLoaded(const TQString& filePath, bool success) +{ + d->zoom = 1.0; + d->im->zoom(d->zoom); + + if (d->autoZoom) + updateAutoZoom(); + + updateContentsSize(true); + + viewport()->setUpdatesEnabled(true); + viewport()->update(); + + emit signalZoomChanged(d->zoom); + + emit signalLoadingFinished(filePath, success); +} + +void Canvas::preload(const TQString& /*filename*/) +{ +// d->im->preload(filename); +} + +/* +These code part are unused and untested +void Canvas::save(const TQString& filename, IOFileSettingsContainer *IOFileSettings) +{ + d->im->save(filename, IOFileSettings); + emit signalSavingStarted(filename); +} + +void Canvas::saveAs(const TQString& filename,IOFileSettingsContainer *IOFileSettings, + const TQString& mimeType) +{ + d->im->saveAs(filename, IOFileSettings, mimeType); + emit signalSavingStarted(filename); +} +*/ + +void Canvas::saveAs(const TQString& filename, IOFileSettingsContainer *IOFileSettings, + bool setExifOrientationTag, const TQString& mimeType) +{ + d->im->saveAs(filename, IOFileSettings, setExifOrientationTag, mimeType); + emit signalSavingStarted(filename); +} + +void Canvas::slotImageSaved(const TQString& filePath, bool success) +{ + emit signalSavingFinished(filePath, success); +} + +void Canvas::switchToLastSaved(const TQString& newFilename) +{ + d->im->switchToLastSaved(newFilename); +} + +void Canvas::abortSaving() +{ + d->im->abortSaving(); +} + +void Canvas::setModified() +{ + d->im->setModified(); +} + +void Canvas::readMetadataFromFile(const TQString &file) +{ + d->im->readMetadataFromFile(file); +} + +void Canvas::clearUndoHistory() +{ + d->im->clearUndoManager(); +} + +void Canvas::setUndoHistoryOrigin() +{ + d->im->setUndoManagerOrigin(); +} + +void Canvas::updateUndoState() +{ + d->im->updateUndoState(); +} + +DImg Canvas::currentImage() +{ + return DImg(*d->im->getImg()); +} + +TQString Canvas::currentImageFileFormat() +{ + return d->im->getImageFormat(); +} + +TQString Canvas::currentImageFilePath() +{ + return d->im->getImageFilePath(); +} + +int Canvas::imageWidth() +{ + return d->im->origWidth(); +} + +int Canvas::imageHeight() +{ + return d->im->origHeight(); +} + +bool Canvas::isReadOnly() +{ + return d->im->isReadOnly(); +} + +TQRect Canvas::getSelectedArea() +{ + int x, y, w, h; + d->im->getSelectedArea(x, y, w, h); + return ( TQRect(x, y, w, h) ); +} + +DImgInterface *Canvas::interface() const +{ + return d->im; +} + +void Canvas::makeDefaultEditingCanvas() +{ + DImgInterface::setDefaultInterface(d->im); +} + +double Canvas::calcAutoZoomFactor() +{ + if (!d->im->imageValid()) return d->zoom; + + double srcWidth = d->im->origWidth(); + double srcHeight = d->im->origHeight(); + double dstWidth = contentsRect().width(); + double dstHeight = contentsRect().height(); + return TQMIN(dstWidth/srcWidth, dstHeight/srcHeight); +} + +void Canvas::updateAutoZoom() +{ + d->zoom = calcAutoZoomFactor(); + d->im->zoom(d->zoom); + emit signalZoomChanged(d->zoom); +} + +void Canvas::updateContentsSize(bool deleteRubber) +{ + viewport()->setUpdatesEnabled(false); + + if (deleteRubber && d->rubber) + { + delete d->rubber; + d->rubber = 0; + d->ltActive = false; + d->rtActive = false; + d->lbActive = false; + d->rbActive = false; + d->pressedMoved = false; + viewport()->unsetCursor(); + viewport()->setMouseTracking(false); + if (d->im->imageValid()) + emit signalSelected(false); + } + + int wZ = d->im->width(); + int hZ = d->im->height(); + + if (visibleWidth() > wZ || visibleHeight() > hZ) + { + // Center the image + int centerx = contentsRect().width()/2; + int centery = contentsRect().height()/2; + int xoffset = int(centerx - wZ/2); + int yoffset = int(centery - hZ/2); + xoffset = TQMAX(xoffset, 0); + yoffset = TQMAX(yoffset, 0); + + d->pixmapRect = TQRect(xoffset, yoffset, wZ, hZ); + } + else + { + d->pixmapRect = TQRect(0, 0, wZ, hZ); + } + + if (!deleteRubber && d->rubber) + { + int xSel, ySel, wSel, hSel; + d->im->getSelectedArea(xSel, ySel, wSel, hSel); + xSel = (int)((xSel * d->tileSize) / floor(d->tileSize / d->zoom)); + ySel = (int)((ySel * d->tileSize) / floor(d->tileSize / d->zoom)); + wSel = (int)((wSel * d->tileSize) / floor(d->tileSize / d->zoom)); + hSel = (int)((hSel * d->tileSize) / floor(d->tileSize / d->zoom)); + d->rubber->setX(xSel); + d->rubber->setY(ySel); + d->rubber->setWidth(wSel); + d->rubber->setHeight(hSel); + d->rubber->moveBy(d->pixmapRect.x(), d->pixmapRect.y()); + } + + d->tileCache.clear(); + resizeContents(wZ, hZ); + viewport()->setUpdatesEnabled(true); +} + +void Canvas::resizeEvent(TQResizeEvent* e) +{ + if (!e) + return; + + TQScrollView::resizeEvent(e); + + if (d->autoZoom) + updateAutoZoom(); + + updateContentsSize(false); + + // No need to repaint. its called + // automatically after resize + + // To be sure than corner widget used to pan image will be hide/show + // accordinly with resize event. + slotZoomChanged(d->zoom); +} + +void Canvas::viewportPaintEvent(TQPaintEvent *e) +{ + TQRect er(e->rect()); + er = TQRect(TQMAX(er.x() - 1, 0), + TQMAX(er.y() - 1, 0), + TQMIN(er.width() + 2, contentsRect().width()), + TQMIN(er.height() + 2, contentsRect().height())); + + paintViewport(er, (d->zoom <= 1.0) ? true : false); +} + +void Canvas::paintViewport(const TQRect& er, bool antialias) +{ + TQRect o_cr(viewportToContents(er.topLeft()), viewportToContents(er.bottomRight())); + TQRect cr = o_cr; + + TQRegion clipRegion(er); + cr = d->pixmapRect.intersect(cr); + + if (!cr.isEmpty() && d->im->imageValid()) + { + clipRegion -= TQRect(contentsToViewport(cr.topLeft()), cr.size()); + + TQRect pr = TQRect(cr.x() - d->pixmapRect.x(), cr.y() - d->pixmapRect.y(), + cr.width(), cr.height()); + + int x1 = (int)floor((double)pr.x() / (double)d->tileSize) * d->tileSize; + int y1 = (int)floor((double)pr.y() / (double)d->tileSize) * d->tileSize; + int x2 = (int)ceilf((double)pr.right() / (double)d->tileSize) * d->tileSize; + int y2 = (int)ceilf((double)pr.bottom() / (double)d->tileSize) * d->tileSize; + + TQPixmap pix(d->tileSize, d->tileSize); + int sx, sy, sw, sh; + int step = (int)floor(d->tileSize / d->zoom); + + bool hasRubber = (d->rubber && d->pressedMoved && d->pressedMoving && d->rubber->intersects(pr)); + if (hasRubber) + { + // remove rubber + drawRubber(); + } + + for (int j = y1 ; j < y2 ; j += d->tileSize) + { + for (int i = x1 ; i < x2 ; i += d->tileSize) + { + TQString key = TQString("%1,%2").arg(i).arg(j); + TQPixmap *pix = d->tileCache.find(key); + + if (!pix) + { + if (antialias) + { + pix = new TQPixmap(d->tileSize, d->tileSize); + d->tileCache.insert(key, pix); + } + else + { + pix = d->tileTmpPix; + } + + if (d->im->hasAlpha()) + { + TQPainter p(pix); + p.drawTiledPixmap(0, 0, d->tileSize, d->tileSize, + d->qcheck, 0, 0); + p.end(); + } + else + { + pix->fill(d->bgColor); + } + + // NOTE : with implementations <= 0.9.1, the canvas doesn't work properly using high zoom level (> 500). + // The sx, sy, sw, sh values haven't be computed properly and "tile" artefacts been appears + // over the image. Look the example here: + // http://digikam3rdparty.free.fr/Screenshots/editorhighzoomartefact.png + // Note than these "tile" artifacts are not the real tiles of canvas. + // The new implementation below fix this problem to handle properly the areas to + // use from the source image to generate the canvas pixmap tiles. + + sx = (int)floor((double)i / d->tileSize) * step; + sy = (int)floor((double)j / d->tileSize) * step; + sw = step; + sh = step; + + if (d->rubber && d->pressedMoved && !d->pressedMoving) + { + TQRect rr(d->rubber->normalize()); + TQRect r(i, j, d->tileSize, d->tileSize); + + d->im->paintOnDevice(pix, sx, sy, sw, sh, + 0, 0, d->tileSize, d->tileSize, + rr.x() - i - d->pixmapRect.x(), + rr.y() - j - d->pixmapRect.y(), + rr.width(), rr.height(), + antialias); + + rr.moveBy(-i -d->pixmapRect.x(), -j -d->pixmapRect.y()); + + TQPainter p(pix); + p.setPen(TQPen(TQColor(250, 250, 255), 1)); + p.drawRect(rr); + if (rr.width() >= 10 && rr.height() >= 10) + { + p.drawRect(rr.x(), rr.y(), 5, 5); + p.drawRect(rr.x(), rr.y()+rr.height()-5, 5, 5); + p.drawRect(rr.x()+rr.width()-5, rr.y()+rr.height()-5, 5, 5); + p.drawRect(rr.x()+rr.width()-5, rr.y(), 5, 5); + } + p.end(); + } + else + { + d->im->paintOnDevice(pix, sx, sy, sw, sh, + 0, 0, d->tileSize, d->tileSize, + antialias); + } + } + + TQRect r(i, j, d->tileSize, d->tileSize); + TQRect ir = pr.intersect(r); + TQPoint pt(contentsToViewport(TQPoint(ir.x() + d->pixmapRect.x(), + ir.y() + d->pixmapRect.y()))); + + bitBlt(viewport(), pt.x(), pt.y(), + pix, + ir.x()-r.x(), ir.y()-r.y(), + ir.width(), ir.height()); + } + } + + if (hasRubber) + { + // restore rubber + drawRubber(); + } + } + + TQPainter painter(viewport()); + painter.setClipRegion(clipRegion); + painter.fillRect(er, d->bgColor); + painter.end(); +} + +void Canvas::drawRubber() +{ + if (!d->rubber || !d->im->imageValid()) + return; + + TQPainter p(viewport()); + p.setRasterOp(TQt::NotROP ); + p.setPen(TQPen(TQt::color0, 1)); + p.setBrush(NoBrush); + + TQRect r(d->rubber->normalize()); + r = TQRect(contentsToViewport(TQPoint(r.x(), r.y())), r.size()); + + TQPoint pnt(r.x(), r.y()); + + style().drawPrimitive(TQStyle::PE_FocusRect, &p, + TQRect(pnt.x(), pnt.y(), r.width(), r.height()), + colorGroup(), TQStyle::Style_Default, + TQStyleOption(colorGroup().base())); + p.end(); +} + +void Canvas::contentsMousePressEvent(TQMouseEvent *e) +{ + if (!e || e->button() == TQt::RightButton) + return; + + d->midButtonPressed = false; + + if (e->button() == TQt::LeftButton) + { + if (d->ltActive || d->rtActive || + d->lbActive || d->rbActive) + { + Q_ASSERT( d->rubber ); + if (!d->rubber) + return; + + // Set diagonally opposite corner as anchor + + TQRect r(d->rubber->normalize()); + + if (d->ltActive) + { + d->rubber->setTopLeft(r.bottomRight()); + d->rubber->setBottomRight(r.topLeft()); + } + else if (d->rtActive) + { + d->rubber->setTopLeft(r.bottomLeft()); + d->rubber->setBottomRight(r.topRight()); + } + else if (d->lbActive) + { + d->rubber->setTopLeft(r.topRight()); + d->rubber->setBottomRight(r.bottomLeft()); + } + else if (d->rbActive) + { + d->rubber->setTopLeft(r.topLeft()); + d->rubber->setBottomRight(r.bottomLeft()); + } + + viewport()->setMouseTracking(false); + d->pressedMoved = false; + d->pressedMoving = true; + + d->tileCache.clear(); + viewport()->repaint(false); + + return; + } + } + else if (e->button() == TQt::MidButton) + { + if (visibleWidth() < d->im->width() || + visibleHeight() < d->im->height()) + { + viewport()->setCursor(TQt::SizeAllCursor); + d->midButtonPressed = true; + d->midButtonX = e->x(); + d->midButtonY = e->y(); + } + return; + } + + if (d->rubber) + { + delete d->rubber; + d->rubber = 0; + } + + d->rubber = new TQRect(e->x(), e->y(), 0, 0); + + if (d->pressedMoved) + { + d->tileCache.clear(); + viewport()->update(); + } + + d->pressedMoved = false; + d->pressedMoving = true; + + viewport()->setMouseTracking(false); +} + +void Canvas::contentsMouseMoveEvent(TQMouseEvent *e) +{ + if (!e) + return; + + if (e->state() & TQt::MidButton) + { + if (d->midButtonPressed) + { + scrollBy(d->midButtonX - e->x(), + d->midButtonY - e->y()); + } + } + else if (!viewport()->hasMouseTracking()) + { + if (!d->rubber) + return; + + if (e->state() != TQt::LeftButton && + !(d->ltActive || d->rtActive || + d->lbActive || d->rbActive)) + return; + + // Clear old rubber. + if (d->pressedMoved) + drawRubber(); + + // Move content if necessary. + blockSignals(true); + setUpdatesEnabled(false); + ensureVisible(e->x(), e->y(), 10, 10); + setUpdatesEnabled(true); + blockSignals(false); + + // draw the new rubber position. + int r, b; + r = (e->x() > d->pixmapRect.left()) ? e->x() : d->pixmapRect.left(); + r = (r < d->pixmapRect.right()) ? r : d->pixmapRect.right(); + b = (e->y() > d->pixmapRect.top()) ? e->y() : d->pixmapRect.top(); + b = (b < d->pixmapRect.bottom()) ? b : d->pixmapRect.bottom(); + d->rubber->setRight(r); + d->rubber->setBottom(b); + drawRubber(); + + d->pressedMoved = true; + d->pressedMoving = true; + + // To refresh editor status bar with current selection. + emit signalSelectionChanged(calcSeletedArea()); + } + else + { + if (!d->rubber) + return; + + TQRect r(d->rubber->normalize()); + + TQRect lt(r.x()-5, r.y()-5, 10, 10); + TQRect rt(r.x()+r.width()-5, r.y()-5, 10, 10); + TQRect lb(r.x()-5, r.y()+r.height()-5, 10, 10); + TQRect rb(r.x()+r.width()-5, r.y()+r.height()-5, 10, 10); + + d->ltActive = false; + d->rtActive = false; + d->lbActive = false; + d->rbActive = false; + + if (lt.contains(e->x(), e->y())) + { + viewport()->setCursor(TQt::SizeFDiagCursor); + d->ltActive = true; + } + else if (rb.contains(e->x(), e->y())) + { + viewport()->setCursor(TQt::SizeFDiagCursor); + d->rbActive = true; + } + else if (lb.contains(e->x(), e->y())) + { + viewport()->setCursor(TQt::SizeBDiagCursor); + d->lbActive = true; + } + else if (rt.contains(e->x(), e->y())) + { + viewport()->setCursor(TQt::SizeBDiagCursor); + d->rtActive = true; + } + else + viewport()->unsetCursor(); + } +} + +void Canvas::contentsMouseReleaseEvent(TQMouseEvent *e) +{ + if (!e) + return; + + d->midButtonPressed = false; + + if (d->pressedMoving) + { + d->pressedMoving = false; + viewport()->update(); + } + + if (d->pressedMoved && d->rubber) + { + // Normalize rubber rectangle to always have the selection into the image + TQRect rec = d->rubber->normalize(); + + if (rec.left() < d->pixmapRect.left()) rec.setLeft(d->pixmapRect.left()); + if (rec.right() > d->pixmapRect.right()) rec.setRight(d->pixmapRect.right()); + if (rec.top() < d->pixmapRect.top()) rec.setTop(d->pixmapRect.top()); + if (rec.bottom() > d->pixmapRect.bottom()) rec.setBottom(d->pixmapRect.bottom()); + + d->rubber->setLeft(rec.left()); + d->rubber->setRight(rec.right()); + d->rubber->setTop(rec.top()); + d->rubber->setBottom(rec.bottom()); + + d->tileCache.clear(); + viewport()->setMouseTracking(true); + if (d->im->imageValid()) + emit signalSelected(true); + } + else + { + d->ltActive = false; + d->rtActive = false; + d->lbActive = false; + d->rbActive = false; + viewport()->setMouseTracking(false); + viewport()->unsetCursor(); + if (d->im->imageValid()) + emit signalSelected(false); + } + + if (e->button() != TQt::LeftButton) + { + viewport()->unsetCursor(); + } + + if (e->button() == TQt::RightButton) + { + emit signalRightButtonClicked(); + } +} + +void Canvas::contentsWheelEvent(TQWheelEvent *e) +{ + e->accept(); + + if (e->state() & TQt::ShiftButton) + { + if (e->delta() < 0) + emit signalShowNextImage(); + else if (e->delta() > 0) + emit signalShowPrevImage(); + return; + } + else if (e->state() & TQt::ControlButton) + { + if (e->delta() < 0) + slotDecreaseZoom(); + else if (e->delta() > 0) + slotIncreaseZoom(); + return; + } + + TQScrollView::contentsWheelEvent(e); +} + +bool Canvas::maxZoom() +{ + return ((d->zoom * d->zoomMultiplier) >= d->maxZoom); +} + +bool Canvas::minZoom() +{ + return ((d->zoom / d->zoomMultiplier) <= d->minZoom); +} + +bool Canvas::exifRotated() +{ + return d->im->exifRotated(); +} + +double Canvas::snapZoom(double zoom) +{ + // If the zoom value gets changed from d->zoom to zoom + // across 50%, 100% or fit-to-window, then return the + // the corresponding special value. Otherwise zoom is returned unchanged. + double fit = calcAutoZoomFactor(); + TQValueList<double> snapValues; + snapValues.append(0.5); + snapValues.append(1.0); + snapValues.append(fit); + + qHeapSort(snapValues); + TQValueList<double>::const_iterator it; + + if (d->zoom < zoom) + { + for(it = snapValues.constBegin(); it != snapValues.constEnd(); ++it) + { + double z = *it; + if ((d->zoom < z) && (zoom > z)) + { + zoom = z; + break; + } + } + } + else + { + // We need to go through the list in reverse order, + // however, tqCopyBackward does not seem to work here, so + // a simple for loop over integers is used instead. + for(int i=snapValues.size()-1; i>=0; i--) + { + double z = snapValues[i]; + if ((d->zoom > z) && (zoom < z)) + { + zoom = z; + break; + } + } + } + + return zoom; +} + +void Canvas::slotIncreaseZoom() +{ + if (maxZoom()) + return; + + double zoom = d->zoom * d->zoomMultiplier; + zoom = snapZoom(zoom); + setZoomFactor(zoom); +} + +void Canvas::slotDecreaseZoom() +{ + if (minZoom()) + return; + + double zoom = d->zoom / d->zoomMultiplier; + zoom = snapZoom(zoom); + setZoomFactor(zoom); +} + +void Canvas::setZoomFactorSnapped(double zoom) +{ + double fit = calcAutoZoomFactor(); + + if (fabs(zoom-fit) < 0.05) + { + // If 1.0 or 0.5 are even closer to zoom than fit, then choose these. + if (fabs(zoom-fit) > fabs(zoom-1.0) ) + { + zoom = 1.0; + } + else if (fabs(zoom-fit) > fabs(zoom-0.5) ) + { + zoom = 0.5; + } + else + { + zoom = fit; + } + } + else + { + if (fabs(zoom-1.0) < 0.05) + { + zoom = 1.0; + } + if (fabs(zoom-0.5) < 0.05) + { + zoom = 0.5; + } + } + setZoomFactor(zoom); +} + +double Canvas::zoomFactor() +{ + return d->zoom; +} + +void Canvas::setZoomFactor(double zoom) +{ + if (d->autoZoom) + { + d->autoZoom = false; + emit signalToggleOffFitToWindow(); + } + + // Zoom using center of canvas and given zoom factor. + + double cpx = contentsX() + visibleWidth() / 2.0; + double cpy = contentsY() + visibleHeight() / 2.0; + + cpx = (cpx / d->tileSize) * floor(d->tileSize / d->zoom); + cpy = (cpy / d->tileSize) * floor(d->tileSize / d->zoom); + + d->zoom = zoom; + + d->im->zoom(d->zoom); + updateContentsSize(false); + + viewport()->setUpdatesEnabled(false); + center((int)((cpx * d->tileSize) / floor(d->tileSize / d->zoom)), + (int)((cpy * d->tileSize) / floor(d->tileSize / d->zoom))); + viewport()->setUpdatesEnabled(true); + viewport()->update(); + + emit signalZoomChanged(d->zoom); +} + +void Canvas::fitToSelect() +{ + int xSel, ySel, wSel, hSel; + d->im->getSelectedArea(xSel, ySel, wSel, hSel); + + if (wSel && hSel ) + { + // If selected area, use center of selection + // and recompute zoom factor accordinly. + double cpx = xSel + wSel / 2.0; + double cpy = ySel + hSel / 2.0; + + double srcWidth = wSel; + double srcHeight = hSel; + double dstWidth = contentsRect().width(); + double dstHeight = contentsRect().height(); + + d->zoom = TQMIN(dstWidth/srcWidth, dstHeight/srcHeight); + + d->autoZoom = false; + emit signalToggleOffFitToWindow(); + d->im->zoom(d->zoom); + updateContentsSize(true); + + viewport()->setUpdatesEnabled(false); + center((int)((cpx * d->tileSize) / floor(d->tileSize / d->zoom)), + (int)((cpy * d->tileSize) / floor(d->tileSize / d->zoom))); + viewport()->setUpdatesEnabled(true); + viewport()->update(); + + emit signalZoomChanged(d->zoom); + } +} + +bool Canvas::fitToWindow() +{ + return d->autoZoom; +} + +void Canvas::toggleFitToWindow() +{ + d->autoZoom = !d->autoZoom; + + if (d->autoZoom) + updateAutoZoom(); + else + { + d->zoom = 1.0; + emit signalZoomChanged(d->zoom); + } + + d->im->zoom(d->zoom); + updateContentsSize(false); + slotZoomChanged(d->zoom); + viewport()->update(); +} + +void Canvas::slotRotate90() +{ + d->im->rotate90(); +} + +void Canvas::slotRotate180() +{ + d->im->rotate180(); +} + +void Canvas::slotRotate270() +{ + d->im->rotate270(); +} + +void Canvas::slotFlipHoriz() +{ + d->im->flipHoriz(); +} + +void Canvas::slotFlipVert() +{ + d->im->flipVert(); +} + +void Canvas::slotCrop() +{ + int x, y, w, h; + d->im->getSelectedArea(x, y, w, h); + + if (!w && !h ) // No current selection. + return; + + d->im->crop(x, y, w, h); +} + +void Canvas::resizeImage(int w, int h) +{ + d->im->resize(w, h); +} + +void Canvas::setBackgroundColor(const TQColor& color) +{ + if (d->bgColor == color) + return; + + d->bgColor = color; + viewport()->update(); +} + +void Canvas::setICCSettings(ICCSettingsContainer *cmSettings) +{ + d->im->setICCSettings(cmSettings); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::setExposureSettings(ExposureSettingsContainer *expoSettings) +{ + d->im->setExposureSettings(expoSettings); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::setExifOrient(bool exifOrient) +{ + d->im->setExifOrient(exifOrient); + viewport()->update(); +} + +void Canvas::increaseGamma() +{ + d->im->changeGamma(1); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::decreaseGamma() +{ + d->im->changeGamma(-1); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::increaseBrightness() +{ + d->im->changeBrightness(1); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::decreaseBrightness() +{ + d->im->changeBrightness(-1); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::increaseContrast() +{ + d->im->changeContrast(5); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::decreaseContrast() +{ + d->im->changeContrast(-5); + d->tileCache.clear(); + viewport()->update(); +} + +void Canvas::slotRestore() +{ + d->im->restore(); +} + +void Canvas::slotUndo(int steps) +{ + while(steps > 0) + { + d->im->undo(); + --steps; + } +} + +void Canvas::getUndoHistory(TQStringList &titles) +{ + d->im->getUndoHistory(titles); +} + +void Canvas::getRedoHistory(TQStringList &titles) +{ + d->im->getRedoHistory(titles); +} + +void Canvas::slotRedo(int steps) +{ + while(steps > 0) + { + d->im->redo(); + --steps; + } +} + +void Canvas::slotCopy() +{ + int x, y, w, h; + d->im->getSelectedArea(x, y, w, h); + + if (!w && !h ) // No current selection. + return; + + TQApplication::setOverrideCursor (TQt::waitCursor); + uchar* data = d->im->getImageSelection(); + DImg selDImg = DImg(w, h, d->im->sixteenBit(), d->im->hasAlpha(), data); + delete [] data; + + TQImage selImg = selDImg.copyTQImage(); + TQApplication::clipboard()->setData(new TQImageDrag(selImg), TQClipboard::Clipboard); + TQApplication::restoreOverrideCursor (); +} + +void Canvas::slotSelected() +{ + int x=0, y=0, w=0, h=0; + + if (d->rubber && d->pressedMoved) + { + TQRect sel = calcSeletedArea(); + x = sel.x(); + y = sel.y(); + w = sel.width(); + h = sel.height(); + } + + d->im->setSelectedArea(x, y, w, h); +} + +TQRect Canvas::calcSeletedArea() +{ + int x=0, y=0, w=0, h=0; + TQRect r(d->rubber->normalize()); + + if (r.isValid()) + { + r.moveBy(- d->pixmapRect.x(), - d->pixmapRect.y()); + + x = (int)(((double)r.x() / d->tileSize) * floor(d->tileSize / d->zoom)); + y = (int)(((double)r.y() / d->tileSize) * floor(d->tileSize / d->zoom)); + w = (int)(((double)r.width() / d->tileSize) * floor(d->tileSize / d->zoom)); + h = (int)(((double)r.height() / d->tileSize) * floor(d->tileSize / d->zoom)); + + x = TQMIN(imageWidth(), TQMAX(x, 0)); + y = TQMIN(imageHeight(), TQMAX(y, 0)); + w = TQMIN(imageWidth(), TQMAX(w, 0)); + h = TQMIN(imageHeight(), TQMAX(h, 0)); + + // Avoid empty selection by rubberband - at least mark one pixel + // At high zoom factors, the rubberband may operate at subpixel level! + if (w == 0) + w = 1; + if (h == 0) + h = 1; + } + + return TQRect(x, y, w, h); +} + +void Canvas::slotModified() +{ + if (d->autoZoom) + updateAutoZoom(); + d->im->zoom(d->zoom); + + updateContentsSize(true); + viewport()->update(); + + // To be sure than corner widget used to pan image will be hide/show + // accordinly with new image size (if changed). + slotZoomChanged(d->zoom); + + emit signalChanged(); +} + +void Canvas::slotCornerButtonPressed() +{ + if (d->panIconPopup) + { + d->panIconPopup->hide(); + delete d->panIconPopup; + d->panIconPopup = 0; + } + + d->panIconPopup = new TDEPopupFrame(this); + ImagePanIconWidget *pan = new ImagePanIconWidget(180, 120, d->panIconPopup); + d->panIconPopup->setMainWidget(pan); + + TQRect r((int)(contentsX() / d->zoom), (int)(contentsY() / d->zoom), + (int)(visibleWidth() / d->zoom), (int)(visibleHeight() / d->zoom)); + 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 Canvas::slotPanIconHiden() +{ + d->cornerButton->blockSignals(true); + d->cornerButton->animateClick(); + d->cornerButton->blockSignals(false); +} + +void Canvas::slotPanIconSelectionMoved(const TQRect& r, bool b) +{ + setContentsPos((int)(r.x()*d->zoom), (int)(r.y()*d->zoom)); + + if (b) + { + d->panIconPopup->hide(); + delete d->panIconPopup; + d->panIconPopup = 0; + slotPanIconHiden(); + } +} + +void Canvas::slotZoomChanged(double /*zoom*/) +{ + updateScrollBars(); + + if (horizontalScrollBar()->isVisible() || verticalScrollBar()->isVisible()) + d->cornerButton->show(); + else + d->cornerButton->hide(); +} + +void Canvas::slotSelectAll() +{ + if (d->rubber) + { + delete d->rubber; + d->rubber = 0; + } + + d->rubber = new TQRect(d->pixmapRect); + d->pressedMoved = true; + d->tileCache.clear(); + viewport()->setMouseTracking(true); + viewport()->update(); + + if (d->im->imageValid()) + emit signalSelected(true); +} + +void Canvas::slotSelectNone() +{ + reset(); + viewport()->update(); +} + +} // namespace Digikam |