Main Page | Namespace List | Class Hierarchy | Class List | File List | Class Members | File Members

ALabel.cpp

Go to the documentation of this file.
00001 //============================================== 00002 // copyright : (C) 2003-2005 by Will Stokes 00003 //============================================== 00004 // This program is free software; you can redistribute it 00005 // and/or modify it under the terms of the GNU General 00006 // Public License as published by the Free Software 00007 // Foundation; either version 2 of the License, or 00008 // (at your option) any later version. 00009 //============================================== 00010 00011 //Systemwide includes 00012 #include <qtimer.h> 00013 #include <qpixmap.h> 00014 #include <qimage.h> 00015 #include <qapplication.h> 00016 #include <qpainter.h> 00017 #include <qstyle.h> 00018 #include <qmutex.h> 00019 #include <qcursor.h> 00020 00021 //Projectwide includes 00022 #include "ALabel.h" 00023 00024 //============================================== 00025 ALabel::ALabel( QWidget* parent, const char* name, 00026 QPixmap* hoverOverImage, 00027 int setMethod, int removalMethod, 00028 int resetMethod, int removalBeforeResetMethod, 00029 int initDelay, int accel) : QLabel(parent,name) 00030 { 00031 delayedActionHead = NULL; 00032 delayedActionTail = NULL; 00033 00034 //set animation defaults 00035 this->setMethod = setMethod; 00036 this->removalMethod = removalMethod; 00037 this->removalBeforeResetMethod = removalBeforeResetMethod; 00038 this->resetMethod = resetMethod; 00039 00040 //set hover over image 00041 this->hoverOverImage = hoverOverImage; 00042 drawHoverOverImage = false; 00043 00044 setMouseTracking(true); 00045 handCursorShown = false; 00046 00047 //by default image is not shown, we are not animating, nor resetting the image 00048 imageShown = false; 00049 animating = false; 00050 resettingImage = false; 00051 pixStore = NULL; 00052 resetPixStore = NULL; 00053 00054 //don't clear pixmap area before painting, prevents flicker 00055 setWFlags(WRepaintNoErase); 00056 00057 //set delay defaults 00058 this->initDelay = initDelay; 00059 this->accel = accel; 00060 this->minDelay = 1; 00061 00062 //create timer object and setup signals 00063 timer = new QTimer(); 00064 connect(timer, SIGNAL(timeout()), this, SLOT(animate()) ); 00065 } 00066 //============================================== 00067 void ALabel::setAnimationMethods( int setMethod, int removalMethod, 00068 int resetMethod, int removalBeforeResetMethod ) 00069 { 00070 //set animation defaults 00071 this->setMethod = setMethod; 00072 this->removalMethod = removalMethod; 00073 this->removalBeforeResetMethod = removalBeforeResetMethod; 00074 this->resetMethod = resetMethod; 00075 } 00076 //============================================== 00077 void ALabel::setPixmap ( const QPixmap & p ) 00078 { 00079 //get locks on queues 00080 queueMutex.lock(); 00081 animatingBoolMutex.lock(); 00082 //if currently animating then append job to queue 00083 if(animating || delayedActionHead != NULL) 00084 { 00085 appendJob(new QPixmap(p)); 00086 animatingBoolMutex.unlock(); 00087 queueMutex.unlock(); 00088 return; 00089 } 00090 //else set animating to true, actually initiate job 00091 else 00092 { 00093 animating = true; 00094 animatingBoolMutex.unlock(); 00095 queueMutex.unlock(); 00096 internalSetPixmap( p ); 00097 } 00098 } 00099 //============================================== 00100 void ALabel::internalSetPixmap ( const QPixmap & p ) 00101 { 00102 //if previous image already present remove it first 00103 if(pixStore) 00104 { 00105 resettingImage = true; 00106 00107 //backup new set image to be retrieved later 00108 if(resetPixStore) 00109 { 00110 // cout << "LEAK DETECTED when trying to resetPixStore\n"; 00111 delete resetPixStore; 00112 } 00113 resetPixStore = new QImage(p.convertToImage()); 00114 00115 //set animation method 00116 animationType = removalBeforeResetMethod; 00117 00118 //remove old pixmap 00119 animatePixmap(); 00120 } 00121 //else immediately set image 00122 else 00123 { 00124 resettingImage = false; 00125 //set pixmap store and animate setting it 00126 pixStore = new QImage(p.convertToImage()); 00127 animationType = setMethod; 00128 animatePixmap(); 00129 } 00130 } 00131 //============================================== 00132 void ALabel::removePixmap ( bool forceImmediate) 00133 { 00134 //get locks on queues 00135 queueMutex.lock(); 00136 animatingBoolMutex.lock(); 00137 //if currently animating then append job to queue 00138 if(animating || delayedActionHead != NULL) 00139 { 00140 appendJob( NULL ); 00141 animatingBoolMutex.unlock(); 00142 queueMutex.unlock(); 00143 return; 00144 } 00145 //else set animating to true, actually initiate job (only if no image currently exists) 00146 else 00147 { 00148 //only remove image if image currently exists 00149 if(pixStore != NULL) 00150 animating = true; 00151 00152 animatingBoolMutex.unlock(); 00153 queueMutex.unlock(); 00154 00155 if(animating) 00156 internalRemovePixmap( forceImmediate ); 00157 } 00158 } 00159 //============================================== 00160 void ALabel::internalRemovePixmap( bool forceImmediate ) 00161 { 00162 //set animation method and animate 00163 if(forceImmediate) 00164 animationType = DISAPPEAR_IMMEDIATELY; 00165 else 00166 animationType = removalMethod; 00167 00168 animatePixmap(); 00169 } 00170 //============================================== 00171 void ALabel::animatePixmap() 00172 { 00173 //set the step paramater. for slide transitions this is the number 00174 //of columns to be displayed. for fade transitions this is what percentage 00175 //of the fade we have completed so start out at 1. 00176 if(animationType == SLIDE_OUT_LEFT || 00177 animationType == SLIDE_OUT_RIGHT || 00178 animationType == DISAPPEAR_IMMEDIATELY) 00179 { step = pixStore->width()-1; } 00180 else 00181 { step = 1; } 00182 00183 //set initial delay/speed 00184 delay = initDelay; 00185 00186 //find current time, used to decide how many new columns to reveal in first iteration 00187 lastTime.start(); 00188 00189 //begin animation 00190 animate(); 00191 } 00192 //============================================== 00193 void ALabel::animate() 00194 { 00195 //--------------------------------- 00196 //backup last # of columns shown 00197 int lastStep = step; 00198 //--------------------------------- 00199 //determine new number of columns to be shown 00200 if(animationType == APPEAR_IMMEDIATELY) 00201 step = pixStore->width(); 00202 else if(animationType == DISAPPEAR_IMMEDIATELY) 00203 step = 0; 00204 else 00205 { 00206 //determine # of ms that have passed since last redraw 00207 currentTime.start(); 00208 double ms = lastTime.msecsTo(currentTime); 00209 00210 //determine increment 00211 int inc = (int)(ms/(delay+1)); 00212 00213 if(animationType == SLIDE_OUT_LEFT || 00214 animationType == SLIDE_OUT_RIGHT) 00215 { inc = -inc; } 00216 00217 //if increment is not zero then update last time 00218 if(inc != 0) 00219 { 00220 lastTime = currentTime; 00221 } 00222 00223 //update number of columns shown 00224 step = step + inc; 00225 00226 //boundary conditions 00227 if( animationType == FADE_TRANSITION) 00228 { 00229 if(step > 100) 00230 step = 100; 00231 } 00232 else 00233 { 00234 if(step > pixStore->width()) 00235 step = pixStore->width(); 00236 else if(step < 0) 00237 step = 0; 00238 } 00239 } 00240 //--------------------------------- 00241 //if percentage of frade has changed then redraw 00242 if(animationType == FADE_TRANSITION) 00243 { 00244 if(step != lastStep) 00245 { 00246 //compute intermediate image resolution 00247 double w1 = pixStore->width(); 00248 double h1 = pixStore->height(); 00249 double w2 = resetPixStore->width(); 00250 double h2 = resetPixStore->height(); 00251 double alpha = ((double)step) / 100.0; 00252 int w = (int) ( w1 + (w2-w1)*alpha ); 00253 int h = (int) ( h1 + (h2-h1)*alpha ); 00254 00255 //resize old and new images 00256 QImage oldImg = pixStore->scale( w, h ); 00257 QImage newImg = resetPixStore->scale( w, h ); 00258 00259 //scale each image by alpha (step/100) and 1-alpha and combine 00260 int maxDepth = pixStore->depth(); 00261 if(resetPixStore->depth() > maxDepth) 00262 maxDepth = resetPixStore->depth(); 00263 00264 QImage tmpImage(w, h, maxDepth); 00265 00266 int x,y; 00267 for(x = 0; x<w; x++) 00268 { 00269 for(y = 0; y<h; y++) 00270 { 00271 QRgb v1 = oldImg.pixel(x,y); 00272 QRgb v2 = newImg.pixel(x,y); 00273 int r = (int) (alpha* qRed(v2) + (1-alpha)*qRed(v1)); 00274 int g = (int) (alpha* qGreen(v2) + (1-alpha)*qGreen(v1)); 00275 int b = (int) (alpha* qBlue(v2) + (1-alpha)*qBlue(v1)); 00276 00277 tmpImage.setPixel(x, y, qRgb(r,g,b) ); 00278 } 00279 } 00280 00281 //set image shown to this interpolated image 00282 QPixmap tmpPixmap(step, pixStore->height() ); 00283 tmpPixmap.convertFromImage( tmpImage ); 00284 QLabel::setPixmap( tmpPixmap ); 00285 00286 //I've noticed when we replace images with ones which are shorter (height wise) some pixels 00287 //are left behind. this is annoying. a quick fix is to so a erase repaint in this situation. 00288 //another kludgly (but flickerless) improvement woudl be to create a pixmap that is the 00289 //same size as the current but paints the bottom and top edges with the background color. 00290 //I honestly think this is a bug in Qt that Trolltech needs to address. Maybe I should report it to them. 00291 if(h2 < h1) 00292 repaint(true); 00293 else 00294 repaint(false); 00295 } 00296 } 00297 //if the number of cols to be shown has changed then redraw 00298 else if(step != lastStep && 00299 ( 00300 animationType != DISAPPEAR_IMMEDIATELY || 00301 (animationType == DISAPPEAR_IMMEDIATELY && !resettingImage) 00302 ) 00303 ) 00304 { 00305 //draw empty image 00306 if(step == 0) 00307 { 00308 QLabel::setPixmap( NULL ); 00309 emit pixmapRemoved(); 00310 repaint(true); 00311 } 00312 //draw full image 00313 else if(step == pixStore->width() ) 00314 { 00315 QLabel::setPixmap( *pixStore ); 00316 repaint(false); 00317 } 00318 //draw a portion the image 00319 else 00320 { 00321 //construct temp image 00322 QImage tmpImage(step, pixStore->height(), pixStore->depth() ); 00323 int x,y; 00324 for(x = 0; x<step; x++) 00325 { 00326 for(y = 0; y<pixStore->height(); y++) 00327 { 00328 if(animationType == SLIDE_IN_LEFT || 00329 animationType == SLIDE_OUT_LEFT) 00330 { tmpImage.setPixel( x, y, pixStore->pixel(pixStore->width()-step+x,y) ); } 00331 else 00332 { tmpImage.setPixel( x, y, pixStore->pixel(x,y) ); } 00333 } //end for y 00334 } //end for x 00335 00336 //set label to use temp image (a portion of the full image) 00337 QPixmap tmpPixmap(step, pixStore->height() ); 00338 tmpPixmap.convertFromImage( tmpImage ); 00339 QLabel::setPixmap( tmpPixmap ); 00340 00341 if(animationType == SLIDE_OUT_LEFT || 00342 animationType == SLIDE_OUT_RIGHT) 00343 repaint(true); 00344 else 00345 repaint(false); 00346 } 00347 } 00348 //--------------------------------- 00349 //not done animating, reiterate 00350 if( 00351 ((animationType == SLIDE_IN_LEFT || 00352 animationType == SLIDE_IN_RIGHT) && step < pixStore->width()) || 00353 ((animationType == SLIDE_OUT_LEFT || 00354 animationType == SLIDE_OUT_RIGHT) && step > 0) || 00355 (animationType == FADE_TRANSITION && step < 100) 00356 ) 00357 { 00358 //update speed 00359 delay = delay - accel; 00360 if(delay < minDelay) delay = minDelay; 00361 00362 //restart timer 00363 timer->start( delay, TRUE ); 00364 } 00365 //--------------------------------- 00366 //done animating.... 00367 else 00368 { 00369 //If we just removed the image then delete the old pixStore object 00370 if(step == 0) 00371 { 00372 imageShown = false; 00373 delete pixStore; 00374 pixStore = NULL; 00375 } 00376 else 00377 { 00378 imageShown = true; 00379 } 00380 00381 //if we are actually in the middle of a reset action then we've 00382 //just finished removing the old image, now it's time to start setting the new image 00383 if(resettingImage && animationType != FADE_TRANSITION) 00384 { 00385 resettingImage = false; 00386 00387 if(pixStore) 00388 { 00389 // cout << "ERROR! Leak detected!\n"; 00390 delete pixStore; 00391 pixStore = NULL; 00392 } 00393 00394 pixStore = resetPixStore; 00395 resetPixStore = NULL; 00396 animationType = resetMethod; 00397 animatePixmap(); 00398 } 00399 //else truely done 00400 else 00401 { 00402 //we were running a fade effect then delete old pixmap and repalce it with new one 00403 if( animationType == FADE_TRANSITION ) 00404 { 00405 resettingImage = false; 00406 delete pixStore; 00407 pixStore = resetPixStore; 00408 resetPixStore = NULL; 00409 } 00410 00411 //check to see pending animations exist, if so pop off next action from list 00412 queueMutex.lock(); 00413 00414 //simplify list of pending actions 00415 cleanStack(); 00416 00417 if(delayedActionHead == NULL) 00418 { 00419 //done! 00420 queueMutex.unlock(); 00421 animatingBoolMutex.lock(); 00422 animating = false; 00423 animatingBoolMutex.unlock(); 00424 return; 00425 } 00426 00427 //ok, we're not done, pop off first entry from queue, then start up actual job 00428 Action* currAction = delayedActionHead; 00429 delayedActionHead = delayedActionHead->getNext(); 00430 if(delayedActionHead == NULL) 00431 delayedActionTail = NULL; 00432 00433 queueMutex.unlock(); 00434 00435 //start job 00436 if(currAction->getImage() == NULL) internalRemovePixmap(); 00437 else internalSetPixmap( *currAction->getImage() ); 00438 00439 //free old action object 00440 delete currAction; 00441 } 00442 } 00443 //--------------------------------------- 00444 } 00445 //============================================== 00446 void ALabel::drawContents( QPainter* p ) 00447 { 00448 //draw conents of label 00449 QLabel::drawContents(p); 00450 00451 //if animation complete and image is being shown, draw hover over image 00452 if(!animating && imageShown && drawHoverOverImage) 00453 { 00454 QRect r = style().itemRect( p, contentsRect(), alignment(), isEnabled(), pixmap(), text()); 00455 00456 int minDim = r.width(); 00457 if(r.height() < minDim) 00458 minDim = r.height(); 00459 if(minDim > hoverOverImage->width()) 00460 { 00461 r.setLeft( r.right() - hoverOverImage->width() ); 00462 r.setBottom( r.top() + hoverOverImage->height() ); 00463 hoverOverRect = r; 00464 p->drawPixmap( r, *hoverOverImage); 00465 } 00466 else 00467 { 00468 QImage resizedImage = hoverOverImage->convertToImage().scale(minDim, minDim); 00469 QPixmap resizedPixmap(resizedImage); 00470 r.setLeft( r.right() - resizedPixmap.width() ); 00471 r.setBottom( r.top() + resizedPixmap.height() ); 00472 hoverOverRect = r; 00473 p->drawPixmap( r, resizedPixmap); 00474 } 00475 } 00476 } 00477 //============================================== 00478 void ALabel::enterEvent( QEvent*) 00479 { 00480 if(hoverOverImage) 00481 { 00482 drawHoverOverImage = true; 00483 repaint( false ); 00484 } 00485 } 00486 //============================================== 00487 void ALabel::leaveEvent( QEvent*) 00488 { 00489 if(hoverOverImage) 00490 { 00491 drawHoverOverImage = false; 00492 repaint(false); 00493 } 00494 } 00495 //============================================== 00496 void ALabel::mousePressEvent( QMouseEvent* ) 00497 { emit mousePress(); } 00498 //============================================== 00499 void ALabel::mouseReleaseEvent( QMouseEvent* e) 00500 { 00501 //if there is no hover over image then ignore event 00502 if( hoverOverImage == NULL ) return; 00503 00504 QPainter* p = new QPainter(); 00505 QRect r = style().itemRect( p, contentsRect(), alignment(), isEnabled(), pixmap(), text()); 00506 delete p; 00507 int minDim = r.width(); 00508 if(r.height() < minDim) 00509 minDim = r.height(); 00510 if(minDim > hoverOverImage->width()) 00511 { 00512 r.setLeft( r.right() - hoverOverImage->width() ); 00513 r.setBottom( r.top() + hoverOverImage->height() ); 00514 } 00515 else 00516 { 00517 QImage resizedImage = hoverOverImage->convertToImage().scale(minDim, minDim); 00518 QPixmap resizedPixmap(resizedImage); 00519 r.setLeft( r.right() - resizedPixmap.width() ); 00520 r.setBottom( r.top() + resizedPixmap.height() ); 00521 } 00522 00523 if(r.contains( e->pos() ) ) 00524 { 00525 removePixmap(); 00526 emit mouseRelease(); 00527 } 00528 } 00529 //============================================== 00530 void ALabel::mouseDoubleClickEvent( QMouseEvent* ) 00531 { emit mouseDoubleClick(); } 00532 //============================================== 00533 void ALabel::mouseMoveEvent( QMouseEvent* e) 00534 { 00535 //need rect so draw hover over image must exist before any checks can occur 00536 if( !drawHoverOverImage ) 00537 return; 00538 00539 //if hand not shown but over hover over image then turn hand cursor on 00540 if( !handCursorShown && hoverOverRect.contains( e->x(), e->y() ) ) 00541 { 00542 setCursor( QCursor( Qt::PointingHandCursor ) ); 00543 handCursorShown = true; 00544 return; 00545 } 00546 00547 //if hand cursor shown but nolonger over hover over image set cursor back to normal 00548 if( handCursorShown && !hoverOverRect.contains( e->x(), e->y() ) ) 00549 { 00550 setCursor( QCursor( Qt::ArrowCursor ) ); 00551 handCursorShown = false; 00552 return; 00553 } 00554 } 00555 //============================================== 00556 void ALabel::appendJob(QPixmap* pix) 00557 { 00558 Action* newAct = new Action(pix); 00559 if(delayedActionHead == NULL) 00560 delayedActionHead = newAct; 00561 else 00562 delayedActionTail->setNext( newAct ); 00563 00564 delayedActionTail = newAct; 00565 } 00566 //============================================== 00567 void ALabel::cleanStack() 00568 { 00569 //if stack empty already clean 00570 if(delayedActionHead == NULL) 00571 return; 00572 00573 //if no image currently displayed, pop off all remove actions from head of list 00574 if(pixStore == NULL) 00575 { 00576 Action* currAction = delayedActionHead; 00577 while(currAction != NULL && currAction->getImage() == NULL) 00578 { 00579 Action* next = currAction->getNext(); 00580 delete currAction; 00581 currAction = next; 00582 } 00583 00584 delayedActionHead = currAction; 00585 if(currAction == NULL) 00586 delayedActionTail = NULL; 00587 } 00588 00589 //if one pending operations no simplifications possible 00590 if(delayedActionHead == delayedActionTail) 00591 return; 00592 00593 //if last action is a set operation then remove all other entries 00594 if(delayedActionTail->getImage() != NULL) 00595 { 00596 Action* temp = delayedActionHead; 00597 delayedActionHead = delayedActionTail; 00598 while(temp != delayedActionHead) 00599 { 00600 Action* next = temp->getNext(); 00601 delete temp; 00602 temp = next; 00603 } 00604 return; 00605 } 00606 00607 //last action is a remove operation, if no current image then cull entire stack 00608 if(pixStore == NULL) 00609 { 00610 Action* temp = delayedActionHead; 00611 while(temp != NULL) 00612 { 00613 Action* next = temp->getNext(); 00614 delete temp; 00615 temp = next; 00616 } 00617 delayedActionHead = NULL; 00618 delayedActionTail = NULL; 00619 return; 00620 } 00621 //else remove all but last pending operations since final remove action will remove current image 00622 else 00623 { 00624 Action* temp = delayedActionHead; 00625 while(temp != delayedActionTail) 00626 { 00627 Action* next = temp->getNext(); 00628 delete temp; 00629 temp = next; 00630 } 00631 delayedActionHead = delayedActionTail; 00632 return; 00633 } 00634 } 00635 //============================================== 00636 Action::Action(QPixmap* image) 00637 { 00638 this->image = image; 00639 next = NULL; 00640 } 00641 //============================================== 00642 Action::~Action() 00643 { 00644 delete image; 00645 image = NULL; 00646 } 00647 //============================================== 00648 Action* Action::getNext() 00649 { return next; } 00650 //============================================== 00651 void Action::setNext( Action* next) 00652 { this->next = next; } 00653 //============================================== 00654 QPixmap* Action::getImage() 00655 { return image; } 00656 //==============================================

Generated on Sun Mar 4 19:42:55 2007 for AlbumShaper by doxygen 1.3.7