Salome HOME
Updated copyright comment
[modules/gui.git] / src / Qtx / QtxRubberBand.cxx
1 // Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 // File:      QtxRubberBand.cxx
21 // Author:    Alexander A. BORODIN
22 //
23 #include "QtxRubberBand.h"
24
25 #include <QBitmap>
26 #include <QImage>
27 #include <QPaintEvent>
28 #include <QPainter>
29 #include <QPalette>
30 #include <QShowEvent>
31 #include <QVectorIterator>
32
33 #include <math.h>
34
35 /*!
36   \class QtxAbstractRubberBand
37   \brief Analog of class QRubberBand with possibility of creation non-rectangular contour for selection.
38   
39   Currently this class does not support Style functionality in full.
40 */
41
42 /*!
43   \brief Constructor
44   \param theParent parent widget
45  */
46
47 QtxAbstractRubberBand::QtxAbstractRubberBand( QWidget* theParent)
48   : QWidget( theParent/*,Qt::ToolTip*/ ),
49     myPoints(),
50     myIsClosed( false )
51 {
52   setAttribute(Qt::WA_TransparentForMouseEvents);
53 #ifndef WIN32
54   setAttribute(Qt::WA_NoSystemBackground);
55 #endif //WIN32
56   setAttribute(Qt::WA_WState_ExplicitShowHide);
57   setVisible(false);
58   theParent->installEventFilter(this);
59   setGeometry( QRect(QPoint(0,0), theParent->size() ) );
60 }
61
62 /*!
63   \brief Destructor
64  */
65 QtxAbstractRubberBand::~QtxAbstractRubberBand()
66 {
67 }
68
69 void QtxAbstractRubberBand::clearGeometry()
70 {
71   myPoints.clear();
72 }
73
74 bool QtxAbstractRubberBand::isClosed()
75 {
76   return myIsClosed;
77 }
78
79 void QtxAbstractRubberBand::paintEvent( QPaintEvent* /*theEvent*/ )
80 {
81   if ( !myPoints.empty() )
82     {
83       QPixmap tiledPixmap(16, 16);
84      
85       QPainter pixmapPainter(&tiledPixmap);
86       pixmapPainter.setPen(Qt::NoPen);
87       pixmapPainter.setBrush(QBrush( Qt::black, Qt::Dense4Pattern ));
88       pixmapPainter.setBackground(QBrush( Qt::white ));
89       pixmapPainter.setBackgroundMode(Qt::OpaqueMode);
90       pixmapPainter.drawRect(0, 0, tiledPixmap.width(), tiledPixmap.height());
91       pixmapPainter.end();
92       // ### workaround for borked XRENDER
93       tiledPixmap = QPixmap::fromImage(tiledPixmap.toImage());
94
95       QPainter aPainter( this );
96       aPainter.setRenderHint( QPainter::Antialiasing );
97       aPainter.drawTiledPixmap( 0, 0, width(), height(), tiledPixmap);
98       aPainter.end();
99     }
100 }
101
102 void QtxAbstractRubberBand::showEvent( QShowEvent* theEvent )
103 {
104   raise();
105   theEvent->ignore();
106 }
107
108 void QtxAbstractRubberBand::moveEvent( QMoveEvent* )
109 {
110 }
111
112 void QtxAbstractRubberBand::resizeEvent( QResizeEvent* )
113 {
114 }
115
116 bool QtxAbstractRubberBand::eventFilter( QObject* obj, QEvent* e )
117 {
118   if ( obj && obj == parent() && e->type() == QEvent::Resize )
119     {
120       QWidget* p = (QWidget*)parent();
121       setGeometry( QRect(QPoint(0,0), p->size() ) );
122     }
123   return QWidget::eventFilter( obj, e );
124 }
125
126 QRegion createRegion( const QPointF& p1, const QPointF& p2 )
127 {
128   if ( p1 == p2 )
129     return QRegion();
130
131   QLineF n = QLineF( p1, p2 ).normalVector();//.unitVector();
132   n.setLength( 1 );
133   n.translate( p1 * -1 );
134   QPointF nPoint = n.p2();
135
136   QPolygonF p;
137   p << p1 + nPoint << p2 + nPoint << p2 - nPoint << p1 - nPoint << p1 + nPoint;
138
139   return QRegion( p.toPolygon() );
140 }
141
142 void QtxAbstractRubberBand::updateMask()
143 {
144   QRegion r;
145
146   QVectorIterator<QPoint> it(myPoints);
147   while( it.hasNext() )
148     {
149       QPoint p = it.next();
150       if( !it.hasNext() )
151         break;
152
153       QPoint np = it.peekNext();
154       
155       if ( p == np ) continue;
156
157       r += createRegion( p, np );
158     }
159
160   if ( isClosed() )
161     r += createRegion( myPoints.last(), myPoints.first() );
162
163   if ( !r.isEmpty() )
164     setMask( r );
165 }
166
167
168 QtxRectRubberBand::QtxRectRubberBand(QWidget* parent)
169   :QtxAbstractRubberBand( parent )      
170 {
171   myPoints.resize( 4 );
172   myIsClosed = true;
173 }
174
175 QtxRectRubberBand::~QtxRectRubberBand()
176 {
177 }
178
179 void QtxRectRubberBand::initGeometry( const QRect& theRect )
180 {
181   myPoints.clear();
182   myPoints << theRect.topLeft() << theRect.topRight() << theRect.bottomRight() << theRect.bottomLeft();
183   //setMask( QRegion( myPoints ) );
184   updateMask();
185 }
186
187 void QtxRectRubberBand::setStartPoint( const QPoint& thePoint )
188 {
189   myPoints[0] = thePoint;
190   myPoints[1].setY( thePoint.y() );
191   myPoints[3].setX( thePoint.x() );
192   updateMask();
193 }
194
195 void QtxRectRubberBand::setEndPoint( const QPoint& thePoint)
196 {
197   myPoints[2] = thePoint;       
198   myPoints[1].setX( thePoint.x() );
199   myPoints[3].setY( thePoint.y() );
200   updateMask();
201 }
202
203 void QtxRectRubberBand::clearGeometry()
204 {
205   QMutableVectorIterator<QPoint> i(myPoints);
206   while (i.hasNext())
207     {
208       i.next();
209       i.setValue( QPoint( -1, -1 ) );
210     }
211 }
212
213
214 QtxPolyRubberBand::QtxPolyRubberBand(QWidget* parent)
215   :QtxAbstractRubberBand( parent )
216 {
217 }
218
219 QtxPolyRubberBand::~QtxPolyRubberBand()
220 {
221 }
222
223 void QtxPolyRubberBand::initGeometry( const QPolygon& thePoints )
224 {
225   myPoints = thePoints;
226   updateMask();
227 }
228
229 void QtxPolyRubberBand::initGeometry( const QPoint& thePoint )
230 {
231   myPoints.clear();  
232   myPoints << thePoint;
233   updateMask();
234 }
235
236 void QtxPolyRubberBand::addNode( const QPoint& thePoint )
237 {
238   myPoints << thePoint;
239   updateMask();
240 }
241
242 void QtxPolyRubberBand::replaceLastNode( const QPoint& thePoint )
243 {
244   if ( !myPoints.empty() )
245     {
246       myPoints.pop_back();
247       myPoints << thePoint;
248       updateMask();
249     }
250 }
251
252 void QtxPolyRubberBand::removeLastNode()
253 {
254   if ( !myPoints.empty() )
255     {
256       myPoints.pop_back();
257       updateMask();
258     }
259 }
260
261 void QtxPolyRubberBand::setClosed( bool theFlag )
262 {
263   if (myIsClosed != theFlag )
264     {
265       myIsClosed = theFlag;
266       updateMask();
267     }
268 }
269
270 QtxCircleRubberBand::QtxCircleRubberBand(QWidget* parent)
271   :QtxAbstractRubberBand(parent), myHasCenter(false)
272 {
273   myPoints.resize(2);
274   myIsClosed = true;
275 }
276
277 QtxCircleRubberBand::~QtxCircleRubberBand()
278 {
279 }
280
281 void QtxCircleRubberBand::initGeometry(const QPoint& thePoint)
282 {
283   myIsClosed = false;
284   myHasCenter = true;
285   myPoints.clear();
286   myPoints << thePoint;
287   updateMask();
288 }
289
290 void QtxCircleRubberBand::setRadius(const QPoint& thePoint)
291 {
292   if (myPoints.size() == 1)
293     myPoints << thePoint;
294   else
295     myPoints.setPoint(1, thePoint);
296   myIsClosed = true;
297   updateMask();
298 }
299
300 void QtxCircleRubberBand::updateMask()
301 {
302   int aLen = radius();
303   if (aLen > MIN_RADIUS) {
304     QRegion aReg1(myPoints[0].x() - aLen,
305       myPoints[0].y() - aLen, aLen * 2, aLen * 2, QRegion::Ellipse);
306     QRegion aReg2(myPoints[0].x() - aLen + 2,
307       myPoints[0].y() - aLen + 2, aLen * 2 - 4, aLen * 2 - 4, QRegion::Ellipse);
308     setMask(aReg1 - aReg2);
309   }
310 }
311
312 bool QtxCircleRubberBand::isCenterDefined() const
313 {
314   return myHasCenter;
315 }
316
317 void QtxCircleRubberBand::clearGeometry()
318 {
319   QtxAbstractRubberBand::clearGeometry();
320   myHasCenter = false;
321   myIsClosed = false;
322 }
323
324 QPoint rotatePoint(const QPoint& theStart, const QPoint& theCenter, double theAngle)
325 {
326   double cosTheta = cos(theAngle);
327   double sinTheta = sin(theAngle);
328   int aX = (int)(cosTheta * (theStart.x() - theCenter.x()) -
329     sinTheta * (theStart.y() - theCenter.y()) + theCenter.x());
330   int aY = (int)(sinTheta * (theStart.x() - theCenter.x()) +
331     cosTheta * (theStart.y() - theCenter.y()) + theCenter.y());
332   return QPoint(aX, aY);
333 }
334
335 static double m_pi = 4 * atan(1);
336 static double angle_deg = 360. / CIRCLE_NB_POINTS;
337 static double angle_rad = angle_deg * (m_pi / 180.);
338
339
340 void QtxCircleRubberBand::getPoligon(QPolygon* thePoints) const
341 {
342   int aLen = radius();
343   if (aLen > MIN_RADIUS) {
344     thePoints->clear();
345     QPoint aCenter = myPoints[0];
346     QPoint aStart = myPoints[1];
347     for (int i = 0; i < CIRCLE_NB_POINTS; i++) {
348       thePoints->append(aStart);
349       aStart = rotatePoint(aStart, aCenter, angle_rad);
350     }
351   }
352 }
353
354 int QtxCircleRubberBand::radius() const
355 {
356   if (myPoints.size() < 2)
357     return -1;
358   QPoint aDist = myPoints[1] - myPoints[0];
359   return (int)std::sqrt(std::pow(aDist.x(), 2) + std::pow(aDist.y(), 2));
360 }