1 // krazy:excludeall=qclasses
2 
3 //////////////////////////////////////////////////////////////////////////////
4 // oxygenlineeditdata.cpp
5 // data container for QLineEdit transition
6 // -------------------
7 //
8 // SPDX-FileCopyrightText: 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
9 //
10 // SPDX-License-Identifier: MIT
11 //////////////////////////////////////////////////////////////////////////////
12 
13 #include "oxygenlineeditdata.h"
14 
15 #include <QEvent>
16 #include <QDateTimeEdit>
17 #include <QDoubleSpinBox>
18 #include <QPainter>
19 #include <QSpinBox>
20 #include <QStyle>
21 #include <QStyleOptionFrameV2>
22 
23 namespace Oxygen
24 {
25 
26     // use 20 milliseconds for animation lock
27     const int LineEditData::_lockTime = 20;
28 
29     //______________________________________________________
LineEditData(QObject * parent,QLineEdit * target,int duration)30     LineEditData::LineEditData( QObject* parent, QLineEdit* target, int duration ):
31         TransitionData( parent, target, duration ),
32         _target( target ),
33         _hasClearButton( false ),
34         _edited( false )
35     {
36         _target.data()->installEventFilter( this );
37 
38         checkClearButton();
39 
40         connect( _target.data(), SIGNAL(destroyed()), SLOT(targetDestroyed()) );
41         connect( _target.data(), SIGNAL(textEdited(QString)), SLOT(textEdited()) );
42         connect( _target.data(), SIGNAL(textChanged(QString)), SLOT(textChanged()) );
43 
44         /*
45         Additional signal/slot connections depending on widget's parent.
46         This is needed because parents sometime disable the textChanged signal of the embedded
47         QLineEdit
48         */
49         if( qobject_cast<QSpinBox*>( _target.data()->parentWidget() ) ||qobject_cast<QDoubleSpinBox*>( _target.data()->parentWidget() ) )
50         {
51 
52             connect( _target.data()->parentWidget(), SIGNAL(valueChanged(QString)), SLOT(textChanged()) );
53 
54         } else if( qobject_cast<QDateTimeEdit*>( _target.data()->parentWidget() ) ) {
55 
56             connect( _target.data()->parentWidget(), SIGNAL(dateTimeChanged(QDateTime)), SLOT(textChanged()) );
57 
58         }
59 
60         // update cached pixmap on selection change
61         connect( _target.data(), SIGNAL(selectionChanged()), SLOT(selectionChanged()) );
62 
63     }
64 
65     //___________________________________________________________________
eventFilter(QObject * object,QEvent * event)66     bool LineEditData::eventFilter( QObject* object, QEvent* event )
67     {
68 
69         if( !( enabled() && object && object == _target.data() ) )
70         { return TransitionData::eventFilter( object, event ); }
71 
72         switch ( event->type() )
73         {
74             case QEvent::Show:
75             case QEvent::Resize:
76             case QEvent::Move:
77             transition().data()->setEndPixmap( QPixmap() );
78             break;
79 
80             default: break;
81         }
82 
83         return TransitionData::eventFilter( object, event );
84 
85     }
86 
87     //___________________________________________________________________
timerEvent(QTimerEvent * event)88     void LineEditData::timerEvent( QTimerEvent* event )
89     {
90         if( event->timerId() == _timer.timerId() )
91         {
92 
93             _timer.stop();
94             checkClearButton();
95             if( enabled() && transition() && _target && _target.data()->isVisible() )
96             {
97                 setRecursiveCheck( true );
98                 transition().data()->setEndPixmap( transition().data()->grab( _target.data(), targetRect() ) );
99                 setRecursiveCheck( false );
100             }
101 
102         } else if( event->timerId() == _animationLockTimer.timerId() ) {
103 
104             unlockAnimations();
105 
106         } else return TransitionData::timerEvent( event );
107 
108     }
109 
110     //___________________________________________________________________
checkClearButton(void)111     void LineEditData::checkClearButton( void )
112     {
113         if( !_target ) return;
114         QObjectList children( _target.data()->children() );
115         _hasClearButton = false;
116         foreach( QObject* child, children )
117         {
118             if( child->inherits( "KLineEditButton" ) )
119             {
120                 _hasClearButton = true;
121                 _clearButtonRect = static_cast<QWidget*>(child)->geometry();
122                 break;
123             }
124         }
125 
126         return;
127     }
128 
129     //___________________________________________________________________
textEdited(void)130     void LineEditData::textEdited( void )
131     {
132         _edited = true;
133         if( !recursiveCheck() )
134         { _timer.start( 0, this ); }
135     }
136 
137 
138     //___________________________________________________________________
selectionChanged(void)139     void LineEditData::selectionChanged( void )
140     {
141         if( !recursiveCheck() )
142         { _timer.start( 0, this ); }
143     }
144 
145     //___________________________________________________________________
textChanged(void)146     void LineEditData::textChanged( void )
147     {
148 
149         // check whether text change was triggered manually
150         // in which case do not start transition
151         if( _edited )
152         {
153             _edited = false;
154             return;
155         }
156 
157         if( transition().data()->isAnimated() )
158         { transition().data()->endAnimation(); }
159 
160         if( isLocked() )
161         {
162             // if locked one do not start the new animation, to prevent flicker
163             // instead, one hides the transition pixmap, trigger an update, and return.
164             // animations are re-locked.
165             transition().data()->hide();
166             lockAnimations();
167             _timer.start( 0, this );
168             return;
169         }
170 
171         if( initializeAnimation() )
172         {
173 
174             lockAnimations();
175             animate();
176 
177         } else {
178 
179             transition().data()->hide();
180 
181         }
182     }
183 
184     //___________________________________________________________________
initializeAnimation(void)185     bool LineEditData::initializeAnimation( void )
186     {
187         if( !( enabled() && _target && _target.data()->isVisible() ) ) return false;
188 
189         if( recursiveCheck() ) return false;
190 
191         QRect current( targetRect() );
192 
193         transition().data()->setOpacity(0);
194         transition().data()->setGeometry( current );
195 
196         if( _widgetRect.isValid() &&
197             !transition().data()->currentPixmap().isNull() &&
198             _widgetRect != current )
199         {
200 
201           // if label geometry has changed since last animation
202           // one must clone the pixmap to make it match the right
203           // geometry before starting the animation.
204           QPixmap pixmap( current.size() );
205           pixmap.fill( Qt::transparent );
206           QPainter p( &pixmap );
207           p.drawPixmap( _widgetRect.topLeft() - current.topLeft(), transition().data()->currentPixmap() );
208           p.end();
209           transition().data()->setStartPixmap( pixmap );
210 
211         } else {
212 
213             transition().data()->setStartPixmap( transition().data()->currentPixmap() );
214 
215         }
216 
217         bool valid( !transition().data()->startPixmap().isNull() );
218         if( valid )
219         {
220             transition().data()->show();
221             transition().data()->raise();
222         }
223 
224         setRecursiveCheck( true );
225         transition().data()->setEndPixmap( transition().data()->grab( _target.data(), targetRect() ) );
226         setRecursiveCheck( false );
227 
228         return valid;
229 
230     }
231 
232     //___________________________________________________________________
animate(void)233     bool LineEditData::animate( void )
234     {
235         transition().data()->animate();
236         return true;
237     }
238 
239     //___________________________________________________________________
targetDestroyed(void)240     void LineEditData::targetDestroyed( void )
241     {
242         setEnabled( false );
243         _target.clear();
244     }
245 
246 }
247