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"
38
39//==============================================
40class PhotoDrag : public Q3UriDrag
41{
42public:
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//==============================================
51PhotosIconView::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//==============================================
318void 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//==============================================
347void 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);
396 }
397}
398//==============================================
399void 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
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 {
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//==============================================
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)
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//==============================================
int width
Definition blur.cpp:79
virtual bool drag(DragMode mode)
force all drags to COPY (not move/delete) original files!!!
PhotoDrag(QWidget *dragSource=0, const char *name=0)
Displays photo thumbnail and description.
void setPixmap(const QPixmap &p, bool redraw)
Photo * getPhoto()
Returns photo pointer.
void contentsDropEvent(QDropEvent *e)
void rotate270SelectedPhotos()
void contentsMousePressEvent(QMouseEvent *e)
void drawContents(QPainter *p, int clipx, int clipy, int clipw, int cliph)
QPixmap * backgroundImage
QPixmap * dragIcon
PhotoPreviewWidget * currentPseudoSelection
void rotate90SelectedPhotos()
void contextMenuEvent(QContextMenuEvent *e)
PhotoPreviewWidget * rightClickedPhoto
void itemHasMoved()
PhotoDescEdit * curPhotoDescEdit
void contentsMouseMoveEvent(QMouseEvent *e)
void repaintGroup(Q3IconViewItem *pseudoSelection)
void addPhotos(QStringList)
void keyPressEvent(QKeyEvent *e)
PhotosIconView(QWidget *parent)
void drawBackground(QPainter *p, const QRect &r)
void removeSelectedPhotos()
void editSelectedPhoto()
void captureClick(Q3IconViewItem *, const QPoint &)
bool findNearestUnselectedPhoto(const QPoint &pos, Q3IconViewItem **nearestItem, bool &posIsleftOfItem)
Q3DragObject * dragObject()
Top level widget, encapsulates the title widget, the layout widget, and the toolbar widget.
Definition window.h:40
QString IMAGE_PATH
Definition config.cpp:18
QPoint topLeft