//============================================== // 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 "photosIconView.h" #include "layoutWidget.h" #include "window.h" #include "titleWidget.h" #include "photoPreviewWidget.h" #include "photoDescEdit.h" #include "../config.h" #include "../backend/photo.h" #include "../backend/album.h" #include "../configuration/configuration.h" //============================================== class PhotoDrag : public QUriDrag { public: PhotoDrag( QWidget* dragSource=0, const char* name=0) { QUriDrag(dragSource, name); } ///force all drags to COPY (not move/delete) original files!!! virtual bool drag(DragMode mode) { return QDragObject::drag( QDragObject::DragCopy ); } }; //============================================== PhotosIconView::PhotosIconView( QWidget *parent ) : QIconView( parent, "iconView", WNoAutoErase ) { viewport()->setBackgroundMode( Qt::NoBackground ); currentPseudoSelection = NULL; handCursorShown = false; curPhotoDescEdit = NULL; //by default no photo has been right clicked on rightClickedPhoto = NULL; //load background image backgroundImage = new QPixmap( QString(IMAGE_PATH)+"miscImages/backgroundImage.png" ); //load drag icon dragIcon = new QPixmap( QString(IMAGE_PATH)+"miscImages/moveImage.png" ); //connect mouse over events to paint pseudo selection in ligher blue connect( this, SIGNAL(onItem(QIconViewItem*)), this, SLOT(repaintGroup(QIconViewItem*)) ); //clear any pseudo selection when mouse moves off icons connect( this, SIGNAL(onViewport()), this, SLOT(clearPseudoSelection()) ); connect( this, SIGNAL(pressed( QIconViewItem*, const QPoint& )), this, SLOT(captureClick(QIconViewItem*, const QPoint&)) ); } //============================================== QDragObject* PhotosIconView::dragObject() { //no item selected? if( !currentItem() ) return 0; //create drag object // PhotoDrag *drag = new PhotoDrag( viewport() ); QIconDrag *drag = new QIconDrag( viewport() ); //use small icon to represent drag, does not cover up too much of the screen drag->setPixmap( *dragIcon ); //!!!!!!!!!!!!!!!!!!!! //TODO find out how we can add filenames and PREVENT them from being MOVED to new location //like whendropping photoson the desktop!!! //create stringlist of all selected photo filenames //QStringList filenames; //QIconViewItem* current = firstItem(); //while(current != NULL) //{ // if(current->isSelected()) // { // filenames.append( ((PhotoPreviewWidget*)current)->getPhoto()->getImageFilename() ); // } // // current = current->nextItem(); //} // drag->setFileNames( filenames ); //!!!!!!!!!!!!!!!!!!!! return drag; } //============================================== bool PhotosIconView::findNearestUnselectedPhoto( const QPoint &pos, QIconViewItem** nearestItem, bool &posIsleftOfItem ) { //if there are no items we can't find one now can we? if ( firstItem() == NULL ) return false; //------------------------------------------------ //first see if there is an unselected photo here QIconViewItem* item = firstItem(); while(item != NULL) { if( !item->isSelected() && item->contains(pos) ) { (*nearestItem) = item; posIsleftOfItem = pos.x() < item->x() + (item->width()/2); return true; } item = item->nextItem(); } //------------------------------------------------ //see if drop occurred below the last unselected photo //find last unselected photo item = lastItem(); while( item != NULL && item->isSelected() ) { item = item->prevItem(); } //is point below last unselected photo? if( item != NULL && pos.y() > (item->y() + item->height()) ) { (*nearestItem) = item; posIsleftOfItem = false; return true; } //------------------------------------------------ //see if drop occurred above the first unselected photo //find first unselected photo item = firstItem(); while( item != NULL && item->isSelected() ) { item = item->nextItem(); } //is point below last unselected photo? if( item != NULL && pos.y() < item->y() ) { (*nearestItem) = item; posIsleftOfItem = true; return true; } //------------------------------------------------ //next try checking within this row, walk left first int x; for(x = pos.x()-1; x>=0; x--) { item = findItem( QPoint(x, pos.y()) ); if( item == NULL || item->isSelected() ) continue; else { (*nearestItem) = item; posIsleftOfItem = false; return true; } } //walking left failed, try walking right for(x = pos.x()+1; xisSelected() ) continue; else { (*nearestItem) = item; posIsleftOfItem = true; return true; } } //------------------------------------------------ //ok, no unselected item is at this point, to the left, to the right, and the point //is not above the first unselected photo or below the last unselected photo. the //only way this is possible is if the point is between two rows of photos in the margin. //while it's certain there are photos both above and below, it is possible //either of the adjacent rows of photos only contain selected photos so searching //a single row will not necessarily find the nearest photo. //however, we are guaranteed to find an unselected photo both above and below //this point, somewhere, so only searching up or down is necessary. we'll search //up and the first unselected photo we find we'll use as the nearst. All selects //photos must come after (aka to the right of) this photo. int itemWidth = firstItem()->width(); int itemHeight = firstItem()->height(); int y; for(y = pos.y()-(itemHeight/2); y >= 0; y-=(itemHeight/2) ) { for(x = width(); x >= 0; x-=(itemWidth/2)) { item = findItem( QPoint(x, y) ); if( item == NULL || item->isSelected() ) { continue; } else { (*nearestItem) = item; posIsleftOfItem = false; return true; } } } //------------------------------------------------ //unable to find nearest unselected item return false; } //============================================== void PhotosIconView::contentsDropEvent( QDropEvent *e ) { QIconView::contentsDropEvent( e ); //if item is from the viewport emit item moved signal if(e->source() == viewport() ) { //find nearest unselected item QIconViewItem* item; bool leftOf; if( !findNearestUnselectedPhoto( e->pos(), &item, leftOf ) ) { //unable to find nearest item. this should be impossible // cout << "ERROR! Failed to find nearest item!\n"; emit itemHasMoved(); } //selected photos dropped on LEFT of nearest item if( leftOf ) { //count number of items being moved int num=0; QIconViewItem* current = firstItem(); while(current != NULL) { if(current->isSelected()) { num++; } current = current->nextItem(); } //move items to their new locations int xpos = item->x() - num; current = firstItem(); while(current != NULL) { if(current->isSelected()) { current->move(xpos, item->y()); xpos++; } current = current->nextItem(); } } //selected phtos dropped on RIGHT of nearest item else { //move items to their new locations int xpos = item->x() + (item->width()/2) + 1; QIconViewItem* current = firstItem(); while(current != NULL) { if(current->isSelected()) { current->move(xpos, item->y()); xpos++; } current = current->nextItem(); } } //items have moved! emit itemHasMoved(); } //else it's from off the viewport, if we can get filename then try adding photos being added to subalbum else { QStringList fileNames; if( QUriDrag::decodeLocalFiles( e, fileNames ) ) { //for some reason the order in which we receive file names is entire illogical. //it does not appear to be based on filename, data, modified data, size, anything! //thus, before adding files, first sort by ascending filename fileNames.sort(); emit addPhotos(fileNames); } } } //============================================== int PhotosIconView::numSelected() { int num = 0; QIconViewItem* current = firstItem(); while(current != NULL) { if(current->isSelected()) num++; current = current->nextItem(); } return num; } //============================================== void PhotosIconView::contextMenuEvent ( QContextMenuEvent * e ) { rightClickedPhoto = (PhotoPreviewWidget*) findItem( QPoint(e->x(), e->y()+contentsY()) ); if(rightClickedPhoto == NULL) return; QPopupMenu contextMenu( this ); contextMenu.insertItem( QIconSet( QPixmap(QString(IMAGE_PATH)+"menuIcons/setAlbumImage.png") ), tr("Set album image"), this, SLOT(setAlbumImage()) ); contextMenu.insertItem( QIconSet( QPixmap(QString(IMAGE_PATH)+"menuIcons/setSubalbumImage.png") ), tr("Set collection image"), this, SLOT(setSubalbumImage()) ); contextMenu.exec( QPoint(e->globalX(), e->globalY()) ); } //============================================== void PhotosIconView::setAlbumImage( ) { ((Window*)qApp->mainWidget())->getTitle()->setAlbumImage( rightClickedPhoto->getPhoto() ); rightClickedPhoto = NULL; } //============================================== void PhotosIconView::setSubalbumImage( ) { ((Window*)qApp->mainWidget())->getTitle()->setSubalbumImage( rightClickedPhoto->getPhoto() ); rightClickedPhoto = NULL; } //============================================== void PhotosIconView::drawBackground( QPainter* p, const QRect& r) { QBrush brush; brush.setPixmap( *backgroundImage ); p->fillRect( r, brush ); } //============================================== void PhotosIconView::drawContents ( QPainter * p, int clipx, int clipy, int clipw, int cliph ) { if( bufferPixmap.size() != size()) { bufferPixmap.resize( size() ); } QPainter bufferPainter( &bufferPixmap, viewport() ); int xOffset = clipx - contentsX(); int yOffset = clipy - contentsY(); bufferPainter.translate( -contentsX(), -contentsY() ); QIconView::drawContents( &bufferPainter, clipx, clipy, clipw, cliph ); bitBlt(p->device(), xOffset, yOffset, &bufferPixmap, xOffset, yOffset, clipw, cliph ); } //============================================== void PhotosIconView::repaintGroup( QIconViewItem* pseudoSelection) { //if old pseudo selection unselect it clearPseudoSelection(); //paint new selection if(pseudoSelection != NULL) { currentPseudoSelection = (PhotoPreviewWidget*)pseudoSelection; currentPseudoSelection->setMousedOver(true); repaintItem(currentPseudoSelection); } } //============================================== void PhotosIconView::clearPseudoSelection() { //check to make sure mouse is still off item. when a user expands an image to edit //it's description, the selection is cleard (since hte mouse is over a new window). however, //when that window disappears repaintGroup never got recalled (if the mouse was still on the original window), //so just ignore the original clear (it was invalid anyways right?) if( findItem( viewport()->mapFromGlobal( QCursor::pos() )+=QPoint( contentsX(), contentsY() ) ) == currentPseudoSelection ) return; //if old pseudo selection unselect it if(currentPseudoSelection != NULL) { currentPseudoSelection->setMousedOver(false); repaintItem(currentPseudoSelection); currentPseudoSelection = NULL; } } //============================================== void PhotosIconView::captureClick(QIconViewItem *item, const QPoint& point) { //if no item has been clicked then ignore if(item == NULL) return; //get info button rect QRect infoButtonRec = ((PhotoPreviewWidget*)item)->getPhotoInfoRect(); //remove scroll offset infoButtonRec.moveBy( -contentsX(), -contentsY() ); //convert to screen coordinates by shifting by offset of topleft of widget QPoint topLeft = mapToGlobal( QPoint(0,0) ); infoButtonRec.moveBy( topLeft.x(), topLeft.y() ); //if screen coordinates of mouse click are in rectangle then button pressed if( infoButtonRec.contains( point.x(), point.y() ) ) { //make sure all events have been processed first. if were just //editing a differnt photo description it is first necessary to //repaint before we start editing the next qApp->processEvents(); if( curPhotoDescEdit != NULL ) { delete curPhotoDescEdit; } PhotoPreviewWidget* ppw = (PhotoPreviewWidget*)item; curPhotoDescEdit = new PhotoDescEdit( ppw, ((Window*)qApp->mainWidget())->getConfig()->getBool( "layout", "animation" ) ); } } //============================================== void PhotosIconView::contentsMousePressEvent ( QMouseEvent * e ) { dragStartPos = e->pos(); QIconView::contentsMousePressEvent( e ); } //============================================== void PhotosIconView::contentsMouseMoveEvent( QMouseEvent *e) { QIconView::contentsMouseMoveEvent( e ); //if no item is under mouse then return if(currentPseudoSelection == NULL) { if(handCursorShown) { setCursor( QCursor( Qt::ArrowCursor ) ); handCursorShown = false; } return; } QRect photoInfoRect = currentPseudoSelection->getPhotoInfoRect(); //if hand not shown but over hover over image then turn hand cursor on if( !handCursorShown && photoInfoRect.contains( e->x(), e->y() ) ) { setCursor( QCursor( Qt::PointingHandCursor ) ); handCursorShown = true; return; } //if hand cursor shown but nolonger over hover over image set cursor back to normal if( handCursorShown && !photoInfoRect.contains( e->x(), e->y() ) ) { setCursor( QCursor( Qt::ArrowCursor ) ); handCursorShown = false; return; } } //============================================== void PhotosIconView::keyPressEvent( QKeyEvent *e ) { //next handle additional keys switch( e->key() ) { //edit selected photo case Qt::Key_Enter: case Qt::Key_Return: //only edit photo if one is selected if(numSelected() == 1) { if( curPhotoDescEdit != NULL ) { delete curPhotoDescEdit; } PhotoPreviewWidget* ppw = (PhotoPreviewWidget*)currentItem(); curPhotoDescEdit = new PhotoDescEdit( ppw, ((Window*)qApp->mainWidget())->getConfig()->getBool ( "layout", "animation" ) ); } break; //delete - remove selected photos case Qt::Key_Delete: if(numSelected() > 0) emit removeSelectedPhotos(); break; //ctrl + r - rotate photo right case Qt::Key_R: if( e->state() & Qt::ControlButton && numSelected() > 0) emit rotate90SelectedPhotos(); break; //ctrl + l - rotate photo left case Qt::Key_L: if(e->state() & Qt::ControlButton && numSelected() > 0) emit rotate270SelectedPhotos(); break; //e - edit photo using editing interface case Qt::Key_E: if(e->state() & Qt::ControlButton && numSelected() >= 1) emit editSelectedPhoto(); break; //allow base class to handle key event default: QIconView::keyPressEvent(e); break; } } //==============================================