AlbumShaper  1.0a3
subalbum.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 <qimage.h>
13 #include <qpixmap.h>
14 #include <qstring.h>
15 #include <q3textstream.h>
16 #include <qdom.h>
17 #include <fstream>
18 #include <qdir.h>
19 #include <qregexp.h>
20 #include <qapplication.h>
21 #include <QDateTime>
22 
23 //Projectwide includes
24 #include "album.h"
25 #include "subalbum.h"
26 #include "photo.h"
27 #include "tools/imageTools.h"
28 #include "tools/xmlTools.h"
29 #include "tools/md5.h"
30 #include "../config.h"
31 #include "../gui/photoPreviewWidget.h"
32 #include "../gui/statusWidget.h"
33 #include "../gui/subalbumPreviewWidget.h"
34 
35 //==============================================
36 Subalbum::Subalbum(Album* albm, int number)
37 {
38  //set subalbum number
39  this->number = number;
40 
41  //by default no photos in subalbum
42  numPhotos = 0;
43  loadedPhotos = 0;
44 
45  //set strings to default values
46  name = qApp->translate("Subalbum", "Collection %1").arg(number);
47  description ="";
48 
49  //set default rep images
52  ( QString(IMAGE_PATH)+"miscImages/subalbum.png" );
54 
55  //no photos by default
56  firstPhoto = NULL;
57  lastPhoto = NULL;
58 
59  //next and prev pointers null by default
60  prevSubalbum = NULL;
61  nextSubalbum = NULL;
62 
63  //set album pointer
64  this->albm = albm;
65 }
66 //==============================================
68 {
69  //delete representative images
73 
74  //delete all photos
75  Photo* current = firstPhoto;
76  while(current != NULL)
77  {
78  Photo* temp = current->getNext();
79  delete current;
80  current = temp;
81  }
82 }
83 //==============================================
84 QString Subalbum::getName() { return QString(name); }
85 QString Subalbum::getDescription() { return QString(description); }
86 //==============================================
88 {
89  if(size == SMALL) return smallRepresentativeImage;
90  if(size == MEDIUM) return mediumRepresentativeImage;
91  if(size == LARGE) return largeRepresentativeImage;
92  else return NULL;
93 }
94 //==============================================
96 
99 
102 
106 //==============================================
107 void Subalbum::setName(QString val)
108 {
109  if(name != val)
110  {
111  name = val;
112  albm->setModified();
113  }
114 }
115 //==============================================
116 void Subalbum::setDescription(QString val)
117 {
118  if(description != val)
119  {
120  description = val;
121  albm->setModified();
122  }
123 }
124 //==============================================
125 void Subalbum::setRepresentativeImage(QString imageFilename)
126 {
127  //delete old representative images
131 
132  //if being set to null, set back to defaults
133  if(imageFilename.isNull())
134  {
137 
139  ( QString(IMAGE_PATH)+"miscImages/subalbum.png" );
140  }
141  //else create various sized cover images
142  else
143  {
144  int imageWidth, imageHeight;
145  getImageSize( imageFilename, imageWidth, imageHeight );
146 
147  //small version (show at top)
148  int smallRepWidth = 0;
149  int smallRepHeight = 0;
150  calcScaledImageDimensions( imageWidth, imageHeight,
151  107, REP_IMAGE_HEIGHT,
152  smallRepWidth, smallRepHeight);
153  QImage thumbnailSmall;
154  scaleImage( imageFilename, thumbnailSmall, smallRepWidth, smallRepHeight );
155  smallRepresentativeImage = new QPixmap( thumbnailSmall.width(), thumbnailSmall.height() );
156  smallRepresentativeImage->convertFromImage( thumbnailSmall );
157 
158  //medium version (seen in collections listing)
160 
161  //large version, used for actually saving out
162  largeRepresentativeImage = new QPixmap( imageFilename );
163  //---------------------------------------------------------
164  }
165 
166  //set modified
167  albm->setModified();
168 }
169 //==============================================
170 void Subalbum::setSubalbumNumber(int newVal) { number = newVal; }
173 //==============================================
174 void Subalbum::addPhoto(Photo* newPhoto)
175 {
176  //set the photos next and prev points to NULL by default
177  newPhoto->setNext(NULL);
178  newPhoto->setPrev(NULL);
179 
180  //if list empty set to head
181  if(firstPhoto == NULL)
182  {
183  firstPhoto = newPhoto;
184  lastPhoto = newPhoto;
185  }
186  //else append to end of list
187  else
188  {
189  lastPhoto->setNext(newPhoto);
190  newPhoto->setPrev( lastPhoto );
191  lastPhoto = newPhoto;
192  }
193 
194  numPhotos++;
195  albm->setModified();
196 }
197 //==============================================
198 bool Subalbum::addPhoto(QString fileName, bool replaceDescription, Photo* newPhoto)
199 {
200  //create new photo if necessary
201  if(newPhoto == NULL)
202  newPhoto = new Photo(this, getLast(), numPhotos);
203 
204  //replace description with filename if instructed
205  //(aka /home/bob/happy.jpg -> happy)
206  if(replaceDescription)
207  {
208  //get filename
209  QString desc(fileName);
210 
211  //remove path
212  desc = desc.section( QRegExp("/"), -1);
213 
214  //remove extension if it exists
215  int extensionIndex = desc.findRev( '.' );
216  if(extensionIndex > 0 )
217  desc.truncate(extensionIndex);
218 
219  //convert _'s to spaces
220  desc = desc.replace('_', ' ');
221 
222  //set photos description
223  newPhoto->setDescription( desc );
224  }
225 
226  //attempt to set image
227  if(!newPhoto->setImage(fileName, albm->getNextUniquePhotoID() ))
228  {
229  delete newPhoto;
230  return false;
231  }
232 
233  //if this is the first photo set as the head
234  if(firstPhoto == NULL)
235  {
236  firstPhoto = newPhoto;
237  lastPhoto = newPhoto;
238  }
239  //else append to end of list
240  else
241  {
242  lastPhoto->setNext(newPhoto);
243  newPhoto->setPrev( lastPhoto );
244  lastPhoto = newPhoto;
245  }
246 
247  numPhotos++;
248  albm->setModified();
249  return true;
250 }
251 //==============================================
252 bool Subalbum::lazyAddPhoto(QString imageName, QString slideshowName, QString thumbnailName,
253  Photo* newPhoto)
254 {
255  //attempt to set image
256  if(!newPhoto->setImage(imageName, slideshowName, thumbnailName))
257  {
258  delete newPhoto;
259  return false;
260  }
261 
262  //if this is the first photo set as the head
263  if(firstPhoto == NULL)
264  {
265  firstPhoto = newPhoto;
266  lastPhoto = newPhoto;
267  }
268  //else append to end of list
269  else
270  {
271  lastPhoto->setNext(newPhoto);
272  newPhoto->setPrev( lastPhoto );
273  lastPhoto = newPhoto;
274  }
275 
276  numPhotos++;
277  albm->setModified();
278  return true;
279 }
280 //==============================================
282 {
283  //if photo pointer is null then bail
284  if(val == NULL) return;
285 
286  //reset head and tail pointers if necessary
287  if( val == firstPhoto ) firstPhoto = val->getNext();
288  if( val == lastPhoto ) lastPhoto = val->getPrev();
289 
290  //splice out
291  if( val->getPrev() != NULL ) val->getPrev()->setNext( val->getNext() );
292  if( val->getNext() != NULL ) val->getNext()->setPrev( val->getPrev() );
293 
294  //delete object
295  delete val;
296  val = NULL;
297  numPhotos--;
298  albm->setModified();
299 }
300 //==============================================
302 {
303  prevSubalbum = val;
304  albm->setModified();
305 }
306 //==============================================
308 {
309  nextSubalbum = val;
310  albm->setModified();
311 }
312 //==============================================
313 void Subalbum::exportToXML(StatusWidget*, Q3TextStream& stream)
314 {
315  //write subalbum information
316  stream << " <subalbum>\n";
317 
318  //if subalbum has a represenatative image save it's path
319  if(getRepresentativeImage(LARGE) != NULL )
320  {
321  stream << " <thumb path=\"img/" << number << "_thumb.jpg\"/>\n";
322  stream << " <name>" << fixXMLString(name) << "</name>\n";
323  }
324  //else if the name is empty or just whitespace override it with "Subalbum #" so
325  //it is not invisible on the coverpage
326  else
327  {
328  QString strippedName = fixXMLString(name);
329  if(strippedName.stripWhiteSpace() == "")
330  stream << QString(" <name>%1 %2</name>\n").arg(qApp->translate("Subalbum", "Collection")).arg(number);
331  else
332  stream << " <name>" << fixXMLString(name) << "</name>\n";
333  }
334 
335  stream << " <description>" << fixXMLString(description) << "</description>\n";
336 
337  //write photos
338  Photo* current = firstPhoto;
339  while(current != NULL)
340  {
341  current->exportToXML(stream);
342  current = current->getNext();
343  }
344 
345  //close subalbum
346  stream << " </subalbum>\n";
347 
348 }
349 //==============================================
350 void Subalbum::importFromDisk(QDomNode* root,
351  int subalbumNum,
353  QString dirName,
354  bool disableCheckPhotoMods)
355 {
356  //if representative image exists load
357  QString repName = QString(dirName + "/img/%1_thumb.jpg").arg(subalbumNum);
358  QImage repImage(repName);
359  if(!repImage.isNull())
360  {
361  setRepresentativeImage(repName);
362  }
363 
364  QDomNode node = root->firstChild();
365  QDomText val;
366  int photoNum = 0;
367  while( !node.isNull() )
368  {
369  //------------------------------------------------------------
370  //subalbum name
371  if( node.isElement() && node.nodeName() == "name" )
372  {
373  val = node.firstChild().toText();
374  if(!val.isNull())
375  name = val.nodeValue();
376  name.replace("\\&quot;","\"");
377  }
378  //------------------------------------------------------------
379  //subalbum description
380  else if( node.isElement() && node.nodeName() == "description" )
381  {
382  val = node.firstChild().toText();
383  if(!val.isNull())
384  description = val.nodeValue();
385  description.replace("\\&quot;","\"");
386  }
387  //------------------------------------------------------------
388  //photo
389  else if( node.isElement() && node.nodeName() == "photo" )
390  {
391  //increase counter
392  photoNum++;
393 
394  //create new photo object
395  QString imageName = QString(dirName + "img/%1/%2.jpg").arg(subalbumNum).arg(photoNum);
396  QString slideshowName = QString(dirName + "img/%1/%2_slideshow.jpg").arg(subalbumNum).arg(photoNum);
397  QString thumbName = QString(dirName + "img/%1/%2_thumb.jpg").arg(subalbumNum).arg(photoNum);
398  Photo* newPhoto = new Photo(this, getLast(), photoNum);
399 
400  //load photo information from disk
401  QDateTime* modificationTimes = newPhoto->importFromDisk( &node );
402 
403  //first check to see if modifications times have changed
404  bool lazyLoad = true; //assume no modifications
405 
406  //skip checking for mods if disable checking is set
407  if(!disableCheckPhotoMods)
408  {
409  QFileInfo info[3];
410  info[0].setFile( imageName );
411  info[1].setFile( slideshowName );
412  info[2].setFile( thumbName );
413  if(
414  modificationTimes[0] != info[0].lastModified() ||
415  modificationTimes[1] != info[1].lastModified() ||
416  modificationTimes[2] != info[2].lastModified()
417  )
418  lazyLoad = false;
419  }
420 
421  //if no changes have occured do lazy load - don't
422  //bother scaling down thumbnail and slideshow images
423  //from original image
424  std::ifstream imageFile ( QFile::encodeName(imageName) );
425  std::ifstream slideshowFile( QFile::encodeName(slideshowName) );
426  std::ifstream thumbnailFile( QFile::encodeName(thumbName) );
427 
428  if( imageFile.is_open() &&
429  thumbnailFile.is_open() &&
430  slideshowFile.is_open() &&
431  (
432  lazyLoad ||
433  (
434  getMD5(imageFile) == newPhoto->getImageChecksum() &&
435  getMD5(slideshowFile) == newPhoto->getSlideshowChecksum() &&
436  getMD5(thumbnailFile) == newPhoto->getThumbnailChecksum()
437  )
438  )
439  )
440  {
441  //close ifstreams
442  imageFile.close();
443  slideshowFile.close();
444  thumbnailFile.close();
445 
446  //populate image
447  lazyAddPhoto(imageName, slideshowName, thumbName, newPhoto);
448  }
449  //else reload image and scale it since changes have occured.
450  else
451  {
452  //close ifstreams if open
453  if(imageFile.is_open())
454  imageFile.close();
455  if(thumbnailFile.is_open())
456  thumbnailFile.close();
457 
458  //populate image
459  addPhoto(imageName, false, newPhoto);
460  }
461 
462  if(imageFile.is_open())
463  imageFile.close();
464  if(slideshowFile.is_open())
465  slideshowFile.close();
466  if(thumbnailFile.is_open())
467  thumbnailFile.close();
468 
469  //update progress bar
470  status->incrementProgress();
471  qApp->processEvents();
472  }
473  //------------------------------------------------------------
474  //advance to next node
475  node = node.nextSibling();
476  //------------------------------------------------------------
477  }
478  //------------------------------------------------------------
479  //set loaded number
481  //------------------------------------------------------------
482 }
483 //==============================================
485 {
486  //if null pointer bail
487  if(val == NULL) return;
488 
489  //update first and last pointers if necessary
490  if(val == firstPhoto) firstPhoto = val->getNext();
491  if(val == lastPhoto) lastPhoto = val->getPrev();
492 
493  //splice out
494  if(val->getPrev() != NULL) val->getPrev()->setNext( val->getNext() );
495  if(val->getNext() != NULL) val->getNext()->setPrev( val->getPrev() );
496 
497  numPhotos--;
498  albm->setModified();
499 }
500 //==============================================
502 {
503  //base case, no items
504  if(item == NULL)
505  {
506  firstPhoto = NULL;
507  lastPhoto = NULL;
508  return;
509  }
510 
511  //set first and last pointers
512  firstPhoto = item->getPhoto();
513  firstPhoto->setNext(NULL);
514  firstPhoto->setPrev(NULL);
516 
517  //set all next/prev pointers
518  while(item->nextItem() != NULL)
519  {
520  item->getPhoto()->setNext( ((PhotoPreviewWidget*)item->nextItem())->getPhoto() );
521  item->getPhoto()->getNext()->setPrev( item->getPhoto() );
522  item = (PhotoPreviewWidget*)item->nextItem();
523  lastPhoto = item->getPhoto();
524  lastPhoto->setNext(NULL);
525  }
526 
527 }
528 //==============================================
void resetNumLoadedPhotos()
Definition: subalbum.cpp:171
Photo * firstPhoto
Pointer to first photo.
Definition: subalbum.h:168
bool addPhoto(QString fileName, bool replaceDescription=false, Photo *newPhoto=NULL)
Adds a new photo to the Subalbum and appends it to the end, returns TRUE if successful.
Definition: subalbum.cpp:198
void importFromDisk(QDomNode *root, int subalbumNum, StatusWidget *status, QString dirName, bool disableCheckPhotoMods)
Builds subalbum from XML DOM node.
Definition: subalbum.cpp:350
QString getSlideshowChecksum()
Get thumbanil checksum.
Definition: photo.cpp:202
#define LARGE
Definition: album.h:19
A photo consists of a full size image, a smaller slide show image, a very small thumbnail image...
Definition: photo.h:44
int number
Subalbum Number.
Definition: subalbum.h:148
void setRepresentativeImage(QString imageFilename)
sets a sized representative image
Definition: subalbum.cpp:125
Photo * getPhoto()
Returns photo pointer.
Photo * lastPhoto
Pointer to last photo.
Definition: subalbum.h:171
void incrementProgress()
Updates the progress bar by one step.
#define MEDIUM
Definition: album.h:18
Displays photo thumbnail and description.
void exportToXML(StatusWidget *status, Q3TextStream &stream)
Exports subalbum to xml.
Definition: subalbum.cpp:313
void setPrev(Subalbum *val)
Sets pointer of prev subalbum.
Definition: subalbum.cpp:301
QPixmap * mediumRepresentativeImage
Definition: subalbum.h:164
QString getMD5(std::ifstream &stream)
Definition: md5.cpp:542
#define SMALL
Definition: album.h:17
void exportToXML(Q3TextStream &stream)
Exports photo to xml.
Definition: photo.cpp:414
Photo * getNext()
Returns next photo pointer.
Definition: photo.cpp:225
#define REP_IMAGE_HEIGHT
Definition: config.h:29
Album * getAlbum()
returns the album pointer
Definition: subalbum.cpp:95
QString description
Longer description of subalbum.
Definition: subalbum.h:160
QString IMAGE_PATH
Definition: config.cpp:18
int getNumLoadedPhotos()
Returns the number of loaded photos in subalbum.
Definition: subalbum.cpp:105
void setNext(Subalbum *val)
Sets pointer of next subalbum.
Definition: subalbum.cpp:307
bool setImage(QString imageName, QString slideshowName, QString thumbnailName)
Setup photo using preexisting full size, slideshow, and thumbnail images.
Definition: photo.cpp:105
StatusWidget * status
A subalbum contains photos.
Definition: subalbum.h:48
void setModified(bool val=true)
Sets the album as modified.
Definition: album.cpp:1418
void setDescription(QString val)
Sets the description.
Definition: photo.cpp:210
int numPhotos
Number of photos in subalbum.
Definition: subalbum.h:151
~Subalbum()
Frees photos.
Definition: subalbum.cpp:67
Subalbum * prevSubalbum
Pointer to prev subalbum.
Definition: subalbum.h:174
int getNumPhotos()
Returns the number of photos in the subalbum.
Definition: subalbum.cpp:104
bool scaleImage(QString fileIn, QString fileOut, int newWidth, int newHeight)
Scale image and save copy to disk.
Definition: imageTools.cpp:157
Subalbum(Album *albm, int number)
Sets default information is the Subalbum number.
Definition: subalbum.cpp:36
QPixmap * getRepresentativeImage(int size)
gets a sized representative image
Definition: subalbum.cpp:87
Photo * getFirst()
Returns first photo in subalbum.
Definition: subalbum.cpp:100
Album * albm
Pointer to album subalbum is in.
Definition: subalbum.h:180
bool getImageSize(const char *filename, QSize &size)
Get image dimensions.
Definition: imageTools.cpp:192
void photoMoved(Photo *val)
Removes a specified photo without deleting the object.
Definition: subalbum.cpp:484
QDateTime * importFromDisk(QDomNode *root)
Builds photo from XML DOM node, returns date modified info from xml.
Definition: photo.cpp:239
QString getDescription()
Gets the Subalbum description.
Definition: subalbum.cpp:85
static QPixmap * createSubalbumPixmap(QString imageName)
QString fixXMLString(QString text)
Fix strings before exporting to XML such that & becomes &, etc...
Definition: xmlTools.cpp:36
Subalbum * nextSubalbum
Pointer to next subalbum.
Definition: subalbum.h:177
QString getName()
Gets the Subalbum Name.
Definition: subalbum.cpp:84
void setName(QString val)
Sets the Subalbum Name.
Definition: subalbum.cpp:107
An album contains Subalbums.
Definition: album.h:52
Subalbum * getNext()
Returns pointer to next subalbum.
Definition: subalbum.cpp:98
bool lazyAddPhoto(QString imageName, QString slideshowName, QString thumbnailName, Photo *newPhoto)
Lazily adds a new photo to the subalbum without rescaling from scrath, returns TRUE if successful...
Definition: subalbum.cpp:252
QPixmap * largeRepresentativeImage
Definition: subalbum.h:165
int getSubalbumNumber()
Returns subalbum number.
Definition: subalbum.cpp:103
QString name
Short Name for subalbum.
Definition: subalbum.h:157
void setDescription(QString val)
Sets the Subalbum description.
Definition: subalbum.cpp:116
void setNext(Photo *val)
Sets next photo pointer.
Definition: photo.cpp:233
void setSubalbumNumber(int newVal)
Sets the subalbum number to newVal.
Definition: subalbum.cpp:170
int getNextUniquePhotoID()
Returns the next unique photo id.
Definition: album.cpp:1420
QPixmap * smallRepresentativeImage
Definition: subalbum.h:163
void calcScaledImageDimensions(int origWidth, int origHeight, int idealWidth, int idealHeight, int &width, int &height)
Computes scale of image dimensions while respecting aspect ratio, equivalent to a QImage::scaleMin wi...
Definition: imageTools.cpp:39
void syncPhotoList(PhotoPreviewWidget *item)
Syncs photo ordering with front end gui ordering.
Definition: subalbum.cpp:501
Photo * getPrev()
Returns the previous photo pointer.
Definition: photo.cpp:224
void setModified()
Definition: subalbum.cpp:172
Subalbum * getPrev()
Returns pointer to prev subalbum.
Definition: subalbum.cpp:97
QString getImageChecksum()
Get image checksum.
Definition: photo.cpp:200
QString getThumbnailChecksum()
Get thumbanil checksum.
Definition: photo.cpp:201
void removePhoto(Photo *val)
Removes a specified photo.
Definition: subalbum.cpp:281
void setPrev(Photo *val)
Sets prev photo pointer.
Definition: photo.cpp:227
Photo * getLast()
Returns last photo in subalbum.
Definition: subalbum.cpp:101
int loadedPhotos
Number of photos in subalbum when last loaded.
Definition: subalbum.h:154