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 //==============================================
29 ALabel::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 //==============================================
73 {
74  //set animation defaults
75  this->setMethod = setMethod;
76  this->removalMethod = removalMethod;
77  this->removalBeforeResetMethod = removalBeforeResetMethod;
78  this->resetMethod = resetMethod;
79 }
80 //==============================================
81 void 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();
100  internalSetPixmap( p );
101  }
102 }
103 //==============================================
104 void 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
123  animatePixmap();
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());
132  animatePixmap();
133  }
134 }
135 //==============================================
136 void 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 //==============================================
164 void ALabel::internalRemovePixmap( bool forceImmediate )
165 {
166  //set animation method and animate
167  if(forceImmediate)
169  else
171 
172  animatePixmap();
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
188  delay = initDelay;
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(
356  animationType == SLIDE_IN_RIGHT) && step < pixStore->width()) ||
358  animationType == SLIDE_OUT_RIGHT) && step > 0) ||
359  (animationType == FADE_TRANSITION && step < 100)
360  )
361  {
362  //update speed
363  delay = delay - accel;
364  if(delay < minDelay) delay = minDelay;
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;
401  animatePixmap();
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 //==============================================
450 void 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 //==============================================
484 void ALabel::enterEvent( QEvent*)
485 {
486  if(hoverOverImage)
487  {
488  drawHoverOverImage = true;
489  repaint( false );
490  }
491 }
492 //==============================================
493 void ALabel::leaveEvent( QEvent*)
494 {
495  if(hoverOverImage)
496  {
497  drawHoverOverImage = false;
498  repaint(false);
499  }
500 }
501 //==============================================
502 void ALabel::mousePressEvent( QMouseEvent* )
503 { emit mousePress(); }
504 //==============================================
505 void 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 //==============================================
536 void ALabel::mouseDoubleClickEvent( QMouseEvent* )
537 { emit mouseDoubleClick(); }
538 //==============================================
539 void 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 //==============================================
562 void 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  {
602  Action* temp = delayedActionHead;
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  {
616  Action* temp = delayedActionHead;
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  {
630  Action* temp = delayedActionHead;
631  while(temp != delayedActionTail)
632  {
633  Action* next = temp->getNext();
634  delete temp;
635  temp = next;
636  }
638  return;
639  }
640 }
641 //==============================================
642 Action::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 SLIDE_OUT_RIGHT
Definition: ALabel.h:23
#define SLIDE_IN_LEFT
Definition: ALabel.h:20
void setPixmap(const QPixmap &p)
animates setting an image
Definition: ALabel.cpp:81
Action(QPixmap *image)
Definition: ALabel.cpp:642
QPixmap * hoverOverImage
Definition: ALabel.h:121
bool resettingImage
Definition: ALabel.h:134
int initDelay
Definition: ALabel.h:109
void mouseReleaseEvent(QMouseEvent *)
Definition: ALabel.cpp:505
void cleanStack()
Definition: ALabel.cpp:573
QTime currentTime
Definition: ALabel.h:114
bool drawHoverOverImage
Definition: ALabel.h:117
void setNext(Action *next)
Definition: ALabel.cpp:657
#define SLIDE_IN_RIGHT
Definition: ALabel.h:22
void paintEvent(QPaintEvent *pe)
Definition: ALabel.cpp:450
#define FADE_TRANSITION
Definition: ALabel.h:24
void internalSetPixmap(const QPixmap &p)
Definition: ALabel.cpp:104
QImage * resetPixStore
Definition: ALabel.h:100
void pixmapRemoved()
int accel
Definition: ALabel.h:109
long b
Definition: jpegInternal.h:125
void leaveEvent(QEvent *e)
Definition: ALabel.cpp:493
void mousePress()
various mouse-click signals
void removePixmap(bool forceImmediate=false)
animates removing an image
Definition: ALabel.cpp:136
QImage * pixStore
Definition: ALabel.h:100
void internalRemovePixmap(bool forceImmediate=false)
Definition: ALabel.cpp:164
Definition: ALabel.h:146
~Action()
Definition: ALabel.cpp:648
QMutex animatingBoolMutex
Definition: ALabel.h:131
int width
Definition: blur.cpp:79
Action * getNext()
Definition: ALabel.cpp:654
int minDelay
Definition: ALabel.h:109
#define DISAPPEAR_IMMEDIATELY
Definition: ALabel.h:19
void mouseRelease()
QTimer * timer
Definition: ALabel.h:113
int delay
Definition: ALabel.h:109
void animate()
Definition: ALabel.cpp:197
void mouseMoveEvent(QMouseEvent *)
Definition: ALabel.cpp:539
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
QRect hoverOverRect
Definition: ALabel.h:118
void mouseDoubleClick()
void enterEvent(QEvent *e)
Definition: ALabel.cpp:484
int removalBeforeResetMethod
Definition: ALabel.h:105
int step
Definition: ALabel.h:110
bool animating
Definition: ALabel.h:130
#define SLIDE_OUT_LEFT
Definition: ALabel.h:21
Action * delayedActionHead
Definition: ALabel.h:137
void mouseDoubleClickEvent(QMouseEvent *)
Definition: ALabel.cpp:536
Action * delayedActionTail
Definition: ALabel.h:138
int removalMethod
Definition: ALabel.h:104
int animationType
Definition: ALabel.h:109
void mousePressEvent(QMouseEvent *)
Definition: ALabel.cpp:502
int setMethod
Definition: ALabel.h:103
QMutex queueMutex
Definition: ALabel.h:139
QPixmap * getImage()
Definition: ALabel.cpp:660
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
#define APPEAR_IMMEDIATELY
Definition: ALabel.h:18
QTime lastTime
Definition: ALabel.h:114
bool handCursorShown
Definition: ALabel.h:127
int resetMethod
Definition: ALabel.h:106
bool imageShown
Definition: ALabel.h:124
void animatePixmap()
begin animating the pixmap
Definition: ALabel.cpp:175
void appendJob(QPixmap *pix)
Definition: ALabel.cpp:562