File indexing completed on 2024-05-12 16:34:24

0001 /* This file is part of the KDE project
0002    Copyright 2011 Silvio Heinrich <plassy@web.de>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "SelectionRect.h"
0021 
0022 #include <QRectF>
0023 #include <QPointF>
0024 #include <QSizeF>
0025 #include <limits>
0026 
0027 SelectionRect::SelectionRect(const QRectF &rect, qreal handleSize):
0028     m_rect(rect),
0029     m_aspectRatio(1),
0030     m_aConstr(0),
0031     m_handleSize(handleSize),
0032     m_currentHandle(0)
0033 {
0034     m_lConstr = -std::numeric_limits<qreal>::infinity();
0035     m_rConstr = std::numeric_limits<qreal>::infinity();
0036     m_tConstr = -std::numeric_limits<qreal>::infinity();
0037     m_bConstr = std::numeric_limits<qreal>::infinity();
0038 }
0039 
0040 void SelectionRect::setRect(const QRectF &rect)
0041 {
0042     m_rect = rect;
0043 }
0044 
0045 void SelectionRect::setHandleSize(qreal size)
0046 {
0047     m_handleSize = size;
0048 }
0049 
0050 void SelectionRect::setAspectRatio(qreal aspect)
0051 {
0052     m_aspectRatio = aspect;
0053 }
0054 
0055 void SelectionRect::setConstrainingRect(const QRectF &rect)
0056 {
0057     m_lConstr = rect.left();
0058     m_rConstr = rect.right();
0059     m_tConstr = rect.top();
0060     m_bConstr = rect.bottom();
0061 }
0062 
0063 void SelectionRect::setConstrainingAspectRatio(qreal aspect)
0064 {
0065     m_aConstr = aspect;
0066 
0067     if (m_aConstr != 0.0) {
0068         fixAspect(TOP_HANDLE);
0069     }
0070 }
0071 
0072 bool SelectionRect::beginDragging(const QPointF &pos)
0073 {
0074     m_tempPos = pos;
0075     m_currentHandle = getHandleFlags(pos);
0076     return bool(m_currentHandle);
0077 }
0078 
0079 void SelectionRect::doDragging(const QPointF &pos)
0080 {
0081     if (m_currentHandle & INSIDE_RECT) {
0082         m_rect.moveTo(m_rect.topLeft() + (pos - m_tempPos));
0083         m_tempPos = pos;
0084 
0085         if (m_rect.left() < m_lConstr) {
0086             m_rect.moveLeft(m_lConstr);
0087         }
0088         if (m_rect.right() > m_rConstr) {
0089             m_rect.moveRight(m_rConstr);
0090         }
0091         if (m_rect.top() < m_tConstr) {
0092             m_rect.moveTop(m_tConstr);
0093         }
0094         if (m_rect.bottom() > m_bConstr) {
0095             m_rect.moveBottom(m_bConstr);
0096         }
0097     }
0098     else {
0099         if (m_currentHandle & TOP_HANDLE) {
0100             m_rect.setTop(qBound(m_tConstr, pos.y(), m_bConstr));
0101         }
0102         if (m_currentHandle & BOTTOM_HANDLE) {
0103             m_rect.setBottom(qBound(m_tConstr, pos.y(), m_bConstr));
0104         }
0105         if (m_currentHandle & LEFT_HANDLE) {
0106             m_rect.setLeft(qBound(m_lConstr, pos.x(), m_rConstr));
0107         }
0108         if (m_currentHandle & RIGHT_HANDLE) {
0109             m_rect.setRight(qBound(m_lConstr, pos.x(), m_rConstr));
0110         }
0111 
0112         if (m_aConstr != 0.0) {
0113             fixAspect(m_currentHandle);
0114         }
0115     }
0116 }
0117 
0118 void SelectionRect::finishDragging()
0119 {
0120     m_currentHandle = 0;
0121     m_rect = m_rect.normalized();
0122 }
0123 
0124 SelectionRect::HandleFlags SelectionRect::getHandleFlags(const QPointF &pos) const
0125 {
0126     for(int i=0; i<getNumHandles(); ++i) {
0127         if(getHandleRect(getHandleFlags(i)).contains(pos))
0128             return getHandleFlags(i);
0129     }
0130 
0131     return m_rect.contains(pos) ? INSIDE_RECT : 0;
0132 }
0133 
0134 SelectionRect::HandleFlags SelectionRect::getHandleFlags(int handleIndex) const
0135 {
0136     switch(handleIndex)
0137     {
0138         case 0: return TOP_LEFT_HANDLE;
0139         case 1: return TOP_HANDLE;
0140         case 2: return TOP_RIGHT_HANDLE;
0141         case 3: return RIGHT_HANDLE;
0142         case 4: return BOTTOM_RIGHT_HANDLE;
0143         case 5: return BOTTOM_HANDLE;
0144         case 6: return BOTTOM_LEFT_HANDLE;
0145         case 7: return LEFT_HANDLE;
0146     }
0147 
0148     return 0;
0149 }
0150 
0151 QRectF SelectionRect::getHandleRect(HandleFlags handle) const
0152 {
0153     qreal x = (m_rect.left() + m_rect.right()) / 2.0;
0154     qreal y = (m_rect.top() + m_rect.bottom()) / 2.0;
0155     qreal w = m_handleSize;
0156     qreal h = m_handleSize * m_aspectRatio;
0157 
0158     x = (handle & LEFT_HANDLE) ? m_rect.left() : x;
0159     y = (handle & TOP_HANDLE) ? m_rect.top() : y;
0160     x = (handle & RIGHT_HANDLE) ? m_rect.right() : x;
0161     y = (handle & BOTTOM_HANDLE) ? m_rect.bottom() : y;
0162 
0163     return QRectF(x-(w/2.0), y-(h/2.0), w, h);
0164 }
0165 
0166 void SelectionRect::fixAspect(HandleFlags handle)
0167 {
0168     QRectF oldRect = m_rect;
0169 
0170     switch (handle)
0171     {
0172     case TOP_HANDLE:
0173     case BOTTOM_HANDLE:
0174         m_rect.setWidth((m_rect.height() * m_aConstr) / m_aspectRatio);
0175         break;
0176 
0177     case LEFT_HANDLE:
0178     case RIGHT_HANDLE:
0179     case BOTTOM_RIGHT_HANDLE:
0180         m_rect.setHeight((m_rect.width() / m_aConstr) * m_aspectRatio);
0181         break;
0182 
0183     case TOP_RIGHT_HANDLE:
0184         m_rect.setHeight((m_rect.width() / m_aConstr) * m_aspectRatio);
0185         m_rect.moveBottomLeft(oldRect.bottomLeft());
0186         break;
0187 
0188     case BOTTOM_LEFT_HANDLE:
0189         m_rect.setHeight((m_rect.width() / m_aConstr) * m_aspectRatio);
0190         m_rect.moveTopRight(oldRect.topRight());
0191         break;
0192 
0193     case TOP_LEFT_HANDLE:
0194         m_rect.setHeight((m_rect.width() / m_aConstr) * m_aspectRatio);
0195         m_rect.moveBottomRight(oldRect.bottomRight());
0196         break;
0197     }
0198 
0199     if (m_rect.top() < m_tConstr || m_rect.top() > m_bConstr) {
0200         m_rect.setTop(qBound(m_tConstr, m_rect.top(), m_bConstr));
0201 
0202         if (!qFuzzyCompare(1.0 + (oldRect.top() - m_rect.top()), 1.0)) {
0203             fixAspect(TOP_HANDLE);
0204         }
0205     }
0206 
0207     if (m_rect.bottom() < m_tConstr || m_rect.bottom() > m_bConstr) {
0208         m_rect.setBottom(qBound(m_tConstr, m_rect.bottom(), m_bConstr));
0209 
0210         if (!qFuzzyCompare(1.0 + (oldRect.bottom() - m_rect.bottom()), 1.0)) {
0211             fixAspect(BOTTOM_HANDLE);
0212         }
0213 
0214         if (handle & LEFT_HANDLE) {
0215             m_rect.moveTopRight(oldRect.topRight());
0216         }
0217 
0218         if (handle & RIGHT_HANDLE) {
0219             m_rect.moveTopLeft(oldRect.topLeft());
0220         }
0221     }
0222 
0223     if (m_rect.left() < m_lConstr || m_rect.left() > m_rConstr) {
0224         m_rect.setLeft(qBound(m_lConstr, m_rect.left(), m_rConstr));
0225 
0226         if (!qFuzzyCompare(1.0 + (oldRect.left() - m_rect.left()), 1.0)) {
0227             fixAspect(LEFT_HANDLE);
0228         }
0229     }
0230 
0231     if (m_rect.right() < m_lConstr || m_rect.right() > m_rConstr) {
0232         m_rect.setRight(qBound(m_lConstr, m_rect.right(), m_rConstr));
0233 
0234         if (!qFuzzyCompare(1.0 + (oldRect.right() - m_rect.right()), 1.0)) {
0235             fixAspect(RIGHT_HANDLE);
0236         }
0237 
0238         m_rect.moveBottomRight(oldRect.bottomRight());
0239     }
0240 }