diff options
Diffstat (limited to 'digikam/utilities/cameragui/cameraui.cpp')
-rw-r--r-- | digikam/utilities/cameragui/cameraui.cpp | 1734 |
1 files changed, 1734 insertions, 0 deletions
diff --git a/digikam/utilities/cameragui/cameraui.cpp b/digikam/utilities/cameragui/cameraui.cpp new file mode 100644 index 0000000..8b41f1f --- /dev/null +++ b/digikam/utilities/cameragui/cameraui.cpp @@ -0,0 +1,1734 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-09-16 + * Description : Camera interface dialog + * + * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu> + * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#define CAMERA_INFO_MENU_ID 255 + +// Qt includes. + +#include <qvgroupbox.h> +#include <qlayout.h> +#include <qpushbutton.h> +#include <qtoolbutton.h> +#include <qiconview.h> +#include <qvbox.h> +#include <qhbox.h> +#include <qpopupmenu.h> +#include <qsplitter.h> +#include <qpixmap.h> +#include <qcombobox.h> +#include <qtoolbox.h> +#include <qframe.h> +#include <qvbuttongroup.h> +#include <qradiobutton.h> +#include <qcheckbox.h> +#include <qlineedit.h> +#include <qtooltip.h> +#include <qtimer.h> +#include <qwhatsthis.h> +#include <qfile.h> +#include <qfileinfo.h> + +// KDE includes. + +#include <kfiledialog.h> +#include <kimageio.h> +#include <kaboutdata.h> +#include <kmessagebox.h> +#include <kprogress.h> +#include <kglobal.h> +#include <klocale.h> +#include <kconfig.h> +#include <kapplication.h> +#include <kiconloader.h> +#include <kpopupmenu.h> +#include <kstandarddirs.h> +#include <khelpmenu.h> +#include <kcalendarsystem.h> +#include <kurllabel.h> +#include <ksqueezedtextlabel.h> + +#if KDE_IS_VERSION(3,2,0) +#include <kinputdialog.h> +#else +#include <klineeditdlg.h> +#endif + +// LibKDcraw includes. + +#include <libkdcraw/version.h> +#include <libkdcraw/kdcraw.h> + +#if KDCRAW_VERSION < 0x000106 +#include <libkdcraw/dcrawbinary.h> +#endif + +// Local includes. + +#include "daboutdata.h" +#include "ddebug.h" +#include "thumbnailsize.h" +#include "kdatetimeedit.h" +#include "sidebar.h" +#include "scanlib.h" +#include "downloadsettingscontainer.h" +#include "imagepropertiessidebarcamgui.h" +#include "albummanager.h" +#include "albumsettings.h" +#include "album.h" +#include "albumselectdialog.h" +#include "renamecustomizer.h" +#include "animwidget.h" +#include "freespacewidget.h" +#include "camerafolderdialog.h" +#include "camerainfodialog.h" +#include "cameraiconview.h" +#include "cameraiconitem.h" +#include "cameracontroller.h" +#include "cameralist.h" +#include "cameratype.h" +#include "cameraui.h" +#include "cameraui.moc" + +namespace Digikam +{ + +class CameraUIPriv +{ +public: + + enum SettingsTab + { + RENAMEFILEPAGE=0, + AUTOALBUMPAGE, + ONFLYPAGE + }; + + enum DateFormatOptions + { + IsoDateFormat=0, + TextDateFormat, + LocalDateFormat + }; + + CameraUIPriv() + { + deleteAfter = false; + busy = false; + closed = false; + helpMenu = 0; + advBox = 0; + downloadMenu = 0; + deleteMenu = 0; + imageMenu = 0; + cancelBtn = 0; + splitter = 0; + rightSidebar = 0; + fixDateTimeCheck = 0; + autoRotateCheck = 0; + autoAlbumDateCheck = 0; + autoAlbumExtCheck = 0; + status = 0; + progress = 0; + controller = 0; + view = 0; + renameCustomizer = 0; + anim = 0; + dateTimeEdit = 0; + setPhotographerId = 0; + setCredits = 0; + losslessFormat = 0; + convertJpegCheck = 0; + formatLabel = 0; + folderDateLabel = 0; + folderDateFormat = 0; + freeSpaceWidget = 0; + } + + bool deleteAfter; + bool busy; + bool closed; + + QString cameraTitle; + + QStringList currentlyDeleting; + QStringList foldersToScan; + QStringList cameraFolderList; + + QPopupMenu *downloadMenu; + QPopupMenu *deleteMenu; + QPopupMenu *imageMenu; + + QToolButton *cancelBtn; + + QToolBox *advBox; + + QCheckBox *autoRotateCheck; + QCheckBox *autoAlbumDateCheck; + QCheckBox *autoAlbumExtCheck; + QCheckBox *fixDateTimeCheck; + QCheckBox *setPhotographerId; + QCheckBox *setCredits; + QCheckBox *convertJpegCheck; + + QLabel *formatLabel; + QLabel *folderDateLabel; + + QComboBox *losslessFormat; + QComboBox *folderDateFormat; + + QSplitter *splitter; + + QDateTime lastAccess; + + KProgress *progress; + + KSqueezedTextLabel *status; + + KURL lastDestURL; + + KHelpMenu *helpMenu; + + KDateTimeEdit *dateTimeEdit; + + CameraController *controller; + + CameraIconView *view; + + RenameCustomizer *renameCustomizer; + + AnimWidget *anim; + + ImagePropertiesSideBarCamGui *rightSidebar; + + FreeSpaceWidget *freeSpaceWidget; +}; + +CameraUI::CameraUI(QWidget* /*parent*/, const QString& cameraTitle, + const QString& model, const QString& port, + const QString& path, const QDateTime lastAccess) + : KDialogBase(Plain, cameraTitle, + Help|User1|User2|User3|Close, Close, + 0, // B.K.O # 116485: no parent for this modal dialog. + 0, false, true, + i18n("D&elete"), + i18n("&Download"), + i18n("&Images")) +{ + d = new CameraUIPriv; + d->lastAccess = lastAccess; + d->cameraTitle = cameraTitle; + setHelp("camerainterface.anchor", "digikam"); + + // ------------------------------------------------------------------------- + + QGridLayout* viewBoxLayout = new QGridLayout(plainPage(), 2, 7); + + QHBox* widget = new QHBox(plainPage()); + d->splitter = new QSplitter(widget); + d->view = new CameraIconView(this, d->splitter); + + QSizePolicy rightSzPolicy(QSizePolicy::Preferred, QSizePolicy::Expanding, 2, 1); + d->view->setSizePolicy(rightSzPolicy); + + d->rightSidebar = new ImagePropertiesSideBarCamGui(widget, "CameraGui Sidebar Right", d->splitter, + Sidebar::Right, true); + d->splitter->setFrameStyle( QFrame::NoFrame ); + d->splitter->setFrameShadow( QFrame::Plain ); + d->splitter->setFrameShape( QFrame::NoFrame ); + d->splitter->setOpaqueResize(false); + + // ------------------------------------------------------------------------- + + d->advBox = new QToolBox(d->rightSidebar); + d->renameCustomizer = new RenameCustomizer(d->advBox, d->cameraTitle); + d->view->setRenameCustomizer(d->renameCustomizer); + + QWhatsThis::add( d->advBox, i18n("<p>Set how digiKam will rename files as they are downloaded.")); + + d->advBox->insertItem(CameraUIPriv::RENAMEFILEPAGE, d->renameCustomizer, + SmallIconSet("fileimport"), i18n("File Renaming Options")); + + // -- Albums Auto-creation options ----------------------------------------- + + QWidget* albumBox = new QWidget(d->advBox); + QVBoxLayout* albumVlay = new QVBoxLayout(albumBox, marginHint(), spacingHint()); + d->autoAlbumExtCheck = new QCheckBox(i18n("Extension-based sub-albums"), albumBox); + d->autoAlbumDateCheck = new QCheckBox(i18n("Date-based sub-albums"), albumBox); + QHBox *hbox1 = new QHBox(albumBox); + d->folderDateLabel = new QLabel(i18n("Date format:"), hbox1); + d->folderDateFormat = new QComboBox(hbox1); + d->folderDateFormat->insertItem(i18n("ISO"), CameraUIPriv::IsoDateFormat); + d->folderDateFormat->insertItem(i18n("Full Text"), CameraUIPriv::TextDateFormat); + d->folderDateFormat->insertItem(i18n("Local Settings"), CameraUIPriv::LocalDateFormat); + albumVlay->addWidget(d->autoAlbumExtCheck); + albumVlay->addWidget(d->autoAlbumDateCheck); + albumVlay->addWidget(hbox1); + albumVlay->addStretch(); + + QWhatsThis::add( albumBox, i18n("<p>Set how digiKam creates albums automatically when downloading.")); + QWhatsThis::add( d->autoAlbumExtCheck, i18n("<p>Enable this option if you want to download your " + "pictures into automatically created file extension-based sub-albums of the destination " + "album. This way, you can separate JPEG and RAW files as they are downloaded from your camera.")); + QWhatsThis::add( d->autoAlbumDateCheck, i18n("<p>Enable this option if you want to " + "download your pictures into automatically created file date-based sub-albums " + "of the destination album.")); + QWhatsThis::add( d->folderDateFormat, i18n("<p>Select your preferred date format used to " + "create new albums. The options available are:<p>" + "<b>ISO</b>: the date format is in accordance with ISO 8601 " + "(YYYY-MM-DD). E.g.: <i>2006-08-24</i><p>" + "<b>Full Text</b>: the date format is in a user-readable string. " + "E.g.: <i>Thu Aug 24 2006</i><p>" + "<b>Local Settings</b>: the date format depending on KDE control panel settings.<p>")); + + d->advBox->insertItem(CameraUIPriv::AUTOALBUMPAGE, albumBox, SmallIconSet("folder_new"), + i18n("Auto-creation of Albums")); + + // -- On the Fly options --------------------------------------------------- + + QWidget* onFlyBox = new QWidget(d->advBox); + QVBoxLayout* onFlyVlay = new QVBoxLayout(onFlyBox, marginHint(), spacingHint()); + d->setPhotographerId = new QCheckBox(i18n("Set default photographer identity"), onFlyBox); + d->setCredits = new QCheckBox(i18n("Set default credit and copyright"), onFlyBox); + d->fixDateTimeCheck = new QCheckBox(i18n("Fix internal date && time"), onFlyBox); + d->dateTimeEdit = new KDateTimeEdit(onFlyBox, "datepicker"); + d->autoRotateCheck = new QCheckBox(i18n("Auto-rotate/flip image"), onFlyBox); + d->convertJpegCheck = new QCheckBox(i18n("Convert to lossless file format"), onFlyBox); + QHBox *hbox2 = new QHBox(onFlyBox); + d->formatLabel = new QLabel(i18n("New image format:"), hbox2); + d->losslessFormat = new QComboBox(hbox2); + d->losslessFormat->insertItem("PNG", 0); + onFlyVlay->addWidget(d->setPhotographerId); + onFlyVlay->addWidget(d->setCredits); + onFlyVlay->addWidget(d->fixDateTimeCheck); + onFlyVlay->addWidget(d->dateTimeEdit); + onFlyVlay->addWidget(d->autoRotateCheck); + onFlyVlay->addWidget(d->convertJpegCheck); + onFlyVlay->addWidget(hbox2); + onFlyVlay->addStretch(); + + QWhatsThis::add( onFlyBox, i18n("<p>Set here all options to fix/transform JPEG files automatically " + "as they are downloaded.")); + QWhatsThis::add( d->autoRotateCheck, i18n("<p>Enable this option if you want images automatically " + "rotated or flipped using EXIF information provided by the camera.")); + QWhatsThis::add( d->setPhotographerId, i18n("<p>Enable this option to store the default " + "photographer identity in the IPTC tags using digiKam's metadata settings.")); + QWhatsThis::add( d->setCredits, i18n("<p>Enable this option to store the default credit " + "and copyright information in the IPTC tags using digiKam's metadata settings.")); + QWhatsThis::add( d->fixDateTimeCheck, i18n("<p>Enable this option to set date and time metadata " + "tags to the right values if your camera does not set " + "these tags correctly when pictures are taken. The values will " + "be saved in the DateTimeDigitized and DateTimeCreated EXIF/IPTC fields.")); + QWhatsThis::add( d->convertJpegCheck, i18n("<p>Enable this option to automatically convert " + "all JPEG files to a lossless image format. <b>Note:</b> Image conversion can take a " + "while on a slow computer.")); + QWhatsThis::add( d->losslessFormat, i18n("<p>Select your preferred lossless image file format to " + "convert to. <b>Note:</b> All metadata will be preserved during the conversion.")); + + d->advBox->insertItem(CameraUIPriv::ONFLYPAGE, onFlyBox, SmallIconSet("run"), + i18n("On the Fly Operations (JPEG only)")); + + d->rightSidebar->appendTab(d->advBox, SmallIcon("configure"), i18n("Settings")); + d->rightSidebar->loadViewState(); + + // ------------------------------------------------------------------------- + + d->cancelBtn = new QToolButton(plainPage()); + d->cancelBtn->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ) ); + d->cancelBtn->setPixmap( SmallIcon( "cancel" ) ); + d->cancelBtn->setEnabled(false); + + d->status = new KSqueezedTextLabel(plainPage()); + d->progress = new KProgress(plainPage()); + d->progress->setMaximumHeight( fontMetrics().height()+4 ); + d->progress->hide(); + + QWidget *frame = new QWidget(plainPage()); + QHBoxLayout* layout = new QHBoxLayout(frame); + frame->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); + + KURLLabel *pixmapLogo = new KURLLabel( Digikam::webProjectUrl(), QString(), frame ); + pixmapLogo->setMargin(0); + pixmapLogo->setScaledContents( false ); + pixmapLogo->setSizePolicy(QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum)); + QToolTip::add(pixmapLogo, i18n("Visit digiKam project website")); + KGlobal::dirs()->addResourceType("logo-digikam", KGlobal::dirs()->kde_default("data") + "digikam/data"); + QString directory = KGlobal::dirs()->findResourceDir("logo-digikam", "logo-digikam.png"); + pixmapLogo->setPixmap( QPixmap( directory + "logo-digikam.png" ) ); + pixmapLogo->setFocusPolicy(QWidget::NoFocus); + + d->anim = new AnimWidget(frame, pixmapLogo->height()-2); + + layout->setMargin(0); + layout->setSpacing(0); + layout->addWidget( pixmapLogo ); + layout->addWidget( d->anim ); + + d->freeSpaceWidget = new FreeSpaceWidget(plainPage(), 100); + + viewBoxLayout->addMultiCellWidget(widget, 0, 0, 0, 7); + viewBoxLayout->addMultiCellWidget(d->cancelBtn, 2, 2, 0, 0); + viewBoxLayout->addMultiCellWidget(d->status, 2, 2, 2, 2); + viewBoxLayout->addMultiCellWidget(d->progress, 2, 2, 3, 3); + viewBoxLayout->addMultiCellWidget(d->freeSpaceWidget, 2, 2, 5, 5); + viewBoxLayout->addMultiCellWidget(frame, 2, 2, 7, 7); + viewBoxLayout->setRowSpacing(1, spacingHint()); + viewBoxLayout->setColSpacing(1, spacingHint()); + viewBoxLayout->setColSpacing(4, spacingHint()); + viewBoxLayout->setColSpacing(6, spacingHint()); + viewBoxLayout->setColStretch( 0, 0 ); + viewBoxLayout->setColStretch( 1, 0 ); + viewBoxLayout->setColStretch( 2, 3 ); + viewBoxLayout->setColStretch( 3, 1 ); + viewBoxLayout->setColStretch( 4, 0 ); + viewBoxLayout->setColStretch( 5, 0 ); + viewBoxLayout->setColStretch( 6, 0 ); + viewBoxLayout->setColStretch( 7, 0 ); + + // ------------------------------------------------------------------------- + + d->imageMenu = new QPopupMenu(this); + d->imageMenu->insertItem(i18n("Select &All"), d->view, SLOT(slotSelectAll()), CTRL+Key_A, 0); + d->imageMenu->insertItem(i18n("Select N&one"), d->view, SLOT(slotSelectNone()), CTRL+Key_U, 1); + d->imageMenu->insertItem(i18n("&Invert Selection"), d->view, SLOT(slotSelectInvert()), CTRL+Key_Asterisk, 2); + d->imageMenu->insertSeparator(); + d->imageMenu->insertItem(i18n("Select &New Items"), d->view, SLOT(slotSelectNew()), 0, 3); + d->imageMenu->insertSeparator(); + d->imageMenu->insertItem(i18n("Increase Thumbnail Size"), this, SLOT(slotIncreaseThumbSize()), CTRL+Key_Plus, 4); + d->imageMenu->insertItem(i18n("Decrease Thumbnail Size"), this, SLOT(slotDecreaseThumbSize()), CTRL+Key_Minus, 5); + d->imageMenu->insertSeparator(); + d->imageMenu->insertItem(i18n("Toggle Lock"), this, SLOT(slotToggleLock()), 0, 6); + actionButton(User3)->setPopup(d->imageMenu); + + // ------------------------------------------------------------------------- + + d->downloadMenu = new QPopupMenu(this); + d->downloadMenu->insertItem(i18n("Download Selected"), + this, SLOT(slotDownloadSelected()), 0, 0); + d->downloadMenu->insertItem(i18n("Download All"), + this, SLOT(slotDownloadAll()), 0, 1); + d->downloadMenu->insertSeparator(); + d->downloadMenu->insertItem(i18n("Download/Delete Selected"), + this, SLOT(slotDownloadAndDeleteSelected()), 0, 2); + d->downloadMenu->insertItem(i18n("Download/Delete All"), + this, SLOT(slotDownloadAndDeleteAll()), 0, 3); + d->downloadMenu->insertSeparator(); + d->downloadMenu->insertItem(i18n("Upload..."), + this, SLOT(slotUpload()), 0, 4); + d->downloadMenu->setItemEnabled(0, false); + d->downloadMenu->setItemEnabled(2, false); + actionButton(User2)->setPopup(d->downloadMenu); + + // ------------------------------------------------------------------------- + + d->deleteMenu = new QPopupMenu(this); + d->deleteMenu->insertItem(i18n("Delete Selected"), this, SLOT(slotDeleteSelected()), 0, 0); + d->deleteMenu->insertItem(i18n("Delete All"), this, SLOT(slotDeleteAll()), 0, 1); + d->deleteMenu->setItemEnabled(0, false); + actionButton(User1)->setPopup(d->deleteMenu); + + // ------------------------------------------------------------------------- + + QPushButton *helpButton = actionButton( Help ); + d->helpMenu = new KHelpMenu(this, kapp->aboutData(), false); + d->helpMenu->menu()->insertItem(SmallIcon("camera"), i18n("Camera Information"), + this, SLOT(slotInformations()), 0, CAMERA_INFO_MENU_ID, 0); + helpButton->setPopup( d->helpMenu->menu() ); + + // ------------------------------------------------------------------------- + + connect(d->autoAlbumDateCheck, SIGNAL(toggled(bool)), + d->folderDateFormat, SLOT(setEnabled(bool))); + + connect(d->autoAlbumDateCheck, SIGNAL(toggled(bool)), + d->folderDateLabel, SLOT(setEnabled(bool))); + + connect(d->convertJpegCheck, SIGNAL(toggled(bool)), + d->losslessFormat, SLOT(setEnabled(bool))); + + connect(d->convertJpegCheck, SIGNAL(toggled(bool)), + d->formatLabel, SLOT(setEnabled(bool))); + + connect(d->convertJpegCheck, SIGNAL(toggled(bool)), + d->view, SLOT(slotDownloadNameChanged())); + + connect(d->fixDateTimeCheck, SIGNAL(toggled(bool)), + d->dateTimeEdit, SLOT(setEnabled(bool))); + + connect(pixmapLogo, SIGNAL(leftClickedURL(const QString&)), + this, SLOT(slotProcessURL(const QString&))); + + // ------------------------------------------------------------------------- + + connect(d->view, SIGNAL(signalSelected(CameraIconViewItem*, bool)), + this, SLOT(slotItemsSelected(CameraIconViewItem*, bool))); + + connect(d->view, SIGNAL(signalFileView(CameraIconViewItem*)), + this, SLOT(slotFileView(CameraIconViewItem*))); + + connect(d->view, SIGNAL(signalUpload(const KURL::List&)), + this, SLOT(slotUploadItems(const KURL::List&))); + + connect(d->view, SIGNAL(signalDownload()), + this, SLOT(slotDownloadSelected())); + + connect(d->view, SIGNAL(signalDownloadAndDelete()), + this, SLOT(slotDownloadAndDeleteSelected())); + + connect(d->view, SIGNAL(signalDelete()), + this, SLOT(slotDeleteSelected())); + + connect(d->view, SIGNAL(signalToggleLock()), + this, SLOT(slotToggleLock())); + + connect(d->view, SIGNAL(signalNewSelection(bool)), + this, SLOT(slotNewSelection(bool))); + + // ------------------------------------------------------------------------- + + connect(d->rightSidebar, SIGNAL(signalFirstItem()), + this, SLOT(slotFirstItem())); + + connect(d->rightSidebar, SIGNAL(signalNextItem()), + this, SLOT(slotNextItem())); + + connect(d->rightSidebar, SIGNAL(signalPrevItem()), + this, SLOT(slotPrevItem())); + + connect(d->rightSidebar, SIGNAL(signalLastItem()), + this, SLOT(slotLastItem())); + + // ------------------------------------------------------------------------- + + connect(d->cancelBtn, SIGNAL(clicked()), + this, SLOT(slotCancelButton())); + + // -- Read settings & Check free space availability on album root path ----- + + readSettings(); + + // -- camera controller ---------------------------------------------------- + + d->controller = new CameraController(this, d->cameraTitle, model, port, path); + + connect(d->controller, SIGNAL(signalConnected(bool)), + this, SLOT(slotConnected(bool))); + + connect(d->controller, SIGNAL(signalInfoMsg(const QString&)), + d->status, SLOT(setText(const QString&))); + + connect(d->controller, SIGNAL(signalErrorMsg(const QString&)), + this, SLOT(slotErrorMsg(const QString&))); + + connect(d->controller, SIGNAL(signalCameraInformations(const QString&, const QString&, const QString&)), + this, SLOT(slotCameraInformations(const QString&, const QString&, const QString&))); + + connect(d->controller, SIGNAL(signalBusy(bool)), + this, SLOT(slotBusy(bool))); + + connect(d->controller, SIGNAL(signalFolderList(const QStringList&)), + this, SLOT(slotFolderList(const QStringList&))); + + connect(d->controller, SIGNAL(signalFileList(const GPItemInfoList&)), + this, SLOT(slotFileList(const GPItemInfoList&))); + + connect(d->controller, SIGNAL(signalThumbnail(const QString&, const QString&, const QImage&)), + this, SLOT(slotThumbnail(const QString&, const QString&, const QImage&))); + + connect(d->controller, SIGNAL(signalDownloaded(const QString&, const QString&, int)), + this, SLOT(slotDownloaded(const QString&, const QString&, int))); + + connect(d->controller, SIGNAL(signalSkipped(const QString&, const QString&)), + this, SLOT(slotSkipped(const QString&, const QString&))); + + connect(d->controller, SIGNAL(signalDeleted(const QString&, const QString&, bool)), + this, SLOT(slotDeleted(const QString&, const QString&, bool))); + + connect(d->controller, SIGNAL(signalLocked(const QString&, const QString&, bool)), + this, SLOT(slotLocked(const QString&, const QString&, bool))); + + connect(d->controller, SIGNAL(signalExifFromFile(const QString&, const QString&)), + this, SLOT(slotExifFromFile(const QString&, const QString&))); + + connect(d->controller, SIGNAL(signalExifData(const QByteArray&)), + this, SLOT(slotExifFromData(const QByteArray&))); + + connect(d->controller, SIGNAL(signalUploaded(const GPItemInfo&)), + this, SLOT(slotUploaded(const GPItemInfo&))); + + // ------------------------------------------------------------------------- + + d->view->setFocus(); + QTimer::singleShot(0, d->controller, SLOT(slotConnect())); +} + +CameraUI::~CameraUI() +{ + delete d->rightSidebar; + delete d->controller; + delete d; +} + +void CameraUI::readSettings() +{ + KConfig* config = kapp->config(); + config->setGroup("Camera Settings"); + d->advBox->setCurrentIndex(config->readNumEntry("Settings Tab", CameraUIPriv::RENAMEFILEPAGE)); + d->autoRotateCheck->setChecked(config->readBoolEntry("AutoRotate", true)); + d->autoAlbumDateCheck->setChecked(config->readBoolEntry("AutoAlbumDate", false)); + d->autoAlbumExtCheck->setChecked(config->readBoolEntry("AutoAlbumExt", false)); + d->fixDateTimeCheck->setChecked(config->readBoolEntry("FixDateTime", false)); + d->setPhotographerId->setChecked(config->readBoolEntry("SetPhotographerId", false)); + d->setCredits->setChecked(config->readBoolEntry("SetCredits", false)); + d->convertJpegCheck->setChecked(config->readBoolEntry("ConvertJpeg", false)); + d->losslessFormat->setCurrentItem(config->readNumEntry("LossLessFormat", 0)); // PNG by default + d->folderDateFormat->setCurrentItem(config->readNumEntry("FolderDateFormat", CameraUIPriv::IsoDateFormat)); + + d->view->setThumbnailSize(ThumbnailSize((ThumbnailSize::Size)config->readNumEntry("ThumbnailSize", + ThumbnailSize::Large))); + + if(config->hasKey("Splitter Sizes")) + d->splitter->setSizes(config->readIntListEntry("Splitter Sizes")); + + d->dateTimeEdit->setEnabled(d->fixDateTimeCheck->isChecked()); + d->losslessFormat->setEnabled(convertLosslessJpegFiles()); + d->formatLabel->setEnabled(convertLosslessJpegFiles()); + d->folderDateFormat->setEnabled(d->autoAlbumDateCheck->isChecked()); + d->folderDateLabel->setEnabled(d->autoAlbumDateCheck->isChecked()); + + resize(configDialogSize("Camera Settings")); +} + +void CameraUI::saveSettings() +{ + saveDialogSize("Camera Settings"); + + KConfig* config = kapp->config(); + config->setGroup("Camera Settings"); + config->writeEntry("Settings Tab", d->advBox->currentIndex()); + config->writeEntry("AutoRotate", d->autoRotateCheck->isChecked()); + config->writeEntry("AutoAlbumDate", d->autoAlbumDateCheck->isChecked()); + config->writeEntry("AutoAlbumExt", d->autoAlbumExtCheck->isChecked()); + config->writeEntry("FixDateTime", d->fixDateTimeCheck->isChecked()); + config->writeEntry("SetPhotographerId", d->setPhotographerId->isChecked()); + config->writeEntry("SetCredits", d->setCredits->isChecked()); + config->writeEntry("ConvertJpeg", convertLosslessJpegFiles()); + config->writeEntry("LossLessFormat", d->losslessFormat->currentItem()); + config->writeEntry("ThumbnailSize", d->view->thumbnailSize().size()); + config->writeEntry("Splitter Sizes", d->splitter->sizes()); + config->writeEntry("FolderDateFormat", d->folderDateFormat->currentItem()); + config->sync(); +} + +void CameraUI::slotProcessURL(const QString& url) +{ + KApplication::kApplication()->invokeBrowser(url); +} + +bool CameraUI::isBusy() const +{ + return d->busy; +} + +bool CameraUI::isClosed() const +{ + return d->closed; +} + +bool CameraUI::convertLosslessJpegFiles() const +{ + return d->convertJpegCheck->isChecked(); +} + +bool CameraUI::autoRotateJpegFiles() const +{ + return d->autoRotateCheck->isChecked(); +} + +QString CameraUI::losslessFormat() +{ + return d->losslessFormat->currentText(); +} + +QString CameraUI::cameraTitle() const +{ + return d->cameraTitle; +} + +void CameraUI::slotCancelButton() +{ + d->status->setText(i18n("Cancelling current operation, please wait...")); + d->progress->hide(); + QTimer::singleShot(0, d->controller, SLOT(slotCancel())); + d->currentlyDeleting.clear(); +} + +void CameraUI::closeEvent(QCloseEvent* e) +{ + if (dialogClosed()) + e->accept(); + else + e->ignore(); +} + +void CameraUI::slotClose() +{ + if (dialogClosed()) + reject(); +} + +bool CameraUI::dialogClosed() +{ + if (d->closed) + return true; + + if (isBusy()) + { + if (KMessageBox::questionYesNo(this, + i18n("Do you want to close the dialog " + "and cancel the current operation?")) + == KMessageBox::No) + return false; + } + + d->status->setText(i18n("Disconnecting from camera, please wait...")); + d->progress->hide(); + + if (isBusy()) + { + d->controller->slotCancel(); + // will be read in slotBusy later and finishDialog + // will be called only when everything is finished + d->closed = true; + } + else + { + d->closed = true; + finishDialog(); + } + + return true; +} + +void CameraUI::finishDialog() +{ + // Look if an item have been downloaded to computer during camera gui session. + // If yes, update the lastAccess date property of camera in digiKam camera list. + + if (d->view->itemsDownloaded() > 0) + { + CameraList* clist = CameraList::instance(); + if (clist) + clist->changeCameraAccessTime(d->cameraTitle, QDateTime::QDateTime::currentDateTime()); + } + + // When a directory is created, a watch is put on it to spot new files + // but it can occur that the file is copied there before the watch is + // completely setup. That is why as an extra safeguard run scanlib + // over the folders we used. Bug: 119201 + + d->status->setText(i18n("Scanning for new files, please wait...")); + ScanLib sLib; + for (QStringList::iterator it = d->foldersToScan.begin(); + it != d->foldersToScan.end(); ++it) + { + //DDebug() << "Scanning " << (*it) << endl; + sLib.findMissingItems( (*it) ); + } + + // Never call finalScan after deleteLater() - ScanLib will call processEvent(), + // and the delete event may be executed! + deleteLater(); + + if(!d->lastDestURL.isEmpty()) + emit signalLastDestination(d->lastDestURL); + + saveSettings(); +} + +void CameraUI::slotBusy(bool val) +{ + if (!val) + { + if (!d->busy) + return; + + d->busy = false; + d->cancelBtn->setEnabled(false); + d->view->viewport()->setEnabled(true); + + d->advBox->setEnabled(true); + // B.K.O #127614: The Focus need to be restored in custom prefix widget. + //commenting this out again: If we do not disable, no need to restore focus + //d->renameCustomizer->restoreFocus(); + + enableButton(User3, true); + enableButton(User2, true); + enableButton(User1, true); + d->helpMenu->menu()->setItemEnabled(CAMERA_INFO_MENU_ID, true); + + d->anim->stop(); + d->status->setText(i18n("Ready")); + d->progress->hide(); + + // like WDestructiveClose, but after camera controller operation has safely finished + if (d->closed) + { + finishDialog(); + } + } + else + { + if (d->busy) + return; + + if (!d->anim->running()) + d->anim->start(); + + d->busy = true; + d->cancelBtn->setEnabled(true); + + // Has camera icon view item selection is used to control download post processing, + // all selection actions are disable when camera interface is busy. + d->view->viewport()->setEnabled(false); + + // Settings tab is disabled in slotDownload, selectively when downloading + // Fast dis/enabling would create the impression of flicker, e.g. when retrieving EXIF from camera + //d->advBox->setEnabled(false); + + enableButton(User3, false); + enableButton(User2, false); + enableButton(User1, false); + d->helpMenu->menu()->setItemEnabled(CAMERA_INFO_MENU_ID, false); + } +} + +void CameraUI::slotIncreaseThumbSize() +{ + int thumbSize = d->view->thumbnailSize().size(); + if (thumbSize >= ThumbnailSize::Huge) return; + + thumbSize += ThumbnailSize::Step; + + if (thumbSize >= ThumbnailSize::Huge) + { + d->imageMenu->setItemEnabled(4, false); + } + d->imageMenu->setItemEnabled(5, true); + + d->view->setThumbnailSize(thumbSize); +} + +void CameraUI::slotDecreaseThumbSize() +{ + int thumbSize = d->view->thumbnailSize().size(); + if (thumbSize <= ThumbnailSize::Small) return; + + thumbSize -= ThumbnailSize::Step; + + if (thumbSize <= ThumbnailSize::Small) + { + d->imageMenu->setItemEnabled(5, false); + } + d->imageMenu->setItemEnabled(4, true); + + d->view->setThumbnailSize(thumbSize); +} + +void CameraUI::slotConnected(bool val) +{ + if (!val) + { + if (KMessageBox::warningYesNo(this, + i18n("Failed to connect to the camera. " + "Please make sure it is connected " + "properly and turned on. " + "Would you like to try again?"), + i18n("Connection Failed"), + i18n("Retry"), + i18n("Abort")) + == KMessageBox::Yes) + QTimer::singleShot(0, d->controller, SLOT(slotConnect())); + else + close(); + } + else + { + d->controller->listFolders(); + } +} + +void CameraUI::slotFolderList(const QStringList& folderList) +{ + if (d->closed) + return; + + d->progress->setProgress(0); + d->progress->setTotalSteps(0); + d->progress->show(); + + d->cameraFolderList = folderList; + for (QStringList::const_iterator it = folderList.begin(); + it != folderList.end(); ++it) + { + d->controller->listFiles(*it); + } +} + +void CameraUI::slotFileList(const GPItemInfoList& fileList) +{ + if (d->closed) + return; + + if (fileList.empty()) + return; + + kdDebug() << fileList.count() << endl; + + // We sort the map by time stamp + // and we remove internal camera files which are not image/video/sounds. + QStringList fileNames, fileExts; + QFileInfo info; + + // JVC camera (see B.K.O #133185). + fileNames.append("mgr_data"); + fileNames.append("pgr_mgr"); + + // HP Photosmart camera (see B.K.O #156338). + fileExts.append("dsp"); + + // Minolta camera in PTP mode + fileExts.append("dps"); + + // We sort the map by time stamp. + GPItemInfoList sfileList; + GPItemInfoList::const_iterator it; + GPItemInfoList::iterator it2; + + for(it = fileList.begin() ; it != fileList.end() ; ++it) + { + info.setFile((*it).name); + if (!fileNames.contains(info.fileName().lower()) && + !fileExts.contains(info.extension(false).lower())) + { + kdDebug() << info.fileName() << " : " << (*it).mtime << endl; + + for(it2 = sfileList.begin() ; it2 != sfileList.end() ; ++it2) + if ((*it2).mtime <= (*it).mtime) break; + + sfileList.insert(it2, *it); + } + } + + if (sfileList.empty()) + return; + + kdDebug() << sfileList.count() << endl; + + GPItemInfoList::const_iterator it3 = sfileList.begin(); + + do + { + GPItemInfo item = *it3; + + if (item.mtime > (time_t)d->lastAccess.toTime_t() && item.downloaded == GPItemInfo::DownloadUnknow) + item.downloaded = GPItemInfo::NewPicture; + + d->view->addItem(item); + d->controller->getThumbnail(item.folder, item.name); + ++it3; + } + while(it3 != sfileList.end()); + + d->progress->setTotalSteps(d->progress->totalSteps() + fileList.count()); +} + +void CameraUI::slotThumbnail(const QString& folder, const QString& file, + const QImage& thumbnail) +{ + d->view->setThumbnail(folder, file, thumbnail); + int curr = d->progress->progress(); + d->progress->setProgress(curr+1); +} + +void CameraUI::slotInformations() +{ + if (d->busy) + return; + + d->controller->getCameraInformations(); +} + +void CameraUI::slotCameraInformations(const QString& summary, const QString& manual, const QString& about) +{ + CameraInfoDialog *infoDlg = new CameraInfoDialog(this, summary, manual, about); + infoDlg->show(); +} + +void CameraUI::slotErrorMsg(const QString& msg) +{ + KMessageBox::error(this, msg); +} + +void CameraUI::slotUpload() +{ + if (d->busy) + return; + + QString fileformats; + + QStringList patternList = QStringList::split('\n', KImageIO::pattern(KImageIO::Reading)); + + // All Images from list must been always the first entry given by KDE API + QString allPictures = patternList[0]; + + // Add RAW file format to All Images" type mime and remplace current. +#if KDCRAW_VERSION < 0x000106 + allPictures.insert(allPictures.find("|"), QString(KDcrawIface::DcrawBinary::instance()->rawFiles())); +#else + allPictures.insert(allPictures.find("|"), QString(KDcrawIface::KDcraw::rawFiles())); +#endif + patternList.remove(patternList[0]); + patternList.prepend(allPictures); + + // Added RAW file formats supported by dcraw program like a type mime. + // Nota: we cannot use here "image/x-raw" type mime from KDE because it uncomplete + // or unavailable(dcraw_0)(see file #121242 in B.K.O). +#if KDCRAW_VERSION < 0x000106 + patternList.append(QString("\n%1|Camera RAW files").arg(QString(KDcrawIface::DcrawBinary::instance()->rawFiles()))); +#else + patternList.append(QString("\n%1|Camera RAW files").arg(QString(KDcrawIface::KDcraw::rawFiles()))); +#endif + + fileformats = patternList.join("\n"); + + DDebug () << "fileformats=" << fileformats << endl; + + KURL::List urls = KFileDialog::getOpenURLs(AlbumManager::instance()->getLibraryPath(), + fileformats, this, i18n("Select Image to Upload")); + if (!urls.isEmpty()) + slotUploadItems(urls); +} + +void CameraUI::slotUploadItems(const KURL::List& urls) +{ + if (d->busy) + return; + + if (urls.isEmpty()) + return; + + CameraFolderDialog dlg(this, d->view, d->cameraFolderList, d->controller->getCameraTitle(), + d->controller->getCameraPath()); + + if (dlg.exec() != QDialog::Accepted) + return; + + QString cameraFolder = dlg.selectedFolderPath(); + + for (KURL::List::const_iterator it = urls.begin() ; it != urls.end() ; ++it) + { + QFileInfo fi((*it).path()); + if (!fi.exists()) continue; + if (fi.isDir()) continue; + + QString ext = QString(".") + fi.extension(); + QString name = fi.fileName(); + name.truncate(fi.fileName().length() - ext.length()); + + bool ok; + + while (d->view->findItem(cameraFolder, name + ext)) + { + QString msg(i18n("Camera Folder <b>%1</b> already contains item <b>%2</b><br>" + "Please enter a new file name (without extension):") + .arg(cameraFolder).arg(fi.fileName())); +#if KDE_IS_VERSION(3,2,0) + name = KInputDialog::getText(i18n("File already exists"), msg, name, &ok, this); + +#else + name = KLineEditDlg::getText(i18n("File already exists"), msg, name, &ok, this); +#endif + if (!ok) + return; + } + + d->controller->upload(fi, name + ext, cameraFolder); + } +} + +void CameraUI::slotUploaded(const GPItemInfo& itemInfo) +{ + if (d->closed) + return; + + d->view->addItem(itemInfo); + d->controller->getThumbnail(itemInfo.folder, itemInfo.name); +} + +void CameraUI::slotDownloadSelected() +{ + slotDownload(true, false); +} + +void CameraUI::slotDownloadAndDeleteSelected() +{ + slotDownload(true, true); +} + +void CameraUI::slotDownloadAll() +{ + slotDownload(false, false); +} + +void CameraUI::slotDownloadAndDeleteAll() +{ + slotDownload(false, true); +} + +void CameraUI::slotDownload(bool onlySelected, bool deleteAfter, Album *album) +{ + // See B.K.O #143934: force to select all items to prevent problem + // when !renameCustomizer->useDefault() ==> iconItem->getDownloadName() + // can return an empty string in this case because it depends on selection. + if (!onlySelected) + d->view->slotSelectAll(); + + // See B.K.O #139519: Always check free space available before to + // download items selection from camera. + unsigned long fSize = 0; + unsigned long dSize = 0; + d->view->itemsSelectionSizeInfo(fSize, dSize); + if (d->freeSpaceWidget->isValid() && (dSize >= d->freeSpaceWidget->kBAvail())) + { + KMessageBox::error(this, i18n("There is no enough free space on Album Library Path " + "to download and process selected pictures from camera.\n\n" + "Estimated space require: %1\n" + "Available free space: %2") + .arg(KIO::convertSizeFromKB(dSize)) + .arg(KIO::convertSizeFromKB(d->freeSpaceWidget->kBAvail()))); + return; + } + + QString newDirName; + IconItem* firstItem = d->view->firstItem(); + if (firstItem) + { + CameraIconViewItem* iconItem = static_cast<CameraIconViewItem*>(firstItem); + + QDateTime dateTime; + dateTime.setTime_t(iconItem->itemInfo()->mtime); + + switch(d->folderDateFormat->currentItem()) + { + case CameraUIPriv::TextDateFormat: + newDirName = dateTime.date().toString(Qt::TextDate); + break; + case CameraUIPriv::LocalDateFormat: + newDirName = dateTime.date().toString(Qt::LocalDate); + break; + default: // IsoDateFormat + newDirName = dateTime.date().toString(Qt::ISODate); + break; + } + } + + // -- Get the destination album from digiKam library if necessary --------------- + + if (!album) + { + AlbumManager* man = AlbumManager::instance(); + album = man->currentAlbum(); + + if (album && album->type() != Album::PHYSICAL) + album = 0; + + QString header(i18n("<p>Please select the destination album from the digiKam library to " + "import the camera pictures into.</p>")); + + album = AlbumSelectDialog::selectAlbum(this, (PAlbum*)album, header, newDirName, + d->autoAlbumDateCheck->isChecked()); + + if (!album) return; + } + + PAlbum *pAlbum = dynamic_cast<PAlbum*>(album); + if (!pAlbum) return; + + // -- Prepare downloading of camera items ------------------------ + + KURL url; + url.setPath(pAlbum->folderPath()); + + d->controller->downloadPrep(); + + DownloadSettingsContainer downloadSettings; + QString downloadName; + time_t mtime; + int total = 0; + + downloadSettings.autoRotate = d->autoRotateCheck->isChecked(); + downloadSettings.fixDateTime = d->fixDateTimeCheck->isChecked(); + downloadSettings.newDateTime = d->dateTimeEdit->dateTime(); + downloadSettings.setPhotographerId = d->setPhotographerId->isChecked(); + downloadSettings.setCredits = d->setCredits->isChecked(); + downloadSettings.convertJpeg = convertLosslessJpegFiles(); + downloadSettings.losslessFormat = losslessFormat(); + + AlbumSettings* settings = AlbumSettings::instance(); + if (settings) + { + downloadSettings.author = settings->getIptcAuthor(); + downloadSettings.authorTitle = settings->getIptcAuthorTitle(); + downloadSettings.credit = settings->getIptcCredit(); + downloadSettings.source = settings->getIptcSource(); + downloadSettings.copyright = settings->getIptcCopyright(); + } + + // -- Download camera items ------------------------------- + // Since we show camera items in reverse order, downloading need to be done also in reverse order. + + for (IconItem* item = d->view->lastItem(); item; + item = item->prevItem()) + { + if (onlySelected && !(item->isSelected())) + continue; + + CameraIconViewItem* iconItem = static_cast<CameraIconViewItem*>(item); + downloadSettings.folder = iconItem->itemInfo()->folder; + downloadSettings.file = iconItem->itemInfo()->name; + downloadName = iconItem->getDownloadName(); + mtime = iconItem->itemInfo()->mtime; + + KURL u(url); + QString errMsg; + QDateTime dateTime; + dateTime.setTime_t(mtime); + + // Auto sub-albums creation based on file date. + + if (d->autoAlbumDateCheck->isChecked()) + { + QString dirName; + + switch(d->folderDateFormat->currentItem()) + { + case CameraUIPriv::TextDateFormat: + dirName = dateTime.date().toString(Qt::TextDate); + break; + case CameraUIPriv::LocalDateFormat: + dirName = dateTime.date().toString(Qt::LocalDate); + break; + default: // IsoDateFormat + dirName = dateTime.date().toString(Qt::ISODate); + break; + } + // See B.K.O #136927 : we need to support file system which do not + // handle upper case properly. + dirName = dirName.lower(); + if (!createAutoAlbum(url, dirName, dateTime.date(), errMsg)) + { + KMessageBox::error(this, errMsg); + return; + } + + u.addPath(dirName); + } + + // Auto sub-albums creation based on file extensions. + + if (d->autoAlbumExtCheck->isChecked()) + { + // We use the target file name to compute sub-albums name to take a care about + // convertion on the fly option. + QFileInfo fi(downloadName); + + QString subAlbum = fi.extension(false).upper(); + if (fi.extension(false).upper() == QString("JPEG") || + fi.extension(false).upper() == QString("JPE")) + subAlbum = QString("JPG"); + if (fi.extension(false).upper() == QString("TIFF")) + subAlbum = QString("TIF"); + if (fi.extension(false).upper() == QString("MPEG") || + fi.extension(false).upper() == QString("MPE") || + fi.extension(false).upper() == QString("MPO")) + subAlbum = QString("MPG"); + + // See B.K.O #136927 : we need to support file system which do not + // handle upper case properly. + subAlbum = subAlbum.lower(); + if (!createAutoAlbum(u, subAlbum, dateTime.date(), errMsg)) + { + KMessageBox::error(this, errMsg); + return; + } + + u.addPath(subAlbum); + } + + d->foldersToScan.append(u.path()); + u.addPath(downloadName.isEmpty() ? downloadSettings.file : downloadName); + + downloadSettings.dest = u.path(); + + d->controller->download(downloadSettings); + addFileExtension(QFileInfo(u.path()).extension(false)); + total++; + } + + if (total <= 0) + return; + + d->lastDestURL = url; + d->progress->setProgress(0); + d->progress->setTotalSteps(total); + d->progress->show(); + + // disable settings tab here instead of slotBusy: + // Only needs to be disabled while downloading + d->advBox->setEnabled(false); + + d->deleteAfter = deleteAfter; +} + +void CameraUI::slotDownloaded(const QString& folder, const QString& file, int status) +{ + CameraIconViewItem* iconItem = d->view->findItem(folder, file); + if (iconItem) + iconItem->setDownloaded(status); + + if (status == GPItemInfo::DownloadedYes || status == GPItemInfo::DownloadFailed) + { + int curr = d->progress->progress(); + d->progress->setProgress(curr+1); + } + + // Download all items is complete. + if (d->progress->progress() == d->progress->totalSteps()) + { + if (d->deleteAfter) + deleteItems(true, true); + } +} + +void CameraUI::slotSkipped(const QString& folder, const QString& file) +{ + CameraIconViewItem* iconItem = d->view->findItem(folder, file); + if (iconItem) + iconItem->setDownloaded(GPItemInfo::DownloadedNo); + + int curr = d->progress->progress(); + d->progress->setProgress(curr+1); +} + +void CameraUI::slotToggleLock() +{ + int count = 0; + for (IconItem* item = d->view->firstItem(); item; + item = item->nextItem()) + { + CameraIconViewItem* iconItem = static_cast<CameraIconViewItem*>(item); + if (iconItem->isSelected()) + { + QString folder = iconItem->itemInfo()->folder; + QString file = iconItem->itemInfo()->name; + int writePerm = iconItem->itemInfo()->writePermissions; + bool lock = true; + + // If item is currently locked, unlock it. + if (writePerm == 0) + lock = false; + + d->controller->lockFile(folder, file, lock); + count++; + } + } + + if (count > 0) + { + d->progress->setProgress(0); + d->progress->setTotalSteps(count); + d->progress->show(); + } +} + +void CameraUI::slotLocked(const QString& folder, const QString& file, bool status) +{ + if (status) + { + CameraIconViewItem* iconItem = d->view->findItem(folder, file); + if (iconItem) + { + iconItem->toggleLock(); + //if (iconItem->isSelected()) + // slotItemsSelected(iconItem, true); + } + } + + int curr = d->progress->progress(); + d->progress->setProgress(curr+1); +} + +void CameraUI::checkItem4Deletion(CameraIconViewItem* iconItem, QStringList& folders, QStringList& files, + QStringList& deleteList, QStringList& lockedList) +{ + if (iconItem->itemInfo()->writePermissions != 0) // Item not locked ? + { + QString folder = iconItem->itemInfo()->folder; + QString file = iconItem->itemInfo()->name; + folders.append(folder); + files.append(file); + deleteList.append(folder + QString("/") + file); + } + else + { + lockedList.append(iconItem->itemInfo()->name); + } +} + +void CameraUI::deleteItems(bool onlySelected, bool onlyDownloaded) +{ + QStringList folders; + QStringList files; + QStringList deleteList; + QStringList lockedList; + + for (IconItem* item = d->view->firstItem(); item; item = item->nextItem()) + { + CameraIconViewItem* iconItem = dynamic_cast<CameraIconViewItem*>(item); + if (iconItem) + { + if (onlySelected) + { + if (iconItem->isSelected()) + { + if (onlyDownloaded) + { + if (iconItem->isDownloaded()) + checkItem4Deletion(iconItem, folders, files, deleteList, lockedList); + } + else + { + checkItem4Deletion(iconItem, folders, files, deleteList, lockedList); + } + } + } + else // All items + { + if (onlyDownloaded) + { + if (iconItem->isDownloaded()) + checkItem4Deletion(iconItem, folders, files, deleteList, lockedList); + } + else + { + checkItem4Deletion(iconItem, folders, files, deleteList, lockedList); + } + } + } + } + + // If we want to delete some locked files, just give a feedback to user. + if (!lockedList.isEmpty()) + { + QString infoMsg(i18n("The items listed below are locked by camera (read-only). " + "These items will not be deleted. If you really want to delete these items, " + "please unlock them and try again.")); + KMessageBox::informationList(this, infoMsg, lockedList, i18n("Information")); + } + + if (folders.isEmpty()) + return; + + QString warnMsg(i18n("About to delete this image. " + "Deleted files are unrecoverable. " + "Are you sure?", + "About to delete these %n images. " + "Deleted files are unrecoverable. " + "Are you sure?", + deleteList.count())); + if (KMessageBox::warningContinueCancelList(this, warnMsg, + deleteList, + i18n("Warning"), + i18n("Delete")) + == KMessageBox::Continue) + { + QStringList::iterator itFolder = folders.begin(); + QStringList::iterator itFile = files.begin(); + + d->progress->setProgress(0); + d->progress->setTotalSteps(deleteList.count()); + d->progress->show(); + + for ( ; itFolder != folders.end(); ++itFolder, ++itFile) + { + d->controller->deleteFile(*itFolder, *itFile); + // the currentlyDeleting list is used to prevent loading items which + // will immenently be deleted into the sidebar and wasting time + d->currentlyDeleting.append(*itFolder + *itFile); + } + } +} + +void CameraUI::slotDeleteSelected() +{ + deleteItems(true, false); +} + +void CameraUI::slotDeleteAll() +{ + deleteItems(false, false); +} + +void CameraUI::slotDeleted(const QString& folder, const QString& file, bool status) +{ + if (status) + { + d->view->removeItem(folder, file); + // do this after removeItem, which will signal to slotItemsSelected, which checks for the list + d->currentlyDeleting.remove(folder + file); + } + + int curr = d->progress->progress(); + d->progress->setProgress(curr+1); +} + +void CameraUI::slotFileView(CameraIconViewItem* item) +{ + d->controller->openFile(item->itemInfo()->folder, item->itemInfo()->name); +} + +void CameraUI::slotExifFromFile(const QString& folder, const QString& file) +{ + CameraIconViewItem* item = d->view->findItem(folder, file); + if (!item) + return; + + d->rightSidebar->itemChanged(item->itemInfo(), folder + QString("/") + file, + QByteArray(), d->view, item); +} + +void CameraUI::slotExifFromData(const QByteArray& exifData) +{ + CameraIconViewItem* item = dynamic_cast<CameraIconViewItem*>(d->view->currentItem()); + + if (!item) + return; + + KURL url(item->itemInfo()->folder + '/' + item->itemInfo()->name); + + // Sometimes, GPhoto2 drivers return complete APP1 JFIF section. Exiv2 cannot + // decode (yet) exif metadata from APP1. We will find Exif header to get data at this place + // to please with Exiv2... + + DDebug() << "Size of Exif metadata from camera = " << exifData.size() << endl; + char exifHeader[] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 }; + + if (!exifData.isEmpty()) + { + int i = exifData.find(*exifHeader); + if (i != -1) + { + DDebug() << "Exif header found at position " << i << endl; + i = i + sizeof(exifHeader); + QByteArray data(exifData.size()-i); + memcpy(data.data(), exifData.data()+i, data.size()); + d->rightSidebar->itemChanged(item->itemInfo(), url, data, d->view, item); + return; + } + } + + d->rightSidebar->itemChanged(item->itemInfo(), url, exifData, d->view, item); +} + +void CameraUI::slotNewSelection(bool hasSelection) +{ + if (!d->renameCustomizer->useDefault()) + { + d->downloadMenu->setItemEnabled(0, hasSelection); + d->downloadMenu->setItemEnabled(2, hasSelection); + } + else + { + d->downloadMenu->setItemEnabled(0, hasSelection); + d->downloadMenu->setItemEnabled(2, hasSelection); + } + + unsigned long fSize = 0; + unsigned long dSize = 0; + d->view->itemsSelectionSizeInfo(fSize, dSize); + d->freeSpaceWidget->setEstimatedDSizeKb(dSize); +} + +void CameraUI::slotItemsSelected(CameraIconViewItem* item, bool selected) +{ + d->downloadMenu->setItemEnabled(0, selected); + d->downloadMenu->setItemEnabled(2, selected); + d->deleteMenu->setItemEnabled(0, selected); + + if (selected) + { + // if selected item is in the list of item which will be deleted, set no current item + if (d->currentlyDeleting.find(item->itemInfo()->folder + item->itemInfo()->name) + == d->currentlyDeleting.end()) + { + KURL url(item->itemInfo()->folder + '/' + item->itemInfo()->name); + d->rightSidebar->itemChanged(item->itemInfo(), url, QByteArray(), d->view, item); + d->controller->getExif(item->itemInfo()->folder, item->itemInfo()->name); + } + else + { + d->rightSidebar->slotNoCurrentItem(); + } + } + else + d->rightSidebar->slotNoCurrentItem(); +} + +bool CameraUI::createAutoAlbum(const KURL& parentURL, const QString& sub, + const QDate& date, QString& errMsg) +{ + KURL u(parentURL); + u.addPath(sub); + + // first stat to see if the album exists + QFileInfo info(u.path()); + if (info.exists()) + { + // now check if its really a directory + if (info.isDir()) + return true; + else + { + errMsg = i18n("A file with same name (%1) exists in folder %2") + .arg(sub) + .arg(parentURL.path()); + return false; + } + } + + // looks like the directory does not exist, try to create it + + AlbumManager* aman = AlbumManager::instance(); + PAlbum* parent = aman->findPAlbum(parentURL); + if (!parent) + { + errMsg = i18n("Failed to find Album for path '%1'") + .arg(parentURL.path()); + return false; + } + + return aman->createPAlbum(parent, sub, QString(""), date, QString(""), errMsg); +} + +void CameraUI::addFileExtension(const QString& ext) +{ + AlbumSettings* settings = AlbumSettings::instance(); + if (!settings) + return; + + if (settings->getImageFileFilter().upper().contains(ext.upper()) || + settings->getMovieFileFilter().upper().contains(ext.upper()) || + settings->getAudioFileFilter().upper().contains(ext.upper()) || + settings->getRawFileFilter().upper().contains(ext.upper())) + return; + + settings->setImageFileFilter(settings->getImageFileFilter() + QString(" *.") + ext); + emit signalAlbumSettingsChanged(); +} + +void CameraUI::slotFirstItem() +{ + CameraIconViewItem *currItem = dynamic_cast<CameraIconViewItem*>(d->view->firstItem()); + d->view->clearSelection(); + d->view->updateContents(); + if (currItem) + d->view->setCurrentItem(currItem); +} + +void CameraUI::slotPrevItem() +{ + CameraIconViewItem *currItem = dynamic_cast<CameraIconViewItem*>(d->view->currentItem()); + d->view->clearSelection(); + d->view->updateContents(); + if (currItem) + d->view->setCurrentItem(currItem->prevItem()); +} + +void CameraUI::slotNextItem() +{ + CameraIconViewItem *currItem = dynamic_cast<CameraIconViewItem*>(d->view->currentItem()); + d->view->clearSelection(); + d->view->updateContents(); + if (currItem) + d->view->setCurrentItem(currItem->nextItem()); +} + +void CameraUI::slotLastItem(void) +{ + CameraIconViewItem *currItem = dynamic_cast<CameraIconViewItem*>(d->view->lastItem()); + d->view->clearSelection(); + d->view->updateContents(); + if (currItem) + d->view->setCurrentItem(currItem); +} + +// Backport KDialog::keyPressEvent() implementation from KDELibs to ignore Enter/Return Key events +// to prevent any conflicts between dialog keys events and SpinBox keys events. + +void CameraUI::keyPressEvent(QKeyEvent *e) +{ + if ( e->state() == 0 ) + { + switch ( e->key() ) + { + case Key_Escape: + e->accept(); + reject(); + break; + case Key_Enter: + case Key_Return: + e->ignore(); + break; + default: + e->ignore(); + return; + } + } + else + { + // accept the dialog when Ctrl-Return is pressed + if ( e->state() == ControlButton && + (e->key() == Key_Return || e->key() == Key_Enter) ) + { + e->accept(); + accept(); + } + else + { + e->ignore(); + } + } +} + +} // namespace Digikam |