//============================================== // copyright : (C) 2003-2005 by Will Stokes //============================================== // This program is free software; you can redistribute it // and/or modify it under the terms of the GNU General // Public License as published by the Free Software // Foundation; either version 2 of the License, or // (at your option) any later version. //============================================== //Systemwide includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //Projectwide includes #include "window.h" #include "titleWidget.h" #include "layoutWidget.h" #include "subalbumsWidget.h" #include "subalbumWidget.h" #include "statusWidget.h" #include "ALabel.h" #include "subalbumPreviewWidget.h" #include "recentAlbumMenuItem.h" #include "dialogs/albumStatistics.h" #include "dialogs/about.h" #include "help/helpWindow.h" #include "dialogs/questionDialog.h" #include "dialogs/alertDialog.h" #include "dialogs/saveDialog.h" #include "../config.h" #include "../backend/album.h" #include "../backend/subalbum.h" #include "../backend/photo.h" #include "../backend/tools/fileTools.h" #include "../backend/tools/guiTools.h" #include "../configuration/configurationWidget.h" #include "../configuration/configuration.h" //============================================== TitleWidget::TitleWidget(QWidget *parent, const char* name ) : QFrame(parent,name) { tmpDirErrorMessage = tr("Error! Unable to create temp directory!"); tmpDirErrorDesc = tr("Album Shaper was unable to create the necessary temp directory required: "); //-------------------------------------------------------------- QColor white(255, 255, 255); QColor lightBlue(193, 210, 238); QColor darkBlue(35, 75, 139); QColor black(0, 0, 0); //-------------------------------------------------------------- ///Store parent pointer window = (Window*)parent; ///program not busy at first busy = false; //detect changes to text fields detectModifications = true; ///no windows open by default albumStats = NULL; settingsWindow = NULL; about = NULL; helpWindow = NULL; //create backend album object albm = new Album( createTmpDir() ); if(albm->getTmpDir().isNull() ) { AlertDialog alert( tmpDirErrorMessage, tmpDirErrorDesc + window->getConfig()->getString( "loadSave", "tempImageDirectory" ), "alertIcons/warning.png", this ); alert.exec(); quitApplication(); } //------------------------------------- //initialize recent albums object int i; QString recentName, recentLocation, recentPhotoCount; for(i = 0; igetConfig()->getString( "recentAlbums", QString("%1_name").arg(i) ); recentLocation = window->getConfig()->getString("recentAlbums", QString("%1_location").arg(i) ); recentPhotoCount = window->getConfig()->getString("recentAlbums", QString("%1_photoCount").arg(i) ); //no such listing? since listings are continious all listings must be loaded if(recentLocation.compare("-1") == 0) break; //insert item into list recentAlbums.insertEntry( recentName, recentLocation, recentPhotoCount ); } //-------------------------------------------------------------- //create menus menu = new QMenuBar( this, "menuBar" ); //--- //File menu file = new QPopupMenu( this, "fileMenu" ); NEW_ALBUM = file->insertItem( QIconSet( QPixmap(QString(IMAGE_PATH)+"menuIcons/new.png") ), tr("&New"), this, SLOT(newAlbum()), CTRL+Key_N ); OPEN_ALBUM = file->insertItem( QIconSet( QPixmap(QString(IMAGE_PATH)+"menuIcons/open.png") ), tr("&Open..."), this, SLOT(loadAlbum()), CTRL+Key_O ); openRecent = new QPopupMenu( this, "openRecentMenu" ); populateOpenRecentMenu(); refreshOpenRecentMenu(); file->insertItem( tr("Open Recent"), openRecent ); //---------------------- file->insertSeparator(); //---------------------- SAVE_ALBUM = file->insertItem( QIconSet( QPixmap(QString(IMAGE_PATH)+"menuIcons/save.png") ), tr("&Save"), this, SLOT(saveAlbum()), CTRL+Key_S ); SAVEAS_ALBUM = file->insertItem( QIconSet( QPixmap(QString(IMAGE_PATH)+"menuIcons/saveas.png") ), tr("&Save As..."), this, SLOT(saveAsAlbum()), CTRL+SHIFT+Key_S ); REVERT_TO_SAVED_ALBUM = file->insertItem( tr("Revert to Saved"), this, SLOT(revertToSaved()) ); file->setItemEnabled( REVERT_TO_SAVED_ALBUM, false ); //---------------------- file->insertSeparator(); //---------------------- QPopupMenu* exportAs = new QPopupMenu( this, "exportAs" ); EXPORT_SMALL_WEB_GALLERY = exportAs->insertItem( tr("Small Web Gallery..."), this, SLOT(exportSmallWebGallery()) ); EXPORT_LARGE_IMAGES = exportAs->insertItem( tr("Images for Printing..."), this, SLOT(exportLargeImages()) ); file->insertItem( tr("Export"), exportAs ); //---------------------- file->insertSeparator(); //---------------------- file->insertItem( QIconSet( QPixmap(QString(IMAGE_PATH)+"menuIcons/quit.png") ), tr("&Quit"), this, SLOT(quitApplication()), CTRL+Key_Q); menu->insertItem( tr("&File"), file ); //--- //Photo menu photoMenu = new QPopupMenu( this, "phooMenu" ); REMOVE_DESCRIPTIONS = photoMenu->insertItem( tr("Remove Description"), this, SLOT(removeSelectedPhotoDesc()) ); REVERT_PHOTOS = photoMenu->insertItem( tr("Revert to Original"), this, SLOT(revertPhotos()) ); menu->insertItem( tr("&Photos"), photoMenu ); //--- //Tools menu tools = new QPopupMenu( this, "toolsMenu" ); /* BEGIN_PRESENTATION_AT = tools->insertItem( QIconSet( QPixmap(QString(IMAGE_PATH)+"menuIcons/startPresentation.png") ), tr("Begin Presentation"), window, SLOT(startSlideshowWithSelectedPhoto()), CTRL+Key_P ); BEGIN_PRESENTATION = tools->insertItem( tr("Begin Presentation at Beginning"), window, SLOT(startSlideshowAtBeginning()), CTRL+SHIFT+Key_P ); updateMenus(); */ tools->insertItem( QIconSet( QPixmap(QString(IMAGE_PATH)+"menuIcons/albumStatistics.png") ), tr("Album Statistics"), this, SLOT(albumStatistics()), CTRL+Key_I ); tools->insertItem( QIconSet( QPixmap(QString(IMAGE_PATH)+"menuIcons/settings.png") ), tr("Settings"), this, SLOT(settings()) ); menu->insertItem( tr("&Tools"), tools ); //--- //PLATFORM_SPECIFIC_CODE //Window menu #if defined(Q_OS_MACX) windowMenu = new QPopupMenu( this, "windoMenu" ); WINDOW_MINIMIZE = windowMenu->insertItem( tr("&Minimize"), (QWidget*)window, SLOT(showMinimized()), CTRL+Key_M ); menu->insertItem( tr("&Window"), windowMenu ); #endif //--- //Help menu helpMenu = new QPopupMenu( this, "helpMenu" ); helpMenu->insertItem( tr("Album Shaper Help"), this, SLOT(help()), CTRL+Key_Question ); helpMenu->insertItem( QIconSet( QPixmap(QString(IMAGE_PATH)+"menuIcons/about.png") ), tr("About"), this, SLOT(aboutProgram()) ); menu->insertSeparator(); menu->insertItem( tr("&Help"), helpMenu ); //-------------------------------------------------------------- //create all widgets mainFrame = new QFrame( this, "mainFrame" ); mainFrame->setPaletteBackgroundColor( darkBlue ); //------ //album annotations albumAnnotationFrame = new QFrame( mainFrame, "albumAnnotationFrame" ); albumAnnotationFrame->setLineWidth(2); albumAnnotationFrame->setMidLineWidth(0); albumAnnotationFrame->setFrameStyle( QFrame::Panel | QFrame::Plain ); albumAnnotationFrame->setPaletteForegroundColor( white ); albumAnnotationFrame->setPaletteBackgroundColor( darkBlue ); QFrame* albumImageFrame = new QFrame( albumAnnotationFrame, "albumImageFrame" ); albumImage = new ALabel( albumImageFrame, "albumImage", new QPixmap( QString(IMAGE_PATH)+"buttonIcons/removeImage.png") ); connect( albumImage, SIGNAL(mouseRelease()), this, SLOT(unsetAlbumImage()) ); //allow drop events this->setAcceptDrops(true); albumName = new QLabel( tr("Album Name:"), albumAnnotationFrame, "albumName" ); albumNameVal = new QLineEdit( albumAnnotationFrame, "albumNameVal" ); connect( albumNameVal, SIGNAL(textChanged( const QString&)), SLOT( storeAnnotations()) ); albumDescription = new QLabel( tr("Description:"), albumAnnotationFrame, "albumDescription" ); albumDescriptionVal = new QLineEdit( albumAnnotationFrame, "albumDescriptionVal" ); connect( albumDescriptionVal, SIGNAL(textChanged( const QString&)), SLOT( storeAnnotations()) ); albumAuthor = new QLabel( tr("Author:"), albumAnnotationFrame, "albumAuthor" ); albumAuthorVal = new QLineEdit( albumAnnotationFrame, "albumAuthorVal" ); connect( albumAuthorVal, SIGNAL(textChanged( const QString&)), SLOT( storeAnnotations()) ); //------ //subalbum annotations subalbumAnnotationFrame = new QFrame( mainFrame, "subalbumAnnotationFrame" ); subalbumAnnotationFrame->setLineWidth(2); subalbumAnnotationFrame->setMidLineWidth(0); subalbumAnnotationFrame->setFrameStyle( QFrame::Panel | QFrame::Plain ); subalbumAnnotationFrame->setPaletteForegroundColor( white ); subalbumAnnotationFrame->setPaletteBackgroundColor( darkBlue ); QFrame* subalbumImageFrame = new QFrame( subalbumAnnotationFrame, "subalbumImageFrame" ); subalbumImage = new ALabel( subalbumImageFrame, "subalbumImage", new QPixmap( QString(IMAGE_PATH)+"buttonIcons/removeImage.png") ); connect( subalbumImage, SIGNAL(mouseRelease()), this, SLOT(unsetSubalbumImage()) ); subalbumName = new QLabel( tr("Collection Name:"), subalbumAnnotationFrame ); subalbumNameVal = new QLineEdit( subalbumAnnotationFrame ); connect( subalbumNameVal, SIGNAL(textChanged( const QString&)), SLOT( storeAnnotations()) ); subalbumDescription = new QLabel( tr("Description:"), subalbumAnnotationFrame ); subalbumDescriptionVal = new QLineEdit( subalbumAnnotationFrame ); connect( subalbumDescriptionVal, SIGNAL(textChanged( const QString&)), SLOT( storeAnnotations()) ); //-------------------------------------------------------------- //Set fonts + colors QFont labelFont = albumName->font(); labelFont.setWeight(QFont::Bold); albumName->setFont( labelFont ); albumName->setPaletteForegroundColor( white ); albumName->setPaletteBackgroundColor( darkBlue ); albumNameVal->setFont( labelFont ); albumNameVal->setPaletteForegroundColor( black ); albumNameVal->setPaletteBackgroundColor( lightBlue ); albumNameVal->setFrameShape ( QFrame::NoFrame ); albumDescription->setFont( labelFont ); albumDescription->setPaletteForegroundColor( white ); albumDescription->setPaletteBackgroundColor( darkBlue ); albumDescriptionVal->setFont( labelFont ); albumDescriptionVal->setPaletteForegroundColor( black ); albumDescriptionVal->setPaletteBackgroundColor( lightBlue ); albumDescriptionVal->setFrameShape ( QFrame::NoFrame ); albumAuthor->setFont( labelFont ); albumAuthor->setPaletteForegroundColor( white ); albumAuthor->setPaletteBackgroundColor( darkBlue ); albumAuthorVal->setFont( labelFont ); albumAuthorVal->setPaletteForegroundColor( black ); albumAuthorVal->setPaletteBackgroundColor( lightBlue ); albumAuthorVal->setFrameShape ( QFrame::NoFrame ); subalbumName->setFont( labelFont ); subalbumName->setPaletteForegroundColor( white ); subalbumName->setPaletteBackgroundColor( darkBlue ); subalbumNameVal->setFont( labelFont ); subalbumNameVal->setPaletteForegroundColor( black ); subalbumNameVal->setPaletteBackgroundColor( lightBlue ); subalbumNameVal->setFrameShape ( QFrame::NoFrame ); subalbumDescription->setFont( labelFont ); subalbumDescription->setPaletteForegroundColor( white ); subalbumDescription->setPaletteBackgroundColor( darkBlue ); subalbumDescriptionVal->setFont( labelFont ); subalbumDescriptionVal->setPaletteForegroundColor( black ); subalbumDescriptionVal->setPaletteBackgroundColor( lightBlue ); subalbumDescriptionVal->setFrameShape ( QFrame::NoFrame ); //-------------------------------------------------------------- //place widgets in grids //------------------------ //album annotations QGridLayout* albumImageGrid = new QGridLayout( albumImageFrame, 1, 1 ); albumImageGrid->addWidget( albumImage, 0, 0 ); albumImageGrid->setRowSpacing( 0, REP_IMAGE_HEIGHT ); albumAnnotationGrid = new QGridLayout( albumAnnotationFrame, 3, 3); albumAnnotationGrid->setMargin(WIDGET_SPACING); albumAnnotationGrid->setSpacing(WIDGET_SPACING); albumAnnotationGrid->addMultiCellWidget( albumImageFrame, 0, 2, 0, 0 ); albumAnnotationGrid->addWidget ( albumName, 0, 1, Qt::AlignLeft); albumAnnotationGrid->addWidget ( albumDescription, 1, 1, Qt::AlignLeft); albumAnnotationGrid->addWidget ( albumAuthor, 2, 1, Qt::AlignLeft); albumAnnotationGrid->setColStretch(2, 1); albumAnnotationGrid->addWidget( albumNameVal, 0, 2); albumAnnotationGrid->addWidget( albumDescriptionVal, 1, 2); albumAnnotationGrid->addWidget( albumAuthorVal, 2, 2); //------------------------ //subalbum annotations QGridLayout* subalbumImageGrid = new QGridLayout( subalbumImageFrame, 1, 1 ); subalbumImageGrid->addWidget( subalbumImage, 0, 0 ); subalbumImageGrid->setRowSpacing( 0, REP_IMAGE_HEIGHT ); subalbumAnnotationGrid = new QGridLayout( subalbumAnnotationFrame, 5, 5); subalbumAnnotationGrid->setMargin(WIDGET_SPACING); subalbumAnnotationGrid->setSpacing(WIDGET_SPACING); subalbumAnnotationGrid->addMultiCellWidget( subalbumImageFrame, 0, 2, 0, 0); subalbumAnnotationGrid->setRowStretch(2, 1); subalbumAnnotationGrid->addWidget ( subalbumName, 0, 1, Qt::AlignLeft); subalbumAnnotationGrid->addWidget ( subalbumDescription, 1, 1, Qt::AlignLeft); subalbumAnnotationGrid->setColStretch(2, 1); subalbumAnnotationGrid->addWidget( subalbumNameVal, 0, 2); subalbumAnnotationGrid->addWidget( subalbumDescriptionVal, 1, 2); //------------------------ //place menu and album and subalbum annotations into main grid mainGrid = new QGridLayout( mainFrame, 1, 2); mainGrid->setMargin(WIDGET_SPACING); mainGrid->setSpacing(WIDGET_SPACING); mainGrid->addWidget ( albumAnnotationFrame, 0, 0); mainGrid->setColStretch(0, 1); mainGrid->addWidget ( subalbumAnnotationFrame, 0, 1); mainGrid->setColStretch(1, 1); QVBoxLayout* vb = new QVBoxLayout(this); this->layout()->setMenuBar( menu ); vb->addWidget(mainFrame); //----------------------------------------------- } //============================================== void TitleWidget::useAnimation(bool val) { //enable animation if(val) { albumImage->setAnimationMethods( SLIDE_IN_LEFT, SLIDE_OUT_LEFT, FADE_TRANSITION, FADE_TRANSITION); subalbumImage->setAnimationMethods( SLIDE_IN_LEFT, SLIDE_OUT_LEFT, FADE_TRANSITION, FADE_TRANSITION); } //disable animation else { albumImage->setAnimationMethods(); subalbumImage->setAnimationMethods(); } } //============================================== TitleWidget::~TitleWidget() { //delete old album delete albm; albm = NULL; } //============================================== void TitleWidget::storeAnnotations() { //only sync backend album/collection data when detectModifications is enabled if( !detectModifications ) return; //set album annotations albm->setName ( albumNameVal->text() ); albm->setDescription ( albumDescriptionVal->text() ); albm->setAuthor ( albumAuthorVal->text() ); //get subalbum pointer Subalbum* collection = window->getLayout()->getSubalbums()->getSelectedSubalbum(); if(collection != NULL ) { //store old subalbum name QString oldName = collection->getName(); //set name and description collection->setName( subalbumNameVal->text() ); collection->setDescription( subalbumDescriptionVal->text() ); //if subalbum name has changed emit signal if(oldName.compare( collection->getName() ) != 0) emit subalbumNameChanged(); } } //============================================== void TitleWidget::updateAlbumAnnotations() { //disable modification detection while updating fields detectModifications = false; //set album annotations //if no image then remove pixmap if( albm->getRepresentativeImage(LARGE) != NULL) albumImage->setPixmap( *albm->getRepresentativeImage(SMALL) ); else albumImage->removePixmap(); albumNameVal->setText( albm->getName() ); albumNameVal->setCursorPosition(0); albumDescriptionVal->setText( albm->getDescription() ); albumDescriptionVal->setCursorPosition(0); albumAuthorVal->setText( albm->getAuthor() ); albumAuthorVal->setCursorPosition(0); //enable modification detection detectModifications = true; } //============================================== void TitleWidget::refreshCollectionAnnotations(Subalbum* collection) { //disable modification detection while updating fields detectModifications = false; //get subalbum pointer if( collection == NULL ) { subalbumAnnotationFrame->hide(); subalbumImage->removePixmap(true); } else { subalbumAnnotationFrame->show(); //if no image then remove pixmap if( collection->getRepresentativeImage(LARGE) != NULL) subalbumImage->setPixmap( *collection->getRepresentativeImage(SMALL) ); else subalbumImage->removePixmap(); subalbumNameVal->setText( collection->getName() ); subalbumNameVal->setCursorPosition(0); subalbumDescriptionVal->setText( collection->getDescription() ); subalbumDescriptionVal->setCursorPosition(0); } //enable modification detection detectModifications = true; } //============================================== void TitleWidget::setAlbum( Album* val) { //delete old album delete albm; albm = val; } //============================================== Album* TitleWidget::getAlbum() { return albm; } //============================================== void TitleWidget::setAlbumImage() { //--------------------------------------------------------- //determine if a subalbum is even selected SubalbumWidget* sw = window->getLayout()->getSubalbum(); if(sw == NULL) return; //--------------------------------------------------------- //set image to photo setAlbumImage( sw->getSelectedPhoto() ); } //============================================== void TitleWidget::setAlbumImage(Photo* selectedPhoto) { if(selectedPhoto == NULL) return; //--------------------------------------------------------- //set album image qApp->setOverrideCursor( QCursor(Qt::WaitCursor)); albm->setRepresentativeImages( selectedPhoto->getImageFilename() ); //--------------------------------------------------------- //update onscreen image albumImage->setPixmap( *albm->getRepresentativeImage(SMALL) ); qApp->restoreOverrideCursor(); //--------------------------------------------------------- } //============================================== void TitleWidget::unsetAlbumImage() { albm->setRepresentativeImages( QString::null ); } //============================================== void TitleWidget::setSubalbumImage() { //--------------------------------------------------------- //determine if a subalbum is even selected SubalbumWidget* sw = window->getLayout()->getSubalbum(); if(sw == NULL) return; //--------------------------------------------------------- //set collection image to selected photo setSubalbumImage( sw->getSelectedPhoto() ); } //============================================== void TitleWidget::setSubalbumImage(Photo* selectedPhoto) { if(selectedPhoto == NULL) return; //--------------------------------------------------------- //set subalbum image qApp->setOverrideCursor( QCursor(Qt::WaitCursor)); SubalbumWidget* sw = window->getLayout()->getSubalbum(); sw->getSubalbum()->setRepresentativeImage( selectedPhoto->getThumbnailFilename() ); //--------------------------------------------------------- //update onscreen image subalbumImage->setPixmap( *sw->getSubalbum()->getRepresentativeImage(SMALL) ); SubalbumPreviewWidget* item = (SubalbumPreviewWidget*)window->getLayout()->getSubalbums()->getCurrentSelection(); item->setPixmap( *sw->getSubalbum()->getRepresentativeImage(MEDIUM), false ); qApp->restoreOverrideCursor(); //--------------------------------------------------------- } //============================================== void TitleWidget::unsetSubalbumImage() { SubalbumPreviewWidget* item = (SubalbumPreviewWidget*)window->getLayout()->getSubalbums()->getCurrentSelection(); if(item != NULL && ((SubalbumPreviewWidget*)item)->getSubalbum() != NULL) { Subalbum* subalbm = ((SubalbumPreviewWidget*)item)->getSubalbum(); subalbm->setRepresentativeImage( QString::null ); item->setPixmap( *subalbm->getRepresentativeImage(MEDIUM), false ); } } //============================================== void TitleWidget::refresh() { //first refresh the subalbums listing. this is //IMPERATIVE! Right now current subalbum selection contains a pointer //to dead memory where previous subalbum was deleted. //AKA not refreshing the subalbums iconview first will cause a crash! window->refresh(); //update the album annotations (name, desc, author, images) //subalbum auto updated since window refresh auto selected first item updateAlbumAnnotations(); } //============================================== void TitleWidget::newAlbum() { //if modifications exist and user wants to receive destructive action warnings, //ask if they are sure before creating a new album if( albm->albumModified() && window->getConfig()->getBool( "alerts", "showDestructiveAlerts" ) ) { QuestionDialog sure( tr("New album?"), tr("Warning, unsaved modifications to the current album exist. Creating a new album will result in lost work. Are you sure you want to do this?"), "alertIcons/warning.png", this ); if(!sure.exec()) return; } //delete old album delete albm; //create new one albm = new Album( createTmpDir() ); if(albm->getTmpDir().isNull() ) { AlertDialog alert( tmpDirErrorMessage, tmpDirErrorDesc + window->getConfig()->getString( "loadSave", "tempImageDirectory" ), "alertIcons/warning.png", this ); alert.exec(); quitApplication(); } //refresh screen albumImage->removePixmap(); subalbumImage->removePixmap(true); //disable revert menu option since there is no loaded album file->setItemEnabled( REVERT_TO_SAVED_ALBUM, false ); //reset editing interface since old pointers are invalid window->getLayout()->getSubalbum()->setSubalbum( NULL ); refresh(); albm->setModified(false); } //============================================== void TitleWidget::loadAlbum() { ///make sure we want to proceed if(!proceedWithLoad()) return; QString path = window->getConfig()->getString( "loadSave", "loadSaveDir" ); QDir testPath(path); if(!testPath.exists()) { window->getConfig()->resetSetting( "loadSave", "loadSaveDir" ); path = window->getConfig()->getString( "loadSave", "loadSaveDir" ); } QString albumXML = QFileDialog::getOpenFileName( path, tr("XML Files (*.xml)"), this, "open file dialog", tr("Choose an album to load") ); //if null bail if(albumXML.isNull()) return; //attempt to load album loadAlbum( albumXML ); } //============================================== void TitleWidget::loadRecentAlbum(int index) { ///make sure we want to proceed if(!proceedWithLoad()) return; //load album QString recentName, recentLocation, recentPhotoCount; recentAlbums.getEntry( index, recentName, recentLocation, recentPhotoCount ); loadAlbum( QDir::convertSeparators( recentLocation + "/Album.xml") ); } //============================================== bool TitleWidget::proceedWithLoad() { //if modifications exist and user wants to receive destructive action warnings, //ask if they are sure before creating a new album if( albm->albumModified() && window->getConfig()->getBool( "alerts", "showDestructiveAlerts" ) ) { QuestionDialog sure( tr("Load album?"), tr("Warning, unsaved modifications to the current album exist. Loading a new album will result in lost work. Are you sure you want to do this?"), "alertIcons/warning.png", this ); if(!sure.exec()) return false; } return true; } //============================================== void TitleWidget::revertToSaved() { //if there are no changed then immediately return since reverting will have no effect //TODO: disable "revert" menu entry when no modifications exist if( !albm->albumModified() ) return; ///make sure we want to proceed //if modifications exist and user wants to receive destructive action warnings, //ask if they are sure before creating a new album if( window->getConfig()->getBool( "alerts", "showDestructiveAlerts" ) ) { QuestionDialog sure( tr("Revert to Saved?"), tr("Warning, unsaved modifications to the current album exist. These changes will be lost if you revert to the album's last saved form. Are you sure you want to do this?"), "alertIcons/warning.png", this ); if(!sure.exec()) return; } //reload album loadAlbum( QDir::convertSeparators( albm->getSaveLocation() + "/Album.xml") ); } //============================================== void TitleWidget::exportSmallWebGallery() { //if the Album's theme is not currently available alert user and bail if(!SaveDialog::themeAvailable( getAlbum()->getTheme() )) { AlertDialog alert( tr("Previous theme not available!"), QString(tr("Theme previously used to save this album not available on this machine. Before exporting the %1 theme must be installed, or the album must be resaved using a different theme.")).arg( getAlbum()->getTheme() ), "alertIcons/warning.png", this ); alert.exec(); return; } //setup dialog title and default path QString dialogTitle = tr( "Export Small Web Gallery" ); //new directory name in which all images will be contained QString newDir; if(getAlbum()->prevSave()) { newDir = QDir( getAlbum()->getSaveLocation() ).dirName() + "_WEB"; } else { newDir = getAlbum()->getName() + "_WEB"; } newDir = fixFilename( newDir ); //get filename from user QFileDialog* fd = new QFileDialog( this, "file dialog", TRUE ); fd->setCaption( tr("Export Location") ); fd->setMode( QFileDialog::DirectoryOnly ); fd->setDir( window->getConfig()->getString( "loadSave", "loadSaveDir" ) ); //user canceled operation if ( !fd->exec() == QDialog::Accepted ) { return; } //get export path QString exportPath = QDir::convertSeparators( fd->selectedFile() + "/" + newDir ); //check to make sure the album is not in this location, if so raise red flag and abort!! if( getAlbum()->prevSave() && getAlbum()->getSaveLocation().compare( exportPath ) == 0 ) { QString errorMessage = tr("Error! Cannot export to album location on disk!"); QString errorDesc = tr("Exporting small web galleries to the same location the album is stored will corrupt it and is not allowed. Try using the default location when exporting images, or chose a different directory."); AlertDialog alert( errorMessage, errorDesc, "alertIcons/warning.png", this ); alert.exec(); return; } //otherwise check if directory already exists, if so warn user and ask before proceeding QDir tmpDir; if(tmpDir.exists( exportPath ) ) { QString warningMessage = QString(tr("Warning! A directory named %1 already exists in %2. Continue with export?")).arg ( newDir ).arg( fd->selectedFile() ); QuestionDialog sure( tr("Directory Exists!"), warningMessage, "alertIcons/warning.png", this ); if(!sure.exec()) { return; } } //else create directory else { if( !tmpDir.mkdir( exportPath ) ) { AlertDialog alert( tr("Error creating directory!"), tr("Unable to create directory to export images to. Perhaps you are running out of disk space or you don't have sufficient privileges?"), "alertIcons/warning.png", this ); alert.exec(); return; } } //set busy flag and disable buttons setBusy(true); window->getLayout()->getSubalbums()->updateButtons(false); if(window->getLayout()->getSubalbum() != NULL) window->getLayout()->getSubalbum()->updateButtons(false); qApp->setOverrideCursor( QCursor(Qt::WaitCursor)); //setup progress bar int numPhotos = getAlbum()->getNumPhotos(); QString exportMessage = tr( "Exporting %1 photos to web gallery" ); window->getStatus()->showProgressBar( exportMessage.arg(numPhotos), numPhotos ); qApp->processEvents(); //export large images getAlbum()->exportCompressedWebAlbum(window->getStatus(), exportPath, exportMessage); //remove progress bar window->getStatus()->setStatus( tr("Exporting photos complete.") ); //nolonger busy setBusy(false); window->getLayout()->getSubalbums()->updateButtons(true); if(window->getLayout()->getSubalbum() != NULL) window->getLayout()->getSubalbum()->updateButtons(true); qApp->restoreOverrideCursor(); } //============================================== void TitleWidget::exportLargeImages() { //setup dialog title and default path QString dialogTitle = tr( "Export Large Images" ); //new directory name in which all images will be contained QString newDir; if(getAlbum()->prevSave()) { newDir = QDir( getAlbum()->getSaveLocation() ).dirName() + "_IMAGES"; } else { newDir = getAlbum()->getName() + "_IMAGES"; } newDir = fixFilename( newDir ); //get filename from user QFileDialog* fd = new QFileDialog( this, "file dialog", TRUE ); fd->setCaption( tr("Export Location") ); fd->setMode( QFileDialog::DirectoryOnly ); fd->setDir( window->getConfig()->getString( "loadSave", "loadSaveDir" ) ); //user canceled operation if ( !fd->exec() == QDialog::Accepted ) { return; } //get export path QString exportPath = QDir::convertSeparators( fd->selectedFile() + "/" + newDir ); //check to make sure the album is not in this location, if so raise red flag and abort!! if( getAlbum()->prevSave() && getAlbum()->getSaveLocation().compare( exportPath ) == 0 ) { QString errorMessage = tr("Error! Cannot export to album location on disk!"); QString errorDesc = tr("Exporting large images to the same location the album is stored will corrupt it and is not allowed. Try using the default location when exporting images, or chose a different directory."); AlertDialog alert( errorMessage, errorDesc, "alertIcons/warning.png", this ); alert.exec(); return; } //otherwise check if directory already exists, if so warn user and ask before proceeding QDir tmpDir; if(tmpDir.exists( exportPath ) ) { QString warningMessage = QString(tr("Warning! A directory named %1 already exists in %2. Continue with export?")).arg ( newDir ).arg( fd->selectedFile() ); QuestionDialog sure( tr("Directory Exists!"), warningMessage, "alertIcons/warning.png", this ); if(!sure.exec()) { return; } } //else create directory else { if( !tmpDir.mkdir( exportPath ) ) { AlertDialog alert( tr("Error creating directory!"), tr("Unable to create directory to export images to.Perhaps you are running out of disk space or you don't have sufficient privileges?"), "alertIcons/warning.png", this ); alert.exec(); return; } } //set busy flag and disable buttons setBusy(true); window->getLayout()->getSubalbums()->updateButtons(false); if(window->getLayout()->getSubalbum() != NULL) window->getLayout()->getSubalbum()->updateButtons(false); qApp->setOverrideCursor( QCursor(Qt::WaitCursor)); //setup progress bar int numPhotos = getAlbum()->getNumPhotos(); QString exportMessage = tr( "Exporting %1 photos" ); window->getStatus()->showProgressBar( exportMessage.arg(numPhotos), numPhotos ); qApp->processEvents(); //export large images getAlbum()->exportLargeImages(window->getStatus(), exportPath, exportMessage); //remove progress bar window->getStatus()->setStatus( tr("Exporting photos complete.") ); //nolonger busy setBusy(false); window->getLayout()->getSubalbums()->updateButtons(true); if(window->getLayout()->getSubalbum() != NULL) window->getLayout()->getSubalbum()->updateButtons(true); qApp->restoreOverrideCursor(); } //============================================== void TitleWidget::loadAlbum(QString albumXML) { //disable user input window->getStatus()->grabInput(); //enable busy cursor, set busy flag, and deactivate buttons qApp->setOverrideCursor( QCursor(Qt::WaitCursor)); setBusy(true); window->getLayout()->getSubalbums()->updateButtons(false); if(window->getLayout()->getSubalbum() != NULL) window->getLayout()->getSubalbum()->updateButtons(false); //store load/save location QDir lastDir = QDir( QFileInfo(albumXML).dirPath() ); lastDir.cdUp(); window->getConfig()->setString( "loadSave", "loadSaveDir", lastDir.path() ); //create a new album (with no subalbums, hense false) delete albm; albm = new Album( createTmpDir( QFileInfo(albumXML).dirPath() ), false ); if(albm->getTmpDir().isNull() ) { AlertDialog alert( tmpDirErrorMessage, tmpDirErrorDesc + window->getConfig()->getString( "loadSave", "tempImageDirectory" ), "alertIcons/warning.png", this ); alert.exec(); quitApplication(); } //attempt to load xml file int errorCode = albm->importFromDisk(window->getStatus(), albumXML, window->getConfig()->getBool( "loadSave", "disableCheckPhotoMods" ) ); //if no subalbums in album then hide subalbum annotations if(albm->getFirstSubalbum() == NULL) { subalbumAnnotationFrame->hide(); subalbumImage->removePixmap(true); } //reset editing interface since old pointers are invalid window->getLayout()->getSubalbum()->setSubalbum( NULL ); refresh(); //set album as not modified albm->setModified(false); //update recent albums listing recentAlbums.insertEntry( albm->getName(), albm->getSaveLocation(), QString("%1").arg(albm->getNumPhotos()), false ); refreshOpenRecentMenu(); //nolonger busy qApp->restoreOverrideCursor(); setBusy(false); window->getLayout()->getSubalbums()->updateButtons(true); if(window->getLayout()->getSubalbum() != NULL) window->getLayout()->getSubalbum()->updateButtons(true); //enable user input window->getStatus()->releaseInput(); //load successful if(errorCode == ALBUM_LOADED) { //enable "revert" menu option file->setItemEnabled( REVERT_TO_SAVED_ALBUM, true ); //update presentation command based on if there are photos in this album updateMenus(); } //else display appropriate error message else { QString errorMessage, errorDescription; if(errorCode == ALBUM_READ_ERROR) { errorMessage = tr("Unable to open file!"); errorDescription = tr("An error was encountered attempting to load the XML file. Perhaps you do not have read access?"); } else if(errorCode == ALBUM_XML_ERROR) { errorMessage = tr("Unable to construct DOM!"); errorDescription = tr("The XML file you selected is not valid XML."); } else { errorMessage = tr("Unknown loading error!"); errorDescription = tr("An unknown error was encountered loading the specified file."); } AlertDialog alert( errorMessage, errorDescription, "alertIcons/warning.png", this ); alert.exec(); } } //============================================== void TitleWidget::saveAlbum() { //if album not previously saved then //run saveas dialog if(!getAlbum()->prevSave()) { saveAsAlbum(); return; } //if previously used theme not available for use again alert user, //then run saveas dialog if(!SaveDialog::themeAvailable( getAlbum()->getTheme() )) { AlertDialog alert( tr("Previous theme not available!"), tr("Theme previously used to save this album not available on this machine. Click ok to open the save-as dialog to save an alternative theme."), "alertIcons/warning.png", this ); alert.exec(); saveAsAlbum(); return; } //set busy flag and disable buttons setBusy(true); window->getLayout()->getSubalbums()->updateButtons(false); if(window->getLayout()->getSubalbum() != NULL) window->getLayout()->getSubalbum()->updateButtons(false); qApp->setOverrideCursor( QCursor(Qt::WaitCursor)); getAlbum()->exportToDisk(window->getStatus()); window->getConfig()->setString( "loadSave", "lastUsedTheme", getAlbum()->getTheme() ); //update recent albums listing recentAlbums.insertEntry( albm->getName(), albm->getSaveLocation(), QString("%1").arg(albm->getNumPhotos()), false ); refreshOpenRecentMenu(); //enable revert command since saved album now exists file->setItemEnabled( REVERT_TO_SAVED_ALBUM, true ); //nolonger busy setBusy(false); window->getLayout()->getSubalbums()->updateButtons(true); if(window->getLayout()->getSubalbum() != NULL) window->getLayout()->getSubalbum()->updateButtons(true); qApp->restoreOverrideCursor(); } //============================================== void TitleWidget::saveAsAlbum() { //setup dialog title and default path QString dialogTitle = tr( "Save As" ); QString defaultPath; if(getAlbum()->prevSave()) defaultPath = getAlbum()->getSaveLocation(); else { defaultPath = getAlbum()->getName(); defaultPath.replace( QChar(' '), "_" ); defaultPath.replace( "<", "" ); defaultPath.replace( ">", "" ); defaultPath.replace( "&", "and" ); defaultPath.replace( "\"", "" ); defaultPath.replace( "\'", "" ); defaultPath.replace( "?", "" ); defaultPath = QDir::convertSeparators ( window->getConfig()->getString( "loadSave", "loadSaveDir" ) + "/" + defaultPath ); } //get directory name in which album directory will be placed in QString theme, savePath; //if abum saved before then auto select last used theme if(getAlbum()->getTheme().compare("-1") != 0) theme = getAlbum()->getTheme(); else { if(window->getConfig()->getString( "loadSave", "defaultTheme" ).compare( "Last Used" ) == 0) theme = window->getConfig()->getString( "loadSave", "lastUsedTheme" ); else theme = window->getConfig()->getString( "loadSave", "defaultTheme" ); } if( !SaveDialog::selectThemeAndPath( dialogTitle, defaultPath, theme, savePath ) ) return; //check if directory already exists, if not attempt to create it QDir d(savePath); if(!d.exists()) { if(!d.mkdir(savePath)) { AlertDialog alert( tr("Error creating directory!"), tr("Unable to create directory to save album in. Perhaps you are running out of disk space or you don't have sufficient privileges?"), "alertIcons/warning.png", this ); alert.exec(); return; } } else { if(!d.isReadable()) { AlertDialog alert( tr("Destination directory not readable!"), tr("The destination directory is not readable. Perhaps you don't have sufficient privileges?"), "alertIcons/warning.png", this ); alert.exec(); return; } } //store this load/Save location QDir lastDir = QDir( savePath ); lastDir.cdUp(); window->getConfig()->setString( "loadSave", "loadSaveDir", lastDir.path() ); window->getConfig()->setString( "loadSave", "lastUsedTheme", theme ); //set busy flag and disable buttons setBusy(true); window->getLayout()->getSubalbums()->updateButtons(false); if(window->getLayout()->getSubalbum() != NULL) window->getLayout()->getSubalbum()->updateButtons(false); qApp->setOverrideCursor( QCursor(Qt::WaitCursor)); //save getAlbum()->exportToDisk(window->getStatus(), savePath, theme); window->getConfig()->setString( "misc", "defaultAuthor", albumAuthorVal->text() ); //update recent albums listing recentAlbums.insertEntry( albm->getName(), albm->getSaveLocation(), QString("%1").arg(albm->getNumPhotos()), false ); refreshOpenRecentMenu(); //enable revert command since saved album now exists file->setItemEnabled( REVERT_TO_SAVED_ALBUM, true ); //nolonger busy setBusy(false); window->getLayout()->getSubalbums()->updateButtons(true); if(window->getLayout()->getSubalbum() != NULL) window->getLayout()->getSubalbum()->updateButtons(true); qApp->restoreOverrideCursor(); } //============================================== void TitleWidget::albumStatistics() { //create window and center if not already present if(albumStats == NULL) { albumStats = new AlbumStatistics(albm); connect( albumStats, SIGNAL(closed()), this, SLOT(albumStatisticsClosed())); albumStats->show(); centerWindow(albumStats); } albumStats->raise(); albumStats->setActiveWindow(); } //============================================== void TitleWidget::removeSelectedPhotoDesc() { //if no subalbum or photos selected ignore command if(window->getLayout()->getSubalbum() == NULL || !window->getLayout()->getSubalbum()->anyPhotosSelected()) return; //ask user if they are sure they want to remove selected photo descriptions if( window->getConfig()->getBool( "alerts", "showDestructiveAlerts" ) ) { QuestionDialog sure( tr("Remove Selected Photo Descriptions?"), tr("This action cannot be undone. Are you sure you want to proceed?"), "alertIcons/warning.png", this ); if(!sure.exec()) return; } //proceed with stripping of photo descriptions window->getLayout()->getSubalbum()->stripDescriptionsFromSelectedPhotos(); } //============================================== void TitleWidget::revertPhotos() { window->getLayout()->revertPhotos(); } //============================================== void TitleWidget::settings() { //create window and center if not already present if(settingsWindow == NULL) { settingsWindow = new ConfigurationWidget( window->getConfig() ); connect( settingsWindow, SIGNAL(closed()), this, SLOT(settingsWindowClosed())); settingsWindow->show(); centerWindow(settingsWindow); } settingsWindow->raise(); settingsWindow->setActiveWindow(); } //============================================== void TitleWidget::aboutProgram(int mode) { //create window and center if not already present if(about == NULL) { about = new About(mode); connect( about, SIGNAL(closed()), this, SLOT(aboutClosed())); about->show(); centerWindow(about); } about->raise(); about->setActiveWindow(); } //============================================== void TitleWidget::help() { //create window and center if not already present if(helpWindow == NULL) { helpWindow = new HelpWindow(0); connect( helpWindow, SIGNAL(closed()), this, SLOT(helpClosed())); helpWindow->show(); centerWindow(helpWindow); } helpWindow->raise(); helpWindow->setActiveWindow(); } //============================================== void TitleWidget::albumStatisticsClosed() { delete albumStats; albumStats = NULL; } //============================================== void TitleWidget::aboutClosed() { delete about; about = NULL; } //============================================== void TitleWidget::helpClosed() { delete helpWindow; helpWindow = NULL; } //============================================== void TitleWidget::settingsWindowClosed() { delete settingsWindow; settingsWindow = NULL; } //============================================== bool TitleWidget::getBusy() { return busy; } //============================================== void TitleWidget::setBusy(bool val) { busy = val; //disable/enable file operations if(busy) { file->setItemEnabled(NEW_ALBUM, false); file->setItemEnabled(OPEN_ALBUM, false); file->setItemEnabled(SAVE_ALBUM, false); file->setItemEnabled(SAVEAS_ALBUM, false); } else { file->setItemEnabled(NEW_ALBUM, true); file->setItemEnabled(OPEN_ALBUM, true); file->setItemEnabled(SAVE_ALBUM, true); file->setItemEnabled(SAVEAS_ALBUM, true); } } //============================================== void TitleWidget::quitApplication() { window->close(); } //============================================== void TitleWidget::dragEnterEvent( QDragEnterEvent* e) { e->accept(true); } //============================================== void TitleWidget::dropEvent( QDropEvent* e ) { //force redraw so we don't see missing unpainted //region while we resize an image which takes a while. repaint(false); qApp->processEvents(); //if the source is not the origanize icon view then ignore the event if(e->source() == NULL || e->source()->parentWidget() != window->getLayout()->getSubalbum()->getPhotos()) return; if( e->pos().x() < (width() / 2) ) setAlbumImage(); else setSubalbumImage(); } //============================================== QString TitleWidget::createTmpDir(QString albumPath) { //if album path provided attempt to create tmp directory in there to //minimize cost of doing moves when saving album changes. //the other reasoning is that user will have hopefully provided enough //space for saving large files on directory where they previously saved, //so this minmizes the chance of running out of disk hopefully if(!albumPath.isNull()) { QDir rootDir( albumPath ); if(rootDir.exists( "tmp" ) || rootDir.mkdir( "tmp" )) return QDir::convertSeparators( albumPath + "/tmp" ); } //otherwise create unique tmp dir under scratch dir user specified in preferences QDate date = QDate::currentDate(); QTime time = QTime::currentTime(); QString baseDir = window->getConfig()->getString( "loadSave", "tempImageDirectory" ); QDir testPath(baseDir); if(!testPath.exists()) { window->getConfig()->resetSetting( "loadSave", "tempImageDirectory" ); baseDir = window->getConfig()->getString( "loadSave", "tempImageDirectory" ); } QString tmpDir = QString("albumshaper_tmp%1%2%3%4%5%6%7").arg( date.year() ).arg( date.month() ).arg ( date.day() ).arg( time.hour() ).arg( time.minute() ).arg( time.second() ).arg( time.msec() ); QDir rootDir( baseDir ); if(rootDir.exists() && (rootDir.exists( tmpDir ) || rootDir.mkdir( tmpDir) ) ) { /* AlertDialog alert( "tmpDir:", QDir::convertSeparators( QString("(" + baseDir + "/" + tmpDir ) ), "alertIcons/warning.png", this ); alert.exec(); */ return QDir::convertSeparators( baseDir + "/" + tmpDir ); } else { // cout << "ERROR!\n"; return QString::null; } } //============================================== //PLATFORM_SPECIFIC_CODE #if defined(Q_OS_MACX) void TitleWidget::windowStateChanged(bool state) { //disable "Minimize" menu entry if window is minimized windowMenu->setItemEnabled(WINDOW_MINIMIZE, state); } #else void TitleWidget::windowStateChanged(bool) { //Do nothing } #endif //============================================== void TitleWidget::clearOpenRecentMenu() { //clear recent list recentAlbums.clearList(); //refresh menu refreshOpenRecentMenu(); } //============================================== void TitleWidget::populateOpenRecentMenu() { int maxItems = recentAlbums.getMaxItems(); numRecentMenuItems = maxItems + 2; //+2 for seperator and clear entry recentMenuItems = new int[numRecentMenuItems]; customRecentMenuItems = new RecentAlbumMenuItem*[maxItems]; //insert recent albums into menu int i; for(i = 0; iinsertItem( "uninitialized recent album", this, SLOT(loadRecentAlbum(int)) ); //---------------------------------------------- //Under other operating systems (Windows, Linux, FreeBSD) use custom recent album menu item //such that album image is larger and more detail is provided #else customRecentMenuItems[i] = new RecentAlbumMenuItem(key); recentMenuItems[i] = openRecent->insertItem( customRecentMenuItems[i] ); openRecent->connectItem( recentMenuItems[i], this, SLOT(loadRecentAlbum(int)) ); #endif //---------------------------------------------- //Set accelerator key sequence if valid if( key != Key_unknown ) { openRecent->setAccel( CTRL+key, recentMenuItems[i] ); openRecent->setItemParameter( recentMenuItems[i], i ); } //hide + disable entry openRecent->setItemVisible( recentMenuItems[i], false ); openRecent->setItemEnabled( recentMenuItems[i], false ); //---------------------------------------------- } //insert separator and "clear menu" entry. recentMenuItems[numRecentMenuItems-2] = openRecent->insertSeparator(); recentMenuItems[numRecentMenuItems-1] = openRecent->insertItem( tr("Clear Menu"), this, SLOT(clearOpenRecentMenu()) ); //hide separtor, disable clear entry openRecent->setItemVisible( recentMenuItems[numRecentMenuItems-2], false ); openRecent->setItemEnabled( recentMenuItems[numRecentMenuItems-1], false ); } //============================================== void TitleWidget::refreshOpenRecentMenu() { //update text, visibility, and enabled bit for all items in list int i; #ifndef Q_OS_MACX int maxWidth=0; #endif for(i=0; ichangeItem( recentMenuItems[i], QIconSet( scaledAlbumImagePixmap ), QString("%1%2").arg(recentName).arg(recentPhotoCount) ); } //otherwise simply display the album name and number of phots (if available) else { //using just text openRecent->changeItem( recentMenuItems[i], QIconSet(NULL), QString("%1%2").arg(recentName).arg(recentPhotoCount) ); } //---------------------------------------------- //Under other operating systems (Windows, Linux, FreeBSD) use custom recent album menu item //such that album image is larger and more detail is provided #else customRecentMenuItems[i]->changeItem( recentName, recentLocation, recentPhotoCount ); maxWidth = QMAX( maxWidth, customRecentMenuItems[i]->sizeHint().width() ); #endif //---------------------------------------------- openRecent->setItemEnabled( recentMenuItems[i], true ); openRecent->setItemVisible( recentMenuItems[i], true ); //if the Album.xml file is unavailable then disable menu entry if( !tempDir.exists( QDir::convertSeparators(recentLocation + "/Album.xml") ) ) openRecent->setItemEnabled( recentMenuItems[i], false ); } //---------------------------------------------- //hidden item - disable and hide else if( i >= recentAlbums.numEntries() && i < numRecentMenuItems-2 ) { openRecent->setItemEnabled( recentMenuItems[i], false ); openRecent->setItemVisible( recentMenuItems[i], false ); } //---------------------------------------------- //separtor - show if one or more items in list else if (i == numRecentMenuItems-2) { openRecent->setItemVisible( recentMenuItems[numRecentMenuItems-2], recentAlbums.numEntries() > 0 ); } //---------------------------------------------- //clear items - enable if items in list else if (i == numRecentMenuItems-1) { openRecent->setItemEnabled( recentMenuItems[numRecentMenuItems-1], recentAlbums.numEntries() > 0 ); } //---------------------------------------------- } //pass over custom menu items a second time letting them know the maximum item width #ifndef Q_OS_MACX for(i=0; isetMaxWidth( maxWidth ); } #endif } //============================================== RecentAlbums* TitleWidget::getRecentAlbums() { return &recentAlbums; } //============================================== void TitleWidget::updateMenus(bool anySelected, bool anyRevertable) { //no photos? disable begin presentation command // tools->setItemEnabled( BEGIN_PRESENTATION, albm->getNumPhotos() != 0 ); // tools->setItemEnabled( BEGIN_PRESENTATION_AT, albm->getNumPhotos() != 0 ); //none selected? disable removing photo descriptions photoMenu->setItemEnabled( REMOVE_DESCRIPTIONS, anySelected ); //none revertable? disable revert photos photoMenu->setItemEnabled( REVERT_PHOTOS, anyRevertable ); } //==============================================