/* ============================================================
 *
 * This file is a part of digiKam project
 * http://www.digikam.org
 *
 * Date        : 2004-12-09
 * Description : image selection widget used by ratio crop tool.
 *
 * Copyright (C) 2007 by Jaromir Malenko <malenko at email.cz>
 * Copyright (C) 2008 by Roberto Castagnola <roberto dot castagnola at gmail dot com>
 * Copyright (C) 2004-2009 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.
 *
 * ============================================================ */

#define OPACITY  0.7
#define RCOL     0xAA
#define GCOL     0xAA
#define BCOL     0xAA

#define MINRANGE 0

// Golden number (1+sqrt(5))/2
#define PHI      1.61803398874989479
// 1/PHI
#define INVPHI   0.61803398874989479

// C++ includes.

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>

// TQt includes.

#include <tqregion.h>
#include <tqcolor.h>
#include <tqpainter.h>
#include <tqbrush.h>
#include <tqpixmap.h>
#include <tqimage.h>
#include <tqpen.h>
#include <tqpoint.h>
#include <tqtimer.h>
#include <tqsizepolicy.h>

// KDE includes.

#include <kstandarddirs.h>
#include <kcursor.h>
#include <tdeglobal.h>

// Local includes.

#include "ddebug.h"
#include "imageiface.h"
#include "dimg.h"
#include "imageselectionwidget.h"
#include "imageselectionwidget.moc"

namespace DigikamImagesPluginCore
{

class ImageSelectionWidgetPriv
{
public:

    enum ResizingMode
    {
        ResizingNone = 0,
        ResizingTopLeft,
        ResizingTopRight,
        ResizingBottomLeft,
        ResizingBottomRight
    };

    ImageSelectionWidgetPriv()
    {
        currentResizing = ResizingNone;
        iface           = 0;
        pixmap          = 0;
        guideSize       = 1;
    }

    // Golden guide types.
    bool                 drawGoldenSection;
    bool                 drawGoldenSpiralSection;
    bool                 drawGoldenSpiral;
    bool                 drawGoldenTriangle;

    // Golden guide translations.
    bool                 flipHorGoldenGuide;
    bool                 flipVerGoldenGuide;

    bool                 moving;
    bool                 autoOrientation;
    bool                 preciseCrop;

    int                  guideLinesType;
    int                  guideSize;

    int                  currentAspectRatioType;
    int                  currentResizing;
    int                  currentOrientation;

    float                currentWidthRatioValue;
    float                currentHeightRatioValue;

    TQPoint               lastPos;

    TQRect                rect;
    TQRect                image;                   // Real image dimension.
    TQRect                regionSelection;         // Real size image selection.
    TQRect                localRegionSelection;    // Local size selection.

    // Draggable local region selection corners.
    TQRect                localTopLeftCorner;
    TQRect                localBottomLeftCorner;
    TQRect                localTopRightCorner;
    TQRect                localBottomRightCorner;

    TQPixmap             *pixmap;

    TQColor               guideColor;

    Digikam::DImg        preview;

    Digikam::ImageIface *iface;
};

ImageSelectionWidget::ImageSelectionWidget(int w, int h, TQWidget *parent,
                                           int widthRatioValue, int heightRatioValue,
                                           int aspectRatioType, int orient, int guideLinesType)
                    : TQWidget(parent, 0, TQt::WDestructiveClose)
{
    d = new ImageSelectionWidgetPriv;
    d->currentAspectRatioType  = aspectRatioType;
    d->currentWidthRatioValue  = widthRatioValue;
    d->currentHeightRatioValue = heightRatioValue;
    d->currentOrientation      = orient;
    d->guideLinesType          = guideLinesType;
    d->autoOrientation         = false;
    d->preciseCrop             = false;
    d->moving                  = true;
    reverseRatioValues();

    setBackgroundMode(TQt::NoBackground);
    setMinimumSize(w, h);
    setMouseTracking(true);

    d->iface        = new Digikam::ImageIface(w, h);
    uchar *data     = d->iface->getPreviewImage();
    int width       = d->iface->previewWidth();
    int height      = d->iface->previewHeight();
    bool sixteenBit = d->iface->previewSixteenBit();
    bool hasAlpha   = d->iface->previewHasAlpha();
    d->preview      = Digikam::DImg(width, height, sixteenBit, hasAlpha, data);
    delete [] data;
    d->preview.convertToEightBit();
    d->pixmap  = new TQPixmap(w, h);

    d->image = TQRect(0, 0, d->iface->originalWidth(), d->iface->originalHeight());
    d->rect = TQRect(w/2-d->preview.width()/2, h/2-d->preview.height()/2,
                    d->preview.width(), d->preview.height());
    updatePixmap();
    setGoldenGuideTypes(true, false, false, false, false, false);
}

ImageSelectionWidget::~ImageSelectionWidget()
{
    delete d->iface;
    delete d->pixmap;
    delete d;
}

Digikam::ImageIface* ImageSelectionWidget::imageIface()
{
    return d->iface;
}

void ImageSelectionWidget::resizeEvent(TQResizeEvent *e)
{
    delete d->pixmap;

    int w           = e->size().width();
    int h           = e->size().height();

    uchar *data     = d->iface->setPreviewImageSize(w, h);
    int width       = d->iface->previewWidth();
    int height      = d->iface->previewHeight();
    bool sixteenBit = d->iface->previewSixteenBit();
    bool hasAlpha   = d->iface->previewHasAlpha();
    d->preview      = Digikam::DImg(width, height, sixteenBit, hasAlpha, data);
    delete [] data;
    d->preview.convertToEightBit();

    d->pixmap = new TQPixmap(w, h);

    d->rect = TQRect(w/2-d->preview.width()/2, h/2-d->preview.height()/2,
                    d->preview.width(), d->preview.height());
    updatePixmap();
}

int ImageSelectionWidget::getOriginalImageWidth()
{
    return d->image.width();
}

int ImageSelectionWidget::getOriginalImageHeight()
{
    return d->image.height();
}

TQRect ImageSelectionWidget::getRegionSelection()
{
    return d->regionSelection;
}

int ImageSelectionWidget::getMinWidthRange()
{
    return MINRANGE;
}

int ImageSelectionWidget::getMinHeightRange()
{
    return MINRANGE;
}

int ImageSelectionWidget::getMaxWidthRange()
{
    int maxW = d->image.width() - d->regionSelection.left();

    if (d->currentAspectRatioType != RATIONONE)
    {
        // Compute max width taking aspect ratio into account
        int t = d->currentWidthRatioValue > d->currentHeightRatioValue ? 1 : 0;
        int h = d->image.height() - d->regionSelection.top();
        int w = rint( ( h + t ) * d->currentWidthRatioValue /
                        d->currentHeightRatioValue ) - t;
        if ( w < maxW )
            maxW = w;
    }

    // Return max width adjusted if a precise crop is wanted
    return computePreciseSize(maxW, d->currentWidthRatioValue);
}

int ImageSelectionWidget::getMaxHeightRange()
{
    int maxH = d->image.height() - d->regionSelection.top();

    if (d->currentAspectRatioType != RATIONONE)
    {
        // Compute max height taking aspect ratio into account
        int t = d->currentHeightRatioValue > d->currentWidthRatioValue ? 1 : 0;
        int w = d->image.width() - d->regionSelection.left();
        int h = rint( ( w + t ) * d->currentHeightRatioValue /
                        d->currentWidthRatioValue ) - t;
        if ( h < maxH )
            maxH = h;
    }

    // Return max height adjusted if a precise crop is wanted
    return computePreciseSize(maxH, d->currentHeightRatioValue);
}

int ImageSelectionWidget::getWidthStep()
{
    if ( d->preciseCrop && preciseCropAvailable() )
        return d->currentWidthRatioValue;
    else
        return 1;
}

int ImageSelectionWidget::getHeightStep()
{
    if ( d->preciseCrop && preciseCropAvailable() )
        return d->currentHeightRatioValue;
    else
        return 1;
}

// Draw a new centered selection with half width (if orientation = Landscape)
// or with half height (if orientation = Portrait)
void ImageSelectionWidget::resetSelection()
{
    d->regionSelection.setWidth(d->image.width()/2);
    d->regionSelection.setHeight(d->image.height()/2);
    applyAspectRatio(d->currentOrientation == Portrait, false);

    setCenterSelection(CenterImage);
}

void ImageSelectionWidget::setCenterSelection(int centerType)
{
    // Adjust selection size if bigger than real image
    if ( d->regionSelection.height() > d->image.height() )
    {
        d->regionSelection.setHeight(d->image.height());
        applyAspectRatio(true, false);
    }
    if ( d->regionSelection.width() > d->image.width() )
    {
        d->regionSelection.setWidth(d->image.width());
        applyAspectRatio(false, false);
    }

    // Set center point for selection
    TQPoint center = d->image.center();
    switch (centerType)
    {
        case CenterWidth:
            center.setY(d->regionSelection.center().y());
            break;

        case CenterHeight:
            center.setX(d->regionSelection.center().x());
            break;
    }
    d->regionSelection.moveCenter(center);

    // Repaint
    updatePixmap();
    repaint(false);
    regionSelectionChanged();
}

// Draw a new centered selection with max size
void ImageSelectionWidget::maxAspectSelection()
{
    d->regionSelection.setWidth(d->image.width());
    d->regionSelection.setHeight(d->image.height());
    if ( d->currentAspectRatioType != RATIONONE )
        applyAspectRatio(d->currentOrientation == Portrait, false);

    setCenterSelection(CenterImage);
}

void ImageSelectionWidget::setGoldenGuideTypes(bool drawGoldenSection,  bool drawGoldenSpiralSection,
                                               bool drawGoldenSpiral,   bool drawGoldenTriangle,
                                               bool flipHorGoldenGuide, bool flipVerGoldenGuide)
{
    d->drawGoldenSection       = drawGoldenSection;
    d->drawGoldenSpiralSection = drawGoldenSpiralSection;
    d->drawGoldenSpiral        = drawGoldenSpiral;
    d->drawGoldenTriangle      = drawGoldenTriangle;
    d->flipHorGoldenGuide      = flipHorGoldenGuide;
    d->flipVerGoldenGuide      = flipVerGoldenGuide;
}

void ImageSelectionWidget::slotGuideLines(int guideLinesType)
{
    d->guideLinesType = guideLinesType;
    updatePixmap();
    repaint(false);
}

void ImageSelectionWidget::slotChangeGuideColor(const TQColor &color)
{
    d->guideColor = color;
    updatePixmap();
    repaint(false);
}

void ImageSelectionWidget::slotChangeGuideSize(int size)
{
    d->guideSize = size;
    updatePixmap();
    repaint(false);
}

void ImageSelectionWidget::setSelectionOrientation(int orient)
{
    d->currentOrientation = orient;
    reverseRatioValues();
    applyAspectRatio(true);
    emit signalSelectionOrientationChanged( d->currentOrientation );
}

void ImageSelectionWidget::setSelectionAspectRatioType(int aspectRatioType)
{
    d->currentAspectRatioType = aspectRatioType;

    // Set ratio values
    switch(aspectRatioType)
    {
       case RATIO01X01:
          d->currentWidthRatioValue = 1.0;
          d->currentHeightRatioValue = 1.0;
          break;

       case RATIO03X04:
          d->currentWidthRatioValue = 4.0;
          d->currentHeightRatioValue = 3.0;
          break;

       case RATIO02x03:
          d->currentWidthRatioValue = 3.0;
          d->currentHeightRatioValue = 2.0;
          break;

       case RATIO05x07:
          d->currentWidthRatioValue = 7.0;
          d->currentHeightRatioValue = 5.0;
          break;

       case RATIO07x10:
          d->currentWidthRatioValue = 10.0;
          d->currentHeightRatioValue = 7.0;
          break;

       case RATIO04X05:
          d->currentWidthRatioValue = 5.0;
          d->currentHeightRatioValue = 4.0;
          break;

       case RATIOGOLDEN:
          d->currentWidthRatioValue = PHI;
          d->currentHeightRatioValue = 1.0;
          break;
    }

    reverseRatioValues();
    applyAspectRatio(false);
}

void ImageSelectionWidget::setSelectionAspectRatioValue(int widthRatioValue,
                                                        int heightRatioValue)
{
    int gdc = widthRatioValue;

    // Compute greatest common divisor using Euclidean algorithm
    for (int tmp, mod = heightRatioValue; mod != 0; mod = tmp % mod)
    {
        tmp = gdc;
        gdc = mod;
    }

    d->currentWidthRatioValue  = widthRatioValue / gdc;
    d->currentHeightRatioValue = heightRatioValue / gdc;
    d->currentAspectRatioType  = RATIOCUSTOM;

    // Fix orientation
    if ( d->autoOrientation )
    {
        if ( heightRatioValue > widthRatioValue &&
             d->currentOrientation == Landscape )
        {
            d->currentOrientation = Portrait;
            emit signalSelectionOrientationChanged( d->currentOrientation );
        }
        else if ( widthRatioValue > heightRatioValue &&
                  d->currentOrientation == Portrait )
        {
            d->currentOrientation = Landscape;
            emit signalSelectionOrientationChanged( d->currentOrientation );
        }
    }
    else
        reverseRatioValues();

    applyAspectRatio(false);
}

void ImageSelectionWidget::reverseRatioValues()
{
    // Reverse ratio values if needed
    if ( ( d->currentWidthRatioValue > d->currentHeightRatioValue &&
           d->currentOrientation == Portrait ) ||
         ( d->currentHeightRatioValue > d->currentWidthRatioValue &&
           d->currentOrientation == Landscape ) )
    {
        float tmp = d->currentWidthRatioValue;
        d->currentWidthRatioValue = d->currentHeightRatioValue;
        d->currentHeightRatioValue = tmp;
    }
}

bool ImageSelectionWidget::preciseCropAvailable()
{
    // Define when precise crop feature can be used
    // No needed when aspect ratio is 1:1
    switch(d->currentAspectRatioType)
    {
        case RATIONONE:
        case RATIO01X01:
        case RATIOGOLDEN:
            return false;

        case RATIOCUSTOM:
            return ( d->currentWidthRatioValue != d->currentHeightRatioValue );

        default:
            return true;
    }
}

void ImageSelectionWidget::setPreciseCrop(bool precise)
{
    d->preciseCrop = precise;
    applyAspectRatio(false, true);
    regionSelectionChanged();
}

void ImageSelectionWidget::setAutoOrientation(bool orientation)
{
    d->autoOrientation = orientation;
}

void ImageSelectionWidget::setSelectionX(int x)
{
    d->regionSelection.moveLeft(x);
    regionSelectionMoved();
}

void ImageSelectionWidget::setSelectionY(int y)
{
    d->regionSelection.moveTop(y);
    regionSelectionMoved();
}

void ImageSelectionWidget::setSelectionWidth(int w)
{
    d->regionSelection.setWidth(w);
    applyAspectRatio(false, true);

    regionSelectionChanged();
}

void ImageSelectionWidget::setSelectionHeight(int h)
{
    d->regionSelection.setHeight(h);
    applyAspectRatio(true, true);

    regionSelectionChanged();
}

TQPoint ImageSelectionWidget::convertPoint(const TQPoint pm, bool localToReal)
{
    return convertPoint(pm.x(), pm.y(), localToReal);
}

TQPoint ImageSelectionWidget::convertPoint(int x, int y, bool localToReal)
{
    int pmX, pmY;

    if (localToReal)
    {
        pmX = ( x - d->rect.left() ) * (float)d->image.width() /
                    (float)d->preview.width();

        pmY = ( y - d->rect.top() ) * (float)d->image.height() /
                    (float)d->preview.height();
    }
    else
    {
        pmX = d->rect.left() + ( x * (float)d->preview.width() /
                                    (float)d->image.width() );

        pmY = d->rect.top() + ( y * (float)d->preview.height() /
                                    (float)d->image.height() );
    }

    return TQPoint(pmX, pmY);
}

int ImageSelectionWidget::computePreciseSize(int size, int step)
{
    // Adjust size if precise crop is wanted
    if ( d->preciseCrop && preciseCropAvailable() )
        size = int(size / step) * step;

    return size;
}

void ImageSelectionWidget::applyAspectRatio(bool useHeight, bool repaintWidget)
{
    // Save selection area for re-adjustment after changing width and height.
    TQRect oldRegionSelection = d->regionSelection;

    if ( !useHeight )  // Width changed.
    {
        int w = computePreciseSize(d->regionSelection.width(),
                                   d->currentWidthRatioValue);

        d->regionSelection.setWidth(w);
        switch(d->currentAspectRatioType)
        {
            case RATIONONE:
                break;

            default:
                d->regionSelection.setHeight(rint( w * d->currentHeightRatioValue /
                                             d->currentWidthRatioValue ) );
                break;
        }
    }
    else      // Height changed.
    {
        int h = computePreciseSize(d->regionSelection.height(),
                                   d->currentHeightRatioValue);

        d->regionSelection.setHeight(h);
        switch(d->currentAspectRatioType)
        {
            case RATIONONE:
                break;

            default:
                d->regionSelection.setWidth(rint( h * d->currentWidthRatioValue /
                                            d->currentHeightRatioValue ) );
                break;
        }
    }

    // If we change selection size by a corner, re-adjust the oposite corner position.
    switch(d->currentResizing)
    {
        case ImageSelectionWidgetPriv::ResizingTopLeft:
            d->regionSelection.moveBottomRight( oldRegionSelection.bottomRight() );
            break;

        case ImageSelectionWidgetPriv::ResizingTopRight:
            d->regionSelection.moveBottomLeft( oldRegionSelection.bottomLeft() );
            break;

        case ImageSelectionWidgetPriv::ResizingBottomLeft:
            d->regionSelection.moveTopRight( oldRegionSelection.topRight() );
            break;

        case ImageSelectionWidgetPriv::ResizingBottomRight:
             d->regionSelection.moveTopLeft( oldRegionSelection.topLeft() );
             break;
    }

    if (repaintWidget)
    {
        updatePixmap();
        repaint(false);
    }
}

void ImageSelectionWidget::normalizeRegion()
{
    // Perform normalization of selection area.

    if (d->regionSelection.left() < d->image.left())
        d->regionSelection.moveLeft(d->image.left());

    if (d->regionSelection.top() < d->image.top())
        d->regionSelection.moveTop(d->image.top());

    if (d->regionSelection.right() > d->image.right())
        d->regionSelection.moveRight(d->image.right());

    if (d->regionSelection.bottom() > d->image.bottom())
        d->regionSelection.moveBottom(d->image.bottom());
}

void ImageSelectionWidget::regionSelectionMoved()
{
    normalizeRegion();

    updatePixmap();
    repaint(false);

    emit signalSelectionMoved( d->regionSelection );
}

void ImageSelectionWidget::regionSelectionChanged()
{
    // Compute the intersection of selection region and image region
    TQRect cut = d->regionSelection & d->image;

    // Adjust selection size if it was cropped
    if ( d->regionSelection.width() > cut.width() )
    {
        d->regionSelection = cut;
        applyAspectRatio(false);
    }
    if ( d->regionSelection.height() > cut.height() )
    {
        d->regionSelection = cut;
        applyAspectRatio(true);
    }

    emit signalSelectionChanged( d->regionSelection );
}

void ImageSelectionWidget::updatePixmap()
{
    // Updated local selection region.
    d->localRegionSelection.setTopLeft(
            convertPoint(d->regionSelection.topLeft(), false));
    d->localRegionSelection.setBottomRight(
            convertPoint(d->regionSelection.bottomRight(), false));

    // Updated dragging corners region.
    d->localTopLeftCorner.setRect(d->localRegionSelection.left(),
                                  d->localRegionSelection.top(), 8, 8);
    d->localBottomLeftCorner.setRect(d->localRegionSelection.left(),
                                     d->localRegionSelection.bottom() - 7, 8, 8);
    d->localTopRightCorner.setRect(d->localRegionSelection.right() - 7,
                                   d->localRegionSelection.top(), 8, 8);
    d->localBottomRightCorner.setRect(d->localRegionSelection.right() - 7,
                                      d->localRegionSelection.bottom() - 7, 8, 8);

    // Drawing background and image.
    d->pixmap->fill(colorGroup().background());

    if (d->preview.isNull())
        return;

    // Drawing region outside selection grayed.

    Digikam::DImg image = d->preview.copy();

    uchar* ptr = image.bits();
    uchar  r, g, b;

    for (int y=d->rect.top() ; y <= d->rect.bottom() ; y++)
    {
        for (int x=d->rect.left() ; x <= d->rect.right() ; x++)
        {
            if (! d->localRegionSelection.contains(x, y, true) )
            {
                b = ptr[0];
                g = ptr[1];
                r = ptr[2];

                r += (uchar)((RCOL - r) * OPACITY);
                g += (uchar)((GCOL - g) * OPACITY);
                b += (uchar)((BCOL - b) * OPACITY);

                ptr[0] = b;
                ptr[1] = g;
                ptr[2] = r;
            }

            ptr+=4;
        }
    }

    TQPixmap pix = d->iface->convertToPixmap(image);
    bitBlt(d->pixmap, d->rect.x(), d->rect.y(), &pix);

    // Stop here if no selection to draw
    if ( d->regionSelection.isEmpty() )
        return;

    TQPainter p(d->pixmap);

    // Drawing selection borders.

    p.setPen(TQPen(TQColor(250, 250, 255), 1, TQt::SolidLine));
    p.drawRect(d->localRegionSelection);

    // Drawing selection corners.

    p.drawRect(d->localTopLeftCorner);
    p.drawRect(d->localBottomLeftCorner);
    p.drawRect(d->localTopRightCorner);
    p.drawRect(d->localBottomRightCorner);

    // Drawing guide lines.

    // Constraint drawing only on local selection region.
    // This is needed because arcs and incurved lines can draw
    // outside a little of local selection region.
    p.setClipping(true);
    p.setClipRect(d->localRegionSelection);

    switch (d->guideLinesType)
    {
       case RulesOfThirds:
       {
            int xThird = d->localRegionSelection.width()  / 3;
            int yThird = d->localRegionSelection.height() / 3;

            p.setPen(TQPen(TQt::white, d->guideSize, TQt::SolidLine));
            p.drawLine( d->localRegionSelection.left() + xThird,   d->localRegionSelection.top(),
                        d->localRegionSelection.left() + xThird,   d->localRegionSelection.bottom() );
            p.drawLine( d->localRegionSelection.left() + 2*xThird, d->localRegionSelection.top(),
                        d->localRegionSelection.left() + 2*xThird, d->localRegionSelection.bottom() );

            p.drawLine( d->localRegionSelection.left(),  d->localRegionSelection.top() + yThird,
                        d->localRegionSelection.right(), d->localRegionSelection.top() + yThird );
            p.drawLine( d->localRegionSelection.left(),  d->localRegionSelection.top() + 2*yThird,
                        d->localRegionSelection.right(), d->localRegionSelection.top() + 2*yThird );

            p.setPen(TQPen(d->guideColor, d->guideSize, TQt::DotLine));
            p.drawLine( d->localRegionSelection.left() + xThird,   d->localRegionSelection.top(),
                        d->localRegionSelection.left() + xThird,   d->localRegionSelection.bottom() );
            p.drawLine( d->localRegionSelection.left() + 2*xThird, d->localRegionSelection.top(),
                        d->localRegionSelection.left() + 2*xThird, d->localRegionSelection.bottom() );

            p.drawLine( d->localRegionSelection.left(),  d->localRegionSelection.top() + yThird,
                        d->localRegionSelection.right(), d->localRegionSelection.top() + yThird );
            p.drawLine( d->localRegionSelection.left(),  d->localRegionSelection.top() + 2*yThird,
                        d->localRegionSelection.right(), d->localRegionSelection.top() + 2*yThird );
            break;
       }

       case DiagonalMethod:
       {
           // Move coordinates to top, left
           p.translate(d->localRegionSelection.topLeft().x(), d->localRegionSelection.topLeft().y());

           float w = (float)d->localRegionSelection.width();
           float h = (float)d->localRegionSelection.height();

           p.setPen(TQPen(TQt::white, d->guideSize, TQt::SolidLine));
           if (w > h) 
           {
               p.drawLine( 0, 0, h, h);
               p.drawLine( 0, h, h, 0);
               p.drawLine( w-h, 0, w, h);
               p.drawLine( w-h, h, w, 0);

           } 
           else 
           {
               p.drawLine( 0, 0, w, w);
               p.drawLine( 0, w, w, 0);
               p.drawLine( 0, h-w, w, h);
               p.drawLine( 0, h, w, h-w);
           }

           p.setPen(TQPen(d->guideColor, d->guideSize, TQt::DotLine));
           if (w > h) 
           {
               p.drawLine( 0, 0, h, h);
               p.drawLine( 0, h, h, 0);
               p.drawLine( w-h, 0, w, h);
               p.drawLine( w-h, h, w, 0);

           }
           else 
           {
               p.drawLine( 0, 0, w, w);
               p.drawLine( 0, w, w, 0);
               p.drawLine( 0, h-w, w, h);
               p.drawLine( 0, h, w, h-w);
           }
           break;
       }

       case HarmoniousTriangles:
       {
            // Move coordinates to local center selection.
            p.translate(d->localRegionSelection.center().x(), d->localRegionSelection.center().y());

            // Flip horizontal.
            if (d->flipHorGoldenGuide)
                p.scale(-1, 1);

            // Flip verical.
            if (d->flipVerGoldenGuide)
                p.scale(1, -1);

            float w = (float)d->localRegionSelection.width();
            float h = (float)d->localRegionSelection.height();
            int dst = (int)((h*cos(atan(w/h)) / (cos(atan(h/w)))));

            p.setPen(TQPen(TQt::white, d->guideSize, TQt::SolidLine));
            p.drawLine( -d->localRegionSelection.width()/2, -d->localRegionSelection.height()/2,
                         d->localRegionSelection.width()/2,  d->localRegionSelection.height()/2);

            p.drawLine( -d->localRegionSelection.width()/2 + dst, -d->localRegionSelection.height()/2,
                        -d->localRegionSelection.width()/2,        d->localRegionSelection.height()/2);

            p.drawLine( d->localRegionSelection.width()/2,       -d->localRegionSelection.height()/2,
                        d->localRegionSelection.width()/2 - dst,  d->localRegionSelection.height()/2);

            p.setPen(TQPen(d->guideColor, d->guideSize, TQt::DotLine));
            p.drawLine( -d->localRegionSelection.width()/2, -d->localRegionSelection.height()/2,
                         d->localRegionSelection.width()/2,  d->localRegionSelection.height()/2);

            p.drawLine( -d->localRegionSelection.width()/2 + dst, -d->localRegionSelection.height()/2,
                        -d->localRegionSelection.width()/2,        d->localRegionSelection.height()/2);

            p.drawLine( d->localRegionSelection.width()/2,       -d->localRegionSelection.height()/2,
                        d->localRegionSelection.width()/2 - dst,  d->localRegionSelection.height()/2);
            break;
       }

       case GoldenMean:
       {
            // Move coordinates to local center selection.
            p.translate(d->localRegionSelection.center().x(), d->localRegionSelection.center().y());

            // Flip horizontal.
            if (d->flipHorGoldenGuide)
                p.scale(-1, 1);

            // Flip vertical.
            if (d->flipVerGoldenGuide)
                p.scale(1, -1);

            int w = d->localRegionSelection.width();
            int h = d->localRegionSelection.height();

            // lengths for the golden mean and half the sizes of the region:
            int w_g = (int)(w*INVPHI);
            int h_g = (int)(h*INVPHI);
            int w_2 = w/2;
            int h_2 = h/2;

            TQRect R1(-w_2, -h_2, w_g, h);
            // w - 2*w_2 corrects for one-pixel difference
            // so that R2.right() is really at the right end of the region
            TQRect R2(w_g-w_2, h_2-h_g, w-w_g+1-(w - 2*w_2), h_g);

            TQRect R3((int)(w_2 - R2.width()*INVPHI), -h_2,
                     (int)(R2.width()*INVPHI), h - R2.height());
            TQRect R4(R2.x(), R1.y(), R3.x() - R2.x(),
                     (int)(R3.height()*INVPHI));
            TQRect R5(R4.x(), R4.bottom(), (int)(R4.width()*INVPHI),
                     R3.height() - R4.height());
            TQRect R6(R5.x() + R5.width(), R5.bottom() - (int)(R5.height()*INVPHI),
                     R3.x() - R5.right(), (int)(R5.height()*INVPHI));
            TQRect R7(R6.right() - (int)(R6.width()*INVPHI), R4.bottom(),
                     (int)(R6.width()*INVPHI), R5.height() - R6.height());

            p.setPen(TQPen(TQt::white, d->guideSize, TQt::SolidLine));

            // Drawing Golden sections.
            if (d->drawGoldenSection)
            {
               // horizontal lines:
               p.drawLine( R1.left(), R2.top(),
                           R2.right(), R2.top());

               p.drawLine( R1.left(), R1.top() + R2.height(),
                           R2.right(), R1.top() + R2.height());

               // vertical lines:
               p.drawLine( R1.right(), R1.top(),
                           R1.right(), R1.bottom() );

               p.drawLine( R1.left()+R2.width(), R1.top(),
                           R1.left()+R2.width(), R1.bottom() );
            }

            // Drawing Golden triangle guides.
            if (d->drawGoldenTriangle)
            {
               p.drawLine( R1.left(),  R1.bottom(),
                           R2.right(), R1.top() );

               p.drawLine( R1.left(), R1.top(),
                           R2.right() - R1.width(), R1.bottom());

               p.drawLine( R1.left() + R1.width(), R1.top(),
                           R2.right(), R1.bottom() );
            }

            // Drawing Golden spiral sections.
            if (d->drawGoldenSpiralSection)
            {
               p.drawLine( R1.topRight(),   R1.bottomRight() );
               p.drawLine( R2.topLeft(),    R2.topRight() );
               p.drawLine( R3.topLeft(),    R3.bottomLeft() );
               p.drawLine( R4.bottomLeft(), R4.bottomRight() );
               p.drawLine( R5.topRight(),   R5.bottomRight() );
               p.drawLine( R6.topLeft(),    R6.topRight() );
               p.drawLine( R7.topLeft(),    R7.bottomLeft() );
            }

            // Drawing Golden Spiral.
            if (d->drawGoldenSpiral)
            {
               p.drawArc ( R1.left(),
                           R1.top() - R1.height(),
                           2*R1.width(), 2*R1.height(),
                           180*16, 90*16);

               p.drawArc ( R2.right() - 2*R2.width(),
                           R1.bottom() - 2*R2.height(),
                           2*R2.width(), 2*R2.height(),
                           270*16, 90*16);

               p.drawArc ( R2.right() - 2*R3.width(),
                           R3.top(),
                           2*R3.width(), 2*R3.height(),
                           0, 90*16);

               p.drawArc ( R4.left(),
                           R4.top(),
                           2*R4.width(), 2*R4.height(),
                           90*16, 90*16);

               p.drawArc ( R5.left(),
                           R5.top()-R5.height(),
                           2*R5.width(), 2*R5.height(),
                           180*16, 90*16);

               p.drawArc ( R6.left()-R6.width(),
                           R6.top()-R6.height(),
                           2*R6.width(), 2*R6.height(),
                           270*16, 90*16);

               p.drawArc ( R7.left()-R7.width(),
                           R7.top(),
                           2*R7.width(), 2*R7.height(),
                           0, 90*16);
            }

            p.setPen(TQPen(d->guideColor, d->guideSize, TQt::DotLine));

            // Drawing Golden sections.
            if (d->drawGoldenSection)
            {
               // horizontal lines:
               p.drawLine( R1.left(), R2.top(),
                           R2.right(), R2.top());

               p.drawLine( R1.left(), R1.top() + R2.height(),
                           R2.right(), R1.top() + R2.height());

               // vertical lines:
               p.drawLine( R1.right(), R1.top(),
                           R1.right(), R1.bottom() );

               p.drawLine( R1.left()+R2.width(), R1.top(),
                           R1.left()+R2.width(), R1.bottom() );
            }

            // Drawing Golden triangle guides.
            if (d->drawGoldenTriangle)
            {
               p.drawLine( R1.left(),  R1.bottom(),
                           R2.right(), R1.top() );

               p.drawLine( R1.left(), R1.top(),
                           R2.right() - R1.width(), R1.bottom());

               p.drawLine( R1.left() + R1.width(), R1.top(),
                           R2.right(), R1.bottom() );
            }

            // Drawing Golden spiral sections.
            if (d->drawGoldenSpiralSection)
            {
               p.drawLine( R1.topRight(),   R1.bottomRight() );
               p.drawLine( R2.topLeft(),    R2.topRight() );
               p.drawLine( R3.topLeft(),    R3.bottomLeft() );
               p.drawLine( R4.bottomLeft(), R4.bottomRight() );
               p.drawLine( R5.topRight(),   R5.bottomRight() );
               p.drawLine( R6.topLeft(),    R6.topRight() );
               p.drawLine( R7.topLeft(),    R7.bottomLeft() );
            }

            // Drawing Golden Spiral.
            if (d->drawGoldenSpiral)
            {
               p.drawArc ( R1.left(),
                           R1.top() - R1.height(),
                           2*R1.width(), 2*R1.height(),
                           180*16, 90*16);

               p.drawArc ( R2.right() - 2*R2.width(),
                           R1.bottom() - 2*R2.height(),
                           2*R2.width(), 2*R2.height(),
                           270*16, 90*16);

               p.drawArc ( R2.right() - 2*R3.width(),
                           R3.top(),
                           2*R3.width(), 2*R3.height(),
                           0, 90*16);

               p.drawArc ( R4.left(),
                           R4.top(),
                           2*R4.width(), 2*R4.height(),
                           90*16, 90*16);

               p.drawArc ( R5.left(),
                           R5.top()-R5.height(),
                           2*R5.width(), 2*R5.height(),
                           180*16, 90*16);

               p.drawArc ( R6.left()-R6.width(),
                           R6.top()-R6.height(),
                           2*R6.width(), 2*R6.height(),
                           270*16, 90*16);

               p.drawArc ( R7.left()-R7.width(),
                           R7.top(),
                           2*R7.width(), 2*R7.height(),
                           0, 90*16);
            }

            break;
       }
    }

    p.setClipping(false);

    p.end();
}

void ImageSelectionWidget::paintEvent( TQPaintEvent * )
{
    bitBlt(this, 0, 0, d->pixmap);
}

TQPoint ImageSelectionWidget::opposite()
{
    TQPoint opp;

    switch(d->currentResizing)
    {
        case ImageSelectionWidgetPriv::ResizingTopRight:
            opp = d->regionSelection.bottomLeft();
            break;

        case ImageSelectionWidgetPriv::ResizingBottomLeft:
            opp = d->regionSelection.topRight();
            break;

        case ImageSelectionWidgetPriv::ResizingBottomRight:
            opp = d->regionSelection.topLeft();
            break;

        case ImageSelectionWidgetPriv::ResizingTopLeft:
        default:
            opp = d->regionSelection.bottomRight();
            break;
    }

    return opp;
}

float ImageSelectionWidget::distance(TQPoint a, TQPoint b)
{
    return sqrt(pow(a.x() - b.x(), 2) + pow(a.y() - b.y(), 2));
}

void ImageSelectionWidget::setCursorResizing()
{
    switch(d->currentResizing)
    {
        case ImageSelectionWidgetPriv::ResizingTopLeft:
            setCursor( KCursor::sizeFDiagCursor() );
            break;

        case ImageSelectionWidgetPriv::ResizingTopRight:
            setCursor( KCursor::sizeBDiagCursor() );
            break;

        case ImageSelectionWidgetPriv::ResizingBottomLeft:
            setCursor( KCursor::sizeBDiagCursor() );
            break;

        case ImageSelectionWidgetPriv::ResizingBottomRight:
            setCursor( KCursor::sizeFDiagCursor() );
            break;
    }
}

void ImageSelectionWidget::placeSelection(TQPoint pm, bool symmetric, TQPoint center)
{
    // Set orientation
    if ( d->autoOrientation )
    {
        TQPoint rel = pm - opposite();

        if ( abs(rel.x()) > abs(rel.y()) )
        {
            if ( d->currentOrientation == Portrait )
            {
                d->currentOrientation = Landscape;
                reverseRatioValues();
                emit signalSelectionOrientationChanged( d->currentOrientation );
            }
        }
        else
        {
            if ( d->currentOrientation == Landscape )
            {
                d->currentOrientation = Portrait;
                reverseRatioValues();
                emit signalSelectionOrientationChanged( d->currentOrientation );
            }
        }
    }

    // Place the corner at the mouse
    // If a symmetric selection is wanted, place opposite corner to
    // the center, double selection size and move it to old center after
    // computing aspect ratio.
    switch(d->currentResizing)
    {
        case ImageSelectionWidgetPriv::ResizingTopLeft:
            // Place corners to the proper position
            d->regionSelection.setTopLeft(pm);
            if ( symmetric )
                d->regionSelection.setBottomRight(center);
            break;

        case ImageSelectionWidgetPriv::ResizingTopRight:
            d->regionSelection.setTopRight(pm);
            if ( symmetric )
                d->regionSelection.setBottomLeft(center);
            break;

        case ImageSelectionWidgetPriv::ResizingBottomLeft:
            d->regionSelection.setBottomLeft(pm);
            if ( symmetric )
                d->regionSelection.setTopRight(center);
            break;

        case ImageSelectionWidgetPriv::ResizingBottomRight:
            d->regionSelection.setBottomRight(pm);
            if ( symmetric )
                d->regionSelection.setTopLeft(center);
            break;
    }

    if ( symmetric )
        d->regionSelection.setSize(d->regionSelection.size()*2);
    applyAspectRatio(d->currentOrientation == Portrait, false);
    if ( symmetric )
        d->regionSelection.moveCenter(center);

    // Repaint
    updatePixmap();
    repaint(false);
}

void ImageSelectionWidget::mousePressEvent ( TQMouseEvent * e )
{
    if ( e->button() == TQt::LeftButton )
    {
        TQPoint pm = TQPoint(e->x(), e->y());
        TQPoint pmVirtual = convertPoint(pm);
        d->moving = false;

        if ( (e->state() & TQt::ShiftButton) == TQt::ShiftButton )
        {
            bool symmetric = (e->state() & TQt::ControlButton ) == TQt::ControlButton;
            TQPoint center = d->regionSelection.center();

            // Find the closest corner

            TQPoint points[] = { d->regionSelection.topLeft(),
                                d->regionSelection.topRight(),
                                d->regionSelection.bottomLeft(),
                                d->regionSelection.bottomRight() };
            int resizings[] = { ImageSelectionWidgetPriv::ResizingTopLeft,
                                ImageSelectionWidgetPriv::ResizingTopRight,
                                ImageSelectionWidgetPriv::ResizingBottomLeft,
                                ImageSelectionWidgetPriv::ResizingBottomRight };
            float dist = -1;
            for (int i = 0 ; i < 4 ; i++)
            {
                TQPoint point = points[i];
                float dist2 = distance(pmVirtual, point);
                if (dist2 < dist || d->currentResizing == ImageSelectionWidgetPriv::ResizingNone)
                {
                    dist = dist2;
                    d->currentResizing = resizings[i];
                }
            }

            setCursorResizing();

            placeSelection(pmVirtual, symmetric, center);
        }
        else
        {
            if ( d->localTopLeftCorner.contains( pm ) )
                d->currentResizing = ImageSelectionWidgetPriv::ResizingTopLeft;
            else if ( d->localTopRightCorner.contains( pm ) )
                d->currentResizing = ImageSelectionWidgetPriv::ResizingTopRight;
            else if ( d->localBottomLeftCorner.contains( pm ) )
                d->currentResizing = ImageSelectionWidgetPriv::ResizingBottomLeft;
            else if ( d->localBottomRightCorner.contains( pm ) )
                d->currentResizing = ImageSelectionWidgetPriv::ResizingBottomRight;
            else
            {
                d->lastPos = pmVirtual;
                setCursor( KCursor::sizeAllCursor() );

                if (d->regionSelection.contains( pmVirtual ) )
                {
                    d->moving = true;
                }
                else
                {
                    d->regionSelection.moveCenter( pmVirtual );
                    normalizeRegion();
                    updatePixmap();
                    repaint(false);
                }
            }
        }
    }
}

void ImageSelectionWidget::mouseReleaseEvent ( TQMouseEvent * )
{
    if ( d->currentResizing != ImageSelectionWidgetPriv::ResizingNone )
    {
        setCursor( KCursor::arrowCursor() );
        regionSelectionChanged();
        d->currentResizing = ImageSelectionWidgetPriv::ResizingNone;
    }
    else if ( d->regionSelection.contains( d->lastPos ) )
    {
        setCursor( KCursor::handCursor() );
        regionSelectionMoved();
    }
    else
    {
        setCursor( KCursor::arrowCursor() );
        regionSelectionMoved();
    }
}

void ImageSelectionWidget::mouseMoveEvent ( TQMouseEvent * e )
{
    if ( ( e->state() & TQt::LeftButton ) == TQt::LeftButton )
    {
        if ( d->moving )
        {
            setCursor( KCursor::sizeAllCursor() );
            TQPoint newPos = convertPoint(e->x(), e->y());

            d->regionSelection.moveBy( newPos.x() - d->lastPos.x(),
                                       newPos.y() - d->lastPos.y() );

            d->lastPos = newPos;

            normalizeRegion();

            updatePixmap();
            repaint(false);
        }
        else
        {
            TQPoint pmVirtual = convertPoint(e->x(), e->y());

            if ( d->currentResizing == ImageSelectionWidgetPriv::ResizingNone )
            {
                d->regionSelection.setTopLeft( pmVirtual );
                d->regionSelection.setBottomRight( pmVirtual );
                d->currentResizing = ImageSelectionWidgetPriv::ResizingTopLeft; // set to anything
            }

            TQPoint center = d->regionSelection.center();
            bool symmetric = (e->state() & TQt::ControlButton ) == TQt::ControlButton;

            // Change resizing mode

            TQPoint opp = symmetric ? center : opposite();
            TQPoint dir = pmVirtual - opp;

            if ( dir.x() > 0 && dir.y() > 0 && d->currentResizing != ImageSelectionWidgetPriv::ResizingBottomRight)
            {
                d->currentResizing = ImageSelectionWidgetPriv::ResizingBottomRight;
                d->regionSelection.setTopLeft( opp );
                setCursor( KCursor::sizeFDiagCursor() );
            }
            else if ( dir.x() > 0 && dir.y() < 0 && d->currentResizing != ImageSelectionWidgetPriv::ResizingTopRight)
            {
                d->currentResizing = ImageSelectionWidgetPriv::ResizingTopRight;
                d->regionSelection.setBottomLeft( opp );
                setCursor( KCursor::sizeBDiagCursor() );
            }
            else if ( dir.x() < 0 && dir.y() > 0 && d->currentResizing != ImageSelectionWidgetPriv::ResizingBottomLeft)
            {
                d->currentResizing = ImageSelectionWidgetPriv::ResizingBottomLeft;
                d->regionSelection.setTopRight( opp );
                setCursor( KCursor::sizeBDiagCursor() );
            }
            else if ( dir.x() < 0 && dir.y() < 0 && d->currentResizing != ImageSelectionWidgetPriv::ResizingTopLeft)
            {
                d->currentResizing = ImageSelectionWidgetPriv::ResizingTopLeft;
                d->regionSelection.setBottomRight( opp );
                setCursor( KCursor::sizeFDiagCursor() );
            }
            else
            {
                if ( dir.x() == 0 && dir.y() == 0 )
                    setCursor( KCursor::sizeAllCursor() );
                else if ( dir.x() == 0 )
                    setCursor( KCursor::sizeHorCursor() );
                else if ( dir.y() == 0 )
                    setCursor( KCursor::sizeVerCursor() );
            }

            placeSelection(pmVirtual, symmetric, center);
        }
    }
    else
    {
        if ( d->localTopLeftCorner.contains( e->x(), e->y() ) ||
             d->localBottomRightCorner.contains( e->x(), e->y() ) )
            setCursor( KCursor::sizeFDiagCursor() );
        else if ( d->localTopRightCorner.contains( e->x(), e->y() ) ||
                  d->localBottomLeftCorner.contains( e->x(), e->y() ) )
            setCursor( KCursor::sizeBDiagCursor() );
        else if ( d->localRegionSelection.contains( e->x(), e->y() ) )
            setCursor( KCursor::handCursor() );
        else
            setCursor( KCursor::arrowCursor() );
    }
}

}  // NameSpace DigikamImagesPluginCore
