AlbumShaper  1.0a3
photosIconView.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 <qpixmap.h>
13 #include <qpainter.h>
14 #include <q3popupmenu.h>
15 #include <qicon.h>
16 #include <qapplication.h>
17 #include <qstringlist.h>
18 #include <qcursor.h>
19 #include <q3dragobject.h>
20 #include <qstringlist.h>
21 //Added by qt3to4:
22 #include <QContextMenuEvent>
23 #include <QDropEvent>
24 #include <QMouseEvent>
25 #include <QKeyEvent>
26 
27 //Projectwide includes
28 #include "photosIconView.h"
29 #include "layoutWidget.h"
30 #include "window.h"
31 #include "titleWidget.h"
32 #include "photoPreviewWidget.h"
33 #include "photoDescEdit.h"
34 #include "../config.h"
35 #include "../backend/photo.h"
36 #include "../backend/album.h"
37 #include "../configuration/configuration.h"
38 
39 //==============================================
40 class PhotoDrag : public Q3UriDrag
41 {
42 public:
43  PhotoDrag( QWidget* dragSource=0, const char* name=0)
44  { Q3UriDrag(dragSource, name); }
45 
47  virtual bool drag(DragMode mode)
48  { return Q3DragObject::drag( Q3DragObject::DragCopy ); }
49 };
50 //==============================================
51 PhotosIconView::PhotosIconView( QWidget *parent ) : Q3IconView( parent, "iconView", Qt::WNoAutoErase )
52 {
53  viewport()->setBackgroundMode( Qt::NoBackground );
54 
56  handCursorShown = false;
57 
58  curPhotoDescEdit = NULL;
59 
60  //by default no photo has been right clicked on
61  rightClickedPhoto = NULL;
62 
63  //load background image
64  backgroundImage = new QPixmap( QString(IMAGE_PATH)+"miscImages/backgroundImage.png" );
65 
66  //load drag icon
67  dragIcon = new QPixmap( QString(IMAGE_PATH)+"miscImages/moveImage.png" );
68 
69  //connect mouse over events to paint pseudo selection in ligher blue
70  connect( this, SIGNAL(onItem(Q3IconViewItem*)),
71  this, SLOT(repaintGroup(Q3IconViewItem*)) );
72 
73  //clear any pseudo selection when mouse moves off icons
74  connect( this, SIGNAL(onViewport()),
75  this, SLOT(clearPseudoSelection()) );
76 
77  connect( this, SIGNAL(pressed( Q3IconViewItem*, const QPoint& )),
78  this, SLOT(captureClick(Q3IconViewItem*, const QPoint&)) );
79 }
80 //==============================================
82 {
83  //no item selected?
84  if( !currentItem() )
85  return 0;
86 
87  //create drag object
88 // PhotoDrag *drag = new PhotoDrag( viewport() );
89  Q3IconDrag *drag = new Q3IconDrag( viewport() );
90 
91  //use small icon to represent drag, does not cover up too much of the screen
92  drag->setPixmap( *dragIcon );
93 
95  //TODO find out how we can add filenames and PREVENT them from being MOVED to new location
96  //like whendropping photoson the desktop!!!
97  //create stringlist of all selected photo filenames
98  //QStringList filenames;
99  //QIconViewItem* current = firstItem();
100  //while(current != NULL)
101  //{
102  // if(current->isSelected())
103  // {
104  // filenames.append( ((PhotoPreviewWidget*)current)->getPhoto()->getImageFilename() );
105  // }
106  //
107  // current = current->nextItem();
108  //}
109  // drag->setFileNames( filenames );
111 
112  return drag;
113 }
114 //==============================================
116  Q3IconViewItem** nearestItem,
117  bool &posIsleftOfItem )
118 {
119  //if there are no items we can't find one now can we?
120  if ( firstItem() == NULL )
121  return false;
122 
123  //------------------------------------------------
124  //first see if there is an unselected photo here
125  Q3IconViewItem* item = firstItem();
126  while(item != NULL)
127  {
128  if( !item->isSelected() && item->contains(pos) )
129  {
130  (*nearestItem) = item;
131  posIsleftOfItem = pos.x() < item->x() + (item->width()/2);
132  return true;
133  }
134 
135  item = item->nextItem();
136  }
137  //------------------------------------------------
138  //see if drop occurred below the last unselected photo
139 
140  //find last unselected photo
141  item = lastItem();
142  while( item != NULL && item->isSelected() )
143  { item = item->prevItem(); }
144 
145  //is point below last unselected photo?
146  if( item != NULL && pos.y() > (item->y() + item->height()) )
147  {
148  (*nearestItem) = item;
149  posIsleftOfItem = false;
150  return true;
151  }
152  //------------------------------------------------
153  //see if drop occurred above the first unselected photo
154 
155  //find first unselected photo
156  item = firstItem();
157  while( item != NULL && item->isSelected() )
158  { item = item->nextItem(); }
159 
160  //is point below last unselected photo?
161  if( item != NULL && pos.y() < item->y() )
162  {
163  (*nearestItem) = item;
164  posIsleftOfItem = true;
165  return true;
166  }
167  //------------------------------------------------
168  //next try checking within this row, walk left first
169  int x;
170  for(x = pos.x()-1; x>=0; x--)
171  {
172  item = findItem( QPoint(x, pos.y()) );
173  if( item == NULL || item->isSelected() ) continue;
174  else
175  {
176  (*nearestItem) = item;
177  posIsleftOfItem = false;
178  return true;
179  }
180  }
181  //walking left failed, try walking right
182  for(x = pos.x()+1; x<width(); x++)
183  {
184  item = findItem( QPoint(x, pos.y()) );
185  if( item == NULL || item->isSelected() ) continue;
186  else
187  {
188  (*nearestItem) = item;
189  posIsleftOfItem = true;
190  return true;
191  }
192  }
193  //------------------------------------------------
194  //ok, no unselected item is at this point, to the left, to the right, and the point
195  //is not above the first unselected photo or below the last unselected photo. the
196  //only way this is possible is if the point is between two rows of photos in the margin.
197 
198  //while it's certain there are photos both above and below, it is possible
199  //either of the adjacent rows of photos only contain selected photos so searching
200  //a single row will not necessarily find the nearest photo.
201 
202  //however, we are guaranteed to find an unselected photo both above and below
203  //this point, somewhere, so only searching up or down is necessary. we'll search
204  //up and the first unselected photo we find we'll use as the nearst. All selects
205  //photos must come after (aka to the right of) this photo.
206  int itemWidth = firstItem()->width();
207  int itemHeight = firstItem()->height();
208  int y;
209  for(y = pos.y()-(itemHeight/2); y >= 0; y-=(itemHeight/2) )
210  {
211  for(x = width(); x >= 0; x-=(itemWidth/2))
212  {
213  item = findItem( QPoint(x, y) );
214  if( item == NULL || item->isSelected() ) { continue; }
215  else
216  {
217  (*nearestItem) = item;
218  posIsleftOfItem = false;
219  return true;
220  }
221  }
222  }
223  //------------------------------------------------
224  //unable to find nearest unselected item
225  return false;
226 }
227 //==============================================
229 {
230  Q3IconView::contentsDropEvent( e );
231 
232  //if item is from the viewport emit item moved signal
233  if(e->source() == viewport() )
234  {
235  //find nearest unselected item
236  Q3IconViewItem* item; bool leftOf;
237  if( !findNearestUnselectedPhoto( e->pos(), &item, leftOf ) )
238  {
239  //unable to find nearest item. this should be impossible
240  // cout << "ERROR! Failed to find nearest item!\n";
241  emit itemHasMoved();
242  }
243 
244  //selected photos dropped on LEFT of nearest item
245  if( leftOf )
246  {
247  //count number of items being moved
248  int num=0;
249  Q3IconViewItem* current = firstItem();
250  while(current != NULL)
251  {
252  if(current->isSelected()) { num++; }
253  current = current->nextItem();
254  }
255 
256  //move items to their new locations
257  int xpos = item->x() - num;
258  current = firstItem();
259  while(current != NULL)
260  {
261  if(current->isSelected())
262  {
263  current->move(xpos, item->y());
264  xpos++;
265  }
266  current = current->nextItem();
267  }
268  }
269  //selected phtos dropped on RIGHT of nearest item
270  else
271  {
272  //move items to their new locations
273  int xpos = item->x() + (item->width()/2) + 1;
274  Q3IconViewItem* current = firstItem();
275  while(current != NULL)
276  {
277  if(current->isSelected())
278  {
279  current->move(xpos, item->y());
280  xpos++;
281  }
282  current = current->nextItem();
283  }
284  }
285 
286  //items have moved!
287  emit itemHasMoved();
288  }
289  //else it's from off the viewport, if we can get filename then try adding photos being added to subalbum
290  else
291  {
292  QStringList fileNames;
293  if( Q3UriDrag::decodeLocalFiles( e, fileNames ) )
294  {
295  //for some reason the order in which we receive file names is entire illogical.
296  //it does not appear to be based on filename, data, modified data, size, anything!
297  //thus, before adding files, first sort by ascending filename
298  fileNames.sort();
299  emit addPhotos(fileNames);
300  }
301  }
302 
303 }
304 //==============================================
306 {
307  int num = 0;
308  Q3IconViewItem* current = firstItem();
309  while(current != NULL)
310  {
311  if(current->isSelected())
312  num++;
313  current = current->nextItem();
314  }
315  return num;
316 }
317 //==============================================
318 void PhotosIconView::contextMenuEvent ( QContextMenuEvent * e )
319 {
320  rightClickedPhoto = (PhotoPreviewWidget*) findItem( QPoint(e->x(), e->y()+contentsY()) );
321  if(rightClickedPhoto == NULL)
322  return;
323 
324  Q3PopupMenu contextMenu( this );
325 
326  contextMenu.insertItem( QIcon( QPixmap(QString(IMAGE_PATH)+"menuIcons/setAlbumImage.png") ),
327  tr("Set album image"), this, SLOT(setAlbumImage()) );
328 
329  contextMenu.insertItem( QIcon( QPixmap(QString(IMAGE_PATH)+"menuIcons/setSubalbumImage.png") ),
330  tr("Set collection image"), this, SLOT(setSubalbumImage()) );
331 
332  contextMenu.exec( QPoint(e->globalX(), e->globalY()) );
333 }
334 //==============================================
336 {
337  ((Window*)qApp->mainWidget())->getTitle()->setAlbumImage( rightClickedPhoto->getPhoto() );
338  rightClickedPhoto = NULL;
339 }
340 //==============================================
342 {
343  ((Window*)qApp->mainWidget())->getTitle()->setSubalbumImage( rightClickedPhoto->getPhoto() );
344  rightClickedPhoto = NULL;
345 }
346 //==============================================
347 void PhotosIconView::drawBackground( QPainter* p, const QRect& r)
348 {
349  QBrush brush;
350  brush.setPixmap( *backgroundImage );
351  p->fillRect( r, brush );
352 }
353 //==============================================
354  void PhotosIconView::drawContents ( QPainter * p, int clipx, int clipy, int clipw, int cliph )
355 {
356  if( bufferPixmap.size() != size())
357  { bufferPixmap.resize( size() ); }
358  QPainter bufferPainter( &bufferPixmap );
359  int xOffset = clipx - contentsX();
360  int yOffset = clipy - contentsY();
361 
362  bufferPainter.translate( -contentsX(), -contentsY() );
363  Q3IconView::drawContents( &bufferPainter, clipx, clipy, clipw, cliph );
364  bitBlt(p->device(), xOffset, yOffset, &bufferPixmap, xOffset, yOffset, clipw, cliph );
365 }
366 //==============================================
368 {
369  //if old pseudo selection unselect it
371 
372  //paint new selection
373  if(pseudoSelection != NULL)
374  {
375  currentPseudoSelection = (PhotoPreviewWidget*)pseudoSelection;
377  repaintItem(currentPseudoSelection);
378  }
379 }
380 //==============================================
382 {
383  //check to make sure mouse is still off item. when a user expands an image to edit
384  //it's description, the selection is cleard (since hte mouse is over a new window). however,
385  //when that window disappears repaintGroup never got recalled (if the mouse was still on the original window),
386  //so just ignore the original clear (it was invalid anyways right?)
387  if( findItem( viewport()->mapFromGlobal( QCursor::pos() )+=QPoint( contentsX(), contentsY() ) ) == currentPseudoSelection )
388  return;
389 
390  //if old pseudo selection unselect it
391  if(currentPseudoSelection != NULL)
392  {
394  repaintItem(currentPseudoSelection);
395  currentPseudoSelection = NULL;
396  }
397 }
398 //==============================================
399 void PhotosIconView::captureClick(Q3IconViewItem *item, const QPoint& point)
400 {
401  //if no item has been clicked then ignore
402  if(item == NULL)
403  return;
404 
405  //get info button rect
406  QRect infoButtonRec = ((PhotoPreviewWidget*)item)->getPhotoInfoRect();
407 
408  //remove scroll offset
409  infoButtonRec.moveBy( -contentsX(), -contentsY() );
410 
411  //convert to screen coordinates by shifting by offset of topleft of widget
412  QPoint topLeft = mapToGlobal( QPoint(0,0) );
413  infoButtonRec.moveBy( topLeft.x(), topLeft.y() );
414 
415  //if screen coordinates of mouse click are in rectangle then button pressed
416  if( infoButtonRec.contains( point.x(), point.y() ) )
417  {
418  //make sure all events have been processed first. if were just
419  //editing a differnt photo description it is first necessary to
420  //repaint before we start editing the next
421  qApp->processEvents();
422 
423  if( curPhotoDescEdit != NULL ) { delete curPhotoDescEdit; }
424 
426  curPhotoDescEdit = new PhotoDescEdit( ppw,
427  ((Window*)qApp->mainWidget())->getConfig()->getBool( "layout", "animation" ) );
428  }
429 }
430 //==============================================
432 {
433  dragStartPos = e->pos();
434  Q3IconView::contentsMousePressEvent( e );
435 }
436 //==============================================
438 {
439  Q3IconView::contentsMouseMoveEvent( e );
440 
441  //if no item is under mouse then return
442  if(currentPseudoSelection == NULL)
443  {
444  if(handCursorShown)
445  {
446  setCursor( QCursor( Qt::ArrowCursor ) );
447  handCursorShown = false;
448  }
449  return;
450  }
451 
452  QRect photoInfoRect = currentPseudoSelection->getPhotoInfoRect();
453 
454  //if hand not shown but over hover over image then turn hand cursor on
455  if( !handCursorShown && photoInfoRect.contains( e->x(), e->y() ) )
456  {
457  setCursor( QCursor( Qt::PointingHandCursor ) );
458  handCursorShown = true;
459  return;
460  }
461 
462  //if hand cursor shown but nolonger over hover over image set cursor back to normal
463  if( handCursorShown && !photoInfoRect.contains( e->x(), e->y() ) )
464  {
465  setCursor( QCursor( Qt::ArrowCursor ) );
466  handCursorShown = false;
467  return;
468  }
469 }
470 //==============================================
471 void PhotosIconView::keyPressEvent( QKeyEvent *e )
472 {
473  //next handle additional keys
474  switch( e->key() )
475  {
476  //edit selected photo
477  case Qt::Key_Enter:
478  case Qt::Key_Return:
479  //only edit photo if one is selected
480  if(numSelected() == 1)
481  {
482  if( curPhotoDescEdit != NULL ) { delete curPhotoDescEdit; }
483 
484  PhotoPreviewWidget* ppw = (PhotoPreviewWidget*)currentItem();
485  curPhotoDescEdit = new PhotoDescEdit( ppw, ((Window*)qApp->mainWidget())->getConfig()->getBool
486  ( "layout", "animation" ) );
487  }
488  break;
489  //delete - remove selected photos
490  case Qt::Key_Delete:
491  if(numSelected() > 0) emit removeSelectedPhotos();
492  break;
493  //ctrl + r - rotate photo right
494  case Qt::Key_R:
495  if( e->state() & Qt::ControlModifier && numSelected() > 0)
496  emit rotate90SelectedPhotos();
497  break;
498  //ctrl + l - rotate photo left
499  case Qt::Key_L:
500  if(e->state() & Qt::ControlModifier && numSelected() > 0)
502  break;
503  //e - edit photo using editing interface
504  case Qt::Key_E:
505  if(e->state() & Qt::ControlModifier && numSelected() >= 1) emit editSelectedPhoto();
506  break;
507  //allow base class to handle key event
508  default:
509  Q3IconView::keyPressEvent(e);
510  break;
511  }
512 }
513 //==============================================
void contentsMouseMoveEvent(QMouseEvent *e)
void contentsMousePressEvent(QMouseEvent *e)
QPoint topLeft
void contentsDropEvent(QDropEvent *e)
void addPhotos(QStringList)
Photo * getPhoto()
Returns photo pointer.
virtual bool drag(DragMode mode)
force all drags to COPY (not move/delete) original files!!!
QPixmap * backgroundImage
PhotoDescEdit * curPhotoDescEdit
void rotate270SelectedPhotos()
Top level widget, encapsulates the title widget, the layout widget, and the toolbar widget...
Definition: window.h:39
Displays photo thumbnail and description.
QString IMAGE_PATH
Definition: config.cpp:18
void repaintGroup(Q3IconViewItem *pseudoSelection)
void contextMenuEvent(QContextMenuEvent *e)
int width
Definition: blur.cpp:79
PhotosIconView(QWidget *parent)
Q3DragObject * dragObject()
void keyPressEvent(QKeyEvent *e)
QPixmap * dragIcon
void drawBackground(QPainter *p, const QRect &r)
void clearPseudoSelection()
void setMousedOver(bool val)
QPixmap bufferPixmap
PhotoDrag(QWidget *dragSource=0, const char *name=0)
bool findNearestUnselectedPhoto(const QPoint &pos, Q3IconViewItem **nearestItem, bool &posIsleftOfItem)
void editSelectedPhoto()
void rotate90SelectedPhotos()
void captureClick(Q3IconViewItem *, const QPoint &)
void itemHasMoved()
void removeSelectedPhotos()
PhotoPreviewWidget * currentPseudoSelection
void drawContents(QPainter *p, int clipx, int clipy, int clipw, int cliph)
PhotoPreviewWidget * rightClickedPhoto