AlbumShaper  1.0a3
photosIconView.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 <qpixmap.h>
00013 #include <qpainter.h>
00014 #include <qpopupmenu.h>
00015 #include <qiconset.h>
00016 #include <qapplication.h>
00017 #include <qstringlist.h>
00018 #include <qcursor.h>
00019 #include <qdragobject.h>
00020 #include <qstringlist.h>
00021 
00022 //Projectwide includes
00023 #include "photosIconView.h"
00024 #include "layoutWidget.h"
00025 #include "window.h"
00026 #include "titleWidget.h"
00027 #include "photoPreviewWidget.h"
00028 #include "photoDescEdit.h"
00029 #include "../config.h"
00030 #include "../backend/photo.h"
00031 #include "../backend/album.h"
00032 #include "../configuration/configuration.h"
00033 
00034 //==============================================
00035 class PhotoDrag : public QUriDrag 
00036 {
00037 public:
00038   PhotoDrag( QWidget* dragSource=0, const char* name=0)
00039   { QUriDrag(dragSource, name); }
00040   
00042   virtual bool drag(DragMode mode)  
00043   { return QDragObject::drag( QDragObject::DragCopy ); }
00044 };
00045 //==============================================
00046 PhotosIconView::PhotosIconView( QWidget *parent ) : QIconView( parent, "iconView", WNoAutoErase )
00047 {
00048   viewport()->setBackgroundMode( Qt::NoBackground );
00049 
00050   currentPseudoSelection = NULL;
00051   handCursorShown = false;
00052   
00053   curPhotoDescEdit = NULL;
00054 
00055   //by default no photo has been right clicked on
00056   rightClickedPhoto = NULL;
00057 
00058   //load background image
00059   backgroundImage = new QPixmap( QString(IMAGE_PATH)+"miscImages/backgroundImage.png" );
00060 
00061   //load drag icon
00062   dragIcon = new QPixmap( QString(IMAGE_PATH)+"miscImages/moveImage.png" );
00063 
00064   //connect mouse over events to paint pseudo selection in ligher blue
00065   connect( this, SIGNAL(onItem(QIconViewItem*)),
00066                 this, SLOT(repaintGroup(QIconViewItem*)) );
00067 
00068   //clear any pseudo selection when mouse moves off icons
00069   connect( this, SIGNAL(onViewport()),
00070                 this, SLOT(clearPseudoSelection()) );
00071 
00072   connect( this, SIGNAL(pressed( QIconViewItem*, const QPoint& )),
00073                  this, SLOT(captureClick(QIconViewItem*, const QPoint&)) );
00074 }
00075 //==============================================
00076 QDragObject* PhotosIconView::dragObject()
00077 {
00078   //no item selected?
00079   if( !currentItem() )
00080     return 0;
00081   
00082   //create drag object
00083 //  PhotoDrag *drag = new PhotoDrag( viewport() );
00084   QIconDrag *drag = new QIconDrag( viewport() );
00085   
00086   //use small icon to represent drag, does not cover up too much of the screen
00087   drag->setPixmap( *dragIcon );
00088   
00090   //TODO find out how we can add filenames and PREVENT them from being MOVED to new location
00091   //like whendropping photoson the desktop!!!
00092   //create stringlist of all selected photo filenames
00093   //QStringList filenames;
00094   //QIconViewItem* current = firstItem();
00095   //while(current != NULL)
00096   //{
00097   //  if(current->isSelected())
00098   //  { 
00099   //    filenames.append( ((PhotoPreviewWidget*)current)->getPhoto()->getImageFilename() ); 
00100   //  }
00101   //  
00102   //  current = current->nextItem();
00103   //}
00104   //  drag->setFileNames( filenames );
00106 
00107   return drag;
00108 }
00109 //==============================================
00110 bool PhotosIconView::findNearestUnselectedPhoto( const QPoint &pos,
00111                                                  QIconViewItem** nearestItem,
00112                                                  bool &posIsleftOfItem )
00113 {
00114   //if there are no items we can't find one now can we?
00115   if ( firstItem() == NULL )
00116     return false;
00117   
00118   //------------------------------------------------
00119   //first see if there is an unselected photo here
00120   QIconViewItem* item = firstItem();
00121   while(item != NULL)
00122   {
00123     if( !item->isSelected() && item->contains(pos) )
00124     {
00125       (*nearestItem) = item;
00126       posIsleftOfItem = pos.x() < item->x() + (item->width()/2);
00127       return true;
00128     }
00129     
00130     item = item->nextItem();
00131   }
00132   //------------------------------------------------
00133   //see if drop occurred below the last unselected photo
00134   
00135   //find last unselected photo
00136   item = lastItem();
00137   while( item != NULL && item->isSelected() )
00138   { item = item->prevItem(); }
00139     
00140   //is point below last unselected photo?
00141   if( item != NULL && pos.y() > (item->y() + item->height()) )
00142   {
00143     (*nearestItem) = item;
00144     posIsleftOfItem = false;
00145     return true;
00146   }
00147   //------------------------------------------------
00148   //see if drop occurred above the first unselected photo
00149   
00150   //find first unselected photo
00151   item = firstItem();
00152   while( item != NULL && item->isSelected() )
00153   { item = item->nextItem(); }
00154   
00155   //is point below last unselected photo?
00156   if( item != NULL && pos.y() < item->y() )
00157   {
00158     (*nearestItem) = item;
00159     posIsleftOfItem = true;
00160     return true;
00161   }
00162   //------------------------------------------------
00163   //next try checking within this row, walk left first
00164   int x;
00165   for(x = pos.x()-1; x>=0; x--)
00166   {
00167     item = findItem( QPoint(x, pos.y()) );
00168     if( item == NULL || item->isSelected() ) continue;
00169     else
00170     {
00171       (*nearestItem) = item;
00172       posIsleftOfItem = false;
00173       return true;
00174     }
00175   }
00176   //walking left failed, try walking right
00177   for(x = pos.x()+1; x<width(); x++)
00178   {
00179     item = findItem( QPoint(x, pos.y()) );
00180     if( item == NULL || item->isSelected() ) continue;
00181     else
00182     {
00183       (*nearestItem) = item;
00184       posIsleftOfItem = true;
00185       return true;
00186     }
00187   }
00188   //------------------------------------------------ 
00189   //ok, no unselected item is at this point, to the left, to the right, and the point
00190   //is not above the first unselected photo or below the last unselected photo. the
00191   //only way this is possible is if the point is between two rows of photos in the margin.
00192 
00193   //while it's certain there are photos both above and below, it is possible 
00194   //either of the adjacent rows of photos only contain selected photos so searching 
00195   //a single row will not necessarily find the nearest photo.
00196   
00197   //however, we are guaranteed to find an unselected photo both above and below 
00198   //this point, somewhere, so only searching up or down is necessary. we'll search 
00199   //up and the first unselected photo we find we'll use as the nearst. All selects 
00200   //photos must come after (aka to the right of) this photo.
00201   int itemWidth  = firstItem()->width();
00202   int itemHeight = firstItem()->height();
00203   int y;
00204   for(y = pos.y()-(itemHeight/2); y >= 0; y-=(itemHeight/2) )
00205   {
00206     for(x = width(); x >= 0; x-=(itemWidth/2))
00207     {
00208       item = findItem( QPoint(x, y) );
00209       if( item == NULL || item->isSelected() ) { continue; }
00210       else
00211       {
00212         (*nearestItem) = item;
00213         posIsleftOfItem = false;
00214         return true;
00215       }
00216     }
00217   }
00218   //------------------------------------------------ 
00219   //unable to find nearest unselected item
00220   return false;
00221 }
00222 //==============================================
00223 void PhotosIconView::contentsDropEvent( QDropEvent *e )
00224 {
00225   QIconView::contentsDropEvent( e );
00226   
00227   //if item is from the viewport emit item moved signal
00228   if(e->source() == viewport() )
00229   { 
00230     //find nearest unselected item
00231     QIconViewItem* item; bool leftOf;
00232     if( !findNearestUnselectedPhoto( e->pos(), &item, leftOf ) )
00233     {
00234       //unable to find nearest item. this should be impossible
00235     //  cout << "ERROR! Failed to find nearest item!\n";
00236       emit itemHasMoved();
00237     }  
00238     
00239     //selected photos dropped on LEFT of nearest item
00240     if( leftOf )
00241     {
00242       //count number of items being moved
00243       int num=0;
00244       QIconViewItem* current = firstItem();
00245       while(current != NULL)
00246       {
00247         if(current->isSelected()) { num++; }
00248         current = current->nextItem();
00249       }
00250     
00251       //move items to their new locations
00252       int xpos = item->x() - num;
00253       current = firstItem();
00254       while(current != NULL)
00255       {
00256         if(current->isSelected())
00257         {
00258           current->move(xpos, item->y()); 
00259           xpos++;
00260         }
00261         current = current->nextItem();
00262       }
00263     }
00264     //selected phtos dropped on RIGHT of nearest item
00265     else
00266     {
00267       //move items to their new locations
00268       int xpos = item->x() + (item->width()/2) + 1;
00269       QIconViewItem* current = firstItem();
00270       while(current != NULL)
00271       {
00272         if(current->isSelected())
00273         {
00274           current->move(xpos, item->y());
00275           xpos++;
00276         }
00277         current = current->nextItem();
00278       }
00279     }
00280 
00281     //items have moved!
00282     emit itemHasMoved();
00283   }
00284   //else it's from off the viewport, if we can get filename then try adding photos being added to subalbum
00285   else
00286   {
00287     QStringList fileNames;
00288     if( QUriDrag::decodeLocalFiles( e, fileNames ) )
00289     {
00290       //for some reason the order in which we receive file names is entire illogical.
00291       //it does not appear to be based on filename, data, modified data, size, anything!
00292       //thus, before adding files, first sort by ascending filename
00293       fileNames.sort();
00294       emit addPhotos(fileNames);
00295     }
00296   }
00297   
00298 }
00299 //==============================================
00300 int PhotosIconView::numSelected()
00301 {
00302   int num = 0;
00303   QIconViewItem* current = firstItem();
00304   while(current != NULL)
00305   {
00306     if(current->isSelected())
00307       num++;
00308     current = current->nextItem();
00309   }
00310   return num;
00311 }
00312 //==============================================
00313 void PhotosIconView::contextMenuEvent ( QContextMenuEvent * e )
00314 {
00315   rightClickedPhoto = (PhotoPreviewWidget*) findItem( QPoint(e->x(), e->y()+contentsY()) );
00316   if(rightClickedPhoto == NULL)
00317     return;
00318 
00319   QPopupMenu contextMenu( this );
00320 
00321   contextMenu.insertItem( QIconSet( QPixmap(QString(IMAGE_PATH)+"menuIcons/setAlbumImage.png") ),
00322                           tr("Set album image"), this, SLOT(setAlbumImage()) );
00323 
00324   contextMenu.insertItem( QIconSet( QPixmap(QString(IMAGE_PATH)+"menuIcons/setSubalbumImage.png") ),
00325                           tr("Set collection image"), this, SLOT(setSubalbumImage()) );
00326 
00327   contextMenu.exec( QPoint(e->globalX(), e->globalY()) );
00328 }
00329 //==============================================
00330 void PhotosIconView::setAlbumImage( )
00331 {
00332   ((Window*)qApp->mainWidget())->getTitle()->setAlbumImage( rightClickedPhoto->getPhoto() );
00333   rightClickedPhoto = NULL;
00334 }
00335 //==============================================
00336 void PhotosIconView::setSubalbumImage( )
00337 {
00338   ((Window*)qApp->mainWidget())->getTitle()->setSubalbumImage( rightClickedPhoto->getPhoto() );
00339   rightClickedPhoto = NULL;
00340 }
00341 //==============================================
00342 void PhotosIconView::drawBackground( QPainter* p, const QRect& r)
00343 {
00344   QBrush brush;
00345   brush.setPixmap( *backgroundImage );
00346   p->fillRect( r, brush );
00347 }
00348 //==============================================
00349  void PhotosIconView::drawContents ( QPainter * p, int clipx, int clipy, int clipw, int cliph )
00350 {
00351     if( bufferPixmap.size() != size())
00352     {  bufferPixmap.resize( size() ); }
00353     QPainter bufferPainter( &bufferPixmap, viewport() );
00354     int xOffset = clipx - contentsX();
00355     int yOffset = clipy - contentsY();
00356 
00357     bufferPainter.translate( -contentsX(), -contentsY() );
00358     QIconView::drawContents( &bufferPainter, clipx, clipy, clipw, cliph );
00359     bitBlt(p->device(), xOffset, yOffset, &bufferPixmap, xOffset, yOffset, clipw, cliph );
00360 }
00361 //==============================================
00362 void PhotosIconView::repaintGroup( QIconViewItem* pseudoSelection)
00363 {
00364   //if old pseudo selection unselect it
00365   clearPseudoSelection();
00366 
00367   //paint new selection
00368   if(pseudoSelection != NULL)
00369   {
00370     currentPseudoSelection = (PhotoPreviewWidget*)pseudoSelection;
00371     currentPseudoSelection->setMousedOver(true);
00372     repaintItem(currentPseudoSelection);
00373   }
00374 }
00375 //==============================================
00376 void PhotosIconView::clearPseudoSelection()
00377 {
00378   //check to make sure mouse is still off item. when a user expands an image to edit
00379   //it's description, the selection is cleard (since hte mouse is over a new window). however,
00380   //when that window disappears repaintGroup never got recalled (if the mouse was still on the original window),
00381   //so just ignore the original clear (it was invalid anyways right?)
00382  if( findItem( viewport()->mapFromGlobal( QCursor::pos() )+=QPoint( contentsX(), contentsY() )  ) == currentPseudoSelection )
00383    return;
00384 
00385   //if old pseudo selection unselect it
00386   if(currentPseudoSelection != NULL)
00387   {
00388     currentPseudoSelection->setMousedOver(false);
00389     repaintItem(currentPseudoSelection);
00390     currentPseudoSelection = NULL;
00391   }
00392 }
00393 //==============================================
00394 void PhotosIconView::captureClick(QIconViewItem *item, const QPoint& point)
00395 {
00396   //if no item has been clicked then ignore
00397   if(item == NULL)
00398     return;
00399 
00400   //get info button rect
00401   QRect infoButtonRec = ((PhotoPreviewWidget*)item)->getPhotoInfoRect();
00402 
00403   //remove scroll offset
00404   infoButtonRec.moveBy( -contentsX(), -contentsY() );
00405 
00406   //convert to screen coordinates by shifting by offset of topleft of widget
00407   QPoint topLeft = mapToGlobal( QPoint(0,0) );
00408   infoButtonRec.moveBy( topLeft.x(), topLeft.y() );
00409 
00410   //if screen coordinates of mouse click are in rectangle then button pressed
00411   if( infoButtonRec.contains( point.x(), point.y() ) )
00412   {
00413     //make sure all events have been processed first. if were just
00414     //editing a differnt photo description it is first necessary to 
00415     //repaint before we start editing the next
00416     qApp->processEvents();
00417 
00418     if( curPhotoDescEdit != NULL ) { delete curPhotoDescEdit; }
00419 
00420     PhotoPreviewWidget* ppw = (PhotoPreviewWidget*)item;
00421     curPhotoDescEdit = new PhotoDescEdit( ppw,
00422         ((Window*)qApp->mainWidget())->getConfig()->getBool( "layout", "animation" ) );
00423   }
00424 }
00425 //==============================================
00426 void PhotosIconView::contentsMousePressEvent ( QMouseEvent * e )
00427 {
00428   dragStartPos = e->pos();
00429   QIconView::contentsMousePressEvent( e );
00430 }
00431 //==============================================
00432 void PhotosIconView::contentsMouseMoveEvent( QMouseEvent *e)
00433 {
00434   QIconView::contentsMouseMoveEvent( e );
00435 
00436   //if no item is under mouse then return
00437   if(currentPseudoSelection == NULL)
00438   {
00439     if(handCursorShown)
00440     {
00441       setCursor( QCursor( Qt::ArrowCursor ) );
00442       handCursorShown = false;
00443     }
00444     return;
00445   }
00446 
00447   QRect photoInfoRect = currentPseudoSelection->getPhotoInfoRect();
00448 
00449   //if hand not shown but over hover over image then turn hand cursor on
00450   if( !handCursorShown && photoInfoRect.contains( e->x(), e->y() ) )
00451   {
00452     setCursor( QCursor( Qt::PointingHandCursor ) );
00453     handCursorShown = true;
00454     return;
00455   }
00456 
00457   //if hand cursor shown but nolonger over hover over image set cursor back to normal
00458   if( handCursorShown && !photoInfoRect.contains( e->x(), e->y() ) )
00459   {
00460     setCursor( QCursor( Qt::ArrowCursor ) );
00461     handCursorShown = false;
00462     return;
00463   }
00464 }
00465 //==============================================
00466 void PhotosIconView::keyPressEvent( QKeyEvent *e )
00467 {
00468   //next handle additional keys
00469   switch( e->key() )
00470   {
00471     //edit selected photo
00472     case Qt::Key_Enter:
00473     case Qt::Key_Return:
00474       //only edit photo if one is selected
00475       if(numSelected() == 1)
00476       {
00477         if( curPhotoDescEdit != NULL ) { delete curPhotoDescEdit; }
00478 
00479         PhotoPreviewWidget* ppw = (PhotoPreviewWidget*)currentItem();
00480         curPhotoDescEdit = new PhotoDescEdit( ppw, ((Window*)qApp->mainWidget())->getConfig()->getBool
00481                                               ( "layout", "animation" ) );
00482       }
00483       break;
00484     //delete - remove selected photos
00485     case Qt::Key_Delete:
00486       if(numSelected() > 0) emit removeSelectedPhotos();
00487       break;
00488     //ctrl + r - rotate photo right
00489     case Qt::Key_R:
00490       if( e->state() & Qt::ControlButton && numSelected() > 0)
00491         emit rotate90SelectedPhotos();
00492       break;
00493     //ctrl + l - rotate photo left
00494     case Qt::Key_L:
00495       if(e->state() & Qt::ControlButton && numSelected() > 0) 
00496         emit rotate270SelectedPhotos();
00497       break;
00498     //e - edit photo using editing interface
00499     case Qt::Key_E:
00500       if(e->state() & Qt::ControlButton && numSelected() >= 1) emit editSelectedPhoto();
00501       break;
00502     //allow base class to handle key event
00503     default:
00504       QIconView::keyPressEvent(e);
00505       break;
00506   }
00507 }
00508 //==============================================