Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

subalbum.cpp

Go to the documentation of this file.
00001 //==============================================
00002 //  copyright            : (C) 2003 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 //  As a special exception, Will Stokes gives permission to 
00011 //  link this program with Qt non-commercial edition, and 
00012 //  distribute the resulting executable, without including the 
00013 //  source code for the Qt non-commercial edition in the 
00014 //  source distribution. 
00015 //==============================================
00016 
00017 //Systemwide includes
00018 #include <qimage.h>
00019 #include <qpixmap.h>
00020 #include <qstring.h>
00021 #include <qtextstream.h>
00022 #include <qdom.h>
00023 #include <fstream>
00024 #include <qdir.h>
00025 #include <qregexp.h>
00026 #include <qapplication.h>
00027 
00028 //Projectwide includes
00029 #include "album.h"
00030 #include "subalbum.h"
00031 #include "photo.h"
00032 #include "imageTools.h"
00033 #include "xmlTools.h"
00034 #include "md5.h"
00035 #include "../gui/photoWidget.h"
00036 #include "../gui/statusWidget.h"
00037 #include "../config.h"
00038 
00039 //==============================================
00041 Subalbum::Subalbum(Album* albm, int number)
00042 {
00043   //set subalbum number
00044   this->number = number;
00045 
00046   //by default no photos in subalbum
00047   numPhotos = 0;
00048   loadedPhotos = 0;
00049   
00050   //set strings to default values
00051   name = "";
00052   description ="";
00053 
00054   //set default rep images
00055   smallRepresentativeImage = new QPixmap( QString(IMAGE_PATH)+"notSpecified.png" );
00056   mediumRepresentativeImage = new QPixmap( QString(IMAGE_PATH)+"subalbum.png" );
00057   largeRepresentativeImage = NULL;
00058 
00059   //no photos by default
00060   firstPhoto = NULL;
00061   lastPhoto = NULL;
00062   
00063   //next pointer null by default
00064   nextSubalbum = NULL;
00065   
00066   //set album pointer
00067   this->albm = albm;
00068 }
00069 //==============================================
00071 Subalbum::~Subalbum()
00072 {
00073   //delete representative images
00074   delete smallRepresentativeImage;
00075   delete mediumRepresentativeImage;
00076   delete largeRepresentativeImage;
00077   
00078   //delete all photos
00079   Photo* current = firstPhoto;
00080   Photo* temp;
00081   while(current != NULL)
00082   {
00083     temp = current->getNext();
00084     delete current;
00085     current = temp;
00086   }
00087 } 
00088 //==============================================
00090 void Subalbum::setName(QString val)
00091 {
00092   if(name != val)
00093   {
00094     name = val;
00095     albm->setModified();
00096   }
00097 }
00098 //==============================================
00100 QString Subalbum::getName()
00101 {
00102   return QString(name);
00103 }
00104 //==============================================
00106 void Subalbum::setDescription(QString val)
00107 {
00108   if(description != val)
00109   {
00110     description = val;
00111     albm->setModified();
00112   }
00113 }
00114 //==============================================
00116 QString Subalbum::getDescription()
00117 {
00118   return QString(description);
00119 }
00120 //==============================================
00121 //gets a sized representative image  
00122 QPixmap* Subalbum::getRepresentativeImage(int size)
00123 {
00124   if(size == SMALL)
00125     return smallRepresentativeImage;
00126   if(size == MEDIUM)
00127     return mediumRepresentativeImage;
00128   if(size == LARGE)
00129     return largeRepresentativeImage;
00130   else
00131     return NULL;
00132 }
00133 //==============================================
00134 //sets a sized representative iamge
00135 void Subalbum::setRepresentativeImages(QImage* rawThumbnail)
00136 {
00137   //---------------------------------------------------------    
00138   //delete old representative images
00139   delete smallRepresentativeImage;
00140   delete mediumRepresentativeImage;
00141   delete largeRepresentativeImage;
00142   //---------------------------------------------------------    
00143   //compute various representative image sizes
00144   int smallRepWidth = 0;
00145   int smallRepHeight = 0;
00146   int mediumRepWidth = 0;
00147   int mediumRepHeight = 0;  
00148   resizeImage( rawThumbnail->width(), rawThumbnail->height(),
00149                131, 98,
00150                mediumRepWidth, mediumRepHeight);
00151   resizeImage( rawThumbnail->width(), rawThumbnail->height(),
00152                107, 80,
00153                smallRepWidth, smallRepHeight);
00154   //---------------------------------------------------------    
00155   //create various representative images
00156   //---------------------------------------------------------    
00157   //copy and scale small version
00158   QImage thumbnailSmall = rawThumbnail->smoothScale( smallRepWidth, smallRepHeight );
00159   smallRepresentativeImage = new QPixmap( smallRepWidth, smallRepHeight );
00160   smallRepresentativeImage->convertFromImage( thumbnailSmall );  
00161   
00162   //copy and scale medium version
00163   QImage thumbnailMedium = rawThumbnail->smoothScale( mediumRepWidth, mediumRepHeight );
00164   QImage* centeredThumbnailMedium = new QImage(131, 98, thumbnailMedium.depth());
00165   centeredThumbnailMedium->setAlphaBuffer(true);
00166  
00167   int xDiff = 131 - mediumRepWidth;
00168   int yDiff = 98  - mediumRepHeight;
00169   
00170   //set all pixels to white
00171   int x, y;
00172   for(x=0; x< 131; x++)
00173   {
00174     for(y=0; y<98; y++)
00175     {
00176       centeredThumbnailMedium->setPixel(x, y, QColor(255, 255, 255).rgb()); 
00177     }
00178   }
00179   
00180   int x2 = 0;
00181   for(x= xDiff/2; x < (xDiff/2) + mediumRepWidth; x++)
00182   {
00183     int y2 = 0;
00184     for(y= yDiff/2; y < (yDiff/2) + mediumRepHeight; y++)
00185     {
00186       centeredThumbnailMedium->setPixel(x, y, thumbnailMedium.pixel(x2, y2));
00187       y2++;
00188     }
00189     x2++;
00190   }
00191   
00192   mediumRepresentativeImage = new QPixmap( centeredThumbnailMedium->width(), centeredThumbnailMedium->height() );
00193   mediumRepresentativeImage->convertFromImage( *centeredThumbnailMedium );
00194   delete centeredThumbnailMedium;
00195   //copy large version 
00196   largeRepresentativeImage = new QPixmap(*rawThumbnail);
00197   //---------------------------------------------------------    
00198     
00199   //set modified
00200   albm->setModified();
00201 }
00202 //==============================================
00203 void Subalbum::addPhoto(Photo* newPhoto)
00204 {
00205   //increase counter
00206   numPhotos++;
00207   
00208   //set it's next pointer to null
00209   newPhoto->setNext(NULL);
00210 
00211   //if this is the only photo, set first and last 
00212   //pointers to this photo.
00213   if(firstPhoto == NULL)
00214   {
00215     firstPhoto = newPhoto;
00216     lastPhoto = newPhoto;
00217   }
00218   //else append to end of list
00219   else
00220   {
00221     lastPhoto->setNext(newPhoto);
00222     lastPhoto = newPhoto;
00223   }
00224     
00225   //set modified
00226   albm->setModified();
00227 }
00228 //==============================================
00230 bool Subalbum::addPhoto(QString fileName, bool replaceDescription, Photo* newPhoto)
00231 {
00232   numPhotos++;
00233   
00234   //create new photo if necessary
00235   if(newPhoto == NULL)
00236     newPhoto = new Photo(this, numPhotos);
00237     
00238   //replace description with filename if instructed
00239   //(aka /home/bob/happy.jpg -> happy)
00240   if(replaceDescription) 
00241   {
00242     QString desc(fileName);  
00243     desc = desc.section( QRegExp("/"), -1);
00244     desc.truncate(desc.length() - 4);
00245     newPhoto->setDescription( desc );
00246   }
00247     
00248   //attempt to set image
00249   if(!newPhoto->setImage(fileName))
00250   {
00251     delete newPhoto;
00252     return false;
00253   }
00254   
00255   //if this is the only photo, set first and last 
00256   //pointers to this photo.
00257   if(firstPhoto == NULL)
00258   {
00259     firstPhoto = newPhoto;
00260     lastPhoto = newPhoto;
00261   }
00262   //else append to end of list
00263   else
00264   {
00265     lastPhoto->setNext(newPhoto);
00266     lastPhoto = newPhoto;
00267   }
00268   
00269   //copy image file over
00270   QString saveName = QDir::homeDirPath() + QString("/.albumShaper/tmp/%1_%2")
00271                                 .arg(number)
00272                                 .arg(numPhotos);
00273   
00274   newPhoto->getImage(IMAGE)->save( saveName + ".jpg", "JPEG", 100);
00275   newPhoto->getImage(SLIDESHOW)->save( saveName + "_slideshow.jpg", "JPEG", 100);
00276   
00277   newPhoto->setImageFilename(saveName + ".jpg");
00278   newPhoto->setSlideshowFilename(saveName + "_slideshow.jpg");
00279   
00280   //deallocate full image and slideshow versions to conserve memory
00281   newPhoto->deallocateLargeImages();
00282     
00283   //set modified
00284   albm->setModified();
00285   
00286   return true;
00287 }
00288 //==============================================
00289 bool Subalbum::lazyAddPhoto(QString imageName, 
00290                                           QString slideshowName, 
00291                                           QString thumbnailName, 
00292                                           Photo* newPhoto)
00293 {
00294   numPhotos++;
00295   
00296   //attempt to set image
00297   if(!newPhoto->setImage(imageName, slideshowName, thumbnailName))
00298   {
00299     delete newPhoto;
00300     return false;
00301   }
00302   
00303   //if this is the only photo, set first and last 
00304   //pointers to this photo.
00305   if(firstPhoto == NULL)
00306   {
00307     firstPhoto = newPhoto;
00308     lastPhoto = newPhoto;
00309   }
00310   //else append to end of list
00311   else
00312   {
00313     lastPhoto->setNext(newPhoto);
00314     lastPhoto = newPhoto;
00315   }
00316     
00317   //set modified
00318   albm->setModified();
00319   
00320   return true;
00321 }
00322 //==============================================
00324 void Subalbum::removePhoto(Photo* val)
00325 { 
00326   //walk through list of photos and find specified photo
00327   Photo* current = firstPhoto;
00328   Photo* prev = NULL;
00329   while(current != val && current->getNext() != NULL)
00330   {
00331     prev = current;
00332     current = current->getNext();
00333   }
00334 
00335   if(current == val)
00336   {
00337     //update prev next pointer
00338     if(prev != NULL)
00339       prev->setNext(current->getNext());
00340     
00341     //update first and last pointers if necessary
00342     if(current == firstPhoto)
00343       firstPhoto = current->getNext();
00344     if(current == lastPhoto)
00345       lastPhoto = prev;
00346 
00347     //free Photo
00348     delete current;
00349     current = NULL;
00350     numPhotos--;
00351   }
00352     
00353   //set modified
00354   albm->setModified();
00355 }
00356 //==============================================
00357 Subalbum* Subalbum::getNext()
00358 {
00359   return nextSubalbum;
00360 }
00361 //==============================================
00362 void Subalbum::setNext(Subalbum* val)
00363 {
00364   nextSubalbum = val;
00365     
00366   //set modified
00367   albm->setModified();
00368 }
00369 //==============================================
00370 void Subalbum::exportToXML(QTextStream& stream)
00371 {
00372   //write subalbum information
00373   stream << "  <subalbum>\n";
00374   stream << "    <name>" << fixXMLString(name) << "</name>\n";
00375   stream << "    <description>" << fixXMLString(description) << "</description>\n";
00376   
00377   //if subalbum has a represenatative image save it's path
00378    if(getRepresentativeImage(LARGE) != NULL )
00379    {
00380      stream << "    <thumb path=\"img/" << number << "_thumb.jpg\"/>\n"; 
00381    }  
00382   
00383   //write photos
00384   Photo* current = firstPhoto;
00385   while(current != NULL)
00386   {
00387     current->exportToXML(stream);
00388     current = current->getNext();
00389   }  
00390   
00391   //close subalbum
00392   stream << "  </subalbum>\n";
00393 
00394 }
00395 //==============================================
00396 Photo* Subalbum::getFirst()
00397 {
00398   return firstPhoto;
00399 }
00400 //==============================================
00401 Photo* Subalbum::getLast()
00402 {
00403   return lastPhoto;
00404 }
00405 //==============================================
00406 void Subalbum::importFromDisk(QDomNode* root, 
00407                                             int subalbumNum, 
00408                                             StatusWidget* status, 
00409                                             QString dirName)
00410 {
00411   //if representative image exists load
00412   QString repName = QString(dirName + "/img/%1_thumb.jpg").arg(subalbumNum);
00413   QImage repImage(repName);
00414   if(!repImage.isNull())
00415   {
00416     setRepresentativeImages(&repImage);  
00417   }  
00418   
00419   QDomNode node = root->firstChild();
00420   QDomText val;  
00421   int photoNum = 0;
00422   while( !node.isNull() )
00423   {
00424     //------------------------------------------------------------
00425     //subalbum name
00426     if( node.isElement() && node.nodeName() == "name" )
00427     { 
00428       val = node.firstChild().toText();
00429       if(!val.isNull())
00430         name = val.nodeValue();
00431     }
00432     //------------------------------------------------------------
00433     //subalbum description
00434     else if( node.isElement() && node.nodeName() == "description" )
00435     { 
00436       val = node.firstChild().toText();
00437       if(!val.isNull())
00438         description = val.nodeValue();
00439     }
00440     //------------------------------------------------------------
00441     //photo
00442     else if( node.isElement() && node.nodeName() == "photo" )
00443     { 
00444       //increase counter
00445       photoNum++;
00446       
00447       //create new photo object
00448       QString imageName = QString(dirName + "img/%1/%2.jpg").arg(subalbumNum).arg(photoNum);
00449       QString slideshowName = QString(dirName + "img/%1/%2_slideshow.jpg").arg(subalbumNum).arg(photoNum);
00450       QString thumbName = QString(dirName + "img/%1/%2_thumb.jpg").arg(subalbumNum).arg(photoNum);
00451       Photo* newPhoto = new Photo(this, photoNum);
00452 
00453       //load photo information from disk
00454       QDateTime* modificationTimes = newPhoto->importFromDisk( &node );      
00455 
00456       //first check to see if modifications times have changed
00457       bool lazyLoad = true; //assume no modifications     
00458       QFileInfo info[3];
00459       info[0].setFile( imageName );
00460       info[1].setFile( slideshowName );
00461       info[2].setFile( thumbName );
00462       if(modificationTimes[0] != info[0].lastModified() ||
00463          modificationTimes[1] != info[1].lastModified() ||
00464          modificationTimes[2] != info[2].lastModified())
00465          lazyLoad = false;
00466 
00467       //if no changes have occured do lazy load - don't 
00468       //bother scaling down thumbnail and slideshow images
00469       //from original image
00470       std::ifstream imageFile( imageName );      
00471       std::ifstream slideshowFile( slideshowName );      
00472       std::ifstream thumbnailFile( thumbName );
00473       
00474       if( lazyLoad ||
00475           (
00476             imageFile.is_open() &&
00477             thumbnailFile.is_open() &&
00478             slideshowFile.is_open() &&
00479             getMD5(imageFile) == newPhoto->getImageChecksum() &&
00480             getMD5(slideshowFile) == newPhoto->getSlideshowChecksum() &&
00481             getMD5(thumbnailFile) == newPhoto->getThumbnailChecksum()
00482           )
00483         )
00484       {
00485         //close ifstreams
00486         imageFile.close();
00487         slideshowFile.close();
00488         thumbnailFile.close();
00489         
00490         //populate image
00491         lazyAddPhoto(imageName, slideshowName, thumbName, newPhoto);
00492       }
00493       //else reload image and scale it since changes have occured.
00494       else
00495       {
00496         //close ifstreams if open
00497         if(imageFile.is_open())
00498           imageFile.close();
00499         if(thumbnailFile.is_open())
00500           thumbnailFile.close();
00501           
00502         //populate image
00503         addPhoto(imageName, false, newPhoto);
00504       }
00505 
00506       if(imageFile.is_open())
00507         imageFile.close();
00508       if(slideshowFile.is_open())
00509         slideshowFile.close();
00510       if(thumbnailFile.is_open())
00511         thumbnailFile.close();
00512       
00513       //update progress bar
00514       status->incrementProgress();
00515       qApp->processEvents();
00516     }
00517     //------------------------------------------------------------
00518     //advance to next node   
00519     node = node.nextSibling();
00520     //------------------------------------------------------------
00521   }
00522   //------------------------------------------------------------
00523   //set loaded number
00524   resetNumLoadedPhotos();
00525   //------------------------------------------------------------  
00526 }
00527 //==============================================
00528 void Subalbum::photoMoved(Photo* val)
00529 {
00530   //decrease counter
00531   numPhotos--;
00532  
00533  //walk through list of photos and find specified photo
00534   Photo* current = firstPhoto;
00535   Photo* prev = NULL;
00536   while(current != NULL &&
00537         current != val)
00538   {
00539     prev = current;
00540     current = current->getNext();
00541   }
00542 
00543   if(current == val)
00544   {
00545     //update prev next pointer
00546     if(prev != NULL)
00547       prev->setNext(current->getNext());
00548     
00549     //update first and last pointers if necessary
00550     if(current == firstPhoto)
00551       firstPhoto = current->getNext();
00552     if(current == lastPhoto)
00553       lastPhoto = prev;
00554   }
00555     
00556   //set modified
00557   albm->setModified();
00558 }
00559 //==============================================
00560 void Subalbum::syncPhotoList(PhotoWidget* item)
00561 {
00562   //base case, no items
00563   if(item == NULL)
00564   {
00565     firstPhoto = NULL;
00566     lastPhoto = NULL;
00567     return;
00568   }
00569   
00570   //set first and last pointers
00571   firstPhoto = item->getPhoto();
00572   firstPhoto->setNext(NULL);
00573   lastPhoto = firstPhoto;
00574     
00575   //set all next pointers
00576   while(item->nextItem() != NULL)
00577   {
00578     item->getPhoto()->setNext( ((PhotoWidget*)item->nextItem())->getPhoto() );
00579     item = (PhotoWidget*)item->nextItem();
00580     lastPhoto = item->getPhoto();
00581     lastPhoto->setNext(NULL);
00582   }
00583 }
00584 //==============================================
00585 int Subalbum::getSubalbumNumber()
00586 {
00587   return number;
00588 }
00589 //==============================================
00590 int Subalbum::getNumPhotos()
00591 {
00592   return numPhotos;;
00593 }
00594 //==============================================
00595 int Subalbum::getNumLoadedPhotos()
00596 {
00597   return loadedPhotos;;
00598 }
00599 //==============================================
00600 void Subalbum::resetNumLoadedPhotos()
00601 {
00602   loadedPhotos = numPhotos;
00603 }
00604 //==============================================
00605 void Subalbum::setModified()
00606 {
00607   albm->setModified();
00608 }
00609 //==============================================

Generated on Thu Nov 13 00:10:54 2003 for AlbumShaper by doxygen 1.3.4