//============================================== // copyright : (C) 2003-2005 by Will Stokes //============================================== // This program is free software; you can redistribute it // and/or modify it under the terms of the GNU General // Public License as published by the Free Software // Foundation; either version 2 of the License, or // (at your option) any later version. //============================================== //Systemwide includes #include #include #include #include #include #include #include #include #include //Projectwide includes #include "photoDescEdit.h" #include "photoPreviewWidget.h" #include "photosIconView.h" #include "clickableLabel.h" #include "../config.h" #include "../backend/photo.h" #include "../backend/tools/imageTools.h" #define EDIT_MARGIN 4 //PLATFORM_SPECIFIC_CODE #if defined(Q_OS_MACX) #include "/Developer/Headers/FlatCarbon/MacWindows.h" #endif //PLATFORM_SPECIFIC_CODE #if defined(Q_OS_WIN) #include #include #define SPI_GETDROPSHADOW 0x1024 #define SPI_SETDROPSHADOW 0x1025 #endif //============================================== PhotoDescEdit::PhotoDescEdit( PhotoPreviewWidget* ppw, bool useAnimation, QWidget* parent, const char* name ) : QWidget(parent,name, Qt::WStyle_Customize | #if defined(Q_OS_MACX) Qt::WStyle_Splash ) #else Qt::WStyle_NoBorder | Qt::WType_Popup ) #endif { //PLATFORM_SPECIFIC_CODE //disable drop shadow on mac os x #if defined(Q_OS_MACX) ChangeWindowAttributes( (OpaqueWindowPtr*)winId(), kWindowNoShadowAttribute, kWindowNoAttributes ); #endif //PLATFORM_SPECIFIC_CODE //disable drop shadow on win xp #if defined(Q_OS_WIN) SystemParametersInfo( SPI_GETDROPSHADOW, 0, &dropShadowsEnabled, 0 ); SystemParametersInfo( SPI_SETDROPSHADOW, 0, NULL, 0 ); #endif this->ppw = ppw; //----------------------------------------------- //don't erase before painting, avoids flicker setWFlags(WNoAutoErase); //----------------------------------------------- //determine small image size getImageSize( ppw->getPhoto()->getThumbnailFilename(), smallWidth, smallHeight ); //------------------------------------------- QRect appRec = qApp->mainWidget()->frameGeometry(); int finalWidth, finalHeight; int actualFinalWidth, actualFinalHeight; //image is wider than tall, place text and buttons below image if(smallWidth > smallHeight ) { finalWidth = 400; finalHeight = (finalWidth * smallHeight) / smallWidth; //fix width if(finalWidth +2*EDIT_MARGIN> appRec.width()) { finalWidth = appRec.width() - 2*EDIT_MARGIN; finalHeight = (finalWidth * smallHeight) / smallWidth; } //fix height QFontMetrics fm( qApp->font() ); idealTextSize = 4*fm.height() + 5*fm.leading() + 4; if(finalHeight + idealTextSize + 2*EDIT_MARGIN > appRec.height() ) { finalHeight = appRec.height() - idealTextSize - 2*EDIT_MARGIN; finalWidth = (finalHeight * smallWidth) / smallHeight; } //sanity check if(finalHeight < 0) { finalHeight = (appRec.height() - 2*EDIT_MARGIN) / 2; finalWidth = (finalHeight * smallWidth) / smallHeight; idealTextSize = finalHeight; } actualFinalWidth = finalWidth + 2*EDIT_MARGIN; actualFinalHeight = finalHeight + idealTextSize + 2*EDIT_MARGIN; //an additional fudge is necessary for MacOSX, not sure why #if defined(Q_OS_MACX) actualFinalHeight+=2; #endif } //image is taller than wide, text and buttons will be placed to the right else { finalHeight = 300; finalWidth = (finalHeight * smallWidth) / smallHeight; //fix height if(finalHeight + 2*EDIT_MARGIN > appRec.height()) { finalHeight = appRec.height() - 2*EDIT_MARGIN; finalWidth = (finalHeight * smallWidth) / smallHeight; } //fix width QString calibrationString( qApp->translate("PhotoDescEdit", "This is the photo description calibration string.") ); QFontMetrics fm( qApp->font() ); idealTextSize = fm.width( calibrationString ); if(finalWidth + idealTextSize + 2*EDIT_MARGIN > appRec.width() ) { finalWidth = appRec.width() - idealTextSize - 2*EDIT_MARGIN; finalHeight = (finalWidth * smallHeight) / smallWidth; } //sanity check if(finalWidth < 0) { finalWidth = (appRec.width() - 2*EDIT_MARGIN) / 2; finalHeight = (finalWidth * smallHeight) / smallWidth; idealTextSize = finalWidth; } actualFinalWidth = finalWidth + idealTextSize + 2*EDIT_MARGIN; actualFinalHeight = finalHeight + 2*EDIT_MARGIN; } //----------------------------------------------- //setup scaled up image //find full size photo dimensions, if unable to then use scaled up thumbnail image int fullWidth, fullHeight; if(!getImageSize( ppw->getPhoto()->getImageFilename(), fullWidth, fullHeight ) ) { imageLarge = new QImage( QImage( ppw->getPhoto()->getThumbnailFilename()). scale(finalWidth,finalHeight, QImage::ScaleFree )); } //else find cropped region of slideshow image using these dimensions else { //load padded slideshow image QImage paddedSSImage( ppw->getPhoto()->getSlideshowFilename() ); //unpadded dimensions int actualWidth, actualHeight; calcScaledImageDimensions( fullWidth, fullHeight, paddedSSImage.width(), paddedSSImage.height(), actualWidth, actualHeight ); //construct new image with padding removed int leftOffset = (paddedSSImage.width() - actualWidth) / 2; int topOffset = (paddedSSImage.height() - actualHeight) / 2; QImage SSImage( actualWidth, actualHeight, paddedSSImage.depth() ); int x, y; for(x=0; xgetPhoto()->getDescription() ); if(smallWidth > smallHeight ) tmpTextEdit.resize( finalWidth, idealTextSize ); else tmpTextEdit.resize( idealTextSize, finalHeight ); tmpTextEdit.setLineWidth( 0 ); tmpTextEdit.setMargin( 0 ); tmpTextEdit.setMidLineWidth( 0 ); tmpTextEdit.setFrameStyle( QFrame::NoFrame | QFrame::Plain ); tmpTextEdit.setWrapPolicy( QTextEdit::AtWordOrDocumentBoundary ); tmpTextEdit.constPolish(); tmpTextEdit.polish(); tmpTextEdit.setWordWrap( QTextEdit::FixedPixelWidth ); if(smallWidth > smallHeight ) tmpTextEdit.setWrapColumnOrWidth( finalWidth ); else tmpTextEdit.setWrapColumnOrWidth( idealTextSize ); tmpTextEdit.updateScrollBars(); tmpTextEdit.constPolish(); tmpTextEdit.polish(); if(smallWidth > smallHeight ) { if(tmpTextEdit.lines() > 4) { tmpTextEdit.setWrapColumnOrWidth( finalWidth - tmpTextEdit.verticalScrollBar()->width() ); tmpTextEdit.updateScrollBars(); tmpTextEdit.constPolish(); tmpTextEdit.polish(); } } else { QFontMetrics fm( qApp->font() ); if(tmpTextEdit.lines() > idealTextSize / (fm.leading() + fm.height()) ) { tmpTextEdit.setWrapColumnOrWidth( idealTextSize - tmpTextEdit.verticalScrollBar()->width() ); tmpTextEdit.updateScrollBars(); tmpTextEdit.constPolish(); tmpTextEdit.polish(); } } //paint to pixmap tmpTextEdit.paintNow(); textRectangle = new QImage( QPixmap::grabWidget(&tmpTextEdit).convertToImage() ); //----------------------------------------------- //set beginning and end positions initPos = ppw->getPhotoPos(); //offset by margin initPos += QPoint( -EDIT_MARGIN, -EDIT_MARGIN ); int initCenterX = initPos.x() + smallWidth/2; int initCenterY = initPos.y() + smallHeight/2; finalPos = QPoint( initCenterX - actualFinalWidth/2, initCenterY - actualFinalHeight/2 ); if(finalPos.x() < appRec.x() ) finalPos.setX( appRec.x() ); if(finalPos.x() + actualFinalWidth > appRec.x() + appRec.width() ) finalPos.setX( appRec.x() + appRec.width()- actualFinalWidth ); if(finalPos.y() < appRec.y() ) finalPos.setY( appRec.y() ); if(finalPos.y() + actualFinalHeight > appRec.y() + appRec.height() ) finalPos.setY( appRec.y() + appRec.height()- actualFinalHeight ); //----------------------------------------------- //find bounding rectangle left = QMIN( finalPos.x(), initPos.x() ); top = QMIN( finalPos.y(), initPos.y() ); right = QMAX( finalPos.x() + actualFinalWidth, initPos.x() + smallWidth ); bottom = QMAX( finalPos.y() + actualFinalHeight, initPos.y() + smallHeight ); //----------------------------------------------- //grab window in region of interest, setup label and use this image backgroundImage = new QPixmap( QPixmap::grabWindow(QApplication::desktop()->winId(), left, top, right-left, bottom-top) ); setBackgroundMode( Qt::NoBackground ); //----------------------------------------------- //Setup animation widgets and place in main grid animationLabel = new QLabel(this, "animationLabel", WNoAutoErase); animationLabel->setPixmap( *backgroundImage ); animationLabel->setBackgroundMode( Qt::NoBackground ); buffer = new QPixmap( backgroundImage->width(), backgroundImage->height() ); mainGrid = new QGridLayout( this, 1, 2, 0 ); mainGrid->addWidget(animationLabel, 0, 0 ); //----------------------------------------------- //Setup static widgets staticFrame = new QWidget(this); staticFrame->hide(); staticFrame->setBackgroundMode( Qt::NoBackground ); mainGrid->addWidget(staticFrame, 0, 1 ); staticPhoto = new QLabel( staticFrame, "staticPhoto", WNoAutoErase); staticPhoto->setPixmap( QPixmap( *imageLarge) ); staticPhoto->setBackgroundMode( Qt::NoBackground ); photoDesc = new TextEdit( staticFrame ); photoDesc->setText( ppw->getPhoto()->getDescription() ); photoDesc->setWrapPolicy( QTextEdit::AtWordOrDocumentBoundary ); photoDesc->setFrameStyle( QFrame::NoFrame ); photoDesc->setLineWidth( 0 ); photoDesc->setMargin( 0 ); photoDesc->setMidLineWidth( 0 ); photoDesc->setFrameStyle( QFrame::MenuBarPanel | QFrame::Plain ); //start disappearing once the text edit reports the user is finished connect( photoDesc, SIGNAL( finished() ), this, SLOT( disappear() ) ); QWidget* bw1 = new QWidget(staticFrame); QWidget* bw2 = new QWidget(staticFrame); QWidget* bw3 = new QWidget(staticFrame); QWidget* bw4 = new QWidget(staticFrame); QColor darkBlue(35, 75, 139); bw1->setPaletteBackgroundColor( darkBlue ); bw2->setPaletteBackgroundColor( darkBlue ); bw3->setPaletteBackgroundColor( darkBlue ); bw4->setPaletteBackgroundColor( darkBlue ); //image is wider than tall, place text and buttons below image if(smallWidth > smallHeight ) { staticGrid = new QGridLayout( staticFrame, 4, 3); staticGrid->addWidget( staticPhoto, 1, 1 ); staticGrid->addWidget( photoDesc, 2, 1 ); staticGrid->setColSpacing( 2, staticPhoto->width() ); staticGrid->setRowSpacing( 2, idealTextSize ); staticGrid->addMultiCellWidget( bw1, 0, 0, 0, 2 ); staticGrid->addMultiCellWidget( bw2, 1, 2, 0, 0 ); staticGrid->addMultiCellWidget( bw3, 1, 2, 2, 2 ); staticGrid->addMultiCellWidget( bw4, 3, 3, 0, 2 ); staticGrid->setRowSpacing( 0, EDIT_MARGIN ); staticGrid->setRowSpacing( 3, EDIT_MARGIN ); staticGrid->setColSpacing( 0, EDIT_MARGIN ); staticGrid->setColSpacing( 2, EDIT_MARGIN ); } else { staticGrid = new QGridLayout( staticFrame, 3, 4); staticGrid->addWidget( staticPhoto, 1, 1 ); staticGrid->addWidget( photoDesc, 1, 2 ); staticGrid->setRowSpacing( 1, staticPhoto->height() ); staticGrid->setColSpacing( 2, idealTextSize ); staticGrid->addMultiCellWidget( bw1, 0, 0, 0, 3 ); staticGrid->addWidget( bw2, 1, 0 ); staticGrid->addWidget( bw3, 1, 3 ); staticGrid->addMultiCellWidget( bw4, 2, 2, 0, 3 ); staticGrid->setRowSpacing( 0, EDIT_MARGIN ); staticGrid->setRowSpacing( 2, EDIT_MARGIN ); staticGrid->setColSpacing( 0, EDIT_MARGIN ); staticGrid->setColSpacing( 3, EDIT_MARGIN ); } //----------------------------------------------- //set delay defaults initDelay = 130; accel = 50; minDelay = 1; this->useAnimation = useAnimation; if(useAnimation) step = 0; else step = 100; mode = STATIC; //create timer object and setup signals timer = new QTimer(); connect(timer, SIGNAL(timeout()), this, SLOT(animate()) ); //--------------------------- //place widget in intial position move( left, top ); show(); //start appearing process mode = APPEARING; delay = initDelay; lastTime.start(); animate(); } //============================================== PhotoDescEdit::~PhotoDescEdit() { delete textRectangle; delete timer; delete buffer; delete backgroundImage; delete imageLarge; } //============================================== void PhotoDescEdit::animate() { //--------------------------------- //determine # of ms that have passed since last redraw currentTime.start(); double ms = lastTime.msecsTo(currentTime); //determine increment int inc = (int)(ms/(delay+1)); //if increment is not zero then update last time if(inc != 0) { lastTime = currentTime; //update step step = step + inc; if(step > 100) step = 100; //update position and size double alpha = ((double)step) / 100.0; int newX, newY; int imageW, imageH; int textDim; QColor darkBlue(35, 75, 139); if(mode == APPEARING) { newX = (int)((1-alpha)*initPos.x() + alpha*finalPos.x()); newY = (int)((1-alpha)*initPos.y() + alpha*finalPos.y()); imageW = (int)((1-alpha)*smallWidth + alpha*imageLarge->width()); imageH = (int)((1-alpha)*smallHeight + alpha*imageLarge->height()); textDim = (int) (alpha * idealTextSize); } else { newX = (int)(alpha*initPos.x() + (1-alpha)*finalPos.x()); newY = (int)(alpha*initPos.y() + (1-alpha)*finalPos.y()); imageW = (int)(alpha*smallWidth + (1-alpha)*imageLarge->width()); imageH = (int)(alpha*smallHeight + (1-alpha)*imageLarge->height()); textDim = (int) ((1-alpha) * idealTextSize); } //draw background image to buffer QPainter bufferPainter( buffer ); bufferPainter.drawPixmap(0,0, *backgroundImage ); //draw selection and white text rectangles if(smallWidth > smallHeight ) { bufferPainter.fillRect( newX - left, newY - top, imageW + 2*EDIT_MARGIN, imageH + 2*EDIT_MARGIN + textDim, darkBlue ); bufferPainter.drawPixmap( newX - left + EDIT_MARGIN, newY - top + EDIT_MARGIN + imageH, QPixmap( textRectangle->scale( imageW, textDim ) ) ); } else { bufferPainter.fillRect( newX - left, newY - top, imageW + 2*EDIT_MARGIN + textDim, imageH + 2*EDIT_MARGIN, darkBlue ); bufferPainter.drawPixmap( newX - left + EDIT_MARGIN + imageW, newY - top + EDIT_MARGIN, QPixmap( textRectangle->scale( textDim, imageH ) ) ); } //draw scaled moved image to buffer bufferPainter.drawPixmap( newX - left + EDIT_MARGIN, newY - top + EDIT_MARGIN, QPixmap( imageLarge->scale( imageW, imageH ) ) ); //set label to use buffer pixmap animationLabel->setPixmap( *buffer ); } //not done restart timer if(step < 100) { //update speed delay = delay - accel; if(delay < minDelay) delay = minDelay; //restart timer timer->start( delay, TRUE ); } else { if(mode == APPEARING) { animationLabel->hide(); staticFrame->show(); //auto focus text area, put cursor at very end photoDesc->setFocus(); mode = STATIC; } else { //reenable drop shadows on windows xp if they were previously enabled #if defined(Q_OS_WIN) if(dropShadowsEnabled) SystemParametersInfo( SPI_SETDROPSHADOW, 0, &dropShadowsEnabled, 0 ); else SystemParametersInfo( SPI_SETDROPSHADOW, 0, NULL, 0 ); #endif //Q_OS_WIN mode = DISAPPEARED; hide(); qApp->mainWidget()->repaint(false); } } } //============================================== void PhotoDescEdit::disappear() { delete textRectangle; textRectangle = new QImage( QPixmap::grabWidget(photoDesc).convertToImage() ); ppw->getPhoto()->setDescription( photoDesc->text() ); ppw->setText( photoDesc->text() ); //start disappearing process staticFrame->hide(); animationLabel->show(); initDelay = 130; accel = 50; minDelay = 1; if(useAnimation) step = 0; else step = 100; mode = DISAPPEARING; lastTime.start(); animate(); } //============================================== void PhotoDescEdit::hide() { if(mode == DISAPPEARED ) { QWidget::hide(); //check to see if mouse is over a new item, //if so immediately set it as being moused over QIconView* iconView = ppw->iconView(); QIconViewItem* item = iconView->findItem( iconView->viewport()->mapFromGlobal( QCursor::pos() )+=QPoint( iconView->contentsX(), iconView->contentsY() ) ); if(item != NULL && item != ppw ) { ((PhotosIconView*)item->iconView())->repaintGroup( item ); } } else if(mode == STATIC) { disappear(); } } //============================================== TextEdit::TextEdit( QWidget* parent, const char* name ) : QTextEdit(parent,name) { setHScrollBarMode( QScrollView::AlwaysOff ); setTextFormat( Qt::PlainText ); contextMenu = NULL; } //============================================== void TextEdit::paintNow() { constPolish(); repaint( rect(), false ); } //============================================== void TextEdit::keyPressEvent ( QKeyEvent * e ) { //finish when user hits escape if( e->key() == Qt::Key_Escape ) { emit finished(); } //if Ctrl+A then select all text, otherwise, apply base class key press rules else if( (e->state() & Qt::ControlButton) && e->key() == Qt::Key_A ) { selectAll(); } else { QTextEdit::keyPressEvent( e ); } } //============================================== void TextEdit::focusOutEvent ( QFocusEvent * ) { //if user right clicked on text field a context menu is popping up so ignore focusOut. //otherwise user has clicked off photo description so close if( contextMenu == NULL ) emit finished(); } //============================================== QPopupMenu* TextEdit::createPopupMenu ( const QPoint& pos ) { //when context menu's are created store their handle contextMenu = QTextEdit::createPopupMenu( pos ); connect( ((QObject*)contextMenu), SIGNAL(aboutToHide()), this, SLOT(contextMenuHiding()) ); return contextMenu; } //============================================== void TextEdit::contextMenuHiding() { //clear context menu handle since it's disappearing disconnect( ((QObject*)contextMenu), SIGNAL(aboutToHide()), this, SLOT(contextMenuHiding()) ); contextMenu = NULL; } //==============================================