AlbumShaper 1.0a3
ALabel.cpp
Go to the documentation of this file.
1//==============================================
2// copyright : (C) 2003-2005 by Will Stokes
3//==============================================
4// This program is free software; you can redistribute it
5// and/or modify it under the terms of the GNU General
6// Public License as published by the Free Software
7// Foundation; either version 2 of the License, or
8// (at your option) any later version.
9//==============================================
10
11//Systemwide includes
12#include <qtimer.h>
13#include <qpixmap.h>
14#include <qimage.h>
15#include <qapplication.h>
16#include <qpainter.h>
17#include <qstyle.h>
18#include <qmutex.h>
19#include <qcursor.h>
20//Added by qt3to4:
21#include <QLabel>
22#include <QMouseEvent>
23#include <QEvent>
24
25//Projectwide includes
26#include "ALabel.h"
27
28//==============================================
29ALabel::ALabel( QWidget* parent, const char* name,
30 QPixmap* hoverOverImage,
31 int setMethod, int removalMethod,
32 int resetMethod, int removalBeforeResetMethod,
33 int initDelay, int accel) : QLabel(parent,name)
34{
35 delayedActionHead = NULL;
36 delayedActionTail = NULL;
37
38 //set animation defaults
39 this->setMethod = setMethod;
40 this->removalMethod = removalMethod;
41 this->removalBeforeResetMethod = removalBeforeResetMethod;
42 this->resetMethod = resetMethod;
43
44 //set hover over image
45 this->hoverOverImage = hoverOverImage;
46 drawHoverOverImage = false;
47
48 setMouseTracking(true);
49 handCursorShown = false;
50
51 //by default image is not shown, we are not animating, nor resetting the image
52 imageShown = false;
53 animating = false;
54 resettingImage = false;
55 pixStore = NULL;
56 resetPixStore = NULL;
57
58 //don't clear pixmap area before painting, prevents flicker
59 setWindowFlags(Qt::WNoAutoErase);
60
61 //set delay defaults
62 this->initDelay = initDelay;
63 this->accel = accel;
64 this->minDelay = 1;
65
66 //create timer object and setup signals
67 timer = new QTimer();
68 connect(timer, SIGNAL(timeout()), this, SLOT(animate()) );
69}
70//==============================================
71void ALabel::setAnimationMethods( int setMethod, int removalMethod,
72 int resetMethod, int removalBeforeResetMethod )
73{
74 //set animation defaults
75 this->setMethod = setMethod;
76 this->removalMethod = removalMethod;
77 this->removalBeforeResetMethod = removalBeforeResetMethod;
78 this->resetMethod = resetMethod;
79}
80//==============================================
81void ALabel::setPixmap ( const QPixmap & p )
82{
83 //get locks on queues
84 queueMutex.lock();
85 animatingBoolMutex.lock();
86 //if currently animating then append job to queue
87 if(animating || delayedActionHead != NULL)
88 {
89 appendJob(new QPixmap(p));
90 animatingBoolMutex.unlock();
91 queueMutex.unlock();
92 return;
93 }
94 //else set animating to true, actually initiate job
95 else
96 {
97 animating = true;
98 animatingBoolMutex.unlock();
99 queueMutex.unlock();
101 }
102}
103//==============================================
104void ALabel::internalSetPixmap ( const QPixmap & p )
105{
106 //if previous image already present remove it first
107 if(pixStore)
108 {
109 resettingImage = true;
110
111 //backup new set image to be retrieved later
112 if(resetPixStore)
113 {
114// cout << "LEAK DETECTED when trying to resetPixStore\n";
115 delete resetPixStore;
116 }
117 resetPixStore = new QImage(p.convertToImage());
118
119 //set animation method
121
122 //remove old pixmap
124 }
125 //else immediately set image
126 else
127 {
128 resettingImage = false;
129 //set pixmap store and animate setting it
130 pixStore = new QImage(p.convertToImage());
133 }
134}
135//==============================================
136void ALabel::removePixmap ( bool forceImmediate)
137{
138 //get locks on queues
139 queueMutex.lock();
140 animatingBoolMutex.lock();
141 //if currently animating then append job to queue
142 if(animating || delayedActionHead != NULL)
143 {
144 appendJob( NULL );
145 animatingBoolMutex.unlock();
146 queueMutex.unlock();
147 return;
148 }
149 //else set animating to true, actually initiate job (only if no image currently exists)
150 else
151 {
152 //only remove image if image currently exists
153 if(pixStore != NULL)
154 animating = true;
155
156 animatingBoolMutex.unlock();
157 queueMutex.unlock();
158
159 if(animating)
160 internalRemovePixmap( forceImmediate );
161 }
162}
163//==============================================
164void ALabel::internalRemovePixmap( bool forceImmediate )
165{
166 //set animation method and animate
167 if(forceImmediate)
169 else
171
173}
174//==============================================
176{
177 //set the step paramater. for slide transitions this is the number
178 //of columns to be displayed. for fade transitions this is what percentage
179 //of the fade we have completed so start out at 1.
183 { step = pixStore->width()-1; }
184 else
185 { step = 1; }
186
187 //set initial delay/speed
189
190 //find current time, used to decide how many new columns to reveal in first iteration
191 lastTime.start();
192
193 //begin animation
194 animate();
195}
196//==============================================
198{
199 //---------------------------------
200 //backup last # of columns shown
201 int lastStep = step;
202 //---------------------------------
203 //determine new number of columns to be shown
205 step = pixStore->width();
207 step = 0;
208 else
209 {
210 //determine # of ms that have passed since last redraw
211 currentTime.start();
212 double ms = lastTime.msecsTo(currentTime);
213
214 //determine increment
215 int inc = (int)(ms/(delay+1));
216
219 { inc = -inc; }
220
221 //if increment is not zero then update last time
222 if(inc != 0)
223 {
225 }
226
227 //update number of columns shown
228 step = step + inc;
229
230 //boundary conditions
232 {
233 if(step > 100)
234 step = 100;
235 }
236 else
237 {
238 if(step > pixStore->width())
239 step = pixStore->width();
240 else if(step < 0)
241 step = 0;
242 }
243 }
244 //---------------------------------
245 //if percentage of frade has changed then redraw
247 {
248 if(step != lastStep)
249 {
250 //compute intermediate image resolution
251 double w1 = pixStore->width();
252 double h1 = pixStore->height();
253 double w2 = resetPixStore->width();
254 double h2 = resetPixStore->height();
255 double alpha = ((double)step) / 100.0;
256 int w = (int) ( w1 + (w2-w1)*alpha );
257 int h = (int) ( h1 + (h2-h1)*alpha );
258
259 //resize old and new images
260 QImage oldImg = pixStore->scaled( w, h );
261 QImage newImg = resetPixStore->scaled( w, h );
262
263 //scale each image by alpha (step/100) and 1-alpha and combine
264 int maxDepth = pixStore->depth();
265 if(resetPixStore->depth() > maxDepth)
266 maxDepth = resetPixStore->depth();
267
268 QImage tmpImage(w, h, maxDepth);
269
270 int x,y;
271 for(x = 0; x<w; x++)
272 {
273 for(y = 0; y<h; y++)
274 {
275 QRgb v1 = oldImg.pixel(x,y);
276 QRgb v2 = newImg.pixel(x,y);
277 int r = (int) (alpha* qRed(v2) + (1-alpha)*qRed(v1));
278 int g = (int) (alpha* qGreen(v2) + (1-alpha)*qGreen(v1));
279 int b = (int) (alpha* qBlue(v2) + (1-alpha)*qBlue(v1));
280
281 tmpImage.setPixel(x, y, qRgb(r,g,b) );
282 }
283 }
284
285 //set image shown to this interpolated image
286 QPixmap tmpPixmap(step, pixStore->height() );
287 tmpPixmap.convertFromImage( tmpImage );
288 QLabel::setPixmap( tmpPixmap );
289
290 //I've noticed when we replace images with ones which are shorter (height wise) some pixels
291 //are left behind. this is annoying. a quick fix is to so a erase repaint in this situation.
292 //another kludgly (but flickerless) improvement woudl be to create a pixmap that is the
293 //same size as the current but paints the bottom and top edges with the background color.
294 //I honestly think this is a bug in Qt that Trolltech needs to address. Maybe I should report it to them.
295 if(h2 < h1)
296 repaint(true);
297 else
298 repaint(false);
299 }
300 }
301 //if the number of cols to be shown has changed then redraw
302 else if(step != lastStep &&
303 (
306 )
307 )
308 {
309 //draw empty image
310 if(step == 0)
311 {
312 QLabel::setPixmap( NULL );
313 emit pixmapRemoved();
314 repaint(true);
315 }
316 //draw full image
317 else if(step == pixStore->width() )
318 {
319 QLabel::setPixmap( QPixmap::fromImage(*pixStore) );
320 repaint(false);
321 }
322 //draw a portion the image
323 else
324 {
325 //construct temp image
326 QImage tmpImage(step, pixStore->height(), pixStore->depth() );
327 int x,y;
328 for(x = 0; x<step; x++)
329 {
330 for(y = 0; y<pixStore->height(); y++)
331 {
334 { tmpImage.setPixel( x, y, pixStore->pixel(pixStore->width()-step+x,y) ); }
335 else
336 { tmpImage.setPixel( x, y, pixStore->pixel(x,y) ); }
337 } //end for y
338 } //end for x
339
340 //set label to use temp image (a portion of the full image)
341 QPixmap tmpPixmap(step, pixStore->height() );
342 tmpPixmap.convertFromImage( tmpImage );
343 QLabel::setPixmap( tmpPixmap );
344
347 repaint(true);
348 else
349 repaint(false);
350 }
351 }
352 //---------------------------------
353 //not done animating, reiterate
354 if(
358 animationType == SLIDE_OUT_RIGHT) && step > 0) ||
360 )
361 {
362 //update speed
363 delay = delay - accel;
365
366 //restart timer
367 timer->start( delay, TRUE );
368 }
369 //---------------------------------
370 //done animating....
371 else
372 {
373 //If we just removed the image then delete the old pixStore object
374 if(step == 0)
375 {
376 imageShown = false;
377 delete pixStore;
378 pixStore = NULL;
379 }
380 else
381 {
382 imageShown = true;
383 }
384
385 //if we are actually in the middle of a reset action then we've
386 //just finished removing the old image, now it's time to start setting the new image
388 {
389 resettingImage = false;
390
391 if(pixStore)
392 {
393// cout << "ERROR! Leak detected!\n";
394 delete pixStore;
395 pixStore = NULL;
396 }
397
399 resetPixStore = NULL;
402 }
403 //else truely done
404 else
405 {
406 //we were running a fade effect then delete old pixmap and repalce it with new one
408 {
409 resettingImage = false;
410 delete pixStore;
412 resetPixStore = NULL;
413 }
414
415 //check to see pending animations exist, if so pop off next action from list
416 queueMutex.lock();
417
418 //simplify list of pending actions
419 cleanStack();
420
421 if(delayedActionHead == NULL)
422 {
423 //done!
424 queueMutex.unlock();
425 animatingBoolMutex.lock();
426 animating = false;
427 animatingBoolMutex.unlock();
428 return;
429 }
430
431 //ok, we're not done, pop off first entry from queue, then start up actual job
432 Action* currAction = delayedActionHead;
434 if(delayedActionHead == NULL)
435 delayedActionTail = NULL;
436
437 queueMutex.unlock();
438
439 //start job
440 if(currAction->getImage() == NULL) internalRemovePixmap();
441 else internalSetPixmap( *currAction->getImage() );
442
443 //free old action object
444 delete currAction;
445 }
446 }
447 //---------------------------------------
448}
449//==============================================
450void ALabel::paintEvent( QPaintEvent* pe )
451{
452 //draw conents of label
453 QLabel::paintEvent(pe);
454
455 QPainter p(this);
456
457 //if animation complete and image is being shown, draw hover over image
459 {
460 QRect r = rect();
461
462 int minDim = r.width();
463 if(r.height() < minDim)
464 minDim = r.height();
465 if(minDim > hoverOverImage->width())
466 {
467 r.setLeft( r.right() - hoverOverImage->width() );
468 r.setBottom( r.top() + hoverOverImage->height() );
469 hoverOverRect = r;
470 p.drawPixmap( r, *hoverOverImage);
471 }
472 else
473 {
474 QImage resizedImage = hoverOverImage->convertToImage().scaled(minDim, minDim);
475 QPixmap resizedPixmap(resizedImage);
476 r.setLeft( r.right() - resizedPixmap.width() );
477 r.setBottom( r.top() + resizedPixmap.height() );
478 hoverOverRect = r;
479 p.drawPixmap( r, resizedPixmap);
480 }
481 }
482}
483//==============================================
484void ALabel::enterEvent( QEvent*)
485{
487 {
488 drawHoverOverImage = true;
489 repaint( false );
490 }
491}
492//==============================================
493void ALabel::leaveEvent( QEvent*)
494{
496 {
497 drawHoverOverImage = false;
498 repaint(false);
499 }
500}
501//==============================================
502void ALabel::mousePressEvent( QMouseEvent* )
503{ emit mousePress(); }
504//==============================================
505void ALabel::mouseReleaseEvent( QMouseEvent* e)
506{
507 //if there is no hover over image then ignore event
508 if( hoverOverImage == NULL ) return;
509
510 QPainter* p = new QPainter();
511 QRect r = rect();
512 delete p;
513 int minDim = r.width();
514 if(r.height() < minDim)
515 minDim = r.height();
516 if(minDim > hoverOverImage->width())
517 {
518 r.setLeft( r.right() - hoverOverImage->width() );
519 r.setBottom( r.top() + hoverOverImage->height() );
520 }
521 else
522 {
523 QImage resizedImage = hoverOverImage->convertToImage().scaled(minDim, minDim);
524 QPixmap resizedPixmap(resizedImage);
525 r.setLeft( r.right() - resizedPixmap.width() );
526 r.setBottom( r.top() + resizedPixmap.height() );
527 }
528
529 if(r.contains( e->pos() ) )
530 {
531 removePixmap();
532 emit mouseRelease();
533 }
534}
535//==============================================
537{ emit mouseDoubleClick(); }
538//==============================================
539void ALabel::mouseMoveEvent( QMouseEvent* e)
540{
541 //need rect so draw hover over image must exist before any checks can occur
542 if( !drawHoverOverImage )
543 return;
544
545 //if hand not shown but over hover over image then turn hand cursor on
546 if( !handCursorShown && hoverOverRect.contains( e->x(), e->y() ) )
547 {
548 setCursor( QCursor( Qt::PointingHandCursor ) );
549 handCursorShown = true;
550 return;
551 }
552
553 //if hand cursor shown but nolonger over hover over image set cursor back to normal
554 if( handCursorShown && !hoverOverRect.contains( e->x(), e->y() ) )
555 {
556 setCursor( QCursor( Qt::ArrowCursor ) );
557 handCursorShown = false;
558 return;
559 }
560}
561//==============================================
562void ALabel::appendJob(QPixmap* pix)
563{
564 Action* newAct = new Action(pix);
565 if(delayedActionHead == NULL)
566 delayedActionHead = newAct;
567 else
568 delayedActionTail->setNext( newAct );
569
570 delayedActionTail = newAct;
571}
572//==============================================
574{
575 //if stack empty already clean
576 if(delayedActionHead == NULL)
577 return;
578
579 //if no image currently displayed, pop off all remove actions from head of list
580 if(pixStore == NULL)
581 {
582 Action* currAction = delayedActionHead;
583 while(currAction != NULL && currAction->getImage() == NULL)
584 {
585 Action* next = currAction->getNext();
586 delete currAction;
587 currAction = next;
588 }
589
590 delayedActionHead = currAction;
591 if(currAction == NULL)
592 delayedActionTail = NULL;
593 }
594
595 //if one pending operations no simplifications possible
597 return;
598
599 //if last action is a set operation then remove all other entries
600 if(delayedActionTail->getImage() != NULL)
601 {
604 while(temp != delayedActionHead)
605 {
606 Action* next = temp->getNext();
607 delete temp;
608 temp = next;
609 }
610 return;
611 }
612
613 //last action is a remove operation, if no current image then cull entire stack
614 if(pixStore == NULL)
615 {
617 while(temp != NULL)
618 {
619 Action* next = temp->getNext();
620 delete temp;
621 temp = next;
622 }
623 delayedActionHead = NULL;
624 delayedActionTail = NULL;
625 return;
626 }
627 //else remove all but last pending operations since final remove action will remove current image
628 else
629 {
631 while(temp != delayedActionTail)
632 {
633 Action* next = temp->getNext();
634 delete temp;
635 temp = next;
636 }
638 return;
639 }
640}
641//==============================================
642Action::Action(QPixmap* image)
643{
644 this->image = image;
645 next = NULL;
646}
647//==============================================
649{
650 delete image;
651 image = NULL;
652}
653//==============================================
655{ return next; }
656//==============================================
658{ this->next = next; }
659//==============================================
661{ return image; }
662//==============================================
#define FADE_TRANSITION
Definition ALabel.h:24
#define SLIDE_IN_LEFT
Definition ALabel.h:20
#define SLIDE_OUT_LEFT
Definition ALabel.h:21
#define SLIDE_OUT_RIGHT
Definition ALabel.h:23
#define APPEAR_IMMEDIATELY
Definition ALabel.h:18
#define SLIDE_IN_RIGHT
Definition ALabel.h:22
#define DISAPPEAR_IMMEDIATELY
Definition ALabel.h:19
int width
Definition blur.cpp:79
void mouseRelease()
Action * delayedActionHead
Definition ALabel.h:137
void cleanStack()
Definition ALabel.cpp:573
void mouseDoubleClickEvent(QMouseEvent *)
Definition ALabel.cpp:536
bool handCursorShown
Definition ALabel.h:127
int initDelay
Definition ALabel.h:109
QRect hoverOverRect
Definition ALabel.h:118
QPixmap * hoverOverImage
Definition ALabel.h:121
void paintEvent(QPaintEvent *pe)
Definition ALabel.cpp:450
void mouseDoubleClick()
ALabel(QWidget *parent=0, const char *name=0, QPixmap *hoverOverImage=NULL, int setMethod=APPEAR_IMMEDIATELY, int removalMethod=DISAPPEAR_IMMEDIATELY, int resetMethod=APPEAR_IMMEDIATELY, int removalBeforeResetMethod=DISAPPEAR_IMMEDIATELY, int initDelay=130, int accel=50)
create the label, optionally set a hover-over image (only displayed when pixmap for label is set and ...
Definition ALabel.cpp:29
bool imageShown
Definition ALabel.h:124
void mouseReleaseEvent(QMouseEvent *)
Definition ALabel.cpp:505
bool animating
Definition ALabel.h:130
int removalBeforeResetMethod
Definition ALabel.h:105
QTime lastTime
Definition ALabel.h:114
void animatePixmap()
begin animating the pixmap
Definition ALabel.cpp:175
int removalMethod
Definition ALabel.h:104
void mousePress()
various mouse-click signals
void mouseMoveEvent(QMouseEvent *)
Definition ALabel.cpp:539
void animate()
Definition ALabel.cpp:197
Action * delayedActionTail
Definition ALabel.h:138
void internalRemovePixmap(bool forceImmediate=false)
Definition ALabel.cpp:164
bool resettingImage
Definition ALabel.h:134
int animationType
Definition ALabel.h:109
void enterEvent(QEvent *e)
Definition ALabel.cpp:484
QMutex animatingBoolMutex
Definition ALabel.h:131
int delay
Definition ALabel.h:109
QImage * pixStore
Definition ALabel.h:100
int minDelay
Definition ALabel.h:109
void leaveEvent(QEvent *e)
Definition ALabel.cpp:493
QTime currentTime
Definition ALabel.h:114
QMutex queueMutex
Definition ALabel.h:139
void appendJob(QPixmap *pix)
Definition ALabel.cpp:562
void internalSetPixmap(const QPixmap &p)
Definition ALabel.cpp:104
QImage * resetPixStore
Definition ALabel.h:100
void pixmapRemoved()
void setPixmap(const QPixmap &p)
animates setting an image
Definition ALabel.cpp:81
int accel
Definition ALabel.h:109
int setMethod
Definition ALabel.h:103
int step
Definition ALabel.h:110
void removePixmap(bool forceImmediate=false)
animates removing an image
Definition ALabel.cpp:136
void setAnimationMethods(int setMethod=APPEAR_IMMEDIATELY, int removalMethod=DISAPPEAR_IMMEDIATELY, int resetMethod=APPEAR_IMMEDIATELY, int removalBeforeResetMethod=DISAPPEAR_IMMEDIATELY)
alter animation methods
Definition ALabel.cpp:71
void mousePressEvent(QMouseEvent *)
Definition ALabel.cpp:502
bool drawHoverOverImage
Definition ALabel.h:117
int resetMethod
Definition ALabel.h:106
QTimer * timer
Definition ALabel.h:113
Action(QPixmap *image)
Definition ALabel.cpp:642
Action * getNext()
Definition ALabel.cpp:654
QPixmap * image
Definition ALabel.h:156
Action * next
Definition ALabel.h:157
~Action()
Definition ALabel.cpp:648
void setNext(Action *next)
Definition ALabel.cpp:657
QPixmap * getImage()
Definition ALabel.cpp:660
long b