/* ============================================================ * * 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 * Copyright (C) 2004-2008 by Gilles Caulier * * 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 #include // TQt includes. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes. #include #include #include #include #include // 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 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, TQT_SIGNAL(signalZoomChanged(double)), this, TQT_SLOT(slotZoomChanged(double))); connect(d->cornerButton, TQT_SIGNAL(pressed()), this, TQT_SLOT(slotCornerButtonPressed())); connect(d->im, TQT_SIGNAL(signalModified()), this, TQT_SLOT(slotModified())); connect(d->im, TQT_SIGNAL(signalUndoStateChanged(bool, bool, bool)), this, TQT_SIGNAL(signalUndoStateChanged(bool, bool, bool))); connect(d->im, TQT_SIGNAL(signalLoadingStarted(const TQString&)), this, TQT_SIGNAL(signalLoadingStarted(const TQString&))); connect(d->im, TQT_SIGNAL(signalImageLoaded(const TQString&, bool)), this, TQT_SLOT(slotImageLoaded(const TQString&, bool))); connect(d->im, TQT_SIGNAL(signalImageSaved(const TQString&, bool)), this, TQT_SLOT(slotImageSaved(const TQString&, bool))); connect(d->im, TQT_SIGNAL(signalLoadingProgress(const TQString&, float)), this, TQT_SIGNAL(signalLoadingProgress(const TQString&, float))); connect(d->im, TQT_SIGNAL(signalSavingProgress(const TQString&, float)), this, TQT_SIGNAL(signalSavingProgress(const TQString&, float))); connect(this, TQT_SIGNAL(signalSelected(bool)), this, TQT_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(TQT_TQPAINTDEVICE(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(TQT_TQPAINTDEVICE(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().tqdrawPrimitive(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 snapValues; snapValues.append(0.5); snapValues.append(1.0); snapValues.append(fit); qHeapSort(snapValues); TQValueList::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, TQT_SIGNAL(signalSelectionMoved(const TQRect&, bool)), this, TQT_SLOT(slotPanIconSelectionMoved(const TQRect&, bool))); connect(pan, TQT_SIGNAL(signalHiden()), this, TQT_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