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"
32#include "../gui/statusWidget.h"
34
35//==============================================
36Subalbum::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//==============================================
84QString Subalbum::getName() { return QString(name); }
85QString 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//==============================================
107void Subalbum::setName(QString val)
108{
109 if(name != val)
110 {
111 name = val;
112 albm->setModified();
113 }
114}
115//==============================================
117{
118 if(description != val)
119 {
120 description = val;
121 albm->setModified();
122 }
123}
124//==============================================
125void 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//==============================================
170void Subalbum::setSubalbumNumber(int newVal) { number = newVal; }
173//==============================================
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//==============================================
198bool 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//==============================================
252bool 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//==============================================
313void 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//==============================================
350void 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
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//==============================================
An album contains Subalbums.
Definition album.h:53
int getNextUniquePhotoID()
Returns the next unique photo id.
Definition album.cpp:1420
void setModified(bool val=true)
Sets the album as modified.
Definition album.cpp:1418
Displays photo thumbnail and description.
Photo * getPhoto()
Returns photo pointer.
A photo consists of a full size image, a smaller slide show image, a very small thumbnail image,...
Definition photo.h:45
QDateTime * importFromDisk(QDomNode *root)
Builds photo from XML DOM node, returns date modified info from xml.
Definition photo.cpp:239
bool setImage(QString imageName, QString slideshowName, QString thumbnailName)
Setup photo using preexisting full size, slideshow, and thumbnail images.
Definition photo.cpp:105
Photo * getNext()
Returns next photo pointer.
Definition photo.cpp:225
QString getThumbnailChecksum()
Get thumbanil checksum.
Definition photo.cpp:201
void setNext(Photo *val)
Sets next photo pointer.
Definition photo.cpp:233
void setPrev(Photo *val)
Sets prev photo pointer.
Definition photo.cpp:227
Photo * getPrev()
Returns the previous photo pointer.
Definition photo.cpp:224
void exportToXML(Q3TextStream &stream)
Exports photo to xml.
Definition photo.cpp:414
QString getSlideshowChecksum()
Get thumbanil checksum.
Definition photo.cpp:202
void setDescription(QString val)
Sets the description.
Definition photo.cpp:210
QString getImageChecksum()
Get image checksum.
Definition photo.cpp:200
void incrementProgress()
Updates the progress bar by one step.
static QPixmap * createSubalbumPixmap(QString imageName)
A subalbum contains photos.
Definition subalbum.h:49
Subalbum * getPrev()
Returns pointer to prev subalbum.
Definition subalbum.cpp:97
int numPhotos
Number of photos in subalbum.
Definition subalbum.h:151
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
int number
Subalbum Number.
Definition subalbum.h:148
int getNumPhotos()
Returns the number of photos in the subalbum.
Definition subalbum.cpp:104
Photo * getLast()
Returns last photo in subalbum.
Definition subalbum.cpp:101
QString name
Short Name for subalbum.
Definition subalbum.h:157
int getSubalbumNumber()
Returns subalbum number.
Definition subalbum.cpp:103
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
void removePhoto(Photo *val)
Removes a specified photo.
Definition subalbum.cpp:281
void setSubalbumNumber(int newVal)
Sets the subalbum number to newVal.
Definition subalbum.cpp:170
Subalbum * getNext()
Returns pointer to next subalbum.
Definition subalbum.cpp:98
Subalbum * prevSubalbum
Pointer to prev subalbum.
Definition subalbum.h:174
void setRepresentativeImage(QString imageFilename)
sets a sized representative image
Definition subalbum.cpp:125
Album * albm
Pointer to album subalbum is in.
Definition subalbum.h:180
Subalbum * nextSubalbum
Pointer to next subalbum.
Definition subalbum.h:177
Photo * lastPhoto
Pointer to last photo.
Definition subalbum.h:171
void photoMoved(Photo *val)
Removes a specified photo without deleting the object.
Definition subalbum.cpp:484
QString getDescription()
Gets the Subalbum description.
Definition subalbum.cpp:85
void setName(QString val)
Sets the Subalbum Name.
Definition subalbum.cpp:107
Photo * firstPhoto
Pointer to first photo.
Definition subalbum.h:168
void setPrev(Subalbum *val)
Sets pointer of prev subalbum.
Definition subalbum.cpp:301
QString description
Longer description of subalbum.
Definition subalbum.h:160
QPixmap * getRepresentativeImage(int size)
gets a sized representative image
Definition subalbum.cpp:87
QString getName()
Gets the Subalbum Name.
Definition subalbum.cpp:84
void setModified()
Definition subalbum.cpp:172
Photo * getFirst()
Returns first photo in subalbum.
Definition subalbum.cpp:100
void resetNumLoadedPhotos()
Definition subalbum.cpp:171
Album * getAlbum()
returns the album pointer
Definition subalbum.cpp:95
void setNext(Subalbum *val)
Sets pointer of next subalbum.
Definition subalbum.cpp:307
int loadedPhotos
Number of photos in subalbum when last loaded.
Definition subalbum.h:154
void importFromDisk(QDomNode *root, int subalbumNum, StatusWidget *status, QString dirName, bool disableCheckPhotoMods)
Builds subalbum from XML DOM node.
Definition subalbum.cpp:350
QPixmap * largeRepresentativeImage
Definition subalbum.h:165
void setDescription(QString val)
Sets the Subalbum description.
Definition subalbum.cpp:116
QPixmap * mediumRepresentativeImage
Definition subalbum.h:164
int getNumLoadedPhotos()
Returns the number of loaded photos in subalbum.
Definition subalbum.cpp:105
~Subalbum()
Frees photos.
Definition subalbum.cpp:67
void exportToXML(StatusWidget *status, Q3TextStream &stream)
Exports subalbum to xml.
Definition subalbum.cpp:313
QPixmap * smallRepresentativeImage
Definition subalbum.h:163
void syncPhotoList(PhotoPreviewWidget *item)
Syncs photo ordering with front end gui ordering.
Definition subalbum.cpp:501
Subalbum(Album *albm, int number)
Sets default information.
Definition subalbum.cpp:36
QString IMAGE_PATH
Definition config.cpp:18
#define REP_IMAGE_HEIGHT
Definition config.h:29
bool scaleImage(QString fileIn, QString fileOut, int newWidth, int newHeight)
Scale image and save copy to disk.
bool getImageSize(const char *filename, QSize &size)
Get image dimensions.
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...
QString getMD5(std::ifstream &stream)
Definition md5.cpp:542
StatusWidget * status
#define SMALL
Definition subalbum.h:18
#define MEDIUM
Definition subalbum.h:19
#define LARGE
Definition subalbum.h:20
QString fixXMLString(QString text)
Fix strings before exporting to XML such that & becomes &, etc...
Definition xmlTools.cpp:36