AlbumShaper  1.0a3
subalbum.cpp
Go to the documentation of this file.
00001 //==============================================
00002 //  copyright            : (C) 2003-2005 by Will Stokes
00003 //==============================================
00004 //  This program is free software; you can redistribute it
00005 //  and/or modify it under the terms of the GNU General
00006 //  Public License as published by the Free Software
00007 //  Foundation; either version 2 of the License, or
00008 //  (at your option) any later version.
00009 //==============================================
00010 
00011 //Systemwide includes
00012 #include <qimage.h>
00013 #include <qpixmap.h>
00014 #include <qstring.h>
00015 #include <qtextstream.h>
00016 #include <qdom.h>
00017 #include <fstream>
00018 #include <qdir.h>
00019 #include <qregexp.h>
00020 #include <qapplication.h>
00021 
00022 //Projectwide includes
00023 #include "album.h"
00024 #include "subalbum.h"
00025 #include "photo.h"
00026 #include "tools/imageTools.h"
00027 #include "tools/xmlTools.h"
00028 #include "tools/md5.h"
00029 #include "../config.h"
00030 #include "../gui/photoPreviewWidget.h"
00031 #include "../gui/statusWidget.h"
00032 #include "../gui/subalbumPreviewWidget.h"
00033 
00034 //==============================================
00035 Subalbum::Subalbum(Album* albm, int number)
00036 {
00037   //set subalbum number
00038   this->number = number;
00039 
00040   //by default no photos in subalbum
00041   numPhotos = 0;
00042   loadedPhotos = 0;
00043 
00044   //set strings to default values
00045   name = qApp->translate("Subalbum", "Collection %1").arg(number);
00046   description ="";
00047 
00048   //set default rep images
00049   smallRepresentativeImage = NULL;
00050   mediumRepresentativeImage = SubalbumPreviewWidget::createSubalbumPixmap
00051   ( QString(IMAGE_PATH)+"miscImages/subalbum.png" );
00052   largeRepresentativeImage = NULL;
00053 
00054   //no photos by default
00055   firstPhoto = NULL;
00056   lastPhoto = NULL;
00057 
00058   //next and prev pointers null by default
00059   prevSubalbum = NULL;
00060   nextSubalbum = NULL;
00061 
00062   //set album pointer
00063   this->albm = albm;
00064 }
00065 //==============================================
00066 Subalbum::~Subalbum()
00067 {
00068   //delete representative images
00069   delete smallRepresentativeImage;
00070   delete mediumRepresentativeImage;
00071   delete largeRepresentativeImage;
00072 
00073   //delete all photos
00074   Photo* current = firstPhoto;
00075   while(current != NULL)
00076   {
00077     Photo* temp = current->getNext();
00078     delete current;
00079     current = temp;
00080   }
00081 }
00082 //==============================================
00083 QString Subalbum::getName()        { return QString(name);        }
00084 QString Subalbum::getDescription() { return QString(description); }
00085 //==============================================
00086 QPixmap* Subalbum::getRepresentativeImage(int size)
00087 {
00088   if(size == SMALL)  return smallRepresentativeImage;
00089   if(size == MEDIUM) return mediumRepresentativeImage;
00090   if(size == LARGE)  return largeRepresentativeImage;
00091   else               return NULL;
00092 }
00093 //==============================================
00094 Album* Subalbum::getAlbum()        { return albm;         }
00095 
00096 Subalbum* Subalbum::getPrev()      { return prevSubalbum; }
00097 Subalbum* Subalbum::getNext()      { return nextSubalbum; }
00098 
00099 Photo* Subalbum::getFirst()        { return firstPhoto;   }
00100 Photo* Subalbum::getLast()         { return lastPhoto;    }
00101 
00102 int Subalbum::getSubalbumNumber()  { return number;       }
00103 int Subalbum::getNumPhotos()       { return numPhotos;    }
00104 int Subalbum::getNumLoadedPhotos() { return loadedPhotos; }
00105 //==============================================
00106 void Subalbum::setName(QString val)
00107 {
00108   if(name != val)
00109   {
00110     name = val;
00111     albm->setModified();
00112   }
00113 }
00114 //==============================================
00115 void Subalbum::setDescription(QString val)
00116 {
00117   if(description != val)
00118   {
00119     description = val;
00120     albm->setModified();
00121   }
00122 }
00123 //==============================================
00124 void Subalbum::setRepresentativeImage(QString imageFilename)
00125 {
00126   //delete old representative images
00127   delete smallRepresentativeImage;
00128   delete mediumRepresentativeImage;
00129   delete largeRepresentativeImage;
00130 
00131   //if being set to null, set back to defaults
00132   if(imageFilename.isNull())
00133   {
00134     smallRepresentativeImage = NULL;
00135     largeRepresentativeImage = NULL;
00136 
00137     mediumRepresentativeImage = SubalbumPreviewWidget::createSubalbumPixmap
00138                                 ( QString(IMAGE_PATH)+"miscImages/subalbum.png" );
00139   }
00140   //else create various sized cover images
00141   else
00142   {
00143     int imageWidth, imageHeight;
00144     getImageSize( imageFilename, imageWidth, imageHeight );
00145     
00146     //small version (show at top)
00147     int smallRepWidth = 0;
00148     int smallRepHeight = 0;
00149     calcScaledImageDimensions( imageWidth, imageHeight,
00150                                107, REP_IMAGE_HEIGHT,
00151                                smallRepWidth, smallRepHeight);
00152     QImage thumbnailSmall;
00153     scaleImage( imageFilename, thumbnailSmall, smallRepWidth, smallRepHeight );
00154     smallRepresentativeImage = new QPixmap( thumbnailSmall.width(), thumbnailSmall.height() );
00155     smallRepresentativeImage->convertFromImage( thumbnailSmall );
00156 
00157     //medium version (seen in collections listing)
00158     mediumRepresentativeImage = SubalbumPreviewWidget::createSubalbumPixmap( imageFilename );
00159     
00160     //large version, used for actually saving out
00161     largeRepresentativeImage = new QPixmap( imageFilename );
00162     //---------------------------------------------------------
00163   }
00164 
00165   //set modified
00166   albm->setModified();
00167 }
00168 //==============================================
00169 void Subalbum::setSubalbumNumber(int newVal) { number = newVal;          }
00170 void Subalbum::resetNumLoadedPhotos()        { loadedPhotos = numPhotos; }
00171 void Subalbum::setModified()                 { albm->setModified();      }
00172 //==============================================
00173 void Subalbum::addPhoto(Photo* newPhoto)
00174 {
00175   //set the photos next and prev points to NULL by default
00176   newPhoto->setNext(NULL);
00177   newPhoto->setPrev(NULL);
00178 
00179   //if list empty set to head
00180   if(firstPhoto == NULL)
00181   {
00182     firstPhoto = newPhoto;
00183     lastPhoto = newPhoto;
00184   }
00185   //else append to end of list
00186   else
00187   {
00188     lastPhoto->setNext(newPhoto);
00189     newPhoto->setPrev( lastPhoto );
00190     lastPhoto = newPhoto;
00191   }
00192 
00193   numPhotos++;
00194   albm->setModified();
00195 }
00196 //==============================================
00197 bool Subalbum::addPhoto(QString fileName, bool replaceDescription, Photo* newPhoto)
00198 {
00199   //create new photo if necessary
00200   if(newPhoto == NULL)
00201     newPhoto = new Photo(this, getLast(), numPhotos);
00202 
00203   //replace description with filename if instructed
00204   //(aka /home/bob/happy.jpg -> happy)
00205   if(replaceDescription)
00206   {
00207     //get filename
00208     QString desc(fileName);
00209 
00210     //remove path
00211     desc = desc.section( QRegExp("/"), -1);
00212 
00213     //remove extension if it exists
00214     int extensionIndex = desc.findRev( '.' );
00215     if(extensionIndex > 0 )
00216       desc.truncate(extensionIndex);
00217 
00218     //convert _'s to spaces
00219     desc = desc.replace('_', ' ');
00220 
00221     //set photos description
00222     newPhoto->setDescription( desc );
00223   }
00224 
00225   //attempt to set image
00226   if(!newPhoto->setImage(fileName, albm->getNextUniquePhotoID() ))
00227   {
00228     delete newPhoto;
00229     return false;
00230   }
00231 
00232   //if this is the first photo set as the head
00233   if(firstPhoto == NULL)
00234   {
00235     firstPhoto = newPhoto;
00236     lastPhoto = newPhoto;
00237   }
00238   //else append to end of list
00239   else
00240   {
00241     lastPhoto->setNext(newPhoto);
00242     newPhoto->setPrev( lastPhoto );
00243     lastPhoto = newPhoto;
00244   }
00245 
00246   numPhotos++;
00247   albm->setModified();
00248   return true;
00249 }
00250 //==============================================
00251 bool Subalbum::lazyAddPhoto(QString imageName, QString slideshowName, QString thumbnailName, 
00252                             Photo* newPhoto)
00253 {
00254   //attempt to set image
00255   if(!newPhoto->setImage(imageName, slideshowName, thumbnailName))
00256   {
00257     delete newPhoto;
00258     return false;
00259   }
00260 
00261   //if this is the first photo set as the head
00262   if(firstPhoto == NULL)
00263   {
00264     firstPhoto = newPhoto;
00265     lastPhoto = newPhoto;
00266   }
00267   //else append to end of list
00268   else
00269   {
00270     lastPhoto->setNext(newPhoto);
00271     newPhoto->setPrev( lastPhoto );
00272     lastPhoto = newPhoto;
00273   }
00274   
00275   numPhotos++;
00276   albm->setModified();
00277   return true;
00278 }
00279 //==============================================
00280 void Subalbum::removePhoto(Photo* val)
00281 {
00282   //if photo pointer is null then bail
00283   if(val == NULL) return;
00284   
00285   //reset head and tail pointers if necessary
00286   if( val == firstPhoto )   firstPhoto = val->getNext();
00287   if( val == lastPhoto )    lastPhoto  = val->getPrev();
00288   
00289   //splice out
00290   if( val->getPrev() != NULL ) val->getPrev()->setNext( val->getNext() );
00291   if( val->getNext() != NULL ) val->getNext()->setPrev( val->getPrev() );
00292 
00293   //delete object
00294   delete val;
00295   val = NULL;
00296   numPhotos--;
00297   albm->setModified();
00298 }
00299 //==============================================
00300 void Subalbum::setPrev(Subalbum* val)
00301 {
00302   prevSubalbum = val;
00303   albm->setModified();
00304 }
00305 //==============================================
00306 void Subalbum::setNext(Subalbum* val)
00307 {
00308   nextSubalbum = val;
00309   albm->setModified();
00310 }
00311 //==============================================
00312 void Subalbum::exportToXML(StatusWidget*, QTextStream& stream)
00313 {
00314   //write subalbum information
00315   stream << "  <subalbum>\n";
00316 
00317   //if subalbum has a represenatative image save it's path
00318    if(getRepresentativeImage(LARGE) != NULL )
00319    {
00320      stream << "    <thumb path=\"img/" << number << "_thumb.jpg\"/>\n";
00321      stream << "    <name>" << fixXMLString(name) << "</name>\n";
00322    }
00323    //else if the name is empty or just whitespace override it with "Subalbum #" so
00324    //it is not invisible on the coverpage
00325    else
00326    {
00327      QString strippedName = fixXMLString(name);
00328      if(strippedName.stripWhiteSpace() == "")
00329        stream << QString("  <name>%1 %2</name>\n").arg(qApp->translate("Subalbum", "Collection")).arg(number);
00330      else
00331        stream << "    <name>" << fixXMLString(name) << "</name>\n";
00332    }
00333 
00334   stream << "    <description>" << fixXMLString(description) << "</description>\n";
00335 
00336   //write photos
00337   Photo* current = firstPhoto;
00338   while(current != NULL)
00339   {
00340     current->exportToXML(stream);
00341     current = current->getNext();
00342   }
00343 
00344   //close subalbum
00345   stream << "  </subalbum>\n";
00346 
00347 }
00348 //==============================================
00349 void Subalbum::importFromDisk(QDomNode* root, 
00350                               int subalbumNum,
00351                               StatusWidget* status,
00352                               QString dirName, 
00353                               bool disableCheckPhotoMods)
00354 {
00355   //if representative image exists load
00356   QString repName = QString(dirName + "/img/%1_thumb.jpg").arg(subalbumNum);
00357   QImage repImage(repName);
00358   if(!repImage.isNull())
00359   {
00360     setRepresentativeImage(repName);
00361   }
00362 
00363   QDomNode node = root->firstChild();
00364   QDomText val;
00365   int photoNum = 0;
00366   while( !node.isNull() )
00367   {
00368     //------------------------------------------------------------
00369     //subalbum name
00370     if( node.isElement() && node.nodeName() == "name" )
00371     {
00372       val = node.firstChild().toText();
00373       if(!val.isNull())
00374         name = val.nodeValue();
00375      name.replace("\\&quot;","\"");
00376     }
00377     //------------------------------------------------------------
00378     //subalbum description
00379     else if( node.isElement() && node.nodeName() == "description" )
00380     {
00381       val = node.firstChild().toText();
00382       if(!val.isNull())
00383         description = val.nodeValue();
00384      description.replace("\\&quot;","\"");
00385     }
00386     //------------------------------------------------------------
00387     //photo
00388     else if( node.isElement() && node.nodeName() == "photo" )
00389     {
00390       //increase counter
00391       photoNum++;
00392 
00393       //create new photo object
00394       QString imageName = QString(dirName + "img/%1/%2.jpg").arg(subalbumNum).arg(photoNum);
00395       QString slideshowName = QString(dirName + "img/%1/%2_slideshow.jpg").arg(subalbumNum).arg(photoNum);
00396       QString thumbName = QString(dirName + "img/%1/%2_thumb.jpg").arg(subalbumNum).arg(photoNum);
00397       Photo* newPhoto = new Photo(this, getLast(), photoNum);
00398 
00399       //load photo information from disk
00400       QDateTime* modificationTimes = newPhoto->importFromDisk( &node );
00401 
00402       //first check to see if modifications times have changed
00403       bool lazyLoad = true; //assume no modifications
00404 
00405       //skip checking for mods if disable checking is set
00406       if(!disableCheckPhotoMods)
00407       {
00408         QFileInfo info[3];
00409         info[0].setFile( imageName );
00410         info[1].setFile( slideshowName );
00411         info[2].setFile( thumbName );
00412         if(
00413               modificationTimes[0] != info[0].lastModified() ||
00414               modificationTimes[1] != info[1].lastModified() ||
00415               modificationTimes[2] != info[2].lastModified()
00416            )
00417           lazyLoad = false;
00418       }
00419 
00420       //if no changes have occured do lazy load - don't
00421       //bother scaling down thumbnail and slideshow images
00422       //from original image
00423       std::ifstream imageFile    ( QFile::encodeName(imageName)     );
00424       std::ifstream slideshowFile( QFile::encodeName(slideshowName) );
00425       std::ifstream thumbnailFile( QFile::encodeName(thumbName)     );
00426 
00427       if(  imageFile.is_open() &&
00428             thumbnailFile.is_open() &&
00429             slideshowFile.is_open() &&
00430            (
00431               lazyLoad ||
00432               (
00433                 getMD5(imageFile) == newPhoto->getImageChecksum() &&
00434                 getMD5(slideshowFile) == newPhoto->getSlideshowChecksum() &&
00435                 getMD5(thumbnailFile) == newPhoto->getThumbnailChecksum()
00436               )
00437            )
00438         )
00439       {
00440         //close ifstreams
00441         imageFile.close();
00442         slideshowFile.close();
00443         thumbnailFile.close();
00444 
00445         //populate image
00446         lazyAddPhoto(imageName, slideshowName, thumbName, newPhoto);
00447       }
00448       //else reload image and scale it since changes have occured.
00449       else
00450       {
00451         //close ifstreams if open
00452         if(imageFile.is_open())
00453           imageFile.close();
00454         if(thumbnailFile.is_open())
00455           thumbnailFile.close();
00456 
00457         //populate image
00458         addPhoto(imageName, false, newPhoto);
00459       }
00460 
00461       if(imageFile.is_open())
00462         imageFile.close();
00463       if(slideshowFile.is_open())
00464         slideshowFile.close();
00465       if(thumbnailFile.is_open())
00466         thumbnailFile.close();
00467 
00468       //update progress bar
00469       status->incrementProgress();
00470       qApp->processEvents();
00471     }
00472     //------------------------------------------------------------
00473     //advance to next node
00474     node = node.nextSibling();
00475     //------------------------------------------------------------
00476   }
00477   //------------------------------------------------------------
00478   //set loaded number
00479   resetNumLoadedPhotos();
00480   //------------------------------------------------------------
00481 }
00482 //==============================================
00483 void Subalbum::photoMoved(Photo* val)
00484 {
00485   //if null pointer bail
00486   if(val == NULL) return;
00487   
00488   //update first and last pointers if necessary
00489   if(val == firstPhoto) firstPhoto = val->getNext();
00490   if(val == lastPhoto)  lastPhoto  = val->getPrev();
00491 
00492   //splice out
00493   if(val->getPrev() != NULL) val->getPrev()->setNext( val->getNext() );
00494   if(val->getNext() != NULL) val->getNext()->setPrev( val->getPrev() );
00495 
00496   numPhotos--;
00497   albm->setModified();
00498 }
00499 //==============================================
00500 void Subalbum::syncPhotoList(PhotoPreviewWidget* item)
00501 {
00502   //base case, no items
00503   if(item == NULL)
00504   {
00505     firstPhoto = NULL;
00506     lastPhoto = NULL;
00507     return;
00508   }
00509 
00510   //set first and last pointers
00511   firstPhoto = item->getPhoto();
00512   firstPhoto->setNext(NULL);
00513   firstPhoto->setPrev(NULL);
00514   lastPhoto = firstPhoto;
00515 
00516   //set all next/prev pointers
00517   while(item->nextItem() != NULL)
00518   {
00519     item->getPhoto()->setNext( ((PhotoPreviewWidget*)item->nextItem())->getPhoto() );
00520     item->getPhoto()->getNext()->setPrev( item->getPhoto() );
00521     item = (PhotoPreviewWidget*)item->nextItem();
00522     lastPhoto = item->getPhoto();
00523     lastPhoto->setNext(NULL);
00524   }
00525   
00526 }
00527 //==============================================