diff --git a/core/app/main/digikamapp.cpp b/core/app/main/digikamapp.cpp index 851bb5746f..fde4f363cb 100644 --- a/core/app/main/digikamapp.cpp +++ b/core/app/main/digikamapp.cpp @@ -1,3594 +1,3594 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2002-16-10 * Description : main digiKam interface implementation * * Copyright (C) 2002-2005 by Renchi Raju * Copyright (C) 2006 by Tom Albers * Copyright (C) 2009-2012 by Andi Clemens * Copyright (C) 2013 by Michael G. Hansen * Copyright (C) 2014-2015 by Mohamed Anwer * Copyright (C) 2002-2018 by Gilles Caulier * * 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. * * ============================================================ */ #include "digikamapp.h" /// @todo Order should be changed according to krazy2, but compilation fails. Try again later. MH #include "digikamapp_p.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include #include #include #include #include #include #include #include #include #include #include #include // Local includes #include "actioncategorizedview.h" #include "drawdecoder.h" #include "dlayoutbox.h" #include "digikam_debug.h" #include "album.h" #include "coredb.h" #include "albummodel.h" #include "albumselectdialog.h" #include "albumthumbnailloader.h" #include "cameratype.h" #include "dbinfoiface.h" #include "imagegps.h" #include "importui.h" #include "cameranamehelper.h" #include "categorizeditemmodel.h" #include "collectionscanner.h" #include "collectionmanager.h" #include "componentsinfo.h" #include "coredbthumbinfoprovider.h" #include "dio.h" #include "dlogoaction.h" #include "fileactionmngr.h" #include "filterstatusbar.h" #include "iccsettings.h" #include "imageattributeswatch.h" #include "imageinfo.h" #include "imagepropertiestab.h" #include "imagewindow.h" #include "lighttablewindow.h" #include "queuemgrwindow.h" #include "loadingcache.h" #include "loadingcacheinterface.h" #include "loadsavethread.h" #include "metaengine_rotation.h" #include "scancontroller.h" #include "setup.h" #include "setupeditor.h" #include "setupicc.h" #include "thememanager.h" #include "thumbnailloadthread.h" #include "thumbnailsize.h" #include "dmetadata.h" #include "tagscache.h" #include "tagsactionmngr.h" #include "databaseserverstarter.h" #include "metadatasettings.h" #include "statusbarprogresswidget.h" #include "dbmigrationdlg.h" #include "progressmanager.h" #include "progressview.h" #include "maintenancedlg.h" #include "maintenancemngr.h" #include "newitemsfinder.h" #include "dbcleaner.h" #include "tagsmanager.h" #include "imagesortsettings.h" #include "metadatahubmngr.h" #include "metadataedit.h" #include "expoblendingmanager.h" #include "calwizard.h" #include "mailwizard.h" #include "advprintwizard.h" #include "dfiledialog.h" #include "dmediaservermngr.h" #include "dmediaserverdlg.h" #include "dbwindow.h" #include "fbwindow.h" #include "flickrwindow.h" #include "gswindow.h" #include "imageshackwindow.h" #include "imgurwindow.h" #include "piwigowindow.h" #include "rajcewindow.h" #include "smugwindow.h" #include "yfwindow.h" #ifdef HAVE_MEDIAWIKI # include "mediawikiwindow.h" #endif #ifdef HAVE_VKONTAKTE # include "vkwindow.h" #endif #ifdef HAVE_KIO # include "ftexportwindow.h" # include "ftimportwindow.h" #endif #ifdef HAVE_MARBLE # include "geolocationedit.h" #endif #ifdef HAVE_HTMLGALLERY # include "htmlwizard.h" #endif #ifdef HAVE_DBUS # include "digikamadaptor.h" #endif #ifdef HAVE_PANORAMA # include "panomanager.h" #endif #ifdef HAVE_MEDIAPLAYER # include "vidslidewizard.h" #endif #ifdef HAVE_KFILEMETADATA # include "baloowrap.h" #endif namespace Digikam { DigikamApp* DigikamApp::m_instance = 0; DigikamApp::DigikamApp() : DXmlGuiWindow(0), d(new Private) { setObjectName(QLatin1String("Digikam")); setConfigGroupName(ApplicationSettings::instance()->generalConfigGroupName()); setFullScreenOptions(FS_ALBUMGUI); setXMLFile(QLatin1String("digikamui5.rc")); m_instance = this; d->config = KSharedConfig::openConfig(); KConfigGroup group = d->config->group(configGroupName()); #ifdef HAVE_DBUS new DigikamAdaptor(this); QDBusConnection::sessionBus().registerObject(QLatin1String("/Digikam"), this); QDBusConnection::sessionBus().registerService(QLatin1String("org.kde.digikam-") + QString::number(QCoreApplication::instance()->applicationPid())); #endif // collection scan if (!CollectionScanner::databaseInitialScanDone()) { ScanController::instance()->completeCollectionScanDeferFiles(); } if (ApplicationSettings::instance()->getShowSplashScreen() && !qApp->isSessionRestored()) { d->splashScreen = new DSplashScreen(); d->splashScreen->show(); } else { // Windows need here QCoreApplication::processEvents(). qApp->processEvents(); } if (d->splashScreen) { d->splashScreen->setMessage(i18n("Initializing...")); } // ensure creation AlbumManager::instance(); LoadingCacheInterface::initialize(); IccSettings::instance()->loadAllProfilesProperties(); MetadataSettings::instance(); DMetadataSettings::instance(); ProgressManager::instance(); ThumbnailLoadThread::setDisplayingWidget(this); DIO::instance(); // creation of the engine on first use - when drawing - // can take considerable time and cause a noticeable hang in the UI thread. QFontMetrics fm(font()); fm.width(QLatin1String("a")); connect(ApplicationSettings::instance(), SIGNAL(setupChanged()), this, SLOT(slotSetupChanged())); connect(IccSettings::instance(), SIGNAL(settingsChanged()), this, SLOT(slotColorManagementOptionsChanged())); d->cameraMenu = new QMenu(this); d->usbMediaMenu = new QMenu(this); d->cardReaderMenu = new QMenu(this); d->quickImportMenu = new QMenu(this); d->cameraList = new CameraList(this, QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1String("/cameras.xml")); connect(d->cameraList, SIGNAL(signalCameraAdded(CameraType*)), this, SLOT(slotCameraAdded(CameraType*))); connect(d->cameraList, SIGNAL(signalCameraRemoved(QAction*)), this, SLOT(slotCameraRemoved(QAction*))); d->modelCollection = new DigikamModelCollection; // This manager must be created after collection setup and before accelerators setup. d->tagsActionManager = new TagsActionMngr(this); // First create everything, then connect. // Otherwise some items may send signals and the slots can try // to access items which were not created yet. setupView(); setupAccelerators(); setupActions(); setupStatusBar(); initGui(); setupViewConnections(); applyMainWindowSettings(group); slotColorManagementOptionsChanged(); // Check ICC profiles repository availability if (d->splashScreen) { d->splashScreen->setMessage(i18n("Checking ICC repository...")); } d->validIccPath = SetupICC::iccRepositoryIsValid(); // Read albums from database if (d->splashScreen) { d->splashScreen->setMessage(i18n("Reading database...")); } AlbumManager::instance()->startScan(); // Setting the initial menu options after all tools have been loaded QList albumList = AlbumManager::instance()->currentAlbums(); d->view->slotAlbumSelected(albumList); // preload additional windows preloadWindows(); readFullScreenSettings(group); #ifdef HAVE_KFILEMETADATA // Create BalooWrap object, because it need to register a listener // to update digiKam data when changes in Baloo occur BalooWrap* const baloo = BalooWrap::instance(); Q_UNUSED(baloo); #endif //HAVE_KFILEMETADATA setAutoSaveSettings(group, true); LoadSaveThread::setInfoProvider(new DatabaseLoadSaveFileInfoProvider); setupSelectToolsAction(); } DigikamApp::~DigikamApp() { ProgressManager::instance()->slotAbortAll(); ImageAttributesWatch::shutDown(); // Close and delete image editor instance. if (ImageWindow::imageWindowCreated()) { // Delete after close ImageWindow::imageWindow()->setAttribute(Qt::WA_DeleteOnClose, true); // close the window ImageWindow::imageWindow()->close(); } // Close and delete light table instance. if (LightTableWindow::lightTableWindowCreated()) { LightTableWindow::lightTableWindow()->setAttribute(Qt::WA_DeleteOnClose, true); LightTableWindow::lightTableWindow()->close(); } // Close and delete Batch Queue Manager instance. if (QueueMgrWindow::queueManagerWindowCreated()) { QueueMgrWindow::queueManagerWindow()->setAttribute(Qt::WA_DeleteOnClose, true); QueueMgrWindow::queueManagerWindow()->close(); } if (TagsManager::isCreated()) { TagsManager::instance()->close(); } if (MetadataHubMngr::isCreated()) { delete MetadataHubMngr::internalPtr; } #ifdef HAVE_KFILEMETADATA if (BalooWrap::isCreated()) { BalooWrap::internalPtr.clear(); } #endif if (ExpoBlendingManager::isCreated()) { delete ExpoBlendingManager::internalPtr; } #ifdef HAVE_PANORAMA if (PanoManager::isCreated()) { delete PanoManager::internalPtr; } #endif delete d->view; ApplicationSettings::instance()->setRecurseAlbums(d->recurseAlbumsAction->isChecked()); ApplicationSettings::instance()->setRecurseTags(d->recurseTagsAction->isChecked()); ApplicationSettings::instance()->setShowThumbbar(d->showBarAction->isChecked()); ApplicationSettings::instance()->saveSettings(); ScanController::instance()->shutDown(); AlbumManager::instance()->cleanUp(); ImageAttributesWatch::cleanUp(); ThumbnailLoadThread::cleanUp(); AlbumThumbnailLoader::instance()->cleanUp(); LoadingCacheInterface::cleanUp(); DIO::cleanUp(); DMediaServerMngr::instance()->saveAtShutdown(); // close database server if (ApplicationSettings::instance()->getDbEngineParameters().internalServer) { DatabaseServerStarter::instance()->stopServerManagerProcess(); } AlbumManager::instance()->removeFakeConnection(); m_instance = 0; delete d->modelCollection; delete d; } DigikamApp* DigikamApp::instance() { return m_instance; } DigikamView* DigikamApp::view() const { return d->view; } void DigikamApp::show() { // Remove Splashscreen. if (d->splashScreen) { d->splashScreen->finish(this); delete d->splashScreen; d->splashScreen = 0; } // Display application window. KMainWindow::show(); // Report errors from ICC repository path. if (!d->validIccPath) { QString message = i18n("

The ICC profiles folder seems to be invalid.

" "

If you want to try setting it again, choose \"Yes\" here, otherwise " "choose \"No\", and the \"Color Management\" feature " "will be disabled until you solve this issue.

"); if (QMessageBox::warning(this, qApp->applicationName(), message, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { if (!setupICC()) { d->config->group(QLatin1String("Color Management")).writeEntry(QLatin1String("EnableCM"), false); d->config->sync(); } } else { d->config->group(QLatin1String("Color Management")).writeEntry(QLatin1String("EnableCM"), false); d->config->sync(); } } // Init album icon view zoom factor. slotThumbSizeChanged(ApplicationSettings::instance()->getDefaultIconSize()); slotZoomSliderChanged(ApplicationSettings::instance()->getDefaultIconSize()); d->autoShowZoomToolTip = true; // Enable finished the collection scan as deferred process if (ApplicationSettings::instance()->getScanAtStart() || !CollectionScanner::databaseInitialScanDone()) { NewItemsFinder* const tool = new NewItemsFinder(NewItemsFinder::ScanDeferredFiles); QTimer::singleShot(1000, tool, SLOT(start())); } if (ApplicationSettings::instance()->getCleanAtStart()) { DbCleaner* const tool = new DbCleaner(false,false); QTimer::singleShot(1000, tool, SLOT(start())); } // Start the Media Server if necessary DMediaServerMngr::instance()->loadAtStartup(); } void DigikamApp::restoreSession() { //TODO: show and restore ImageEditor, Lighttable, and Batch Queue Manager main windows if (qApp->isSessionRestored()) { int n = 1; while (KMainWindow::canBeRestored(n)) { const QString className = KMainWindow::classNameOfToplevel(n); if (className == QLatin1String(Digikam::DigikamApp::staticMetaObject.className())) { restore(n, false); break; } ++n; } } } void DigikamApp::closeEvent(QCloseEvent* e) { // may show a progress dialog to finish actions FileActionMngr::instance()->requestShutDown(); // may show a progress dialog to apply pending metadata if (MetadataHubMngr::isCreated()) MetadataHubMngr::instance()->requestShutDown(); DXmlGuiWindow::closeEvent(e); e->accept(); } void DigikamApp::autoDetect() { // Called from main if command line option is set, or via DBus if (d->splashScreen) { d->splashScreen->setMessage(i18n("Auto-Detecting Camera...")); } QTimer::singleShot(0, this, SLOT(slotCameraAutoDetect())); } void DigikamApp::downloadFrom(const QString& cameraGuiPath) { // Called from main if command line option is set, or via DBus if (!cameraGuiPath.isEmpty()) { if (d->splashScreen) { d->splashScreen->setMessage(i18n("Opening Download Dialog...")); } emit queuedOpenCameraUiFromPath(cameraGuiPath); } } void DigikamApp::downloadFromUdi(const QString& udi) { // Called from main if command line option is set, or via DBus if (!udi.isEmpty()) { if (d->splashScreen) { d->splashScreen->setMessage(i18n("Opening Download Dialog...")); } emit queuedOpenSolidDevice(udi); } } bool DigikamApp::queryClose() { bool ret = true; if (ImageWindow::imageWindowCreated()) { ret &= ImageWindow::imageWindow()->queryClose(); } if (QueueMgrWindow::queueManagerWindowCreated()) { ret &= QueueMgrWindow::queueManagerWindow()->queryClose(); } return ret; } void DigikamApp::setupView() { if (d->splashScreen) { d->splashScreen->setMessage(i18n("Initializing Main View...")); } d->view = new DigikamView(this, d->modelCollection); setCentralWidget(d->view); d->view->applySettings(); } void DigikamApp::setupViewConnections() { connect(d->view, SIGNAL(signalAlbumSelected(Album*)), this, SLOT(slotAlbumSelected(Album*))); connect(d->view, SIGNAL(signalSelectionChanged(int)), this, SLOT(slotSelectionChanged(int))); connect(d->view, SIGNAL(signalImageSelected(ImageInfoList,ImageInfoList)), this, SLOT(slotImageSelected(ImageInfoList,ImageInfoList))); connect(d->view, SIGNAL(signalSwitchedToPreview()), this, SLOT(slotSwitchedToPreview())); connect(d->view, SIGNAL(signalSwitchedToIconView()), this, SLOT(slotSwitchedToIconView())); connect(d->view, SIGNAL(signalSwitchedToMapView()), this, SLOT(slotSwitchedToMapView())); connect(d->view, SIGNAL(signalSwitchedToTableView()), this, SLOT(slotSwitchedToTableView())); connect(d->view, SIGNAL(signalSwitchedToTrashView()), this, SLOT(slotSwitchedToTrashView())); } void DigikamApp::setupStatusBar() { d->statusLabel = new DAdjustableLabel(statusBar()); d->statusLabel->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); statusBar()->addWidget(d->statusLabel, 80); //------------------------------------------------------------------------------ d->metadataStatusBar = new MetadataStatusBar(statusBar()); statusBar()->addWidget(d->metadataStatusBar, 50); //------------------------------------------------------------------------------ d->filterStatusBar = new FilterStatusBar(statusBar()); statusBar()->addWidget(d->filterStatusBar, 50); d->view->connectIconViewFilter(d->filterStatusBar); //------------------------------------------------------------------------------ ProgressView* const view = new ProgressView(statusBar(), this); view->hide(); StatusbarProgressWidget* const littleProgress = new StatusbarProgressWidget(view, statusBar()); littleProgress->show(); statusBar()->addPermanentWidget(littleProgress); //------------------------------------------------------------------------------ d->zoomBar = new DZoomBar(statusBar()); d->zoomBar->setZoomToFitAction(d->zoomFitToWindowAction); d->zoomBar->setZoomTo100Action(d->zoomTo100percents); d->zoomBar->setZoomPlusAction(d->zoomPlusAction); d->zoomBar->setZoomMinusAction(d->zoomMinusAction); d->zoomBar->setBarMode(DZoomBar::ThumbsSizeCtrl); statusBar()->addPermanentWidget(d->zoomBar); //------------------------------------------------------------------------------ connect(d->zoomBar, SIGNAL(signalZoomSliderChanged(int)), this, SLOT(slotZoomSliderChanged(int))); connect(this, SIGNAL(signalWindowHasMoved()), d->zoomBar, SLOT(slotUpdateTrackerPos())); connect(d->zoomBar, SIGNAL(signalZoomValueEdited(double)), d->view, SLOT(setZoomFactor(double))); connect(d->view, SIGNAL(signalZoomChanged(double)), this, SLOT(slotZoomChanged(double))); connect(d->view, SIGNAL(signalThumbSizeChanged(int)), this, SLOT(slotThumbSizeChanged(int))); } void DigikamApp::setupAccelerators() { KActionCollection* const ac = actionCollection(); // Action are added by tag in ui.rc XML file QAction* const escapeAction = new QAction(i18n("Exit Preview Mode"), this); ac->addAction(QLatin1String("exit_preview_mode"), escapeAction); ac->setDefaultShortcut(escapeAction, Qt::Key_Escape); connect(escapeAction, SIGNAL(triggered()), this, SIGNAL(signalEscapePressed())); QAction* const nextImageAction = new QAction(i18n("Next Image"), this); nextImageAction->setIcon(QIcon::fromTheme(QLatin1String("go-next"))); ac->addAction(QLatin1String("next_image"), nextImageAction); ac->setDefaultShortcut(nextImageAction, Qt::Key_Space); connect(nextImageAction, SIGNAL(triggered()), this, SIGNAL(signalNextItem())); QAction* const previousImageAction = new QAction(i18n("Previous Image"), this); previousImageAction->setIcon(QIcon::fromTheme(QLatin1String("go-previous"))); ac->addAction(QLatin1String("previous_image"), previousImageAction); ac->setDefaultShortcuts(previousImageAction, QList() << Qt::Key_Backspace << Qt::SHIFT+Qt::Key_Space); connect(previousImageAction, SIGNAL(triggered()), this, SIGNAL(signalPrevItem())); QAction* const firstImageAction = new QAction(i18n("First Image"), this); ac->addAction(QLatin1String("first_image"), firstImageAction); ac->setDefaultShortcuts(firstImageAction, QList() << Qt::CTRL + Qt::Key_Home); connect(firstImageAction, SIGNAL(triggered()), this, SIGNAL(signalFirstItem())); QAction* const lastImageAction = new QAction(i18n("Last Image"), this); ac->addAction(QLatin1String("last_image"), lastImageAction); ac->setDefaultShortcuts(lastImageAction, QList() << Qt::CTRL + Qt::Key_End); connect(lastImageAction, SIGNAL(triggered()), this, SIGNAL(signalLastItem())); d->cutItemsAction = new QAction(i18n("Cu&t"), this); d->cutItemsAction->setIcon(QIcon::fromTheme(QLatin1String("edit-cut"))); d->cutItemsAction->setWhatsThis(i18n("Cut selection to clipboard")); ac->addAction(QLatin1String("cut_album_selection"), d->cutItemsAction); // NOTE: shift+del keyboard shortcut must not be assigned to Cut action // else the shortcut for Delete permanently collides with secondary shortcut of Cut ac->setDefaultShortcut(d->cutItemsAction, Qt::CTRL + Qt::Key_X); connect(d->cutItemsAction, SIGNAL(triggered()), this, SIGNAL(signalCutAlbumItemsSelection())); d->copyItemsAction = buildStdAction(StdCopyAction, this, SIGNAL(signalCopyAlbumItemsSelection()), this); ac->addAction(QLatin1String("copy_album_selection"), d->copyItemsAction); d->pasteItemsAction = buildStdAction(StdPasteAction, this, SIGNAL(signalPasteAlbumItemsSelection()), this); ac->addAction(QLatin1String("paste_album_selection"), d->pasteItemsAction); // Labels shortcuts must be registered here to be saved in XML GUI files if user customize it. d->tagsActionManager->registerLabelsActions(ac); QAction* const editTitles = new QAction(i18n("Edit Titles"), this); ac->addAction(QLatin1String("edit_titles"), editTitles); ac->setDefaultShortcut(editTitles, Qt::META + Qt::Key_T); connect(editTitles, SIGNAL(triggered()), d->view, SLOT(slotRightSideBarActivateTitles())); QAction* const editComments = new QAction(i18n("Edit Comments"), this); ac->addAction(QLatin1String("edit_comments"), editComments); ac->setDefaultShortcut(editComments, Qt::META + Qt::Key_C); connect(editComments, SIGNAL(triggered()), d->view, SLOT(slotRightSideBarActivateComments())); QAction* const assignedTags = new QAction(i18n("Show Assigned Tags"), this); ac->addAction(QLatin1String("assigned _tags"), assignedTags); ac->setDefaultShortcut(assignedTags, Qt::META + Qt::Key_A); connect(assignedTags, SIGNAL(triggered()), d->view, SLOT(slotRightSideBarActivateAssignedTags())); } void DigikamApp::setupActions() { KActionCollection* const ac = actionCollection(); d->solidCameraActionGroup = new QActionGroup(this); connect(d->solidCameraActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotOpenSolidCamera(QAction*))); d->solidUsmActionGroup = new QActionGroup(this); connect(d->solidUsmActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotOpenSolidUsmDevice(QAction*))); d->manualCameraActionGroup = new QActionGroup(this); connect(d->manualCameraActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotOpenManualCamera(QAction*))); // ----------------------------------------------------------------- d->backwardActionMenu = new KToolBarPopupAction(QIcon::fromTheme(QLatin1String("go-previous")), i18n("&Back"), this); d->backwardActionMenu->setEnabled(false); ac->addAction(QLatin1String("album_back"), d->backwardActionMenu); ac->setDefaultShortcut(d->backwardActionMenu, Qt::ALT+Qt::Key_Left); connect(d->backwardActionMenu->menu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShowBackwardMenu())); // we are using a signal mapper to identify which of a bunch of actions was triggered d->backwardSignalMapper = new QSignalMapper(this); // connect mapper to view connect(d->backwardSignalMapper, SIGNAL(mapped(int)), d->view, SLOT(slotAlbumHistoryBack(int))); // connect action to mapper connect(d->backwardActionMenu, SIGNAL(triggered()), d->backwardSignalMapper, SLOT(map())); // inform mapper about number of steps d->backwardSignalMapper->setMapping(d->backwardActionMenu, 1); // ----------------------------------------------------------------- d->forwardActionMenu = new KToolBarPopupAction(QIcon::fromTheme(QLatin1String("go-next")), i18n("Forward"), this); d->forwardActionMenu->setEnabled(false); ac->addAction(QLatin1String("album_forward"), d->forwardActionMenu); ac->setDefaultShortcut(d->forwardActionMenu, Qt::ALT+Qt::Key_Right); connect(d->forwardActionMenu->menu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShowForwardMenu())); d->forwardSignalMapper = new QSignalMapper(this); connect(d->forwardSignalMapper, SIGNAL(mapped(int)), d->view, SLOT(slotAlbumHistoryForward(int))); connect(d->forwardActionMenu, SIGNAL(triggered()), d->forwardSignalMapper, SLOT(map())); d->forwardSignalMapper->setMapping(d->forwardActionMenu, 1); // ----------------------------------------------------------------- d->refreshAction = new QAction(QIcon::fromTheme(QLatin1String("view-refresh")), i18n("Refresh"), this); d->refreshAction->setWhatsThis(i18n("Refresh the current contents.")); connect(d->refreshAction, SIGNAL(triggered()), d->view, SLOT(slotRefresh())); ac->addAction(QLatin1String("view_refresh"), d->refreshAction); ac->setDefaultShortcut(d->refreshAction, Qt::Key_F5); // ----------------------------------------------------------------- QSignalMapper* const browseActionsMapper = new QSignalMapper(this); connect(browseActionsMapper, SIGNAL(mapped(QWidget*)), d->view, SLOT(slotLeftSideBarActivate(QWidget*))); foreach(SidebarWidget* const leftWidget, d->view->leftSidebarWidgets()) { QString actionName = QLatin1String("browse_") + leftWidget->objectName() .remove(QLatin1Char(' ')) .remove(QLatin1String("Sidebar")) .remove(QLatin1String("FolderView")) .remove(QLatin1String("View")).toLower(); qCDebug(DIGIKAM_GENERAL_LOG) << actionName; QAction* const action = new QAction(leftWidget->getIcon(), leftWidget->getCaption(), this); ac->addAction(actionName, action); ac->setDefaultShortcut(action, QKeySequence(leftWidget->property("Shortcut").toInt())); connect(action, SIGNAL(triggered()), browseActionsMapper, SLOT(map())); browseActionsMapper->setMapping(action, leftWidget); } // ----------------------------------------------------------------- d->newAction = new QAction(QIcon::fromTheme(QLatin1String("folder-new")), i18n("&New..."), this); d->newAction->setWhatsThis(i18n("Creates a new empty Album in the collection.")); connect(d->newAction, SIGNAL(triggered()), d->view, SLOT(slotNewAlbum())); ac->addAction(QLatin1String("album_new"), d->newAction); ac->setDefaultShortcuts(d->newAction, QList() << Qt::CTRL + Qt::Key_N); // ----------------------------------------------------------------- d->moveSelectionToAlbumAction = new QAction(QIcon::fromTheme(QLatin1String("folder-new")), i18n("&Move to Album..."), this); d->moveSelectionToAlbumAction->setWhatsThis(i18n("Move selected images into an album.")); connect(d->moveSelectionToAlbumAction, SIGNAL(triggered()), d->view, SLOT(slotMoveSelectionToAlbum())); ac->addAction(QLatin1String("move_selection_to_album"), d->moveSelectionToAlbumAction); // ----------------------------------------------------------------- d->deleteAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete Album"), this); connect(d->deleteAction, SIGNAL(triggered()), d->view, SLOT(slotDeleteAlbum())); ac->addAction(QLatin1String("album_delete"), d->deleteAction); // ----------------------------------------------------------------- d->renameAction = new QAction(QIcon::fromTheme(QLatin1String("document-edit")), i18n("Rename..."), this); connect(d->renameAction, SIGNAL(triggered()), d->view, SLOT(slotRenameAlbum())); ac->addAction(QLatin1String("album_rename"), d->renameAction); ac->setDefaultShortcut(d->renameAction, Qt::SHIFT + Qt::Key_F2); // ----------------------------------------------------------------- d->propsEditAction = new QAction(QIcon::fromTheme(QLatin1String("configure")), i18n("Properties"), this); d->propsEditAction->setWhatsThis(i18n("Edit album properties and collection information.")); connect(d->propsEditAction, SIGNAL(triggered()), d->view, SLOT(slotAlbumPropsEdit())); ac->addAction(QLatin1String("album_propsEdit"), d->propsEditAction); ac->setDefaultShortcut(d->propsEditAction, Qt::ALT + Qt::Key_Return); // ----------------------------------------------------------------- d->writeAlbumMetadataAction = new QAction(QIcon::fromTheme(QLatin1String("document-edit")), i18n("Write Metadata to Files"), this); d->writeAlbumMetadataAction->setWhatsThis(i18n("Updates metadata of files in the current " "album with the contents of digiKam database " "(file metadata will be overwritten with data from " "the database).")); connect(d->writeAlbumMetadataAction, SIGNAL(triggered()), d->view, SLOT(slotAlbumWriteMetadata())); ac->addAction(QLatin1String("album_write_metadata"), d->writeAlbumMetadataAction); // ----------------------------------------------------------------- d->readAlbumMetadataAction = new QAction(QIcon::fromTheme(QLatin1String("edit-redo")), i18n("Reread Metadata From Files"), this); d->readAlbumMetadataAction->setWhatsThis(i18n("Updates the digiKam database from the metadata " "of the files in the current album " "(information in the database will be overwritten with data from " "the files' metadata).")); connect(d->readAlbumMetadataAction, SIGNAL(triggered()), d->view, SLOT(slotAlbumReadMetadata())); ac->addAction(QLatin1String("album_read_metadata"), d->readAlbumMetadataAction); // ----------------------------------------------------------------- d->openInFileManagerAction = new QAction(QIcon::fromTheme(QLatin1String("folder-open")), i18n("Open in File Manager"), this); connect(d->openInFileManagerAction, SIGNAL(triggered()), d->view, SLOT(slotAlbumOpenInFileManager())); ac->addAction(QLatin1String("album_openinfilemanager"), d->openInFileManagerAction); // ----------------------------------------------------------- d->openTagMngrAction = new QAction(QIcon::fromTheme(QLatin1String("tag")), i18n("Tag Manager"), this); connect(d->openTagMngrAction, SIGNAL(triggered()), d->view, SLOT(slotOpenTagsManager())); ac->addAction(QLatin1String("open_tag_mngr"), d->openTagMngrAction); // ----------------------------------------------------------- d->newTagAction = new QAction(QIcon::fromTheme(QLatin1String("tag-new")), i18nc("new tag", "N&ew..."), this); connect(d->newTagAction, SIGNAL(triggered()), d->view, SLOT(slotNewTag())); ac->addAction(QLatin1String("tag_new"), d->newTagAction); // ----------------------------------------------------------- d->editTagAction = new QAction(QIcon::fromTheme(QLatin1String("tag-properties")), i18n("Properties"), this); connect(d->editTagAction, SIGNAL(triggered()), d->view, SLOT(slotEditTag())); ac->addAction(QLatin1String("tag_edit"), d->editTagAction); // ----------------------------------------------------------- d->deleteTagAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), i18n("Delete"), this); connect(d->deleteTagAction, SIGNAL(triggered()), d->view, SLOT(slotDeleteTag())); ac->addAction(QLatin1String("tag_delete"), d->deleteTagAction); // ----------------------------------------------------------- d->assignTagAction = new QAction(QIcon::fromTheme(QLatin1String("tag-new")), i18n("Assign Tag"), this); connect(d->assignTagAction, SIGNAL(triggered()), d->view, SLOT(slotAssignTag())); ac->addAction(QLatin1String("tag_assign"), d->assignTagAction); ac->setDefaultShortcut(d->assignTagAction, Qt::Key_T); // ----------------------------------------------------------- d->imageViewSelectionAction = new KSelectAction(QIcon::fromTheme(QLatin1String("view-preview")), i18n("Views"), this); ac->addAction(QLatin1String("view_selection"), d->imageViewSelectionAction); d->imageIconViewAction = new QAction(QIcon::fromTheme(QLatin1String("view-list-icons")), i18nc("@action Go to thumbnails (icon) view", "Thumbnails"), this); d->imageIconViewAction->setCheckable(true); ac->addAction(QLatin1String("icon_view"), d->imageIconViewAction); connect(d->imageIconViewAction, SIGNAL(triggered()), d->view, SLOT(slotIconView())); d->imageViewSelectionAction->addAction(d->imageIconViewAction); d->imagePreviewAction = new QAction(QIcon::fromTheme(QLatin1String("view-preview")), i18nc("View the selected image", "Preview"), this); d->imagePreviewAction->setCheckable(true); ac->addAction(QLatin1String("image_view"), d->imagePreviewAction); ac->setDefaultShortcut(d->imagePreviewAction, Qt::Key_F3); connect(d->imagePreviewAction, SIGNAL(triggered()), d->view, SLOT(slotImagePreview())); d->imageViewSelectionAction->addAction(d->imagePreviewAction); #ifdef HAVE_MARBLE d->imageMapViewAction = new QAction(QIcon::fromTheme(QLatin1String("globe")), i18nc("@action Switch to map view", "Map"), this); d->imageMapViewAction->setCheckable(true); ac->addAction(QLatin1String("map_view"), d->imageMapViewAction); connect(d->imageMapViewAction, SIGNAL(triggered()), d->view, SLOT(slotMapWidgetView())); d->imageViewSelectionAction->addAction(d->imageMapViewAction); #endif // HAVE_MARBLE d->imageTableViewAction = new QAction(QIcon::fromTheme(QLatin1String("view-list-details")), i18nc("@action Switch to table view", "Table"), this); d->imageTableViewAction->setCheckable(true); ac->addAction(QLatin1String("table_view"), d->imageTableViewAction); connect(d->imageTableViewAction, SIGNAL(triggered()), d->view, SLOT(slotTableView())); d->imageViewSelectionAction->addAction(d->imageTableViewAction); // ----------------------------------------------------------- d->imageViewAction = new QAction(QIcon::fromTheme(QLatin1String("quickopen-file")), i18n("Open..."), this); d->imageViewAction->setWhatsThis(i18n("Open the selected item.")); connect(d->imageViewAction, SIGNAL(triggered()), d->view, SLOT(slotImageEdit())); ac->addAction(QLatin1String("image_edit"), d->imageViewAction); ac->setDefaultShortcut(d->imageViewAction, Qt::Key_F4); d->openWithAction = new QAction(QIcon::fromTheme(QLatin1String("preferences-desktop-filetype-association")), i18n("Open With Default Application"), this); d->openWithAction->setWhatsThis(i18n("Open the selected item with default assigned application.")); connect(d->openWithAction, SIGNAL(triggered()), d->view, SLOT(slotFileWithDefaultApplication())); ac->addAction(QLatin1String("open_with_default_application"), d->openWithAction); ac->setDefaultShortcut(d->openWithAction, Qt::META + Qt::Key_F4); d->ieAction = new QAction(QIcon::fromTheme(QLatin1String("document-edit")), i18n("Image Editor"), this); d->ieAction->setWhatsThis(i18n("Open the image editor.")); connect(d->ieAction, SIGNAL(triggered()), d->view, SLOT(slotEditor())); ac->addAction(QLatin1String("imageeditor"), d->ieAction); // ----------------------------------------------------------- d->ltAction = new QAction(QIcon::fromTheme(QLatin1String("lighttable")), i18n("Light Table"), this); connect(d->ltAction, SIGNAL(triggered()), d->view, SLOT(slotLightTable())); ac->addAction(QLatin1String("light_table"), d->ltAction); ac->setDefaultShortcut(d->ltAction, Qt::Key_L); d->imageLightTableAction = new QAction(QIcon::fromTheme(QLatin1String("lighttable")), i18n("Place onto Light Table"), this); d->imageLightTableAction->setWhatsThis(i18n("Place the selected items on the light table thumbbar.")); connect(d->imageLightTableAction, SIGNAL(triggered()), d->view, SLOT(slotImageLightTable())); ac->addAction(QLatin1String("image_lighttable"), d->imageLightTableAction); ac->setDefaultShortcut(d->imageLightTableAction, Qt::CTRL+Qt::Key_L); d->imageAddLightTableAction = new QAction(QIcon::fromTheme(QLatin1String("list-add")), i18n("Add to Light Table"), this); d->imageAddLightTableAction->setWhatsThis(i18n("Add selected items to the light table thumbbar.")); connect(d->imageAddLightTableAction, SIGNAL(triggered()), d->view, SLOT(slotImageAddToLightTable())); ac->addAction(QLatin1String("image_add_to_lighttable"), d->imageAddLightTableAction); ac->setDefaultShortcut(d->imageAddLightTableAction, Qt::SHIFT+Qt::CTRL+Qt::Key_L); // ----------------------------------------------------------- d->bqmAction = new QAction(QIcon::fromTheme(QLatin1String("run-build")), i18n("Batch Queue Manager"), this); connect(d->bqmAction, SIGNAL(triggered()), d->view, SLOT(slotQueueMgr())); ac->addAction(QLatin1String("queue_manager"), d->bqmAction); ac->setDefaultShortcut(d->bqmAction, Qt::Key_B); d->imageAddCurrentQueueAction = new QAction(QIcon::fromTheme(QLatin1String("go-up")), i18n("Add to Current Queue"), this); d->imageAddCurrentQueueAction->setWhatsThis(i18n("Add selected items to current queue from batch manager.")); connect(d->imageAddCurrentQueueAction, SIGNAL(triggered()), d->view, SLOT(slotImageAddToCurrentQueue())); ac->addAction(QLatin1String("image_add_to_current_queue"), d->imageAddCurrentQueueAction); ac->setDefaultShortcut(d->imageAddCurrentQueueAction, Qt::CTRL+Qt::Key_B); d->imageAddNewQueueAction = new QAction(QIcon::fromTheme(QLatin1String("list-add")), i18n("Add to New Queue"), this); d->imageAddNewQueueAction->setWhatsThis(i18n("Add selected items to a new queue from batch manager.")); connect(d->imageAddNewQueueAction, SIGNAL(triggered()), d->view, SLOT(slotImageAddToNewQueue())); ac->addAction(QLatin1String("image_add_to_new_queue"), d->imageAddNewQueueAction); ac->setDefaultShortcut(d->imageAddNewQueueAction, Qt::SHIFT+Qt::CTRL+Qt::Key_B); // ----------------------------------------------------------------- d->quickImportMenu->setTitle(i18nc("@action Import photos from camera", "Import")); d->quickImportMenu->setIcon(QIcon::fromTheme(QLatin1String("camera-photo"))); ac->addAction(QLatin1String("import_auto"), d->quickImportMenu->menuAction()); // ----------------------------------------------------------------- d->imageWriteMetadataAction = new QAction(QIcon::fromTheme(QLatin1String("document-edit")), i18n("Write Metadata to Selected Files"), this); d->imageWriteMetadataAction->setWhatsThis(i18n("Updates metadata of files in the current " "album with the contents of digiKam database " "(file metadata will be overwritten with data from " "the database).")); connect(d->imageWriteMetadataAction, SIGNAL(triggered()), d->view, SLOT(slotImageWriteMetadata())); ac->addAction(QLatin1String("image_write_metadata"), d->imageWriteMetadataAction); // ----------------------------------------------------------------- d->imageReadMetadataAction = new QAction(QIcon::fromTheme(QLatin1String("edit-redo")), i18n("Reread Metadata From Selected Files"), this); d->imageReadMetadataAction->setWhatsThis(i18n("Updates the digiKam database from the metadata " "of the files in the current album " "(information in the database will be overwritten with data from " "the files' metadata).")); connect(d->imageReadMetadataAction, SIGNAL(triggered()), d->view, SLOT(slotImageReadMetadata())); ac->addAction(QLatin1String("image_read_metadata"), d->imageReadMetadataAction); // ----------------------------------------------------------- d->imageFindSimilarAction = new QAction(QIcon::fromTheme(QLatin1String("tools-wizard")), i18n("Find Similar..."), this); d->imageFindSimilarAction->setWhatsThis(i18n("Find similar images using selected item as reference.")); connect(d->imageFindSimilarAction, SIGNAL(triggered()), d->view, SLOT(slotImageFindSimilar())); ac->addAction(QLatin1String("image_find_similar"), d->imageFindSimilarAction); // ----------------------------------------------------------- d->imageRenameAction = new QAction(QIcon::fromTheme(QLatin1String("document-edit")), i18n("Rename..."), this); d->imageRenameAction->setWhatsThis(i18n("Change the filename of the currently selected item.")); connect(d->imageRenameAction, SIGNAL(triggered()), d->view, SLOT(slotImageRename())); ac->addAction(QLatin1String("image_rename"), d->imageRenameAction); ac->setDefaultShortcut(d->imageRenameAction, Qt::Key_F2); // ----------------------------------------------------------- // Pop up dialog to ask user whether to move to trash d->imageDeleteAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), i18nc("Non-pluralized", "Move to Trash"), this); connect(d->imageDeleteAction, SIGNAL(triggered()), d->view, SLOT(slotImageDelete())); ac->addAction(QLatin1String("image_delete"), d->imageDeleteAction); ac->setDefaultShortcut(d->imageDeleteAction, Qt::Key_Delete); // ----------------------------------------------------------- // Pop up dialog to ask user whether to permanently delete // FIXME: This action is never used?? How can someone delete a album directly, without moving it to the trash first? // This is especially important when deleting from a different partition or from a net source. // Also note that we use the wrong icon for the default album delete action, which should have a trashcan icon instead // of a red cross, it confuses users. d->imageDeletePermanentlyAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete Permanently"), this); connect(d->imageDeletePermanentlyAction, SIGNAL(triggered()), d->view, SLOT(slotImageDeletePermanently())); ac->addAction(QLatin1String("image_delete_permanently"), d->imageDeletePermanentlyAction); ac->setDefaultShortcut(d->imageDeletePermanentlyAction, Qt::SHIFT+Qt::Key_Delete); // ----------------------------------------------------------- // These two actions are hidden, no menu entry, no toolbar entry, no shortcut. // Power users may add them. d->imageDeletePermanentlyDirectlyAction = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete permanently without confirmation"), this); connect(d->imageDeletePermanentlyDirectlyAction, SIGNAL(triggered()), d->view, SLOT(slotImageDeletePermanentlyDirectly())); ac->addAction(QLatin1String("image_delete_permanently_directly"), d->imageDeletePermanentlyDirectlyAction); // ----------------------------------------------------------- d->imageTrashDirectlyAction = new QAction(QIcon::fromTheme(QLatin1String("user-trash")), i18n("Move to trash without confirmation"), this); connect(d->imageTrashDirectlyAction, SIGNAL(triggered()), d->view, SLOT(slotImageTrashDirectly())); ac->addAction(QLatin1String("image_trash_directly"), d->imageTrashDirectlyAction); // ----------------------------------------------------------------- d->albumSortAction = new KSelectAction(i18n("&Sort Albums"), this); d->albumSortAction->setWhatsThis(i18n("Sort Albums in tree-view.")); connect(d->albumSortAction, SIGNAL(triggered(int)), d->view, SLOT(slotSortAlbums(int))); ac->addAction(QLatin1String("album_sort"), d->albumSortAction); // Use same list order as in applicationsettings enum QStringList sortActionList; sortActionList.append(i18n("By Folder")); sortActionList.append(i18n("By Category")); sortActionList.append(i18n("By Date")); d->albumSortAction->setItems(sortActionList); // ----------------------------------------------------------- d->recurseAlbumsAction = new QAction(i18n("Include Album Sub-Tree"), this); d->recurseAlbumsAction->setCheckable(true); d->recurseAlbumsAction->setWhatsThis(i18n("Activate this option to show all sub-albums below " "the current album.")); connect(d->recurseAlbumsAction, SIGNAL(toggled(bool)), this, SLOT(slotRecurseAlbums(bool))); ac->addAction(QLatin1String("albums_recursive"), d->recurseAlbumsAction); d->recurseTagsAction = new QAction(i18n("Include Tag Sub-Tree"), this); d->recurseTagsAction->setCheckable(true); d->recurseTagsAction->setWhatsThis(i18n("Activate this option to show all images marked by the given tag " "and all its sub-tags.")); connect(d->recurseTagsAction, SIGNAL(toggled(bool)), this, SLOT(slotRecurseTags(bool))); ac->addAction(QLatin1String("tags_recursive"), d->recurseTagsAction); // ----------------------------------------------------------- d->imageSortAction = new KSelectAction(i18n("&Sort Items"), this); d->imageSortAction->setWhatsThis(i18n("The value by which the images in one album are sorted in the thumbnail view")); QSignalMapper* const imageSortMapper = new QSignalMapper(this); connect(imageSortMapper, SIGNAL(mapped(int)), d->view, SLOT(slotSortImages(int))); ac->addAction(QLatin1String("image_sort"), d->imageSortAction); // map to ImageSortSettings enum QAction* const sortByNameAction = d->imageSortAction->addAction(i18n("By Name")); QAction* const sortByPathAction = d->imageSortAction->addAction(i18n("By Path")); QAction* const sortByDateAction = d->imageSortAction->addAction(i18n("By Date")); QAction* const sortByFileSizeAction = d->imageSortAction->addAction(i18n("By File Size")); QAction* const sortByRatingAction = d->imageSortAction->addAction(i18n("By Rating")); QAction* const sortByImageSizeAction = d->imageSortAction->addAction(i18n("By Image Size")); QAction* const sortByAspectRatioAction = d->imageSortAction->addAction(i18n("By Aspect Ratio")); QAction* const sortBySimilarityAction = d->imageSortAction->addAction(i18n("By Similarity")); // activate the sort by similarity if the fuzzy search sidebar is active. Deactivate at start. sortBySimilarityAction->setEnabled(false); connect(d->view, SIGNAL(signalFuzzySidebarActive(bool)), sortBySimilarityAction, SLOT(setEnabled(bool))); connect(sortByNameAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortByPathAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortByDateAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortByFileSizeAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortByRatingAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortByImageSizeAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortByAspectRatioAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); connect(sortBySimilarityAction, SIGNAL(triggered()), imageSortMapper, SLOT(map())); imageSortMapper->setMapping(sortByNameAction, (int)ImageSortSettings::SortByFileName); imageSortMapper->setMapping(sortByPathAction, (int)ImageSortSettings::SortByFilePath); imageSortMapper->setMapping(sortByDateAction, (int)ImageSortSettings::SortByCreationDate); imageSortMapper->setMapping(sortByFileSizeAction, (int)ImageSortSettings::SortByFileSize); imageSortMapper->setMapping(sortByRatingAction, (int)ImageSortSettings::SortByRating); imageSortMapper->setMapping(sortByImageSizeAction, (int)ImageSortSettings::SortByImageSize); imageSortMapper->setMapping(sortByAspectRatioAction, (int)ImageSortSettings::SortByAspectRatio); imageSortMapper->setMapping(sortBySimilarityAction, (int)ImageSortSettings::SortBySimilarity); // ----------------------------------------------------------- d->imageSortOrderAction = new KSelectAction(i18n("Item Sort &Order"), this); d->imageSortOrderAction->setWhatsThis(i18n("Defines whether images are sorted in ascending or descending manner.")); QSignalMapper* const imageSortOrderMapper = new QSignalMapper(this); connect(imageSortOrderMapper, SIGNAL(mapped(int)), d->view, SLOT(slotSortImagesOrder(int))); ac->addAction(QLatin1String("image_sort_order"), d->imageSortOrderAction); QAction* const sortAscendingAction = d->imageSortOrderAction->addAction(QIcon::fromTheme(QLatin1String("view-sort-ascending")), i18n("Ascending")); QAction* const sortDescendingAction = d->imageSortOrderAction->addAction(QIcon::fromTheme(QLatin1String("view-sort-descending")), i18n("Descending")); connect(sortAscendingAction, SIGNAL(triggered()), imageSortOrderMapper, SLOT(map())); connect(sortDescendingAction, SIGNAL(triggered()), imageSortOrderMapper, SLOT(map())); imageSortOrderMapper->setMapping(sortAscendingAction, (int)ImageSortSettings::AscendingOrder); imageSortOrderMapper->setMapping(sortDescendingAction, (int)ImageSortSettings::DescendingOrder); // ----------------------------------------------------------- d->imageSeparationAction = new KSelectAction(i18n("Separate Items"), this); d->imageSeparationAction->setWhatsThis(i18n("The categories in which the images in the thumbnail view are displayed")); QSignalMapper* const imageSeparationMapper = new QSignalMapper(this); connect(imageSeparationMapper, SIGNAL(mapped(int)), d->view, SLOT(slotSeparateImages(int))); ac->addAction(QLatin1String("image_separation"), d->imageSeparationAction); // map to ImageSortSettings enum QAction* const noCategoriesAction = d->imageSeparationAction->addAction(i18n("Flat List")); QAction* const separateByAlbumAction = d->imageSeparationAction->addAction(i18n("By Album")); QAction* const separateByFormatAction = d->imageSeparationAction->addAction(i18n("By Format")); connect(noCategoriesAction, SIGNAL(triggered()), imageSeparationMapper, SLOT(map())); connect(separateByAlbumAction, SIGNAL(triggered()), imageSeparationMapper, SLOT(map())); connect(separateByFormatAction, SIGNAL(triggered()), imageSeparationMapper, SLOT(map())); imageSeparationMapper->setMapping(noCategoriesAction, (int)ImageSortSettings::OneCategory); imageSeparationMapper->setMapping(separateByAlbumAction, (int)ImageSortSettings::CategoryByAlbum); imageSeparationMapper->setMapping(separateByFormatAction, (int)ImageSortSettings::CategoryByFormat); // ----------------------------------------------------------------- d->imageSeparationSortOrderAction = new KSelectAction(i18n("Item Separation Order"), this); d->imageSeparationSortOrderAction->setWhatsThis(i18n("The sort order of the groups of separated items")); QSignalMapper* const imageSeparationSortOrderMapper = new QSignalMapper(this); connect(imageSeparationSortOrderMapper, SIGNAL(mapped(int)), d->view, SLOT(slotImageSeparationSortOrder(int))); ac->addAction(QLatin1String("image_separation_sort_order"), d->imageSeparationSortOrderAction); QAction* const sortSeparationsAscending = d->imageSeparationSortOrderAction->addAction(QIcon::fromTheme(QLatin1String("view-sort-ascending")), i18n("Ascending")); QAction* const sortSeparationsDescending = d->imageSeparationSortOrderAction->addAction(QIcon::fromTheme(QLatin1String("view-sort-descending")), i18n("Descending")); connect(sortSeparationsAscending, SIGNAL(triggered()), imageSeparationSortOrderMapper, SLOT(map())); connect(sortSeparationsDescending, SIGNAL(triggered()), imageSeparationSortOrderMapper, SLOT(map())); imageSeparationSortOrderMapper->setMapping(sortSeparationsAscending, (int)ImageSortSettings::AscendingOrder); imageSeparationSortOrderMapper->setMapping(sortSeparationsDescending, (int)ImageSortSettings::DescendingOrder); // ----------------------------------------------------------------- setupImageTransformActions(); setupExifOrientationActions(); createMetadataEditAction(); createGeolocationEditAction(); createExportActions(); createImportActions(); // ----------------------------------------------------------------- d->selectAllAction = new QAction(i18n("Select All"), this); connect(d->selectAllAction, SIGNAL(triggered()), d->view, SLOT(slotSelectAll())); ac->addAction(QLatin1String("selectAll"), d->selectAllAction); ac->setDefaultShortcut(d->selectAllAction, Qt::CTRL+Qt::Key_A); // ----------------------------------------------------------------- d->selectNoneAction = new QAction(i18n("Select None"), this); connect(d->selectNoneAction, SIGNAL(triggered()), d->view, SLOT(slotSelectNone())); ac->addAction(QLatin1String("selectNone"), d->selectNoneAction); ac->setDefaultShortcut(d->selectNoneAction, Qt::CTRL+Qt::SHIFT+Qt::Key_A); // ----------------------------------------------------------------- d->selectInvertAction = new QAction(i18n("Invert Selection"), this); connect(d->selectInvertAction, SIGNAL(triggered()), d->view, SLOT(slotSelectInvert())); ac->addAction(QLatin1String("selectInvert"), d->selectInvertAction); ac->setDefaultShortcut(d->selectInvertAction, Qt::CTRL+Qt::Key_I); // ----------------------------------------------------------- d->showBarAction = new QAction(QIcon::fromTheme(QLatin1String("view-choose")), i18n("Show Thumbbar"), this); d->showBarAction->setCheckable(true); connect(d->showBarAction, SIGNAL(triggered()), this, SLOT(slotToggleShowBar())); ac->addAction(QLatin1String("showthumbs"), d->showBarAction); ac->setDefaultShortcut(d->showBarAction, Qt::CTRL+Qt::Key_T); // Provides a menu entry that allows showing/hiding the toolbar(s) setStandardToolBarMenuEnabled(true); // Provides a menu entry that allows showing/hiding the statusbar createStandardStatusBarAction(); // Standard 'Configure' menu actions createSettingsActions(); // ----------------------------------------------------------- d->zoomPlusAction = buildStdAction(StdZoomInAction, d->view, SLOT(slotZoomIn()), this); QKeySequence keysPlus(d->zoomPlusAction->shortcut()[0], Qt::Key_Plus); ac->addAction(QLatin1String("album_zoomin"), d->zoomPlusAction); ac->setDefaultShortcut(d->zoomPlusAction, keysPlus); // ----------------------------------------------------------- d->zoomMinusAction = buildStdAction(StdZoomOutAction, d->view, SLOT(slotZoomOut()), this); QKeySequence keysMinus(d->zoomMinusAction->shortcut()[0], Qt::Key_Minus); ac->addAction(QLatin1String("album_zoomout"), d->zoomMinusAction); ac->setDefaultShortcut(d->zoomMinusAction, keysMinus); // ----------------------------------------------------------- d->zoomTo100percents = new QAction(QIcon::fromTheme(QLatin1String("zoom-original")), i18n("Zoom to 100%"), this); connect(d->zoomTo100percents, SIGNAL(triggered()), d->view, SLOT(slotZoomTo100Percents())); ac->addAction(QLatin1String("album_zoomto100percents"), d->zoomTo100percents); ac->setDefaultShortcut(d->zoomTo100percents, Qt::CTRL + Qt::Key_Period); // ----------------------------------------------------------- d->zoomFitToWindowAction = new QAction(QIcon::fromTheme(QLatin1String("zoom-fit-best")), i18n("Fit to &Window"), this); connect(d->zoomFitToWindowAction, SIGNAL(triggered()), d->view, SLOT(slotFitToWindow())); ac->addAction(QLatin1String("album_zoomfit2window"), d->zoomFitToWindowAction); ac->setDefaultShortcut(d->zoomFitToWindowAction, Qt::ALT + Qt::CTRL + Qt::Key_E); // ----------------------------------------------------------- createFullScreenAction(QLatin1String("full_screen")); createSidebarActions(); // ----------------------------------------------------------- d->slideShowAction = new QMenu(i18n("Slideshow"), this); d->slideShowAction->setIcon(QIcon::fromTheme(QLatin1String("view-presentation"))); ac->addAction(QLatin1String("slideshow"), d->slideShowAction->menuAction()); d->slideShowAllAction = new QAction(i18n("All"), this); connect(d->slideShowAllAction, SIGNAL(triggered()), d->view, SLOT(slotSlideShowAll())); ac->addAction(QLatin1String("slideshow_all"), d->slideShowAllAction); ac->setDefaultShortcut(d->slideShowAllAction, Qt::Key_F9); d->slideShowAction->addAction(d->slideShowAllAction); d->slideShowSelectionAction = new QAction(i18n("Selection"), this); connect(d->slideShowSelectionAction, SIGNAL(triggered()), d->view, SLOT(slotSlideShowSelection())); ac->addAction(QLatin1String("slideshow_selected"), d->slideShowSelectionAction); ac->setDefaultShortcut(d->slideShowSelectionAction, Qt::ALT+Qt::Key_F9); d->slideShowAction->addAction(d->slideShowSelectionAction); d->slideShowRecursiveAction = new QAction(i18n("With All Sub-Albums"), this); connect(d->slideShowRecursiveAction, SIGNAL(triggered()), d->view, SLOT(slotSlideShowRecursive())); ac->addAction(QLatin1String("slideshow_recursive"), d->slideShowRecursiveAction); ac->setDefaultShortcut(d->slideShowRecursiveAction, Qt::SHIFT+Qt::Key_F9); d->slideShowAction->addAction(d->slideShowRecursiveAction); createPresentationAction(); // ----------------------------------------------------------- d->viewCMViewAction = new QAction(QIcon::fromTheme(QLatin1String("video-display")), i18n("Color-Managed View"), this); d->viewCMViewAction->setCheckable(true); connect(d->viewCMViewAction, SIGNAL(triggered()), this, SLOT(slotToggleColorManagedView())); ac->addAction(QLatin1String("color_managed_view"), d->viewCMViewAction); ac->setDefaultShortcut(d->viewCMViewAction, Qt::Key_F12); // ----------------------------------------------------------- d->quitAction = buildStdAction(StdQuitAction, this, SLOT(slotExit()), this); ac->addAction(QLatin1String("app_exit"), d->quitAction); // ----------------------------------------------------------- createHelpActions(); // ----------------------------------------------------------- QAction* const findAction = new QAction(QIcon::fromTheme(QLatin1String("edit-find")), i18n("Search..."), this); connect(findAction, SIGNAL(triggered()), d->view, SLOT(slotNewKeywordSearch())); ac->addAction(QLatin1String("search_quick"), findAction); ac->setDefaultShortcut(findAction, Qt::CTRL+Qt::Key_F); // ----------------------------------------------------------- d->advSearchAction = new QAction(QIcon::fromTheme(QLatin1String("edit-find")), i18n("Advanced Search..."), this); connect(d->advSearchAction, SIGNAL(triggered()), d->view, SLOT(slotNewAdvancedSearch())); ac->addAction(QLatin1String("search_advanced"), d->advSearchAction); ac->setDefaultShortcut(d->advSearchAction, Qt::CTRL+Qt::ALT+Qt::Key_F); // ----------------------------------------------------------- QAction* const duplicatesAction = new QAction(QIcon::fromTheme(QLatin1String("tools-wizard")), i18n("Find Duplicates..."), this); connect(duplicatesAction, SIGNAL(triggered()), d->view, SLOT(slotNewDuplicatesSearch())); ac->addAction(QLatin1String("find_duplicates"), duplicatesAction); ac->setDefaultShortcut(duplicatesAction, Qt::CTRL+Qt::Key_D); // ----------------------------------------------------------- #ifdef HAVE_MYSQLSUPPORT QAction* const databaseMigrationAction = new QAction(QIcon::fromTheme(QLatin1String("network-server-database")), i18n("Database Migration..."), this); connect(databaseMigrationAction, SIGNAL(triggered()), this, SLOT(slotDatabaseMigration())); ac->addAction(QLatin1String("database_migration"), databaseMigrationAction); #endif // ----------------------------------------------------------- d->maintenanceAction = new QAction(QIcon::fromTheme(QLatin1String("run-build-prune")), i18n("Maintenance..."), this); connect(d->maintenanceAction, SIGNAL(triggered()), this, SLOT(slotMaintenance())); ac->addAction(QLatin1String("maintenance"), d->maintenanceAction); createExpoBlendingAction(); createPanoramaAction(); createHtmlGalleryAction(); createCalendarAction(); createVideoSlideshowAction(); createSendByMailAction(); createPrintCreatorAction(); createMediaServerAction(); // ----------------------------------------------------------- QAction* const cameraAction = new QAction(i18n("Add Camera Manually..."), this); connect(cameraAction, SIGNAL(triggered()), this, SLOT(slotSetupCamera())); ac->addAction(QLatin1String("camera_add"), cameraAction); // ----------------------------------------------------------- // Load Cameras -- do this before the createGUI so that the cameras // are plugged into the toolbar at startup if (d->splashScreen) { d->splashScreen->setMessage(i18n("Loading cameras...")); } loadCameras(); // Load Themes populateThemes(); createGUI(xmlFile()); cleanupActions(); // NOTE: see bug #252130 and #283281 : we need to disable these actions when BQM is running. // These connections must be done after loading color theme else theme menu cannot be plugged to Settings menu, connect(QueueMgrWindow::queueManagerWindow(), SIGNAL(signalBqmIsBusy(bool)), d->bqmAction, SLOT(setDisabled(bool))); connect(QueueMgrWindow::queueManagerWindow(), SIGNAL(signalBqmIsBusy(bool)), d->imageAddCurrentQueueAction, SLOT(setDisabled(bool))); connect(QueueMgrWindow::queueManagerWindow(), SIGNAL(signalBqmIsBusy(bool)), d->imageAddNewQueueAction, SLOT(setDisabled(bool))); } void DigikamApp::initGui() { // Initialize Actions --------------------------------------- d->deleteAction->setEnabled(false); d->renameAction->setEnabled(false); d->addImagesAction->setEnabled(false); d->propsEditAction->setEnabled(false); d->openInFileManagerAction->setEnabled(false); d->imageViewAction->setEnabled(false); d->imagePreviewAction->setEnabled(false); d->imageLightTableAction->setEnabled(false); d->imageAddLightTableAction->setEnabled(false); d->imageFindSimilarAction->setEnabled(false); d->imageRenameAction->setEnabled(false); d->imageDeleteAction->setEnabled(false); d->imageExifOrientationActionMenu->setEnabled(false); d->openWithAction->setEnabled(false); d->slideShowSelectionAction->setEnabled(false); m_metadataEditAction->setEnabled(false); d->imageAutoExifActionMenu->setEnabled(false); #ifdef HAVE_MARBLE m_geolocationEditAction->setEnabled(false); #endif d->albumSortAction->setCurrentItem((int)ApplicationSettings::instance()->getAlbumSortRole()); d->imageSortAction->setCurrentItem((int)ApplicationSettings::instance()->getImageSortOrder()); d->imageSortOrderAction->setCurrentItem((int)ApplicationSettings::instance()->getImageSorting()); d->imageSeparationAction->setCurrentItem((int)ApplicationSettings::instance()->getImageSeparationMode()-1); // no action for enum 0 d->imageSeparationSortOrderAction->setCurrentItem((int)ApplicationSettings::instance()->getImageSeparationSortOrder()); d->recurseAlbumsAction->setChecked(ApplicationSettings::instance()->getRecurseAlbums()); d->recurseTagsAction->setChecked(ApplicationSettings::instance()->getRecurseTags()); d->showBarAction->setChecked(ApplicationSettings::instance()->getShowThumbbar()); showMenuBarAction()->setChecked(!menuBar()->isHidden()); // NOTE: workaround for bug #171080 slotSwitchedToIconView(); } void DigikamApp::enableZoomPlusAction(bool val) { d->zoomPlusAction->setEnabled(val); } void DigikamApp::enableZoomMinusAction(bool val) { d->zoomMinusAction->setEnabled(val); } void DigikamApp::enableAlbumBackwardHistory(bool enable) { d->backwardActionMenu->setEnabled(enable); } void DigikamApp::enableAlbumForwardHistory(bool enable) { d->forwardActionMenu->setEnabled(enable); } void DigikamApp::slotAboutToShowBackwardMenu() { d->backwardActionMenu->menu()->clear(); QStringList titles; d->view->getBackwardHistory(titles); for (int i = 0; i < titles.size(); ++i) { QAction* const action = d->backwardActionMenu->menu()->addAction(titles.at(i), d->backwardSignalMapper, SLOT(map())); d->backwardSignalMapper->setMapping(action, i + 1); } } void DigikamApp::slotAboutToShowForwardMenu() { d->forwardActionMenu->menu()->clear(); QStringList titles; d->view->getForwardHistory(titles); for (int i = 0; i < titles.size(); ++i) { QAction* const action = d->forwardActionMenu->menu()->addAction(titles.at(i), d->forwardSignalMapper, SLOT(map())); d->forwardSignalMapper->setMapping(action, i + 1); } } void DigikamApp::slotAlbumSelected(Album* album) { if (album) { PAlbum* const palbum = dynamic_cast(album); if (album->type() != Album::PHYSICAL || !palbum) { // Rules if not Physical album. d->deleteAction->setEnabled(false); d->renameAction->setEnabled(false); d->addImagesAction->setEnabled(false); d->propsEditAction->setEnabled(false); d->openInFileManagerAction->setEnabled(false); d->newAction->setEnabled(false); d->addFoldersAction->setEnabled(false); d->writeAlbumMetadataAction->setEnabled(true); d->readAlbumMetadataAction->setEnabled(true); d->pasteItemsAction->setEnabled(!album->isRoot()); // Special case if Tag album. bool enabled = (album->type() == Album::TAG) && !album->isRoot(); d->newTagAction->setEnabled(enabled); d->deleteTagAction->setEnabled(enabled); d->editTagAction->setEnabled(enabled); } else { // Rules if Physical album. // We have either the abstract root album, // the album root album for collection base dirs, or normal albums. bool isRoot = palbum->isRoot(); bool isAlbumRoot = palbum->isAlbumRoot(); bool isNormalAlbum = !isRoot && !isAlbumRoot; d->deleteAction->setEnabled(isNormalAlbum); d->renameAction->setEnabled(isNormalAlbum); d->addImagesAction->setEnabled(isNormalAlbum || isAlbumRoot); d->propsEditAction->setEnabled(isNormalAlbum); d->openInFileManagerAction->setEnabled(isNormalAlbum || isAlbumRoot); d->newAction->setEnabled(isNormalAlbum || isAlbumRoot); d->addFoldersAction->setEnabled(isNormalAlbum || isAlbumRoot); d->writeAlbumMetadataAction->setEnabled(isNormalAlbum || isAlbumRoot); d->readAlbumMetadataAction->setEnabled(isNormalAlbum || isAlbumRoot); d->pasteItemsAction->setEnabled(isNormalAlbum || isAlbumRoot); } } else { // Rules if no current album. d->deleteAction->setEnabled(false); d->renameAction->setEnabled(false); d->addImagesAction->setEnabled(false); d->propsEditAction->setEnabled(false); d->openInFileManagerAction->setEnabled(false); d->newAction->setEnabled(false); d->addFoldersAction->setEnabled(false); d->writeAlbumMetadataAction->setEnabled(false); d->readAlbumMetadataAction->setEnabled(false); d->pasteItemsAction->setEnabled(false); d->newTagAction->setEnabled(false); d->deleteTagAction->setEnabled(false); d->editTagAction->setEnabled(false); } } void DigikamApp::slotImageSelected(const ImageInfoList& selection, const ImageInfoList& listAll) { int numImagesWithGrouped = listAll.count(); int numImagesWithoutGrouped = d->view->allUrls(false).count(); ImageInfoList selectionWithoutGrouped = d->view->selectedInfoList(true, false); QString statusBarSelectionText; QString statusBarSelectionToolTip; switch (selection.count()) { case 0: { if (numImagesWithGrouped == numImagesWithoutGrouped) { statusBarSelectionText = i18np("No item selected (%1 item)", "No item selected (%1 items)", numImagesWithoutGrouped); break; } statusBarSelectionText = i18np("No item selected (%1 [%2] item)", "No item selected (%1 [%2] items)", numImagesWithoutGrouped, numImagesWithGrouped); statusBarSelectionToolTip = i18np("No item selected (%1 item. With grouped items: %2)", "No item selected (%1 items. With grouped items: %2)", numImagesWithoutGrouped, numImagesWithGrouped); break; } default: { if (numImagesWithGrouped == numImagesWithoutGrouped) { statusBarSelectionText = i18n("%1/%2 items selected", selection.count(), numImagesWithoutGrouped); break; } if (selectionWithoutGrouped.count() > 1) { if (selection.count() == selectionWithoutGrouped.count()) { statusBarSelectionText = i18n("%1/%2 [%3] items selected", selectionWithoutGrouped.count(), numImagesWithoutGrouped, numImagesWithGrouped); statusBarSelectionToolTip = i18n("%1/%2 items selected. Total with grouped items: %3", selectionWithoutGrouped.count(), numImagesWithoutGrouped, numImagesWithGrouped); } else { statusBarSelectionText = i18n("%1/%2 [%3/%4] items selected", selectionWithoutGrouped.count(), numImagesWithoutGrouped, selection.count(), numImagesWithGrouped); statusBarSelectionToolTip = i18n("%1/%2 items selected. With grouped items: %3/%4", selectionWithoutGrouped.count(), numImagesWithoutGrouped, selection.count(), numImagesWithGrouped); } break; } // no break; is completely intentional, arriving here is equivalent to case 1: [[fallthrough]]; } case 1: { slotSetCheckedExifOrientationAction(selectionWithoutGrouped.first()); int index = listAll.indexOf(selection.first()) + 1; if (numImagesWithGrouped == numImagesWithoutGrouped) { statusBarSelectionText = selection.first().fileUrl().fileName() + i18n(" (%1 of %2)", index, numImagesWithoutGrouped); } else { int indexWithoutGrouped = d->view->allInfo(false).indexOf(selectionWithoutGrouped.first()) + 1; statusBarSelectionText = selection.first().fileUrl().fileName() + i18n(" (%1 of %2 [%3])", indexWithoutGrouped, numImagesWithoutGrouped, numImagesWithGrouped); statusBarSelectionToolTip = selection.first().fileUrl().fileName() + i18n(" (%1 of %2. Total with grouped items: %3)", indexWithoutGrouped, numImagesWithoutGrouped, numImagesWithGrouped); } break; } } d->statusLabel->setAdjustedText(statusBarSelectionText); d->statusLabel->setToolTip(statusBarSelectionToolTip); } void DigikamApp::slotSelectionChanged(int selectionCount) { // The preview can either be activated when only one image is selected, // or if multiple images are selected, but one image is the 'current image'. bool hasAtLeastCurrent =(selectionCount == 1) || ( (selectionCount > 0) && d->view->hasCurrentItem()); d->imagePreviewAction->setEnabled(hasAtLeastCurrent); d->imageViewAction->setEnabled(hasAtLeastCurrent); d->imageFindSimilarAction->setEnabled(selectionCount == 1); d->imageRenameAction->setEnabled(selectionCount > 0); d->imageLightTableAction->setEnabled(selectionCount > 0); d->imageAddLightTableAction->setEnabled(selectionCount > 0); d->imageAddCurrentQueueAction->setEnabled((selectionCount > 0) && !QueueMgrWindow::queueManagerWindow()->isBusy()); d->imageAddNewQueueAction->setEnabled((selectionCount > 0) && !QueueMgrWindow::queueManagerWindow()->isBusy()); d->imageWriteMetadataAction->setEnabled(selectionCount > 0); d->imageReadMetadataAction->setEnabled(selectionCount > 0); d->imageDeleteAction->setEnabled(selectionCount > 0); d->imageRotateActionMenu->setEnabled(selectionCount > 0); d->imageFlipActionMenu->setEnabled(selectionCount > 0); d->imageExifOrientationActionMenu->setEnabled(selectionCount > 0); d->slideShowSelectionAction->setEnabled(selectionCount > 0); d->moveSelectionToAlbumAction->setEnabled(selectionCount > 0); d->cutItemsAction->setEnabled(selectionCount > 0); d->copyItemsAction->setEnabled(selectionCount > 0); m_metadataEditAction->setEnabled(selectionCount > 0); d->openWithAction->setEnabled(selectionCount > 0); d->imageAutoExifActionMenu->setEnabled(selectionCount > 0); #ifdef HAVE_MARBLE m_geolocationEditAction->setEnabled(selectionCount > 0); #endif if (selectionCount > 0) { d->imageWriteMetadataAction->setText(i18np("Write Metadata to File", "Write Metadata to Selected Files", selectionCount)); d->imageReadMetadataAction->setText(i18np("Reread Metadata From File", - "Reread Metadata From Selected Filess", selectionCount)); + "Reread Metadata From Selected Files", selectionCount)); slotResetExifOrientationActions(); } } void DigikamApp::slotExit() { close(); } void DigikamApp::downloadImages( const QString& folder ) { if (!folder.isNull()) { // activate window when called by media menu and DCOP if (isMinimized()) { KWindowSystem::unminimizeWindow(winId()); } KWindowSystem::activateWindow(winId()); emit queuedOpenCameraUiFromPath(folder); } } void DigikamApp::cameraAutoDetect() { // activate window when called by media menu and DCOP if (isMinimized()) { KWindowSystem::unminimizeWindow(winId()); } KWindowSystem::activateWindow(winId()); slotCameraAutoDetect(); } void DigikamApp::loadCameras() { KActionCollection *ac = actionCollection(); d->cameraMenu->setTitle(i18n("Cameras")); d->cameraMenu->setIcon(QIcon::fromTheme(QLatin1String("camera-photo"))); d->usbMediaMenu->setTitle(i18n("USB Storage Devices")); d->usbMediaMenu->setIcon(QIcon::fromTheme(QLatin1String("drive-removable-media"))); d->cardReaderMenu->setTitle(i18n("Card Readers")); d->cardReaderMenu->setIcon(QIcon::fromTheme(QLatin1String("media-flash-sd-mmc"))); ac->addAction(QLatin1String("cameras"), d->cameraMenu->menuAction()); ac->addAction(QLatin1String("usb_media"), d->usbMediaMenu->menuAction()); ac->addAction(QLatin1String("card_reader"), d->cardReaderMenu->menuAction()); // ----------------------------------------------------------------- d->addImagesAction = new QAction(QIcon::fromTheme(QLatin1String("document-import")), i18n("Add Images..."), this); d->addImagesAction->setWhatsThis(i18n("Adds new items to an Album.")); connect(d->addImagesAction, SIGNAL(triggered()), this, SLOT(slotImportAddImages())); ac->addAction(QLatin1String("import_addImages"), d->addImagesAction); ac->setDefaultShortcut(d->addImagesAction, Qt::CTRL+Qt::ALT+Qt::Key_I); // ----------------------------------------------------------------- d->addFoldersAction = new QAction(QIcon::fromTheme(QLatin1String("folder-new")), i18n("Add Folders..."), this); d->addFoldersAction->setWhatsThis(i18n("Adds new folders to Album library.")); connect(d->addFoldersAction, SIGNAL(triggered()), this, SLOT(slotImportAddFolders())); ac->addAction(QLatin1String("import_addFolders"), d->addFoldersAction); // -- fill manually added cameras ---------------------------------- d->cameraList->load(); // -- scan Solid devices ------------------------------------------- fillSolidMenus(); connect(Solid::DeviceNotifier::instance(), SIGNAL(deviceAdded(QString)), this, SLOT(slotSolidDeviceChanged(QString))); connect(Solid::DeviceNotifier::instance(), SIGNAL(deviceRemoved(QString)), this, SLOT(slotSolidDeviceChanged(QString))); // -- queued connections ------------------------------------------- connect(this, SIGNAL(queuedOpenCameraUiFromPath(QString)), this, SLOT(slotOpenCameraUiFromPath(QString)), Qt::QueuedConnection); connect(this, SIGNAL(queuedOpenSolidDevice(QString)), this, SLOT(slotOpenSolidDevice(QString)), Qt::QueuedConnection); } void DigikamApp::slotCameraAdded(CameraType* ctype) { if (!ctype) { return; } QAction* const cAction = new QAction(QIcon::fromTheme(QLatin1String("camera-photo")), ctype->title(), d->manualCameraActionGroup); cAction->setData(ctype->title()); actionCollection()->addAction(ctype->title(), cAction); ctype->setAction(cAction); updateCameraMenu(); updateQuickImportAction(); } void DigikamApp::slotCameraRemoved(QAction* cAction) { if (cAction) { d->manualCameraActionGroup->removeAction(cAction); } updateCameraMenu(); updateQuickImportAction(); } void DigikamApp::slotCameraAutoDetect() { bool retry = false; CameraType* const ctype = d->cameraList->autoDetect(retry); if (!ctype && retry) { QTimer::singleShot(0, this, SLOT(slotCameraAutoDetect())); return; } if (ctype && ctype->action()) { ctype->action()->activate(QAction::Trigger); } } void DigikamApp::slotOpenCameraUiFromPath(const QString& path) { if (path.isEmpty()) { return; } // the ImportUI will delete itself when it has finished ImportUI* const cgui = new ImportUI(i18n("Images found in %1", path), QLatin1String("directory browse"), QLatin1String("Fixed"), path, 1); cgui->show(); connect(cgui, SIGNAL(signalLastDestination(QUrl)), d->view, SLOT(slotSelectAlbum(QUrl))); } void DigikamApp::slotOpenManualCamera(QAction* action) { CameraType* const ctype = d->cameraList->find(action->data().toString()); if (ctype) { // check not to open two dialogs for the same camera if (ctype->currentImportUI() && !ctype->currentImportUI()->isClosed()) { // show and raise dialog if (ctype->currentImportUI()->isMinimized()) { KWindowSystem::unminimizeWindow(ctype->currentImportUI()->winId()); } KWindowSystem::activateWindow(ctype->currentImportUI()->winId()); } else { // the ImportUI will delete itself when it has finished ImportUI* const cgui = new ImportUI(ctype->title(), ctype->model(), ctype->port(), ctype->path(), ctype->startingNumber()); ctype->setCurrentImportUI(cgui); cgui->show(); connect(cgui, SIGNAL(signalLastDestination(QUrl)), d->view, SLOT(slotSelectAlbum(QUrl))); } } } void DigikamApp::slotOpenSolidDevice(const QString& udi) { // Identifies device as either Camera or StorageAccess and calls methods accordingly Solid::Device device(udi); if (!device.isValid()) { QMessageBox::critical(this, qApp->applicationName(), i18n("The specified device (\"%1\") is not valid.", udi)); return; } if (device.is()) { openSolidUsmDevice(udi); } else if (device.is()) { if (!checkSolidCamera(device)) { QMessageBox::critical(this, qApp->applicationName(), i18n("The specified camera (\"%1\") is not supported.", udi)); return; } openSolidCamera(udi); } } void DigikamApp::slotOpenSolidCamera(QAction* action) { QString udi = action->data().toString(); openSolidCamera(udi, action->iconText()); } void DigikamApp::openSolidCamera(const QString& udi, const QString& cameraLabel) { // if there is already an open ImportUI for the device, show and raise it, and be done if (d->cameraUIMap.contains(udi)) { ImportUI* const ui = d->cameraUIMap.value(udi); if (ui && !ui->isClosed()) { if (ui->isMinimized()) { KWindowSystem::unminimizeWindow(ui->winId()); } KWindowSystem::activateWindow(ui->winId()); return; } } // recreate device from unambiguous UDI Solid::Device device(udi); if ( device.isValid() ) { if (cameraLabel.isNull()) { QString label = labelForSolidCamera(device); } Solid::Camera* const camera = device.as(); QList list = camera->driverHandle(QLatin1String("gphoto")).toList(); // all sanity checks have already been done when creating the action if (list.size() < 3) { return; } // NOTE: See bug #262296: With KDE 4.6, Solid API return device vendor id // and product id in hexadecimal strings. bool ok; int vendorId = list.at(1).toString().toInt(&ok, 16); int productId = list.at(2).toString().toInt(&ok, 16); QString model, port; if (CameraList::findConnectedCamera(vendorId, productId, model, port)) { qCDebug(DIGIKAM_GENERAL_LOG) << "Found camera from ids " << vendorId << " " << productId << " camera is: " << model << " at " << port; // the ImportUI will delete itself when it has finished ImportUI* const cgui = new ImportUI(cameraLabel, model, port, QLatin1String("/"), 1); d->cameraUIMap[udi] = cgui; cgui->show(); connect(cgui, SIGNAL(signalLastDestination(QUrl)), d->view, SLOT(slotSelectAlbum(QUrl))); } else { qCDebug(DIGIKAM_GENERAL_LOG) << "Failed to detect camera with GPhoto2 from Solid information"; } } } void DigikamApp::slotOpenSolidUsmDevice(QAction* action) { QString udi = action->data().toString(); openSolidUsmDevice(udi, action->iconText()); } void DigikamApp::openSolidUsmDevice(const QString& udi, const QString& givenLabel) { QString mediaLabel = givenLabel; // if there is already an open ImportUI for the device, show and raise it if (d->cameraUIMap.contains(udi)) { ImportUI* const ui = d->cameraUIMap.value(udi); if (ui && !ui->isClosed()) { if (ui->isMinimized()) { KWindowSystem::unminimizeWindow(ui->winId()); } KWindowSystem::activateWindow(ui->winId()); return; } } // recreate device from unambiguous UDI Solid::Device device(udi); if ( device.isValid() ) { Solid::StorageAccess* const access = device.as(); if (!access) { return; } if (!access->isAccessible()) { QApplication::setOverrideCursor(Qt::WaitCursor); if (!access->setup()) { return; } d->eventLoop = new QEventLoop(this); connect(access, SIGNAL(setupDone(Solid::ErrorType,QVariant,QString)), this, SLOT(slotSolidSetupDone(Solid::ErrorType,QVariant,QString))); int returnCode = d->eventLoop->exec(QEventLoop::ExcludeUserInputEvents); delete d->eventLoop; d->eventLoop = 0; QApplication::restoreOverrideCursor(); if (returnCode == 1) { QMessageBox::critical(this, qApp->applicationName(), d->solidErrorMessage); return; } } // Create Camera UI QString path = QDir::fromNativeSeparators(access->filePath()); if (mediaLabel.isNull()) { mediaLabel = path; } // the ImportUI will delete itself when it has finished ImportUI* const cgui = new ImportUI(i18n("Images on %1", mediaLabel), QLatin1String("directory browse"), QLatin1String("Fixed"), path, 1); d->cameraUIMap[udi] = cgui; cgui->show(); connect(cgui, SIGNAL(signalLastDestination(QUrl)), d->view, SLOT(slotSelectAlbum(QUrl))); } } void DigikamApp::slotSolidSetupDone(Solid::ErrorType errorType, QVariant errorData, const QString& /*udi*/) { if (!d->eventLoop) { return; } if (errorType == Solid::NoError) { d->eventLoop->exit(0); } else { d->solidErrorMessage = i18n("Cannot access the storage device.\n"); d->solidErrorMessage += errorData.toString(); d->eventLoop->exit(1); } } void DigikamApp::slotSolidDeviceChanged(const QString& udi) { Q_UNUSED(udi) fillSolidMenus(); } bool DigikamApp::checkSolidCamera(const Solid::Device& cameraDevice) { const Solid::Camera* const camera = cameraDevice.as(); if (!camera) { return false; } QStringList drivers = camera->supportedDrivers(); qCDebug(DIGIKAM_GENERAL_LOG) << "fillSolidMenus: Found Camera " << QString::fromUtf8("%1 %2").arg(cameraDevice.vendor()).arg(cameraDevice.product()) << " protocols " << camera->supportedProtocols() << " drivers " << camera->supportedDrivers(QLatin1String("ptp")); // We handle gphoto2 cameras in this loop if (! (camera->supportedDrivers().contains(QLatin1String("gphoto")) || camera->supportedProtocols().contains(QLatin1String("ptp"))) ) { return false; } QVariant driverHandle = camera->driverHandle(QLatin1String("gphoto")); if (!driverHandle.canConvert(QVariant::List)) { qCWarning(DIGIKAM_GENERAL_LOG) << "Solid returns unsupported driver handle for gphoto2"; return false; } QList driverHandleList = driverHandle.toList(); if (driverHandleList.size() < 3 || driverHandleList.at(0).toString() != QLatin1String("usb") || !driverHandleList.at(1).canConvert(QVariant::Int) || !driverHandleList.at(2).canConvert(QVariant::Int) ) { qCWarning(DIGIKAM_GENERAL_LOG) << "Solid returns unsupported driver handle for gphoto2"; return false; } return true; } QString DigikamApp::labelForSolidCamera(const Solid::Device& cameraDevice) { QString vendor = cameraDevice.vendor(); QString product = cameraDevice.product(); if (product == QLatin1String("USB Imaging Interface") || product == QLatin1String("USB Vendor Specific Interface")) { Solid::Device parentUsbDevice = cameraDevice.parent(); if (parentUsbDevice.isValid()) { vendor = parentUsbDevice.vendor(); product = parentUsbDevice.product(); if (!vendor.isEmpty() && !product.isEmpty()) { if (vendor == QLatin1String("Canon, Inc.")) { vendor = QLatin1String("Canon"); if (product.startsWith(QLatin1String("Canon "))) { product = product.mid(6); // cut off another "Canon " from product } if (product.endsWith(QLatin1String(" (ptp)"))) { product.chop(6); // cut off " (ptp)" } } else if (vendor == QLatin1String("Fuji Photo Film Co., Ltd")) { vendor = QLatin1String("Fuji"); } else if (vendor == QLatin1String("Nikon Corp.")) { vendor = QLatin1String("Nikon"); if (product.startsWith(QLatin1String("NIKON "))) { product = product.mid(6); } } } } } return vendor + QLatin1Char(' ') + product; } void DigikamApp::fillSolidMenus() { QHash newAppearanceTimes; d->usbMediaMenu->clear(); d->cardReaderMenu->clear(); // delete the actionGroups to avoid duplicate menu entries delete d->solidUsmActionGroup; delete d->solidCameraActionGroup; d->solidCameraActionGroup = new QActionGroup(this); connect(d->solidCameraActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotOpenSolidCamera(QAction*))); d->solidUsmActionGroup = new QActionGroup(this); connect(d->solidUsmActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotOpenSolidUsmDevice(QAction*))); // -------------------------------------------------------- QList cameraDevices = Solid::Device::listFromType(Solid::DeviceInterface::Camera); foreach(const Solid::Device& cameraDevice, cameraDevices) { // USM camera: will be handled below if (cameraDevice.is()) { continue; } if (!checkSolidCamera(cameraDevice)) { continue; } // -------------------------------------------------------- QString l = labelForSolidCamera(cameraDevice); QString label = CameraNameHelper::cameraNameAutoDetected(l.trimmed()); // -------------------------------------------------------- QString iconName = cameraDevice.icon(); if (iconName.isEmpty()) { iconName = QLatin1String("camera-photo"); } QAction* const action = new QAction(label, d->solidCameraActionGroup); action->setIcon(QIcon::fromTheme(iconName)); // set data to identify device in action slot slotSolidSetupDevice action->setData(cameraDevice.udi()); newAppearanceTimes[cameraDevice.udi()] = d->cameraAppearanceTimes.contains(cameraDevice.udi()) ? d->cameraAppearanceTimes.value(cameraDevice.udi()) : QDateTime::currentDateTime(); d->cameraMenu->addAction(action); } QList storageDevices = Solid::Device::listFromType(Solid::DeviceInterface::StorageAccess); foreach(const Solid::Device& accessDevice, storageDevices) { // check for StorageAccess if (!accessDevice.is()) { continue; } // check for StorageDrive Solid::Device driveDevice; for (Solid::Device currentDevice = accessDevice; currentDevice.isValid(); currentDevice = currentDevice.parent()) { if (currentDevice.is()) { driveDevice = currentDevice; break; } } if (!driveDevice.isValid()) { continue; } const Solid::StorageDrive* const drive = driveDevice.as(); QString driveType; bool isHarddisk = false; switch (drive->driveType()) { // skip these case Solid::StorageDrive::CdromDrive: case Solid::StorageDrive::Floppy: case Solid::StorageDrive::Tape: default: continue; // accept card readers case Solid::StorageDrive::CompactFlash: driveType = i18n("CompactFlash Card Reader"); break; case Solid::StorageDrive::MemoryStick: driveType = i18n("Memory Stick Reader"); break; case Solid::StorageDrive::SmartMedia: driveType = i18n("SmartMedia Card Reader"); break; case Solid::StorageDrive::SdMmc: driveType = i18n("SD / MMC Card Reader"); break; case Solid::StorageDrive::Xd: driveType = i18n("xD Card Reader"); break; case Solid::StorageDrive::HardDisk: // We don't want to list HardDisk partitions, but USB Mass Storage devices. // Don't know what is the exact difference between removable and hotpluggable. if (drive->isRemovable() || drive->isHotpluggable()) { isHarddisk = true; if (drive->bus() == Solid::StorageDrive::Usb) { driveType = i18n("USB Disk"); } else { driveType = i18nc("non-USB removable storage device", "Disk"); } break; } else { continue; } } // check for StorageVolume Solid::Device volumeDevice; for (Solid::Device currentDevice = accessDevice; currentDevice.isValid(); currentDevice = currentDevice.parent()) { if (currentDevice.is()) { volumeDevice = currentDevice; break; } } if (!volumeDevice.isValid()) { continue; } bool isCamera = accessDevice.is(); const Solid::StorageAccess* const access = accessDevice.as(); const Solid::StorageVolume* const volume = volumeDevice.as(); if (volume->isIgnored()) { continue; } QString label; if (isCamera) { label = accessDevice.vendor() + QLatin1Char(' ') + accessDevice.product(); } else { QString labelOrProduct; if (!volume->label().isEmpty()) { labelOrProduct = volume->label(); } else if (!volumeDevice.product().isEmpty()) { labelOrProduct = volumeDevice.product(); } else if (!volumeDevice.vendor().isEmpty()) { labelOrProduct = volumeDevice.vendor(); } else if (!driveDevice.product().isEmpty()) { labelOrProduct = driveDevice.product(); } if (!labelOrProduct.isNull()) { if (!access->filePath().isEmpty()) { label += i18nc(" \"\" at ", "%1 \"%2\" at %3", driveType, labelOrProduct, QDir::toNativeSeparators(access->filePath())); } else { label += i18nc(" \"\"", "%1 \"%2\"", driveType, labelOrProduct); } } else { if (!access->filePath().isEmpty()) { label += i18nc(" at ", "%1 at %2", driveType, QDir::toNativeSeparators(access->filePath())); } else { label += driveType; } } if (volume->size()) { label += i18nc("device label etc... ()", " (%1)", ImagePropertiesTab::humanReadableBytesCount(volume->size())); } } QString iconName; if (!driveDevice.icon().isEmpty()) { iconName = driveDevice.icon(); } else if (!accessDevice.icon().isEmpty()) { iconName = accessDevice.icon(); } else if (!volumeDevice.icon().isEmpty()) { iconName = volumeDevice.icon(); } QAction* const action = new QAction(label, d->solidUsmActionGroup); if (!iconName.isEmpty()) { action->setIcon(QIcon::fromTheme(iconName)); } // set data to identify device in action slot slotSolidSetupDevice action->setData(accessDevice.udi()); newAppearanceTimes[accessDevice.udi()] = d->cameraAppearanceTimes.contains(accessDevice.udi()) ? d->cameraAppearanceTimes.value(accessDevice.udi()) : QDateTime::currentDateTime(); if (isCamera) { d->cameraMenu->addAction(action); } if (isHarddisk) { d->usbMediaMenu->addAction(action); } else { d->cardReaderMenu->addAction(action); } } /* //TODO: Find best usable solution when no devices are connected: One entry, hide, or disable? // Add one entry telling that no device is available if (d->cameraSolidMenu->isEmpty()) { QAction* const action = d->cameraSolidMenu->addAction(i18n("No Camera Connected")); action->setEnabled(false); } if (d->usbMediaMenu->isEmpty()) { QAction* const action = d->usbMediaMenu->addAction(i18n("No Storage Devices Found")); action->setEnabled(false); } if (d->cardReaderMenu->isEmpty()) { QAction* const action = d->cardReaderMenu->addAction(i18n("No Card Readers Available")); action->setEnabled(false); } // hide empty menus d->cameraSolidMenu->menuAction()->setVisible(!d->cameraSolidMenu->isEmpty()); d->usbMediaMenu->menuAction()->setVisible(!d->usbMediaMenu->isEmpty()); d->cardReaderMenu->menuAction()->setVisible(!d->cardReaderMenu->isEmpty()); */ d->cameraAppearanceTimes = newAppearanceTimes; // disable empty menus d->usbMediaMenu->setEnabled(!d->usbMediaMenu->isEmpty()); d->cardReaderMenu->setEnabled(!d->cardReaderMenu->isEmpty()); updateCameraMenu(); updateQuickImportAction(); } void DigikamApp::slotSetup() { setup(); } bool DigikamApp::setup() { return Setup::execDialog(this, Setup::LastPageUsed); } bool DigikamApp::setupICC() { return Setup::execSinglePage(this, Setup::ICCPage); } void DigikamApp::slotSetupCamera() { Setup::execSinglePage(this, Setup::CameraPage); } void DigikamApp::slotSetupChanged() { // raw loading options might have changed LoadingCacheInterface::cleanCache(); // TODO: clear history when location changed //if(ApplicationSettings::instance()->getAlbumLibraryPath() != AlbumManager::instance()->getLibraryPath()) // d->view->clearHistory(); const DbEngineParameters prm = ApplicationSettings::instance()->getDbEngineParameters(); if (!AlbumManager::instance()->databaseEqual(prm)) { AlbumManager::instance()->changeDatabase(ApplicationSettings::instance()->getDbEngineParameters()); } if (ApplicationSettings::instance()->getShowFolderTreeViewItemsCount()) { AlbumManager::instance()->prepareItemCounts(); } // Load full-screen options KConfigGroup group = KSharedConfig::openConfig()->group(configGroupName()); readFullScreenSettings(group); d->view->applySettings(); AlbumThumbnailLoader::instance()->setThumbnailSize(ApplicationSettings::instance()->getTreeViewIconSize()); if (LightTableWindow::lightTableWindowCreated()) { LightTableWindow::lightTableWindow()->applySettings(); } if (QueueMgrWindow::queueManagerWindowCreated()) { QueueMgrWindow::queueManagerWindow()->applySettings(); } d->config->sync(); } void DigikamApp::slotEditKeys() { editKeyboardShortcuts(); } void DigikamApp::slotDBStat() { showDigikamDatabaseStat(); } void DigikamApp::populateThemes() { if (d->splashScreen) { d->splashScreen->setMessage(i18n("Loading themes...")); } ThemeManager::instance()->setThemeMenuAction(new QMenu(i18n("&Themes"), this)); ThemeManager::instance()->registerThemeActions(this); ThemeManager::instance()->setCurrentTheme(ApplicationSettings::instance()->getCurrentTheme()); connect(ThemeManager::instance(), SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); } void DigikamApp::slotThemeChanged() { ApplicationSettings::instance()->setCurrentTheme(ThemeManager::instance()->currentThemeName()); } void DigikamApp::preloadWindows() { if (d->splashScreen) { d->splashScreen->setMessage(i18n("Loading tools...")); } QueueMgrWindow::queueManagerWindow(); ImageWindow::imageWindow(); LightTableWindow::lightTableWindow(); d->tagsActionManager->registerTagsActionCollections(); } void DigikamApp::slotDatabaseMigration() { DatabaseMigrationDialog dlg(this); dlg.exec(); } void DigikamApp::slotMaintenance() { MaintenanceDlg* const dlg = new MaintenanceDlg(this); if (dlg->exec() == QDialog::Accepted) { d->maintenanceAction->setEnabled(false); MaintenanceMngr* const mngr = new MaintenanceMngr(this); connect(mngr, SIGNAL(signalComplete()), this, SLOT(slotMaintenanceDone())); mngr->setSettings(dlg->settings()); } } void DigikamApp::slotMaintenanceDone() { d->maintenanceAction->setEnabled(true); d->view->refreshView(); if (LightTableWindow::lightTableWindowCreated()) { LightTableWindow::lightTableWindow()->refreshView(); } if (QueueMgrWindow::queueManagerWindowCreated()) { QueueMgrWindow::queueManagerWindow()->refreshView(); } } void DigikamApp::slotExpoBlending() { ExpoBlendingManager::instance()->checkBinaries(); ExpoBlendingManager::instance()->setItemsList(view()->selectedUrls(ApplicationSettings::Tools)); ExpoBlendingManager::instance()->run(); } void DigikamApp::slotPanorama() { #ifdef HAVE_PANORAMA PanoManager::instance()->checkBinaries(); PanoManager::instance()->setItemsList(view()->selectedUrls(ApplicationSettings::Tools)); PanoManager::instance()->run(); #endif } void DigikamApp::slotVideoSlideshow() { #ifdef HAVE_MEDIAPLAYER VidSlideWizard w(this, new DBInfoIface(this, QList(), ApplicationSettings::Tools)); w.exec(); #endif } void DigikamApp::slotHtmlGallery() { #ifdef HAVE_HTMLGALLERY HTMLWizard w(this, new DBInfoIface(this, QList(), ApplicationSettings::Tools)); w.exec(); #endif } void DigikamApp::slotCalendar() { CalWizard w(view()->selectedUrls(ApplicationSettings::Tools), this); w.exec(); } void DigikamApp::slotSendByMail() { MailWizard w(this, new DBInfoIface(this, QList(), ApplicationSettings::Tools)); w.exec(); } void DigikamApp::slotPrintCreator() { AdvPrintWizard w(this, new DBInfoIface(this, QList(), ApplicationSettings::Tools)); w.exec(); } void DigikamApp::slotMediaServer() { DBInfoIface* const iface = new DBInfoIface(this, QList(), ApplicationSettings::Tools); // NOTE: We overwrite the default albums chooser object name for load save check items state between sessions. // The goal is not mix these settings with other export tools. iface->setObjectName(QLatin1String("SetupMediaServerIface")); DMediaServerDlg w(this, iface); w.exec(); } void DigikamApp::slotRecurseAlbums(bool checked) { d->view->setRecurseAlbums(checked); } void DigikamApp::slotRecurseTags(bool checked) { d->view->setRecurseTags(checked); } void DigikamApp::slotZoomSliderChanged(int size) { d->view->setThumbSize(size); } void DigikamApp::slotThumbSizeChanged(int size) { d->zoomBar->setThumbsSize(size); if (!fullScreenIsActive() && d->autoShowZoomToolTip) { d->zoomBar->triggerZoomTrackerToolTip(); } } void DigikamApp::slotZoomChanged(double zoom) { double zmin = d->view->zoomMin(); double zmax = d->view->zoomMax(); d->zoomBar->setZoom(zoom, zmin, zmax); if (!fullScreenIsActive() && d->autoShowZoomToolTip) { d->zoomBar->triggerZoomTrackerToolTip(); } } void DigikamApp::slotImportAddImages() { QString startingPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); QUrl url = DFileDialog::getExistingDirectoryUrl(this, i18n("Select folder to parse"), QUrl::fromLocalFile(startingPath)); if (url.isEmpty() || !url.isLocalFile()) { return; } // The folder contents will be parsed by Camera interface in "Directory Browse" mode. downloadFrom(url.toLocalFile()); } void DigikamApp::slotImportAddFolders() { // NOTE: QFileDialog don't have an option to permit multiple selection of directories. // This work around is inspired from http://www.qtcentre.org/threads/34226-QFileDialog-select-multiple-directories // Check Later Qt 5.4 if a new native Qt way have been introduced. QPointer dlg = new DFileDialog(this); dlg->setWindowTitle(i18n("Select folders to import into album")); dlg->setFileMode(DFileDialog::DirectoryOnly); QListView* const l = dlg->findChild(QLatin1String("listView")); if (l) { l->setSelectionMode(QAbstractItemView::MultiSelection); } QTreeView* const t = dlg->findChild(); if (t) { t->setSelectionMode(QAbstractItemView::MultiSelection); } if (dlg->exec() != QDialog::Accepted) { delete dlg; return; } QList urls = dlg->selectedUrls(); delete dlg; if (urls.empty()) { return; } QList albumList = AlbumManager::instance()->currentAlbums(); Album* album = 0; if (!albumList.isEmpty()) { album = albumList.first(); } if (album && album->type() != Album::PHYSICAL) { album = 0; } QString header(i18n("

Please select the destination album from the digiKam library to " "import folders into.

")); album = AlbumSelectDialog::selectAlbum(this, (PAlbum*)album, header); if (!album) { return; } PAlbum* const pAlbum = dynamic_cast(album); if (!pAlbum) { return; } DIO::copy(urls, pAlbum); } void DigikamApp::slotToggleShowBar() { d->view->toggleShowBar(d->showBarAction->isChecked()); } void DigikamApp::moveEvent(QMoveEvent*) { emit signalWindowHasMoved(); } void DigikamApp::updateCameraMenu() { d->cameraMenu->clear(); foreach(QAction* const action, d->solidCameraActionGroup->actions()) { d->cameraMenu->addAction(action); } d->cameraMenu->addSeparator(); foreach(QAction* const action, d->manualCameraActionGroup->actions()) { // remove duplicate entries, prefer manually added cameras foreach(QAction* const actionSolid, d->solidCameraActionGroup->actions()) { if (CameraNameHelper::sameDevices(actionSolid->iconText(), action->iconText())) { d->cameraMenu->removeAction(actionSolid); d->solidCameraActionGroup->removeAction(actionSolid); } } d->cameraMenu->addAction(action); } d->cameraMenu->addSeparator(); d->cameraMenu->addAction(actionCollection()->action(QLatin1String("camera_add"))); } void DigikamApp::updateQuickImportAction() { d->quickImportMenu->clear(); foreach(QAction* const action, d->solidCameraActionGroup->actions()) { d->quickImportMenu->addAction(action); } foreach(QAction* const action, d->solidUsmActionGroup->actions()) { d->quickImportMenu->addAction(action); } foreach(QAction* const action, d->manualCameraActionGroup->actions()) { d->quickImportMenu->addAction(action); } if (d->quickImportMenu->actions().isEmpty()) { d->quickImportMenu->setEnabled(false); } else { disconnect(d->quickImportMenu->menuAction(), SIGNAL(triggered()), 0, 0); QAction* primaryAction = 0; QDateTime latest; foreach(QAction* const action, d->quickImportMenu->actions()) { QDateTime appearanceTime = d->cameraAppearanceTimes.value(action->data().toString()); if (latest.isNull() || appearanceTime > latest) { primaryAction = action; latest = appearanceTime; } } if (!primaryAction) { primaryAction = d->quickImportMenu->actions().first(); } connect(d->quickImportMenu->menuAction(), SIGNAL(triggered()), primaryAction, SLOT(trigger())); d->quickImportMenu->setEnabled(true); } } void DigikamApp::setupExifOrientationActions() { KActionCollection* const ac = actionCollection(); QSignalMapper* const exifOrientationMapper = new QSignalMapper(d->view); connect(exifOrientationMapper, SIGNAL(mapped(int)), d->view, SLOT(slotImageExifOrientation(int))); d->imageExifOrientationActionMenu = new QMenu(i18n("Adjust Exif Orientation Tag"), this); ac->addAction(QLatin1String("image_set_exif_orientation"), d->imageExifOrientationActionMenu->menuAction()); d->imageSetExifOrientation1Action = new QAction(i18nc("normal exif orientation", "Normal"), this); d->imageSetExifOrientation1Action->setCheckable(true); d->imageSetExifOrientation2Action = new QAction(i18n("Flipped Horizontally"), this); d->imageSetExifOrientation2Action->setCheckable(true); d->imageSetExifOrientation3Action = new QAction(i18n("Rotated Upside Down"), this); d->imageSetExifOrientation3Action->setCheckable(true); d->imageSetExifOrientation4Action = new QAction(i18n("Flipped Vertically"), this); d->imageSetExifOrientation4Action->setCheckable(true); d->imageSetExifOrientation5Action = new QAction(i18n("Rotated Right / Horiz. Flipped"), this); d->imageSetExifOrientation5Action->setCheckable(true); d->imageSetExifOrientation6Action = new QAction(i18n("Rotated Right"), this); d->imageSetExifOrientation6Action->setCheckable(true); d->imageSetExifOrientation7Action = new QAction(i18n("Rotated Right / Vert. Flipped"), this); d->imageSetExifOrientation7Action->setCheckable(true); d->imageSetExifOrientation8Action = new QAction(i18n("Rotated Left"), this); d->imageSetExifOrientation8Action->setCheckable(true); d->exifOrientationActionGroup = new QActionGroup(d->imageExifOrientationActionMenu); d->exifOrientationActionGroup->addAction(d->imageSetExifOrientation1Action); d->exifOrientationActionGroup->addAction(d->imageSetExifOrientation2Action); d->exifOrientationActionGroup->addAction(d->imageSetExifOrientation3Action); d->exifOrientationActionGroup->addAction(d->imageSetExifOrientation4Action); d->exifOrientationActionGroup->addAction(d->imageSetExifOrientation5Action); d->exifOrientationActionGroup->addAction(d->imageSetExifOrientation6Action); d->exifOrientationActionGroup->addAction(d->imageSetExifOrientation7Action); d->exifOrientationActionGroup->addAction(d->imageSetExifOrientation8Action); d->imageSetExifOrientation1Action->setChecked(true); ac->addAction(QLatin1String("image_set_exif_orientation_normal"), d->imageSetExifOrientation1Action); ac->addAction(QLatin1String("image_set_exif_orientation_flipped_horizontal"), d->imageSetExifOrientation2Action); ac->addAction(QLatin1String("image_set_exif_orientation_rotated_upside_down"), d->imageSetExifOrientation3Action); ac->addAction(QLatin1String("image_set_exif_orientation_flipped_vertically"), d->imageSetExifOrientation4Action); ac->addAction(QLatin1String("image_set_exif_orientation_rotated_right_hor_flipped"), d->imageSetExifOrientation5Action); ac->addAction(QLatin1String("image_set_exif_orientation_rotated_right"), d->imageSetExifOrientation6Action); ac->addAction(QLatin1String("image_set_exif_orientation_rotated_right_ver_flipped"), d->imageSetExifOrientation7Action); ac->addAction(QLatin1String("image_set_exif_orientation_rotated_left"), d->imageSetExifOrientation8Action); d->imageExifOrientationActionMenu->addAction(d->imageSetExifOrientation1Action); d->imageExifOrientationActionMenu->addAction(d->imageSetExifOrientation2Action); d->imageExifOrientationActionMenu->addAction(d->imageSetExifOrientation3Action); d->imageExifOrientationActionMenu->addAction(d->imageSetExifOrientation4Action); d->imageExifOrientationActionMenu->addAction(d->imageSetExifOrientation5Action); d->imageExifOrientationActionMenu->addAction(d->imageSetExifOrientation6Action); d->imageExifOrientationActionMenu->addAction(d->imageSetExifOrientation7Action); d->imageExifOrientationActionMenu->addAction(d->imageSetExifOrientation8Action); connect(d->imageSetExifOrientation1Action, SIGNAL(triggered()), exifOrientationMapper, SLOT(map())); connect(d->imageSetExifOrientation2Action, SIGNAL(triggered()), exifOrientationMapper, SLOT(map())); connect(d->imageSetExifOrientation3Action, SIGNAL(triggered()), exifOrientationMapper, SLOT(map())); connect(d->imageSetExifOrientation4Action, SIGNAL(triggered()), exifOrientationMapper, SLOT(map())); connect(d->imageSetExifOrientation5Action, SIGNAL(triggered()), exifOrientationMapper, SLOT(map())); connect(d->imageSetExifOrientation6Action, SIGNAL(triggered()), exifOrientationMapper, SLOT(map())); connect(d->imageSetExifOrientation7Action, SIGNAL(triggered()), exifOrientationMapper, SLOT(map())); connect(d->imageSetExifOrientation8Action, SIGNAL(triggered()), exifOrientationMapper, SLOT(map())); exifOrientationMapper->setMapping(d->imageSetExifOrientation1Action, 1); exifOrientationMapper->setMapping(d->imageSetExifOrientation2Action, 2); exifOrientationMapper->setMapping(d->imageSetExifOrientation3Action, 3); exifOrientationMapper->setMapping(d->imageSetExifOrientation4Action, 4); exifOrientationMapper->setMapping(d->imageSetExifOrientation5Action, 5); exifOrientationMapper->setMapping(d->imageSetExifOrientation6Action, 6); exifOrientationMapper->setMapping(d->imageSetExifOrientation7Action, 7); exifOrientationMapper->setMapping(d->imageSetExifOrientation8Action, 8); } void DigikamApp::slotResetExifOrientationActions() { d->imageSetExifOrientation1Action->setChecked(false); d->imageSetExifOrientation2Action->setChecked(false); d->imageSetExifOrientation3Action->setChecked(false); d->imageSetExifOrientation4Action->setChecked(false); d->imageSetExifOrientation5Action->setChecked(false); d->imageSetExifOrientation6Action->setChecked(false); d->imageSetExifOrientation7Action->setChecked(false); d->imageSetExifOrientation8Action->setChecked(false); } void DigikamApp::slotSetCheckedExifOrientationAction(const ImageInfo& info) { //DMetadata meta(info.fileUrl().toLocalFile()); //int orientation = (meta.isEmpty()) ? 0 : meta.getImageOrientation(); int orientation = info.orientation(); switch (orientation) { case 1: d->imageSetExifOrientation1Action->setChecked(true); break; case 2: d->imageSetExifOrientation2Action->setChecked(true); break; case 3: d->imageSetExifOrientation3Action->setChecked(true); break; case 4: d->imageSetExifOrientation4Action->setChecked(true); break; case 5: d->imageSetExifOrientation5Action->setChecked(true); break; case 6: d->imageSetExifOrientation6Action->setChecked(true); break; case 7: d->imageSetExifOrientation7Action->setChecked(true); break; case 8: d->imageSetExifOrientation8Action->setChecked(true); break; default: slotResetExifOrientationActions(); break; } } void DigikamApp::setupImageTransformActions() { KActionCollection* const ac = actionCollection(); d->imageRotateActionMenu = new QMenu(i18n("Rotate"), this); d->imageRotateActionMenu->setIcon(QIcon::fromTheme(QLatin1String("object-rotate-right"))); QAction* const left = ac->addAction(QLatin1String("rotate_ccw")); left->setText(i18nc("rotate image left", "Left")); ac->setDefaultShortcut(left, Qt::SHIFT+Qt::CTRL+Qt::Key_Left); connect(left, SIGNAL(triggered(bool)), this, SLOT(slotTransformAction())); d->imageRotateActionMenu->addAction(left); QAction* const right = ac->addAction(QLatin1String("rotate_cw")); right->setText(i18nc("rotate image right", "Right")); ac->setDefaultShortcut(right, Qt::SHIFT+Qt::CTRL+Qt::Key_Right); connect(right, SIGNAL(triggered(bool)), this, SLOT(slotTransformAction())); d->imageRotateActionMenu->addAction(right); ac->addAction(QLatin1String("image_rotate"), d->imageRotateActionMenu->menuAction()); // ----------------------------------------------------------------------------------- d->imageFlipActionMenu = new QMenu(i18n("Flip"), this); d->imageFlipActionMenu->setIcon(QIcon::fromTheme(QLatin1String("flip-horizontal"))); QAction* const hori = ac->addAction(QLatin1String("flip_horizontal")); hori->setText(i18n("Horizontally")); ac->setDefaultShortcut(hori, Qt::CTRL+Qt::Key_Asterisk); connect(hori, SIGNAL(triggered(bool)), this, SLOT(slotTransformAction())); d->imageFlipActionMenu->addAction(hori); QAction* const verti = ac->addAction(QLatin1String("flip_vertical")); verti->setText(i18n("Vertically")); ac->setDefaultShortcut(verti, Qt::CTRL+Qt::Key_Slash); connect(verti, SIGNAL(triggered(bool)), this, SLOT(slotTransformAction())); d->imageFlipActionMenu->addAction(verti); ac->addAction(QLatin1String("image_flip"), d->imageFlipActionMenu->menuAction()); // ----------------------------------------------------------------------------------- d->imageAutoExifActionMenu = new QAction(i18n("Auto Rotate/Flip Using Exif Information"), this); connect(d->imageAutoExifActionMenu, SIGNAL(triggered(bool)), this, SLOT(slotTransformAction())); ac->addAction(QLatin1String("image_transform_exif"), d->imageAutoExifActionMenu); } void DigikamApp::slotTransformAction() { if (sender()->objectName() == QLatin1String("rotate_ccw")) { d->view->imageTransform(MetaEngineRotation::Rotate270); } else if (sender()->objectName() == QLatin1String("rotate_cw")) { d->view->imageTransform(MetaEngineRotation::Rotate90); } else if (sender()->objectName() == QLatin1String("flip_horizontal")) { d->view->imageTransform(MetaEngineRotation::FlipHorizontal); } else if (sender()->objectName() == QLatin1String("flip_vertical")) { d->view->imageTransform(MetaEngineRotation::FlipVertical); } else if (sender()->objectName() == QLatin1String("image_transform_exif")) { // special value for FileActionMngr d->view->imageTransform(MetaEngineRotation::NoTransformation); } } QMenu* DigikamApp::slideShowMenu() const { return d->slideShowAction; } void DigikamApp::rebuild() { QString file = xmlFile(); if (!file.isEmpty()) { setXMLGUIBuildDocument(QDomDocument()); loadStandardsXmlFile(); setXMLFile(file, true); } } void DigikamApp::showSideBars(bool visible) { visible ? d->view->showSideBars() : d->view->hideSideBars(); } void DigikamApp::slotToggleLeftSideBar() { d->view->toggleLeftSidebar(); } void DigikamApp::slotToggleRightSideBar() { d->view->toggleRightSidebar(); } void DigikamApp::slotPreviousLeftSideBarTab() { d->view->previousLeftSideBarTab(); } void DigikamApp::slotNextLeftSideBarTab() { d->view->nextLeftSideBarTab(); } void DigikamApp::slotNextRightSideBarTab() { d->view->nextRightSideBarTab(); } void DigikamApp::slotPreviousRightSideBarTab() { d->view->previousRightSideBarTab(); } void DigikamApp::showThumbBar(bool visible) { view()->toggleShowBar(visible); } bool DigikamApp::thumbbarVisibility() const { return d->showBarAction->isChecked(); } void DigikamApp::slotSwitchedToPreview() { d->imagePreviewAction->setChecked(true); d->zoomBar->setBarMode(DZoomBar::PreviewZoomCtrl); toggleShowBar(); } void DigikamApp::slotSwitchedToIconView() { d->zoomBar->setBarMode(DZoomBar::ThumbsSizeCtrl); d->imageIconViewAction->setChecked(true); toggleShowBar(); } void DigikamApp::slotSwitchedToMapView() { //TODO: Link to map view's zoom actions d->zoomBar->setBarMode(DZoomBar::ThumbsSizeCtrl); #ifdef HAVE_MARBLE d->imageMapViewAction->setChecked(true); #endif // HAVE_MARBLE toggleShowBar(); } void DigikamApp::slotSwitchedToTableView() { d->zoomBar->setBarMode(DZoomBar::ThumbsSizeCtrl); d->imageTableViewAction->setChecked(true); toggleShowBar(); } void DigikamApp::slotSwitchedToTrashView() { d->zoomBar->setBarMode(DZoomBar::ThumbsSizeCtrl); // TODO: disable all other views toggleShowBar(); } void DigikamApp::customizedFullScreenMode(bool set) { toolBarMenuAction()->setEnabled(!set); showMenuBarAction()->setEnabled(!set); showStatusBarAction()->setEnabled(!set); set ? d->showBarAction->setEnabled(false) : toggleShowBar(); d->view->toggleFullScreen(set); } void DigikamApp::toggleShowBar() { switch (d->view->viewMode()) { case StackedView::PreviewImageMode: case StackedView::MediaPlayerMode: d->showBarAction->setEnabled(true); break; default: d->showBarAction->setEnabled(false); break; } } void DigikamApp::slotComponentsInfo() { showDigikamComponentsInfo(); } void DigikamApp::slotToggleColorManagedView() { if (!IccSettings::instance()->isEnabled()) { return; } bool cmv = !IccSettings::instance()->settings().useManagedPreviews; IccSettings::instance()->setUseManagedPreviews(cmv); } void DigikamApp::slotColorManagementOptionsChanged() { ICCSettingsContainer settings = IccSettings::instance()->settings(); d->viewCMViewAction->blockSignals(true); d->viewCMViewAction->setEnabled(settings.enableCM); d->viewCMViewAction->setChecked(settings.useManagedPreviews); d->viewCMViewAction->blockSignals(false); } QString DigikamApp::scannerTargetPlace() { QString place = QDir::homePath(); QStringList pics = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); if (!pics.isEmpty()) place = pics.first(); Album* const album = AlbumManager::instance()->currentAlbums().first(); if (album->type() == Album::PHYSICAL) { PAlbum* const p = dynamic_cast(album); if (p) { place = p->folderPath(); } } else { QStringList cols = CollectionManager::instance()->allAvailableAlbumRootPaths(); if (!cols.isEmpty()) place = cols.first(); } return place; } void DigikamApp::slotEditGeolocation() { #ifdef HAVE_MARBLE ImageInfoList infos = d->view->selectedInfoList(ApplicationSettings::Metadata); if (infos.isEmpty()) return; TagModel* const tagModel = new TagModel(AbstractAlbumModel::IgnoreRootAlbum, this); TagPropertiesFilterModel* const filterModel = new TagPropertiesFilterModel(this); filterModel->setSourceAlbumModel(tagModel); filterModel->sort(0); QPointer dialog = new GeolocationEdit(filterModel, new DBInfoIface(this, QList(), ApplicationSettings::Tools), QApplication::activeWindow()); dialog->setItems(ImageGPS::infosToItems(infos)); dialog->exec(); delete dialog; // Refresh Database with new metadata from files. foreach(const ImageInfo& inf, infos) { ScanController::instance()->scannedInfo(inf.fileUrl().toLocalFile()); } #endif } void DigikamApp::slotEditMetadata() { QList urls = view()->selectedUrls(ApplicationSettings::Metadata); if (urls.isEmpty()) return; QPointer dialog = new MetadataEditDialog(QApplication::activeWindow(), urls); dialog->exec(); delete dialog; // Refresh Database with new metadata from files. CollectionScanner scanner; foreach(const QUrl& url, urls) { scanner.scanFile(url.toLocalFile(), CollectionScanner::Rescan); } } void DigikamApp::slotImportFromScanner() { #ifdef HAVE_KSANE m_ksaneAction->activate(scannerTargetPlace(), configGroupName()); #endif } void DigikamApp::setupSelectToolsAction() { // Create action model ActionItemModel* const actionModel = new ActionItemModel(this); actionModel->setMode(ActionItemModel::ToplevelMenuCategory | ActionItemModel::SortCategoriesByInsertionOrder); // Builtin actions QString mainCategory = i18nc("@title Main Tools", "Main Tools"); actionModel->addAction(d->ieAction, mainCategory); actionModel->addAction(d->openTagMngrAction, mainCategory); actionModel->addAction(d->bqmAction, mainCategory); actionModel->addAction(d->maintenanceAction, mainCategory); actionModel->addAction(d->ltAction, mainCategory); actionModel->addAction(d->advSearchAction, mainCategory); QString postCategory = i18nc("@title Post Processing Tools", "Post-Processing"); actionModel->addAction(m_expoBlendingAction, postCategory); actionModel->addAction(m_calendarAction, postCategory); actionModel->addAction(m_metadataEditAction, postCategory); actionModel->addAction(m_presentationAction, postCategory); actionModel->addAction(m_sendByMailAction, postCategory); actionModel->addAction(m_printCreatorAction, postCategory); actionModel->addAction(m_mediaServerAction, postCategory); #ifdef HAVE_PANORAMA actionModel->addAction(m_panoramaAction, postCategory); #endif #ifdef HAVE_MEDIAPLAYER actionModel->addAction(m_videoslideshowAction, postCategory); #endif #ifdef HAVE_HTMLGALLERY actionModel->addAction(m_htmlGalleryAction, postCategory); #endif #ifdef HAVE_MARBLE actionModel->addAction(m_geolocationEditAction, postCategory); #endif QString exportCategory = i18nc("@title Export Tools", "Export"); foreach(QAction* const ac, exportActions()) { actionModel->addAction(ac, exportCategory); } QString importCategory = i18nc("@title Import Tools", "Import"); foreach(QAction* const ac, importActions()) { actionModel->addAction(ac, importCategory); } #ifdef HAVE_KSANE actionModel->addAction(m_ksaneAction, importCategory); #endif // setup categorized view DCategorizedSortFilterProxyModel* const filterModel = actionModel->createFilterModel(); ActionCategorizedView* const selectToolsActionView = new ActionCategorizedView; selectToolsActionView->setupIconMode(); selectToolsActionView->setModel(filterModel); selectToolsActionView->adjustGridSize(); connect(selectToolsActionView, SIGNAL(clicked(QModelIndex)), actionModel, SLOT(trigger(QModelIndex))); d->view->setToolsIconView(selectToolsActionView); } void DigikamApp::slotPresentation() { d->view->presentation(); } void DigikamApp::slotExportTool() { QAction* const tool = dynamic_cast(sender()); if (tool == m_exportDropboxAction) { DBWindow w(new DBInfoIface(this, QList(), ApplicationSettings::ImportExport), this); w.exec(); } else if (tool == m_exportFacebookAction) { FbWindow w(new DBInfoIface(this, QList(), ApplicationSettings::ImportExport), this); w.exec(); } else if (tool == m_exportFlickrAction) { FlickrWindow w(new DBInfoIface(this, QList(), ApplicationSettings::ImportExport), this); w.exec(); } else if (tool == m_exportGdriveAction) { GSWindow w(new DBInfoIface(this, QList(), ApplicationSettings::ImportExport), this, QLatin1String("googledriveexport")); w.exec(); } else if (tool == m_exportGphotoAction) { GSWindow w(new DBInfoIface(this, QList(), ApplicationSettings::ImportExport), this, QLatin1String("googlephotoexport")); w.exec(); } else if (tool == m_exportImageshackAction) { ImageShackWindow w(new DBInfoIface(this, QList(), ApplicationSettings::ImportExport), this); w.exec(); } else if (tool == m_exportImgurAction) { ImgurWindow w(new DBInfoIface(this, QList(), ApplicationSettings::ImportExport), this); w.exec(); } else if (tool == m_exportPiwigoAction) { PiwigoWindow w(new DBInfoIface(this, QList(), ApplicationSettings::ImportExport), this); w.exec(); } else if (tool == m_exportRajceAction) { RajceWindow w(new DBInfoIface(this, QList(), ApplicationSettings::ImportExport), this); w.exec(); } else if (tool == m_exportSmugmugAction) { SmugWindow w(new DBInfoIface(this, QList(), ApplicationSettings::ImportExport), this); w.exec(); } else if (tool == m_exportYandexfotkiAction) { YFWindow w(new DBInfoIface(this, QList(), ApplicationSettings::ImportExport), this); w.exec(); } #ifdef HAVE_MEDIAWIKI else if (tool == m_exportMediawikiAction) { MediaWikiWindow w(new DBInfoIface(this, QList(), ApplicationSettings::ImportExport), this); w.exec(); } #endif #ifdef HAVE_VKONTAKTE else if (tool == m_exportVkontakteAction) { VKWindow w(new DBInfoIface(this, QList(), ApplicationSettings::ImportExport), this); w.exec(); } #endif #ifdef HAVE_KIO else if (tool == m_exportFileTransferAction) { FTExportWindow w(new DBInfoIface(this, QList(), ApplicationSettings::ImportExport), this); w.exec(); } #endif } void DigikamApp::slotImportTool() { QAction* const tool = dynamic_cast(sender()); if (tool == m_importGphotoAction) { GSWindow w(new DBInfoIface(this, QList(), ApplicationSettings::ImportExport), this, QLatin1String("googlephotoimport")); w.exec(); } else if (tool == m_importSmugmugAction) { SmugWindow w(new DBInfoIface(this, QList(), ApplicationSettings::ImportExport), this, true); w.exec(); } #ifdef HAVE_KIO else if (tool == m_importFileTransferAction) { FTImportWindow w(new DBInfoIface(this, QList(), ApplicationSettings::ImportExport), this); w.exec(); } #endif } } // namespace Digikam diff --git a/core/libs/database/utils/dbsettingswidget.cpp b/core/libs/database/utils/dbsettingswidget.cpp index c90e751073..ba69a163e3 100644 --- a/core/libs/database/utils/dbsettingswidget.cpp +++ b/core/libs/database/utils/dbsettingswidget.cpp @@ -1,954 +1,954 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2009-11-14 * Description : database settings widget * * Copyright (C) 2009-2010 by Holger Foerster * Copyright (C) 2010-2018 by Gilles Caulier * * 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. * * ============================================================ */ #include "dbsettingswidget.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "applicationsettings.h" #include "dfileselector.h" #include "dbengineparameters.h" #include "dbinarysearch.h" #include "dexpanderbox.h" #include "digikam_config.h" #include "digikam_debug.h" #include "dlayoutbox.h" #include "mysqlinitbinary.h" #include "mysqlservbinary.h" #include "albummanager.h" namespace Digikam { class DatabaseSettingsWidget::Private { public: Private() { mysqlCmdBox = 0; dbType = 0; dbPathLabel = 0; expertSettings = 0; dbNoticeBox = 0; sqlInit = 0; dbNameCore = 0; dbNameThumbs = 0; dbNameFace = 0; dbNameSimilarity = 0; hostName = 0; connectOpts = 0; userName = 0; password = 0; hostPort = 0; dbPathEdit = 0; dbBinariesWidget = 0; tab = 0; dbDetailsBox = 0; ignoreDirectoriesBox = 0; ignoreDirectoriesEdit = 0; ignoreDirectoriesLabel = 0; } DVBox* mysqlCmdBox; QLineEdit* dbNameCore; QLineEdit* dbNameThumbs; QLineEdit* dbNameFace; QLineEdit* dbNameSimilarity; QLineEdit* hostName; QLineEdit* connectOpts; QLineEdit* userName; QLineEdit* password; QSpinBox* hostPort; QComboBox* dbType; QLabel* dbPathLabel; QTextBrowser* sqlInit; QGroupBox* expertSettings; QGroupBox* dbNoticeBox; QGroupBox* dbDetailsBox; QTabWidget* tab; DFileSelector* dbPathEdit; DBinarySearch* dbBinariesWidget; MysqlInitBinary mysqlInitBin; MysqlServBinary mysqlServBin; DbEngineParameters orgPrms; QMap dbTypeMap; QGroupBox* ignoreDirectoriesBox; QLineEdit* ignoreDirectoriesEdit; QLabel* ignoreDirectoriesLabel; }; DatabaseSettingsWidget::DatabaseSettingsWidget(QWidget* const parent) : QWidget(parent), d(new Private) { setupMainArea(); } DatabaseSettingsWidget::~DatabaseSettingsWidget() { delete d; } void DatabaseSettingsWidget::setupMainArea() { QVBoxLayout* const layout = new QVBoxLayout(); setLayout(layout); // -------------------------------------------------------- const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); QGroupBox* const dbConfigBox = new QGroupBox(i18n("Database Configuration"), this); QVBoxLayout* const vlay = new QVBoxLayout(dbConfigBox); DHBox* const typeHbox = new DHBox(); QLabel* const databaseTypeLabel = new QLabel(typeHbox); d->dbType = new QComboBox(typeHbox); databaseTypeLabel->setText(i18n("Type:")); // --------- fill with default values --------------------- int dbTypeIdx = 0; d->dbType->addItem(i18n("SQLite"), SQlite); d->dbTypeMap[SQlite] = dbTypeIdx++; #ifdef HAVE_MYSQLSUPPORT # ifdef HAVE_INTERNALMYSQL d->dbType->addItem(i18n("Mysql Internal (experimental)"), MysqlInternal); d->dbTypeMap[MysqlInternal] = dbTypeIdx++; # endif d->dbType->addItem(i18n("MySQL Server (experimental)"), MysqlServer); d->dbTypeMap[MysqlServer] = dbTypeIdx++; #endif d->dbType->setToolTip(i18n("

Select here the type of database backend.

" "

SQlite backend is for local database storage with a small or medium collection sizes. " "It is the default and recommended backend for collections with less than 100K items.

" #ifdef HAVE_MYSQLSUPPORT # ifdef HAVE_INTERNALMYSQL "

MySQL Internal backend is for local database storage with huge collection sizes. " "This backend is recommend for local collections with more than 100K items.

" "

Be careful: this one still in experimental stage.

" # endif "

MySQL Server backend is a more robust solution especially for remote and shared database storage. " "It is also more efficient to manage huge collection sizes with more than 100K items.

" "

Be careful: this one still in experimental stage.

" #endif )); // -------------------------------------------------------- d->dbPathLabel = new QLabel(i18n("

Set here the location where the database files will be stored on your system. " "There are three databases: one for all collections properties, " "one to store compressed thumbnails, " "and one to store faces recognition metadata.
" "Write access is required to be able to edit image properties.

" "

Databases are digiKam core engines. Take care to use a place hosted by fast " "hardware (as SSD) with enough free space especially for thumbnails database.

" "

Note: a remote file system such as NFS, cannot be used here. " "For performance reasons, it's also recommended not to use removable media.

" "

"), dbConfigBox); d->dbPathLabel->setWordWrap(true); d->dbPathEdit = new DFileSelector(dbConfigBox); d->dbPathEdit->setFileDlgMode(DFileDialog::Directory); // -------------------------------------------------------- d->mysqlCmdBox = new DVBox(dbConfigBox); d->mysqlCmdBox->layout()->setMargin(0); new DLineWidget(Qt::Horizontal, d->mysqlCmdBox); QLabel* const mysqlBinariesLabel = new QLabel(i18n("

Here you can configure locations where MySQL binary tools are located. " "digiKam will try to find these binaries automatically if they are " "already installed on your computer.

"), d->mysqlCmdBox); mysqlBinariesLabel->setWordWrap(true); QGroupBox* const binaryBox = new QGroupBox(d->mysqlCmdBox); QGridLayout* const binaryLayout = new QGridLayout; binaryBox->setLayout(binaryLayout); binaryBox->setTitle(i18nc("@title:group", "MySQL Binaries")); d->dbBinariesWidget = new DBinarySearch(binaryBox); d->dbBinariesWidget->header()->setSectionHidden(2, true); d->dbBinariesWidget->addBinary(d->mysqlInitBin); d->dbBinariesWidget->addBinary(d->mysqlServBin); #ifdef Q_OS_LINUX d->dbBinariesWidget->addDirectory(QLatin1String("/usr/bin")); d->dbBinariesWidget->addDirectory(QLatin1String("/usr/sbin")); #endif #ifdef Q_OS_OSX // Std Macports install d->dbBinariesWidget->addDirectory(QLatin1String("/opt/local/bin")); d->dbBinariesWidget->addDirectory(QLatin1String("/opt/local/sbin")); d->dbBinariesWidget->addDirectory(QLatin1String("/opt/local/lib/mysql56/bin")); // digiKam Bundle PKG install d->dbBinariesWidget->addDirectory(QLatin1String("/opt/digikam/bin")); d->dbBinariesWidget->addDirectory(QLatin1String("/opt/digikam/sbin")); d->dbBinariesWidget->addDirectory(QLatin1String("/opt/digikam/lib/mysql56/bin")); #endif #ifdef Q_OS_WIN d->dbBinariesWidget->addDirectory(QLatin1String("C:/Program Files/MariaDB 10.1/bin")); d->dbBinariesWidget->addDirectory(QLatin1String("C:/Program Files (x86/MariaDB 10.1/bin")); #endif d->dbBinariesWidget->allBinariesFound(); // -------------------------------------------------------- d->tab = new QTabWidget(this); QLabel* const hostNameLabel = new QLabel(i18n("Host Name:")); d->hostName = new QLineEdit(); d->hostName->setPlaceholderText(i18n("Set the host computer name")); d->hostName->setToolTip(i18n("This is the computer name running MySQL server.\nThis can be \"localhost\" for a local server, " "or the network computer\n name (or IP address) in case of remote computer.")); QLabel* const connectOptsLabel = new QLabel(i18n("Connect options:")); connectOptsLabel->setOpenExternalLinks(true); d->connectOpts = new QLineEdit(); d->connectOpts->setPlaceholderText(i18n("Set the database connection options")); d->connectOpts->setToolTip(i18n("Set the MySQL server connection options.\nFor advanced users only.")); QLabel* const userNameLabel = new QLabel(i18n("User:")); d->userName = new QLineEdit(); d->userName->setPlaceholderText(i18n("Set the database account name")); d->userName->setToolTip(i18n("Set the MySQL server account name used\nby digiKam to be connected to the server.\n" "This account must be available on the remote MySQL server when database have been created.")); QLabel* const passwordLabel = new QLabel(i18n("Password:")); d->password = new QLineEdit(); d->password->setToolTip(i18n("Set the MySQL server account password used\nby digiKam to be connected to the server.\n" "You can left this field empty to use an account set without password.")); d->password->setEchoMode(QLineEdit::Password); DHBox* const phbox = new DHBox(); QLabel* const hostPortLabel = new QLabel(i18n("Host Port:")); d->hostPort = new QSpinBox(phbox); d->hostPort->setToolTip(i18n("Set the host computer port.\nUsually, MySQL server use port number 3306 by default")); d->hostPort->setMaximum(65535); d->hostPort->setValue(3306); QWidget* const space = new QWidget(phbox); phbox->setStretchFactor(space, 10); QPushButton* const checkDBConnectBtn = new QPushButton(i18n("Check Connection"), phbox); checkDBConnectBtn->setToolTip(i18n("Run a basic database connection to see if current MySQL server settings is suitable.")); // Only accept printable Ascii char for database names. QRegExp asciiRx(QLatin1String("[\x20-\x7F]+$")); QValidator* const asciiValidator = new QRegExpValidator(asciiRx, this); QLabel* const dbNameCoreLabel = new QLabel(i18n("Core Db Name:")); d->dbNameCore = new QLineEdit(); d->dbNameCore->setPlaceholderText(i18n("Set the core database name")); d->dbNameCore->setToolTip(i18n("The core database is lead digiKam container used to store\nalbums, items, and searches metadata.")); d->dbNameCore->setValidator(asciiValidator); QLabel* const dbNameThumbsLabel = new QLabel(i18n("Thumbs Db Name:")); d->dbNameThumbs = new QLineEdit(); d->dbNameThumbs->setPlaceholderText(i18n("Set the thumbnails database name")); d->dbNameThumbs->setToolTip(i18n("The thumbnails database is used by digiKam to host\nimage thumbs with wavelets compression images.\n" "This one can use quickly a lots of space,\nespecially if you have huge collections.")); d->dbNameThumbs->setValidator(asciiValidator); QLabel* const dbNameFaceLabel = new QLabel(i18n("Face Db Name:")); d->dbNameFace = new QLineEdit(); d->dbNameFace->setPlaceholderText(i18n("Set the face database name")); d->dbNameFace->setToolTip(i18n("The face database is used by digiKam to host image histograms\ndedicated to faces recognition process.\n" "This one can use quickly a lots of space, especially\nif you a lots of image with people faces detected " "and tagged.")); d->dbNameFace->setValidator(asciiValidator); QLabel* const dbNameSimilarityLabel = new QLabel(i18n("Similarity Db Name:")); d->dbNameSimilarity = new QLineEdit(); d->dbNameSimilarity->setPlaceholderText(i18n("Set the similarity database name")); - d->dbNameSimilarity->setToolTip(i18n("The similarity database is used by digiKam to host image haar matrix datas for the similarity search.")); + d->dbNameSimilarity->setToolTip(i18n("The similarity database is used by digiKam to host image Haar matrix data for the similarity search.")); d->dbNameSimilarity->setValidator(asciiValidator); QPushButton* const defaultValuesBtn = new QPushButton(i18n("Default Settings")); defaultValuesBtn->setToolTip(i18n("Reset database names settings to common default values.")); d->expertSettings = new QGroupBox(); d->expertSettings->setFlat(true); QFormLayout* const expertSettinglayout = new QFormLayout(); d->expertSettings->setLayout(expertSettinglayout); expertSettinglayout->addRow(hostNameLabel, d->hostName); expertSettinglayout->addRow(userNameLabel, d->userName); expertSettinglayout->addRow(passwordLabel, d->password); expertSettinglayout->addRow(connectOptsLabel, d->connectOpts); expertSettinglayout->addRow(hostPortLabel, phbox); expertSettinglayout->addRow(new DLineWidget(Qt::Horizontal, d->expertSettings)); expertSettinglayout->addRow(dbNameCoreLabel, d->dbNameCore); expertSettinglayout->addRow(dbNameThumbsLabel, d->dbNameThumbs); expertSettinglayout->addRow(dbNameFaceLabel, d->dbNameFace); expertSettinglayout->addRow(dbNameSimilarityLabel, d->dbNameSimilarity); expertSettinglayout->addRow(new QWidget(), defaultValuesBtn); d->tab->addTab(d->expertSettings, i18n("Remote Server Settings")); // -------------------------------------------------------- d->dbNoticeBox = new QGroupBox(i18n("Database Server Instructions"), this); QVBoxLayout* const vlay2 = new QVBoxLayout(d->dbNoticeBox); QLabel* const notice = new QLabel(i18n("

digiKam expects that database is already created with a dedicated user account. " "This user name digikam will require full access to the database.
" "If your database is not already set up, you can use the following SQL commands " "(after replacing the password with the correct one).
"), d->dbNoticeBox); notice->setWordWrap(true); d->sqlInit = new QTextBrowser(d->dbNoticeBox); d->sqlInit->setOpenExternalLinks(false); d->sqlInit->setOpenLinks(false); d->sqlInit->setReadOnly(false); QLabel* const notice2 = new QLabel(i18n("

Note: with a Linux server, a database can be initialized following the commands below:

" "

# su

" "

# systemctl restart mysqld

" "

# mysql -u root

" "

...

" "

Enter SQL code to Mysql prompt in order to init digiKam databases with grant privileges (see behind)

" "

...

" "

quit

" "

NOTE: If you've an enormous collection, you should start MySQL server with " "mysql --max_allowed_packet=128M OR in my.ini or ~/.my.cnf, change the settings

"), d->dbNoticeBox); notice2->setWordWrap(true); vlay2->addWidget(notice); vlay2->addWidget(d->sqlInit); vlay2->setContentsMargins(spacing, spacing, spacing, spacing); vlay2->setSpacing(spacing); vlay2->addWidget(notice2); d->tab->addTab(d->dbNoticeBox, i18n("Requirements")); // -------------------------------------------------------- d->dbDetailsBox = new QGroupBox(i18n("Database Server Technical Details"), this); QVBoxLayout* const vlay3 = new QVBoxLayout(d->dbDetailsBox); QLabel* const details = new QLabel(i18n("

Use this configuration view to set all information " "to be connected to a remote " "Mysql database server " "(or MariaDB) " "through a network. " "As with Sqlite or MySQL internal server, 3 databases will be stored " "on the remote server: one for all collections properties, " "one to store compressed thumbnails, and one to store faces " "recognition metadata.

" "

Unlike Sqlite or MySQL internal server, you can customize the " "database names to simplify your backups.

" "

Databases are digiKam core engines. To prevent performance issues, " "take a care to use a fast network link between the client and the server " "computers. It's also recommended to host database files on " "fast hardware (as SSD) " "with enough free space, especially for thumbnails database, even if data are compressed using wavelets image format " "PGF.

" "

The databases must be created previously on the remote server by the administrator. " "Look in Requirements tab for details.

"), d->dbDetailsBox); details->setWordWrap(true); vlay3->addWidget(details); vlay3->setContentsMargins(spacing, spacing, spacing, spacing); vlay3->setSpacing(spacing); d->tab->addTab(d->dbDetailsBox, i18n("Documentation")); // -------------------------------------------------------- vlay->addWidget(typeHbox); vlay->addWidget(new DLineWidget(Qt::Horizontal)); vlay->addWidget(d->dbPathLabel); vlay->addWidget(d->dbPathEdit); vlay->addWidget(d->mysqlCmdBox); vlay->addWidget(d->tab); vlay->setContentsMargins(spacing, spacing, spacing, spacing); vlay->setSpacing(spacing); // -------------------------------------------------------- layout->setContentsMargins(QMargins()); layout->setSpacing(spacing); layout->addWidget(dbConfigBox); layout->addStretch(); // -------------------------------------------------------- connect(d->dbType, SIGNAL(currentIndexChanged(int)), this, SLOT(slotHandleDBTypeIndexChanged(int))); connect(checkDBConnectBtn, SIGNAL(clicked()), this, SLOT(slotCheckMysqlServerConnection())); connect(defaultValuesBtn, SIGNAL(clicked()), this, SLOT(slotResetMysqlServerDBNames())); connect(d->dbNameCore, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateSqlInit())); connect(d->dbNameThumbs, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateSqlInit())); connect(d->dbNameFace, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateSqlInit())); connect(d->dbNameSimilarity, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateSqlInit())); connect(d->userName, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateSqlInit())); slotHandleDBTypeIndexChanged(d->dbType->currentIndex()); } int DatabaseSettingsWidget::databaseType() const { return d->dbType->currentData().toInt(); } QString DatabaseSettingsWidget::databasePath() const { return d->dbPathEdit->fileDlgPath(); } void DatabaseSettingsWidget::setDatabasePath(const QString& path) { d->dbPathEdit->setFileDlgPath(path); } DbEngineParameters DatabaseSettingsWidget::orgDatabasePrm() const { return d->orgPrms; } QString DatabaseSettingsWidget::databaseBackend() const { switch (databaseType()) { case MysqlInternal: case MysqlServer: { return DbEngineParameters::MySQLDatabaseType(); } default: // SQlite { return DbEngineParameters::SQLiteDatabaseType(); } } } void DatabaseSettingsWidget::slotResetMysqlServerDBNames() { d->dbNameCore->setText(QLatin1String("digikam")); d->dbNameThumbs->setText(QLatin1String("digikam")); d->dbNameFace->setText(QLatin1String("digikam")); d->dbNameSimilarity->setText(QLatin1String("digikam")); } void DatabaseSettingsWidget::slotHandleDBTypeIndexChanged(int index) { int dbType = d->dbType->itemData(index).toInt(); setDatabaseInputFields(dbType); handleInternalServer(dbType); slotUpdateSqlInit(); } void DatabaseSettingsWidget::setDatabaseInputFields(int index) { switch (index) { case SQlite: { d->dbPathLabel->setVisible(true); d->dbPathEdit->setVisible(true); d->mysqlCmdBox->setVisible(false); d->tab->setVisible(false); connect(d->dbPathEdit->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(slotDatabasePathEditedDelayed())); break; } case MysqlInternal: { d->dbPathLabel->setVisible(true); d->dbPathEdit->setVisible(true); d->mysqlCmdBox->setVisible(true); d->tab->setVisible(false); connect(d->dbPathEdit->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(slotDatabasePathEditedDelayed())); break; } default: // MysqlServer { d->dbPathLabel->setVisible(false); d->dbPathEdit->setVisible(false); d->mysqlCmdBox->setVisible(false); d->tab->setVisible(true); disconnect(d->dbPathEdit->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(slotDatabasePathEditedDelayed())); break; } } } void DatabaseSettingsWidget::handleInternalServer(int index) { bool internal = (index == MysqlInternal); d->hostName->setDisabled(internal); d->hostPort->setDisabled(internal); d->dbNameCore->setDisabled(internal); d->dbNameThumbs->setDisabled(internal); d->dbNameFace->setDisabled(internal); d->dbNameSimilarity->setDisabled(internal); d->userName->setDisabled(internal); d->password->setDisabled(internal); d->connectOpts->setDisabled(internal); } void DatabaseSettingsWidget::slotUpdateSqlInit() { QString sql = QString::fromLatin1("CREATE USER \'%1\'@\'%\' IDENTIFIED BY \'password\';
") .arg(d->userName->text()); sql += QString::fromLatin1("GRANT ALL ON *.* TO \'%1\'@\'%\' IDENTIFIED BY \'password\';
") .arg(d->userName->text()); sql += QString::fromLatin1("CREATE DATABASE %1;
" "GRANT ALL PRIVILEGES ON %2.* TO \'%3\'@\'%\';
") .arg(d->dbNameCore->text()) .arg(d->dbNameCore->text()) .arg(d->userName->text()); if (d->dbNameThumbs->text() != d->dbNameCore->text()) { sql += QString::fromLatin1("CREATE DATABASE %1;
" "GRANT ALL PRIVILEGES ON %2.* TO \'%3\'@\'%\';
") .arg(d->dbNameThumbs->text()) .arg(d->dbNameThumbs->text()) .arg(d->userName->text()); } if ((d->dbNameFace->text() != d->dbNameCore->text()) && (d->dbNameFace->text() != d->dbNameThumbs->text())) { sql += QString::fromLatin1("CREATE DATABASE %1;
" "GRANT ALL PRIVILEGES ON %2.* TO \'%3\'@\'%\';
") .arg(d->dbNameFace->text()) .arg(d->dbNameFace->text()) .arg(d->userName->text()); } if ((d->dbNameSimilarity->text() != d->dbNameCore->text()) && (d->dbNameSimilarity->text() != d->dbNameThumbs->text()) && (d->dbNameSimilarity->text() != d->dbNameFace->text())) { sql += QString::fromLatin1("CREATE DATABASE %1;
" "GRANT ALL PRIVILEGES ON %2.* TO \'%3\'@\'%\';
") .arg(d->dbNameSimilarity->text()) .arg(d->dbNameSimilarity->text()) .arg(d->userName->text()); } sql += QString::fromLatin1("FLUSH PRIVILEGES;
"); d->sqlInit->setText(sql); } void DatabaseSettingsWidget::slotCheckMysqlServerConnection() { QString error; if (checkMysqlServerConnection(error)) { QMessageBox::information(qApp->activeWindow(), i18n("Database connection test"), i18n("Database connection test successful.")); } else { QMessageBox::critical(qApp->activeWindow(), i18n("Database connection test"), i18n("Database connection test was not successful.

Error was: %1

", error)); } } bool DatabaseSettingsWidget::checkMysqlServerConnectionConfig(QString& error) { if (d->hostName->text().isEmpty()) { error = i18n("The server hostname is empty"); return false; } if (d->userName->text().isEmpty()) { error = i18n("The server user name is empty"); return false; } return true; } bool DatabaseSettingsWidget::checkMysqlServerDbNamesConfig(QString& error) { if (d->dbNameCore->text().isEmpty()) { error = i18n("The core database name is empty"); return false; } if (d->dbNameThumbs->text().isEmpty()) { error = i18n("The thumbnails database name is empty"); return false; } if (d->dbNameFace->text().isEmpty()) { error = i18n("The face database name is empty"); return false; } if (d->dbNameSimilarity->text().isEmpty()) { error = i18n("The similarity database name is empty"); return false; } return true; } bool DatabaseSettingsWidget::checkMysqlServerConnection(QString& error) { if (!checkMysqlServerConnectionConfig(error)) { return false; } bool result = false; qApp->setOverrideCursor(Qt::WaitCursor); AlbumManager::instance()->addFakeConnection(); QString databaseID(QLatin1String("ConnectionTest")); { QSqlDatabase testDatabase = QSqlDatabase::addDatabase(databaseBackend(), databaseID); DbEngineParameters prm = getDbEngineParameters(); qCDebug(DIGIKAM_DATABASE_LOG) << "Testing DB connection (" << databaseID << ") with these settings:"; qCDebug(DIGIKAM_DATABASE_LOG) << prm; testDatabase.setHostName(prm.hostName); testDatabase.setPort(prm.port); testDatabase.setUserName(prm.userName); testDatabase.setPassword(prm.password); testDatabase.setConnectOptions(prm.connectOptions); result = testDatabase.open(); error = testDatabase.lastError().text(); testDatabase.close(); } QSqlDatabase::removeDatabase(databaseID); qApp->restoreOverrideCursor(); return result; } void DatabaseSettingsWidget::setParametersFromSettings(const ApplicationSettings* const settings, const bool& migration) { d->orgPrms = settings->getDbEngineParameters(); if (d->orgPrms.databaseType == DbEngineParameters::SQLiteDatabaseType()) { d->dbPathEdit->setFileDlgPath(d->orgPrms.getCoreDatabaseNameOrDir()); d->dbType->setCurrentIndex(d->dbTypeMap[SQlite]); slotResetMysqlServerDBNames(); if (settings->getDatabaseDirSetAtCmd() && !migration) { d->dbType->setEnabled(false); d->dbPathEdit->setEnabled(false); d->dbPathLabel->setText(d->dbPathLabel->text() + i18n("This path was set as a command line" "option (--database-directory).")); } } #ifdef HAVE_MYSQLSUPPORT # ifdef HAVE_INTERNALMYSQL else if (d->orgPrms.databaseType == DbEngineParameters::MySQLDatabaseType() && d->orgPrms.internalServer) { d->dbPathEdit->setFileDlgPath(d->orgPrms.internalServerPath()); d->dbType->setCurrentIndex(d->dbTypeMap[MysqlInternal]); d->mysqlInitBin.setup(QFileInfo(d->orgPrms.internalServerMysqlInitCmd).absoluteFilePath()); d->mysqlServBin.setup(QFileInfo(d->orgPrms.internalServerMysqlServCmd).absoluteFilePath()); d->dbBinariesWidget->allBinariesFound(); slotResetMysqlServerDBNames(); } # endif else { d->dbType->setCurrentIndex(d->dbTypeMap[MysqlServer]); d->dbNameCore->setText(d->orgPrms.databaseNameCore); d->dbNameThumbs->setText(d->orgPrms.databaseNameThumbnails); d->dbNameFace->setText(d->orgPrms.databaseNameFace); d->dbNameSimilarity->setText(d->orgPrms.databaseNameSimilarity); d->hostName->setText(d->orgPrms.hostName); d->hostPort->setValue((d->orgPrms.port == -1) ? 3306 : d->orgPrms.port); d->connectOpts->setText(d->orgPrms.connectOptions); d->userName->setText(d->orgPrms.userName); d->password->setText(d->orgPrms.password); } #endif slotHandleDBTypeIndexChanged(d->dbType->currentIndex()); } DbEngineParameters DatabaseSettingsWidget::getDbEngineParameters() const { DbEngineParameters prm; switch(databaseType()) { case SQlite: prm = DbEngineParameters::parametersForSQLiteDefaultFile(databasePath()); break; case MysqlInternal: prm = DbEngineParameters::defaultParameters(databaseBackend()); prm.setInternalServerPath(databasePath()); prm.internalServerMysqlInitCmd = d->mysqlInitBin.path(); prm.internalServerMysqlServCmd = d->mysqlServBin.path(); break; default: // MysqlServer prm.internalServer = false; prm.databaseType = databaseBackend(); prm.databaseNameCore = d->dbNameCore->text(); prm.databaseNameThumbnails = d->dbNameThumbs->text(); prm.databaseNameFace = d->dbNameFace->text(); prm.databaseNameSimilarity = d->dbNameSimilarity->text(); prm.connectOptions = d->connectOpts->text(); prm.hostName = d->hostName->text(); prm.port = d->hostPort->value(); prm.userName = d->userName->text(); prm.password = d->password->text(); break; } return prm; } void DatabaseSettingsWidget::slotDatabasePathEditedDelayed() { QTimer::singleShot(300, this, SLOT(slotDatabasePathEdited())); } void DatabaseSettingsWidget::slotDatabasePathEdited() { QString newPath = databasePath(); #ifndef Q_OS_WIN if (!newPath.isEmpty() && !QDir::isAbsolutePath(newPath)) { d->dbPathEdit->setFileDlgPath(QDir::homePath() + QLatin1Char('/') + newPath); } #endif d->dbPathEdit->setFileDlgPath(newPath); } bool DatabaseSettingsWidget::checkDatabaseSettings() { switch (databaseType()) { case SQlite: { return checkDatabasePath(); break; } case MysqlInternal: { if (!checkDatabasePath()) return false; if (!d->dbBinariesWidget->allBinariesFound()) return false; return true; break; } default: // MysqlServer { QString error; if (!checkMysqlServerDbNamesConfig(error)) { QMessageBox::critical(qApp->activeWindow(), i18n("Database Configuration"), i18n("The database names configuration is not valid. Error is

%1


" "Please check your configuration.", error)); return false; } if (!checkMysqlServerConnection(error)) { QMessageBox::critical(qApp->activeWindow(), i18n("Database Connection Test"), i18n("Testing database connection has failed with error

%1


" "Please check your configuration.", error)); return false; } break; } } return true; } bool DatabaseSettingsWidget::checkDatabasePath() { QString dbFolder = databasePath(); qCDebug(DIGIKAM_DATABASE_LOG) << "Database directory is : " << dbFolder; if (dbFolder.isEmpty()) { QMessageBox::information(qApp->activeWindow(), qApp->applicationName(), i18n("You must select a folder for digiKam to " "store information and metadata in a database file.")); return false; } QDir targetPath(dbFolder); if (!targetPath.exists()) { int rc = QMessageBox::question(qApp->activeWindow(), i18n("Create Database Folder?"), i18n("

The folder to put your database in does not seem to exist:

" "

%1

" "Would you like digiKam to create it for you?", dbFolder)); if (rc == QMessageBox::No) { return false; } if (!targetPath.mkpath(dbFolder)) { QMessageBox::information(qApp->activeWindow(), i18n("Create Database Folder Failed"), i18n("

digiKam could not create the folder to host your database file.\n" "Please select a different location.

" "

%1

", dbFolder)); return false; } } QFileInfo path(dbFolder); #ifdef Q_OS_WIN // Work around bug #189168 QTemporaryFile temp; temp.setFileTemplate(dbFolder + QLatin1String("XXXXXX")); if (!temp.open()) #else if (!path.isWritable()) #endif { QMessageBox::information(qApp->activeWindow(), i18n("No Database Write Access"), i18n("

You do not seem to have write access " "for the folder to host the database file.
" "Please select a different location.

" "

%1

", dbFolder)); return false; } return true; } } // namespace Digikam diff --git a/core/utilities/searchwindow/searchfields.cpp b/core/utilities/searchwindow/searchfields.cpp index 33088cd874..450566f157 100644 --- a/core/utilities/searchwindow/searchfields.cpp +++ b/core/utilities/searchwindow/searchfields.cpp @@ -1,3075 +1,3075 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2008-01-20 * Description : User interface for searches * * Copyright (C) 2008-2012 by Marcel Wiesweg * Copyright (C) 2011-2018 by Gilles Caulier * * 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. * * ============================================================ */ #include "searchfields.h" // C++ includes #include // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "album.h" #include "coredb.h" #include "squeezedcombobox.h" #include "albummanager.h" #include "albummodel.h" #include "dexpanderbox.h" #include "dlayoutbox.h" #include "albumselectcombobox.h" #include "choicesearchutilities.h" #include "dimg.h" #include "dmetadata.h" #include "imagescanner.h" #include "ddateedit.h" #include "ratingsearchutilities.h" #include "searchfieldgroup.h" #include "searchwindow.h" #include "tagscache.h" #include "colorlabelfilter.h" #include "picklabelfilter.h" #include "applicationsettings.h" #include "imagepropertiestab.h" namespace Digikam { SearchField* SearchField::createField(const QString& name, SearchFieldGroup* const parent) { if (name == QLatin1String("albumid")) { SearchFieldAlbum* const field = new SearchFieldAlbum(parent, SearchFieldAlbum::TypeAlbum); field->setFieldName(name); field->setText(i18n("Album"), i18n("Search items located in")); return field; } else if (name == QLatin1String("albumname")) { SearchFieldText* const field = new SearchFieldText(parent); field->setFieldName(name); field->setText(i18n("Album"), i18n("The album name contains")); return field; } else if (name == QLatin1String("albumcaption")) { SearchFieldText* const field = new SearchFieldText(parent); field->setFieldName(name); field->setText(i18n("Album"), i18n("The album caption contains")); return field; } else if (name == QLatin1String("albumcollection")) { SearchFieldChoice* const field = new SearchFieldChoice(parent); field->setFieldName(name); field->setText(i18n("Album"), i18n("The album category is")); ApplicationSettings* const settings = ApplicationSettings::instance(); if (settings) { QStringList Categories = settings->getAlbumCategoryNames(); int size = Categories.size(); QStringList categorychoices; for (int i=0; isetChoice(categorychoices); } return field; } else if (name == QLatin1String("tagid")) { SearchFieldAlbum* const field = new SearchFieldAlbum(parent, SearchFieldAlbum::TypeTag); field->setFieldName(name); field->setText(i18n("Tags"), i18n("Return items with tag")); return field; } else if (name == QLatin1String("tagname")) { SearchFieldText* const field = new SearchFieldText(parent); field->setFieldName(name); field->setText(i18n("Tags"), i18n("A tag of the item contains")); return field; } else if (name == QLatin1String("notag")) { /** * @todo Merge a "Not tagged" field into TagModel together with AND/OR control * for checked tags and logical connections (AND and Not Tagged checked => all other tags disabled) */ SearchFieldCheckBox* const field = new SearchFieldCheckBox(parent); field->setFieldName(name); field->setText(i18n("Tags"), i18n("item has no tags")); field->setLabel(i18n("Not Tagged")); return field; } else if (name == QLatin1String("filename")) { SearchFieldText* const field = new SearchFieldText(parent); field->setFieldName(name); field->setText(i18n("File Name"), i18n("Return items whose file name contains")); return field; } else if (name == QLatin1String("modificationdate")) { SearchFieldRangeDate* const field = new SearchFieldRangeDate(parent, SearchFieldRangeDate::DateTime); field->setFieldName(name); field->setText(i18n("Modification"), i18n("Return items modified between")); field->setBetweenText(i18nc("'Return items modified between...and...", "and")); return field; } else if (name == QLatin1String("filesize")) { SearchFieldRangeDouble* const field = new SearchFieldRangeDouble(parent); field->setFieldName(name); field->setText(i18n("File Size"), i18n("Size of the file")); field->setBetweenText(i18nc("Size of the file ...-...", "-")); field->setNumberPrefixAndSuffix(QString(), QLatin1String("MB")); field->setBoundary(0, 1000000, 1, 0.5); field->setFactor(1024 * 1024); return field; } else if (name == QLatin1String("labels")) { SearchFieldLabels* const field = new SearchFieldLabels(parent); field->setFieldName(name); field->setText(i18n("Labels"), i18n("Return items with labels")); return field; } else if (name == QLatin1String("rating")) { SearchFieldRating* const field = new SearchFieldRating(parent); field->setFieldName(name); field->setText(i18n("Rating"), i18n("Return items rated at least")); field->setBetweenText(i18nc("Return items rated at least...at most...", "at most")); return field; } else if (name == QLatin1String("creationdate")) { SearchFieldRangeDate* const field = new SearchFieldRangeDate(parent, SearchFieldRangeDate::DateTime); field->setFieldName(name); field->setText(i18n("Date"), i18n("Return items created between")); field->setBetweenText(i18nc("'Return items created between...and...", "and")); return field; } else if (name == QLatin1String("digitizationdate")) { SearchFieldRangeDate* const field = new SearchFieldRangeDate(parent, SearchFieldRangeDate::DateTime); field->setFieldName(name); field->setText(i18n("Digitization"), i18n("Return items digitized between")); field->setBetweenText(i18nc("'Return items digitized between...and...", "and")); return field; } else if (name == QLatin1String("orientation")) { //choice SearchFieldChoice* const field = new SearchFieldChoice(parent); field->setFieldName(name); field->setText(i18n("Exif Orientation"), i18n("Find items with orientation flag")); QMap map = DMetadata::possibleValuesForEnumField(MetadataInfo::Orientation); field->setChoice(map); return field; } else if (name == QLatin1String("dimension")) { // "width", "height", "pixels" } else if (name == QLatin1String("width")) { SearchFieldRangeInt* const field = new SearchFieldRangeInt(parent); field->setFieldName(name); field->setText(i18n("Width"), i18n("Find items with a width between")); field->setBetweenText(i18nc("Find items with a width between...and...", "and")); field->setNumberPrefixAndSuffix(QString(), i18nc("Pixels", "px")); field->setBoundary(1, 1000000, 250); field->setSuggestedValues(QList() << 50 << 100 << 200 << 300 << 400 << 500 << 600 << 700 << 800 << 900 << 1000 << 1250 << 1500 << 1750 << 2000 << 3000 << 4000 << 5000 << 6000 << 7000 << 8000 << 9000 << 10000 ); field->setSuggestedInitialValue(1000); field->setSingleSteps(50, 1000); return field; } else if (name == QLatin1String("height")) { SearchFieldRangeInt* const field = new SearchFieldRangeInt(parent); field->setFieldName(name); field->setText(i18n("Height"), i18n("Find items with a height between")); field->setBetweenText(i18nc("Find items with a height between...and...", "and")); field->setNumberPrefixAndSuffix(QString(), i18nc("Pixels", "px")); field->setBoundary(1, 1000000, 250); field->setSuggestedValues(QList() << 50 << 100 << 200 << 300 << 400 << 500 << 600 << 700 << 800 << 900 << 1000 << 1250 << 1500 << 1750 << 2000 << 3000 << 4000 << 5000 << 6000 << 7000 << 8000 << 9000 << 10000 ); field->setSuggestedInitialValue(1000); field->setSingleSteps(50, 1000); return field; } else if (name == QLatin1String("pageorientation")) { SearchFieldPageOrientation* const field = new SearchFieldPageOrientation(parent); field->setFieldName(name); field->setText(i18n("Orientation"), i18nc("Find items with any orientation / landscape / portrait orientation...", "Find items with")); return field; } else if (name == QLatin1String("format")) { //choice SearchFieldChoice* const field = new SearchFieldChoice(parent); field->setFieldName(name); field->setText(i18n("File Format"), i18n("Return items with the file format")); QStringList formats; foreach(QString fmt, CoreDbAccess().db()->getFormatStatistics(DatabaseItem::Image).keys()) formats << fmt << i18n("%1 [Image]", fmt); foreach(QString fmt, CoreDbAccess().db()->getFormatStatistics(DatabaseItem::Video).keys()) formats << fmt << i18n("%1 [Video]", fmt); foreach(QString fmt, CoreDbAccess().db()->getFormatStatistics(DatabaseItem::Audio).keys()) formats << fmt << i18n("%1 [Audio]", fmt); /* FIXME: This can report 2 times JPG : one as image, one as other. Where is the problem ? foreach(QString fmt, CoreDbAccess().db()->getFormatStatistics(DatabaseItem::Other).keys()) formats << fmt << i18n("%1 [Other]", fmt); */ formats.sort(); qCDebug(DIGIKAM_GENERAL_LOG) << formats; field->setChoice(formats); return field; } else if (name == QLatin1String("colordepth")) { //choice SearchFieldColorDepth* const field = new SearchFieldColorDepth(parent); field->setFieldName(name); field->setText(i18n("Color Depth"), i18nc("Find items with any color depth / 8 bits per channel...", "Find items with")); return field; } else if (name == QLatin1String("colormodel")) { //choice SearchFieldChoice* const field = new SearchFieldChoice(parent); field->setFieldName(name); field->setText(i18n("Color Model"), i18n("Find items with the color model")); QMap map; // Images map.insert(DImg::COLORMODELUNKNOWN, i18n("%1 [Image]", DImg::colorModelToString(DImg::COLORMODELUNKNOWN))); map.insert(DImg::RGB, i18n("%1 [Image]", DImg::colorModelToString(DImg::RGB))); map.insert(DImg::GRAYSCALE, i18n("%1 [Image]", DImg::colorModelToString(DImg::GRAYSCALE))); map.insert(DImg::MONOCHROME, i18n("%1 [Image]", DImg::colorModelToString(DImg::MONOCHROME))); map.insert(DImg::INDEXED, i18n("%1 [Image]", DImg::colorModelToString(DImg::INDEXED))); map.insert(DImg::YCBCR, i18n("%1 [Image]", DImg::colorModelToString(DImg::YCBCR))); map.insert(DImg::CMYK, i18n("%1 [Image]", DImg::colorModelToString(DImg::CMYK))); map.insert(DImg::CIELAB, i18n("%1 [Image]", DImg::colorModelToString(DImg::CIELAB))); map.insert(DImg::COLORMODELRAW, i18n("%1 [Image]", DImg::colorModelToString(DImg::COLORMODELRAW))); // Video map.insert(DMetadata::VIDEOCOLORMODEL_SRGB, i18n("%1 [Video]", DMetadata::videoColorModelToString(DMetadata::VIDEOCOLORMODEL_SRGB))); map.insert(DMetadata::VIDEOCOLORMODEL_BT709, i18n("%1 [Video]", DMetadata::videoColorModelToString(DMetadata::VIDEOCOLORMODEL_BT709))); map.insert(DMetadata::VIDEOCOLORMODEL_BT601, i18n("%1 [Video]", DMetadata::videoColorModelToString(DMetadata::VIDEOCOLORMODEL_BT601))); map.insert(DMetadata::VIDEOCOLORMODEL_OTHER, i18n("%1 [Video]", DMetadata::videoColorModelToString(DMetadata::VIDEOCOLORMODEL_OTHER))); field->setChoice(map); return field; } else if (name == QLatin1String("make")) { //choice SearchFieldChoice* const field = new SearchFieldChoice(parent); field->setFieldName(name); field->setText(i18n("Camera"), i18n("The make of the camera")); QStringList make = CoreDbAccess().db()->getListFromImageMetadata(DatabaseFields::Make); for (int i = 0 ; i < make.count() ; i++) { ImagePropertiesTab::shortenedMakeInfo(make[i]); make[i] = make[i].trimmed(); } make.removeDuplicates(); make += make; make.sort(); for (int i = 0 ; i < make.count() ; i += 2) { make[i] = QLatin1Char('*') + make[i] + QLatin1Char('*'); } field->setChoice(make); return field; } else if (name == QLatin1String("model")) { //choice SearchFieldChoice* const field = new SearchFieldChoice(parent); field->setFieldName(name); field->setText(i18n("Camera"), i18n("The model of the camera")); QStringList model = CoreDbAccess().db()->getListFromImageMetadata(DatabaseFields::Model); for (int i = 0 ; i < model.count() ; i++) { ImagePropertiesTab::shortenedModelInfo(model[i]); model[i] = model[i].trimmed(); } model.removeDuplicates(); model += model; model.sort(); for (int i = 0 ; i < model.count() ; i += 2) { model[i] = QLatin1Char('*') + model[i] + QLatin1Char('*'); } field->setChoice(model); return field; } else if (name == QLatin1String("lenses")) { //choice SearchFieldChoice* const field = new SearchFieldChoice(parent); field->setFieldName(name); field->setText(i18n("Lens"), i18n("The type of the lens")); QStringList lens = CoreDbAccess().db()->getListFromImageMetadata(DatabaseFields::Lens); lens += lens; lens.sort(); field->setChoice(lens); return field; } else if (name == QLatin1String("aperture")) { //double SearchFieldRangeDouble* const field = new SearchFieldRangeDouble(parent); field->setFieldName(name); field->setText(i18n("Aperture"), i18n("Lens aperture as f-number")); field->setBetweenText(i18nc("Lens aperture as f-number ...-...", "-")); field->setNoValueText(QLatin1String("f/#")); field->setNumberPrefixAndSuffix(QLatin1String("f/"), QString()); field->setBoundary(0.3, 65536, 1, 0.1); field->setSuggestedValues(QList() << 0.5 << 0.7 << 1.0 << 1.4 << 2 << 2.8 << 4 << 5.6 << 8 << 11 << 16 << 22 << 32 << 45 << 64 << 90 << 128 ); field->setSuggestedInitialValue(1.0); field->setSingleSteps(0.1, 10); return field; } else if (name == QLatin1String("focallength")) { //double SearchFieldRangeInt* const field = new SearchFieldRangeInt(parent); field->setFieldName(name); field->setText(i18n("Focal length"), i18n("Focal length of the lens")); field->setBetweenText(i18nc("Focal length of the lens ...-...", "-")); field->setNumberPrefixAndSuffix(QString(), QLatin1String("mm")); field->setBoundary(0, 200000, 10); field->setSuggestedValues(QList() << 10 << 15 << 20 << 25 << 30 << 40 << 50 << 60 << 70 << 80 << 90 << 100 << 150 << 200 << 250 << 300 << 400 << 500 << 750 << 1000 ); field->setSuggestedInitialValue(30); field->setSingleSteps(2, 500); return field; } else if (name == QLatin1String("focallength35")) { //double SearchFieldRangeInt* const field = new SearchFieldRangeInt(parent); field->setFieldName(name); field->setText(i18n("Focal length"), i18n("35mm equivalent focal length")); field->setBetweenText(i18nc("35mm equivalent focal length ...-...", "-")); field->setNumberPrefixAndSuffix(QString(), QLatin1String("mm")); field->setBoundary(0, 200000, 10); field->setSuggestedValues(QList() << 8 << 10 << 15 << 16 << 20 << 28 << 30 << 40 << 50 << 60 << 70 << 80 << 90 << 100 << 150 << 200 << 250 << 300 << 400 << 500 << 750 << 1000 ); field->setSuggestedInitialValue(28); field->setSingleSteps(2, 500); return field; } else if (name == QLatin1String("exposuretime")) { //double SearchFieldRangeInt* const field = new SearchFieldRangeInt(parent); field->setFieldName(name); field->setText(i18n("Exposure"), i18n("Exposure time")); field->setBetweenText(i18nc("Exposure time ...-...", "-")); field->setNumberPrefixAndSuffix(QString(), QLatin1String("s")); field->enableFractionMagic(QLatin1String("1/")); // it's 1/250, not 250 as in the spin box field->setBoundary(86400, -1024000, 10); // negative is 1/ field->setSuggestedValues(QList() << 30 << 15 << 8 << 4 << 2 << 1 << -2 << -4 << -8 << -15 << -30 << -50 << -60 << -100 << -125 << -150 << -200 << -250 << -500 << -750 << -1000 << -2000 << -4000 << -8000 << -16000 ); field->setSuggestedInitialValue(-200); field->setSingleSteps(2000, 5); return field; } else if (name == QLatin1String("exposureprogram")) { //choice SearchFieldChoice* const field = new SearchFieldChoice(parent); field->setFieldName(name); field->setText(i18n("Exposure"), i18n("Automatic exposure program")); QMap map = DMetadata::possibleValuesForEnumField(MetadataInfo::ExposureProgram); field->setChoice(map); return field; } else if (name == QLatin1String("exposuremode")) { //choice SearchFieldChoice* const field = new SearchFieldChoice(parent); field->setFieldName(name); field->setText(i18n("Exposure"), i18n("Automatic or manual exposure")); QMap map = DMetadata::possibleValuesForEnumField(MetadataInfo::ExposureMode); field->setChoice(map); return field; } else if (name == QLatin1String("sensitivity")) { //int SearchFieldRangeInt* const field = new SearchFieldRangeInt(parent); field->setFieldName(name); field->setText(i18n("Sensitivity"), i18n("ISO film speed (linear scale, ASA)")); field->setBetweenText(i18nc("ISO film speed (linear scale, ASA) ...-...", "-")); field->setBoundary(0, 2000000, 50); field->setSuggestedValues(QList() << 6 << 8 << 10 << 12 << 16 << 20 << 25 << 32 << 40 << 50 << 64 << 80 << 100 << 125 << 160 << 200 << 250 << 320 << 400 << 500 << 640 << 800 << 1000 << 1250 << 1600 << 2000 << 2500 << 3200 << 4000 << 5000 << 6400 ); field->setSuggestedInitialValue(200); field->setSingleSteps(1, 400); return field; } else if (name == QLatin1String("flashmode")) { //choice /** * @todo This is a bitmask, and gives some more information */ SearchFieldChoice* const field = new SearchFieldChoice(parent); field->setFieldName(name); field->setText(i18n("Flash"), i18n("Flash mode")); QMap map = DMetadata::possibleValuesForEnumField(MetadataInfo::FlashMode); field->setChoice(map); return field; } else if (name == QLatin1String("whitebalance")) { //choice SearchFieldChoice* const field = new SearchFieldChoice(parent); field->setFieldName(name); field->setText(i18n("White Balance"), i18n("Automatic or manual white balance")); QMap map = DMetadata::possibleValuesForEnumField(MetadataInfo::WhiteBalance); field->setChoice(map); return field; } else if (name == QLatin1String("whitebalancecolortemperature")) { //int SearchFieldRangeInt* const field = new SearchFieldRangeInt(parent); field->setFieldName(name); field->setText(i18n("White balance"), i18n("Color temperature used for white balance")); field->setBetweenText(i18nc("Color temperature used for white balance ...-...", "-")); field->setNumberPrefixAndSuffix(QString(), QLatin1String("K")); field->setBoundary(1, 100000, 100); return field; } else if (name == QLatin1String("meteringmode")) { //choice SearchFieldChoice* const field = new SearchFieldChoice(parent); field->setFieldName(name); field->setText(i18n("Metering Mode"), i18n("Method to determine the exposure")); QMap map = DMetadata::possibleValuesForEnumField(MetadataInfo::MeteringMode); field->setChoice(map); return field; } else if (name == QLatin1String("subjectdistance")) { //double SearchFieldRangeDouble* const field = new SearchFieldRangeDouble(parent); field->setFieldName(name); field->setText(i18n("Subject Distance"), i18n("Distance of the subject from the lens")); field->setBetweenText(i18nc("Distance of the subject from the lens ...-...", "-")); field->setNumberPrefixAndSuffix(QString(), QLatin1String("m")); field->setBoundary(0, 50000, 1, 0.1); return field; } else if (name == QLatin1String("subjectdistancecategory")) { //choice SearchFieldChoice* const field = new SearchFieldChoice(parent); field->setFieldName(name); field->setText(i18n("Subject Distance"), i18n("Macro, close or distant view")); QMap map = DMetadata::possibleValuesForEnumField(MetadataInfo::SubjectDistanceCategory); field->setChoice(map); return field; } else if (name == QLatin1String("latitude")) { } else if (name == QLatin1String("longitude")) { } else if (name == QLatin1String("altitude")) { SearchFieldRangeDouble* const field = new SearchFieldRangeDouble(parent); field->setFieldName(name); field->setText(i18n("GPS"), i18n("Altitude range")); field->setBetweenText(i18nc("Altitude range ...-...", "-")); field->setNumberPrefixAndSuffix(QString(), QLatin1String("m")); field->setBoundary(0, 10000, 4, 1); return field; } else if (name == QLatin1String("positionorientation")) { } else if (name == QLatin1String("positiontilt")) { } else if (name == QLatin1String("positionroll")) { } else if (name == QLatin1String("positiondescription")) { } else if (name == QLatin1String("nogps")) { SearchFieldCheckBox* const field = new SearchFieldCheckBox(parent); field->setFieldName(name); - field->setText(i18n("GPS"), i18n("Ttem has no GPS info")); + field->setText(i18n("GPS"), i18n("Item has no GPS info")); field->setLabel(i18n("Not Geo-located")); return field; } else if (name == QLatin1String("comment")) { SearchFieldText* const field = new SearchFieldText(parent); field->setFieldName(name); field->setText(i18n("Caption"), i18n("Return items whose comment contains")); return field; } else if (name == QLatin1String("commentauthor")) { SearchFieldText* const field = new SearchFieldText(parent); field->setFieldName(name); field->setText(i18n("Author"), i18n("Return items commented by")); return field; } else if (name == QLatin1String("headline")) { SearchFieldText* const field = new SearchFieldText(parent); field->setFieldName(name); field->setText(i18n("Headline"), i18n("Return items with the IPTC headline")); return field; } else if (name == QLatin1String("title")) { SearchFieldText* const field = new SearchFieldText(parent); field->setFieldName(name); field->setText(i18n("Title"), i18n("Return items with the IPTC title")); return field; } else if (name == QLatin1String("keyword")) { SearchFieldText* const field = new SearchFieldKeyword(parent); field->setFieldName(name); field->setText(QString(), i18n("Find items that have associated all these words:")); return field; } else if (name == QLatin1String("aspectratioimg")) { SearchFieldText* const field = new SearchFieldText(parent); field->setFieldName(name); field->setText(i18n("Aspect Ratio"), i18n("Return items with the aspect ratio")); return field; } else if (name == QLatin1String("pixelsize")) { SearchFieldRangeInt* const field = new SearchFieldRangeInt(parent); field->setFieldName(name); field->setText(i18n("Pixel Size"), i18n("Value of (Width * Height) between")); field->setBetweenText(i18nc("Value of (Width * Height) between...and...", "and")); field->setNumberPrefixAndSuffix(QString(), QLatin1String("px")); field->setBoundary(1, 2000000000, 100); return field; } else if (name == QLatin1String("videoaspectratio")) { SearchFieldChoice* const field = new SearchFieldChoice(parent); field->setFieldName(name); field->setText(i18n("Aspect Ratio"), i18n("Return video with the frame aspect ratio")); QStringList ratio; ratio << QLatin1String("4:3") << QLatin1String("4:3"); ratio << QLatin1String("3:2") << QLatin1String("3:2"); ratio << QLatin1String("16:9") << QLatin1String("16:9"); ratio << QLatin1String("2:1") << QLatin1String("2:1"); // TODO: add more possible aspect ratio field->setChoice(ratio); return field; } else if (name == QLatin1String("videoduration")) { SearchFieldRangeInt* const field = new SearchFieldRangeInt(parent); field->setFieldName(name); field->setText(i18n("Duration"), i18n("Length of the video")); field->setBetweenText(i18nc("Find video with a length between...and...", "and")); field->setNumberPrefixAndSuffix(QString(), i18nc("Seconds", "s")); field->setBoundary(1, 10000, 100); field->setSuggestedValues(QList() << 10 << 30 << 60 << 90 << 120 << 240 << 360 << 500 << 1000 << 2000 << 3000 << 4000 << 5000 << 6000 << 7000 << 8000 << 9000 << 10000 // TODO : adjust default values ); field->setSuggestedInitialValue(10); field->setSingleSteps(10, 100); return field; } else if (name == QLatin1String("videoframerate")) { SearchFieldRangeInt* const field = new SearchFieldRangeInt(parent); field->setFieldName(name); field->setText(i18n("Frame Rate"), i18n("Return video with the frame rate")); field->setBetweenText(i18nc("Find video with frame rate between...and...", "and")); field->setNumberPrefixAndSuffix(QString(), i18nc("Frames per Second", "fps")); field->setBoundary(10, 60, 5); field->setSuggestedValues(QList() << 10 << 15 << 20 << 25 << 30 << 35 << 40 << 45 << 55 << 60 // TODO : adjust default values ); field->setSuggestedInitialValue(10); field->setSingleSteps(5, 60); return field; } else if (name == QLatin1String("videocodec")) { SearchFieldChoice* const field = new SearchFieldChoice(parent); field->setFieldName(name); field->setText(i18n("Codec"), i18n("Return video codec")); QStringList codec; // List of most common video codecs supported by FFMpeg (see "ffpmpeg -codecs" for details) // // FFMpeg codec name FFMpeg codec description codec << QLatin1String("8bps") << QLatin1String("QuickTime 8BPS video"); codec << QLatin1String("amv") << QLatin1String("AMV Video"); codec << QLatin1String("avs") << QLatin1String("AVS (Audio Video Standard) video"); codec << QLatin1String("cavs") << QLatin1String("Chinese AVS (Audio Video Standard) (AVS1-P2, JiZhun profile)"); codec << QLatin1String("cinepak") << QLatin1String("Cinepak"); codec << QLatin1String("dirac") << QLatin1String("Dirac"); codec << QLatin1String("flv1") << QLatin1String("FLV / Sorenson Spark / Sorenson H.263 (Flash Video)"); codec << QLatin1String("h261") << QLatin1String("H.261"); codec << QLatin1String("h263") << QLatin1String("H.263 / H.263-1996, H.263+ / H.263-1998 / H.263 version 2"); codec << QLatin1String("h263i") << QLatin1String("Intel H.263"); codec << QLatin1String("h263p") << QLatin1String("H.263+ / H.263-1998 / H.263 version 2"); codec << QLatin1String("h264") << QLatin1String("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"); codec << QLatin1String("hevc") << QLatin1String("H.265 / HEVC (High Efficiency Video Coding)"); codec << QLatin1String("jpeg2000") << QLatin1String("JPEG 2000"); codec << QLatin1String("mjpeg") << QLatin1String("Motion JPEG"); codec << QLatin1String("mjpegb") << QLatin1String("Apple MJPEG-B"); codec << QLatin1String("mpeg1video") << QLatin1String("MPEG-1 video"); codec << QLatin1String("mpeg2video") << QLatin1String("MPEG-2 video"); codec << QLatin1String("mpeg4") << QLatin1String("MPEG-4 part 2"); codec << QLatin1String("msmpeg4v1") << QLatin1String("MPEG-4 part 2 Microsoft variant version 1"); codec << QLatin1String("msmpeg4v2") << QLatin1String("MPEG-4 part 2 Microsoft variant version 2"); codec << QLatin1String("msmpeg4v3") << QLatin1String("MPEG-4 part 2 Microsoft variant version 3"); codec << QLatin1String("msvideo1") << QLatin1String("Microsoft Video 1"); codec << QLatin1String("msrle") << QLatin1String("Microsoft RLE"); codec << QLatin1String("mvc1") << QLatin1String("Silicon Graphics Motion Video Compressor 1"); codec << QLatin1String("mvc2") << QLatin1String("Silicon Graphics Motion Video Compressor 2"); codec << QLatin1String("qtrle") << QLatin1String("QuickTime Animation (RLE) video"); codec << QLatin1String("rawvideo") << QLatin1String("Raw video"); codec << QLatin1String("rpza") << QLatin1String("QuickTime video (RPZA)"); codec << QLatin1String("rv10") << QLatin1String("RealVideo 1.0"); codec << QLatin1String("rv20") << QLatin1String("RealVideo 2.0"); codec << QLatin1String("rv30") << QLatin1String("RealVideo 3.0"); codec << QLatin1String("rv40") << QLatin1String("RealVideo 4.0"); codec << QLatin1String("smc") << QLatin1String("QuickTime Graphics (SMC)"); codec << QLatin1String("snow") << QLatin1String("Snow"); codec << QLatin1String("svq1") << QLatin1String("Sorenson Vector Quantizer 1 / Sorenson Video 1 / SVQ1"); codec << QLatin1String("svq3") << QLatin1String("Sorenson Vector Quantizer 3 / Sorenson Video 3 / SVQ3"); codec << QLatin1String("theora") << QLatin1String("Theora"); codec << QLatin1String("vc1") << QLatin1String("SMPTE VC-1"); codec << QLatin1String("vc1image") << QLatin1String("Windows Media Video 9 Image v2"); codec << QLatin1String("vp3") << QLatin1String("On2 VP3"); codec << QLatin1String("vp5") << QLatin1String("On2 VP5"); codec << QLatin1String("vp6") << QLatin1String("On2 VP6"); codec << QLatin1String("vp6a") << QLatin1String("On2 VP6 (Flash version, with alpha channel)"); codec << QLatin1String("vp6f") << QLatin1String("On2 VP6 (Flash version)"); codec << QLatin1String("vp7") << QLatin1String("On2 VP7"); codec << QLatin1String("vp8") << QLatin1String("On2 VP8"); codec << QLatin1String("vp9") << QLatin1String("Google VP9"); codec << QLatin1String("wmv1") << QLatin1String("Windows Media Video 7"); codec << QLatin1String("wmv2") << QLatin1String("Windows Media Video 8"); codec << QLatin1String("wmv3") << QLatin1String("Windows Media Video 9"); codec << QLatin1String("wmv3image") << QLatin1String("Windows Media Video 9 Image"); // TODO: add more possible codec field->setChoice(codec); return field; } else if (name == QLatin1String("videoaudiobitrate")) { SearchFieldRangeInt* const field = new SearchFieldRangeInt(parent); field->setFieldName(name); field->setText(i18n("Audio Bit Rate"), i18n("Return Audio Bits Rate")); field->setBetweenText(i18nc("Find files with audio bit rate between...and...", "and")); field->setNumberPrefixAndSuffix(QString(), i18nc("Bits per Second", "bps")); field->setBoundary(1000, 100000, 1000); field->setSuggestedValues(QList() << 1000 << 4000 << 8000 << 12000 << 16000 << 20000 << 30000 << 40000 << 50000 << 60000 << 700000 << 800000 << 900000 << 100000 // TODO : adjust default values ); field->setSuggestedInitialValue(1000); field->setSingleSteps(1000, 1000); return field; } else if (name == QLatin1String("videoaudiochanneltype")) { SearchFieldChoice* const field = new SearchFieldChoice(parent); field->setFieldName(name); field->setText(i18n("Audio Channel Type"), i18n("Return Audio Channel Type")); QStringList type; type << QLatin1String("Mono") << i18n("Mono"); type << QLatin1String("Stereo") << i18n("Stereo"); type << QLatin1String("5.1") << i18n("5.1 Surround Sound"); type << QLatin1String("7.1") << i18n("7.1 Surround Sound"); type << QLatin1String("16 Channel") << i18n("16 Channels Sequence"); type << QLatin1String("Other") << i18n("Other Channel Type"); // TODO: add more possible audio channel type field->setChoice(type); return field; } else if (name == QLatin1String("videoaudioCodec")) { SearchFieldChoice* const field = new SearchFieldChoice(parent); field->setFieldName(name); field->setText(i18n("Audio Codec"), i18n("Return Audio Codec")); QStringList type; // List of most common audio codecs supported by FFMpeg (see "ffpmpeg -codecs" for details) // // FFMpeg codec name FFMpeg codec description type << QLatin1String("aac") << QLatin1String("AAC (Advanced Audio Coding)"); type << QLatin1String("aac_latm") << QLatin1String("AAC LATM (Advanced Audio Coding LATM syntax)"); type << QLatin1String("ac3") << QLatin1String("ATSC A/52A (AC-3)"); type << QLatin1String("adpcm_g722") << QLatin1String("G.722 ADPCM"); type << QLatin1String("adpcm_g726") << QLatin1String("G.726 ADPCM"); type << QLatin1String("adpcm_g726le") << QLatin1String("G.726 ADPCM little-endian"); type << QLatin1String("adpcm_ima_wav") << QLatin1String("ADPCM IMA WAV"); type << QLatin1String("adpcm_ima_qt") << QLatin1String("ADPCM IMA QuickTime"); type << QLatin1String("adpcm_swf") << QLatin1String("ADPCM Shockwave Flash"); type << QLatin1String("alac") << QLatin1String("ALAC (Apple Lossless Audio Codec)"); type << QLatin1String("amr_nb") << QLatin1String("AMR-NB (Adaptive Multi-Rate NarrowBand)"); type << QLatin1String("amr_wb") << QLatin1String("AMR-WB (Adaptive Multi-Rate WideBand)"); type << QLatin1String("ape") << QLatin1String("Monkey's Audio"); type << QLatin1String("atrac1") << QLatin1String("ATRAC1 (Adaptive TRansform Acoustic Coding)"); type << QLatin1String("atrac3") << QLatin1String("ATRAC3 (Adaptive TRansform Acoustic Coding 3)"); type << QLatin1String("atrac3al") << QLatin1String("ATRAC3 AL (Adaptive TRansform Acoustic Coding 3 Advanced Lossless)"); type << QLatin1String("atrac3p") << QLatin1String("ATRAC3+ (Adaptive TRansform Acoustic Coding 3+)"); type << QLatin1String("atrac3pal") << QLatin1String("ATRAC3+ AL (Adaptive TRansform Acoustic Coding 3+ Advanced Lossless)"); type << QLatin1String("celt") << QLatin1String("Constrained Energy Lapped Transform (CELT)"); type << QLatin1String("cook") << QLatin1String("Cook / Cooker / Gecko (RealAudio G2)"); type << QLatin1String("dts") << QLatin1String("DCA (DTS Coherent Acoustics)"); type << QLatin1String("eac3") << QLatin1String("ATSC A/52B (AC-3, E-AC-3)"); type << QLatin1String("flac") << QLatin1String("FLAC (Free Lossless Audio Codec)"); type << QLatin1String("g723_1") << QLatin1String("G.723.1"); type << QLatin1String("g729") << QLatin1String("G.729"); type << QLatin1String("mp1") << QLatin1String("MP1 (MPEG audio layer 1)"); type << QLatin1String("mp2") << QLatin1String("MP2 (MPEG audio layer 2)"); type << QLatin1String("mp3") << QLatin1String("MP3 (MPEG audio layer 3)"); type << QLatin1String("mp3adu") << QLatin1String("ADU (Application Data Unit) MP3 (MPEG audio layer 3)"); type << QLatin1String("mp3on4") << QLatin1String("MP3 on MP4"); type << QLatin1String("mp4als") << QLatin1String("MPEG-4 Audio Lossless Coding (ALS)"); type << QLatin1String("musepack7") << QLatin1String("Musepack SV7"); type << QLatin1String("musepack8") << QLatin1String("Musepack SV8"); type << QLatin1String("nellymoser") << QLatin1String("Nellymoser Asao"); type << QLatin1String("opus") << QLatin1String("Opus (Opus Interactive Audio Codec)"); type << QLatin1String("pcm_alaw") << QLatin1String("PCM A-law / G.711 A-law"); type << QLatin1String("pcm_bluray") << QLatin1String("PCM signed 16|20|24-bit big-endian for Blu-ray media"); type << QLatin1String("pcm_dvd") << QLatin1String("PCM signed 20|24-bit big-endian"); type << QLatin1String("pcm_f16le") << QLatin1String("PCM 16.8 floating point little-endian"); type << QLatin1String("pcm_f24le") << QLatin1String("PCM 24.0 floating point little-endian"); type << QLatin1String("pcm_f32be") << QLatin1String("PCM 32-bit floating point big-endian"); type << QLatin1String("pcm_f32le") << QLatin1String("PCM 32-bit floating point little-endian"); type << QLatin1String("pcm_f64be") << QLatin1String("PCM 64-bit floating point big-endian"); type << QLatin1String("pcm_f64le") << QLatin1String("PCM 64-bit floating point little-endian"); type << QLatin1String("pcm_lxf") << QLatin1String("PCM signed 20-bit little-endian planar"); type << QLatin1String("pcm_mulaw") << QLatin1String("PCM mu-law / G.711 mu-law"); type << QLatin1String("pcm_s16be") << QLatin1String("PCM signed 16-bit big-endian"); type << QLatin1String("pcm_s16be_planar") << QLatin1String("PCM signed 16-bit big-endian planar"); type << QLatin1String("pcm_s16le") << QLatin1String("PCM signed 16-bit little-endian"); type << QLatin1String("pcm_s16le_planar") << QLatin1String("PCM signed 16-bit little-endian planar"); type << QLatin1String("pcm_s24be") << QLatin1String("PCM signed 24-bit big-endian"); type << QLatin1String("pcm_s24daud") << QLatin1String("PCM D-Cinema audio signed 24-bit"); type << QLatin1String("pcm_s24le") << QLatin1String("PCM signed 24-bit little-endian"); type << QLatin1String("pcm_s24le_planar") << QLatin1String("PCM signed 24-bit little-endian planar"); type << QLatin1String("pcm_s32be") << QLatin1String("PCM signed 32-bit big-endian"); type << QLatin1String("pcm_s32le") << QLatin1String("PCM signed 32-bit little-endian"); type << QLatin1String("pcm_s32le_planar") << QLatin1String("PCM signed 32-bit little-endian planar"); type << QLatin1String("pcm_s64be") << QLatin1String("PCM signed 64-bit big-endian"); type << QLatin1String("pcm_s64le") << QLatin1String("PCM signed 64-bit little-endian"); type << QLatin1String("pcm_s8") << QLatin1String("PCM signed 8-bit"); type << QLatin1String("pcm_s8_planar") << QLatin1String("PCM signed 8-bit planar"); type << QLatin1String("pcm_u16be") << QLatin1String("PCM unsigned 16-bit big-endian"); type << QLatin1String("pcm_u16le") << QLatin1String("PCM unsigned 16-bit little-endian"); type << QLatin1String("pcm_u24be") << QLatin1String("PCM unsigned 24-bit big-endian"); type << QLatin1String("pcm_u24le") << QLatin1String("PCM unsigned 24-bit little-endian"); type << QLatin1String("pcm_u32be") << QLatin1String("PCM unsigned 32-bit big-endian"); type << QLatin1String("pcm_u32le") << QLatin1String("PCM unsigned 32-bit little-endian"); type << QLatin1String("pcm_u8") << QLatin1String("PCM unsigned 8-bit"); type << QLatin1String("pcm_zork") << QLatin1String("PCM Zork"); type << QLatin1String("ra_144") << QLatin1String("RealAudio 1.0 (14.4K)"); type << QLatin1String("ra_288") << QLatin1String("RealAudio 2.0 (28.8K)"); type << QLatin1String("ralf") << QLatin1String("RealAudio Lossless"); type << QLatin1String("sipr") << QLatin1String("RealAudio SIPR / ACELP.NET"); type << QLatin1String("speex") << QLatin1String("Speex"); type << QLatin1String("tak") << QLatin1String("TAK (Tom's lossless Audio Kompressor)"); type << QLatin1String("wavpack") << QLatin1String("WavPack"); type << QLatin1String("wmalossless") << QLatin1String("Windows Media Audio Lossless"); type << QLatin1String("wmapro") << QLatin1String("Windows Media Audio 9 Professional"); type << QLatin1String("wmav1") << QLatin1String("Windows Media Audio 1"); type << QLatin1String("wmav2") << QLatin1String("Windows Media Audio 2"); type << QLatin1String("wmavoice") << QLatin1String("Windows Media Audio Voice"); // TODO: add more possible audio Codec field->setChoice(type); return field; } else { qCWarning(DIGIKAM_GENERAL_LOG) << "SearchField::createField: cannot create SearchField for" << name; } return 0; } // ------------------------------------------------------------------------------------------- SearchField::SearchField(QObject* const parent) : QObject(parent) { m_label = new QLabel; m_detailLabel = new QLabel; m_clearButton = new AnimatedClearButton; m_categoryLabelVisible = true; m_valueIsValid = false; } void SearchField::setup(QGridLayout* const layout, int line) { if (line == -1) { line = layout->rowCount(); } // 10px indent layout->setColumnMinimumWidth(0, 10); // set stretch for the value widget columns layout->setColumnStretch(3, 1); layout->setColumnStretch(5, 1); // push value widgets to the left layout->setColumnStretch(6, 1); setupLabels(layout, line); // value widgets can use columns 3,4,5. // In the case of "from ... to ..." fields, column 3 and 5 can contain spin boxes etc., // and 4 can contain a label in between. // In other cases, a widget or sublayout spanning the three columns is recommended. setupValueWidgets(layout, line, 3); // setup the clear button that appears dynamically if (qApp->isLeftToRight()) { m_clearButton->setPixmap(QIcon::fromTheme(QLatin1String("edit-clear-locationbar-rtl")).pixmap(QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize))); } else { m_clearButton->setPixmap(QIcon::fromTheme(QLatin1String("edit-clear-locationbar-ltr")).pixmap(QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize))); } // Important: Don't cause re-layouting when button gets hidden/shown! m_clearButton->stayVisibleWhenAnimatedOut(true); m_clearButton->setToolTip(i18n("Reset contents")); connect(m_clearButton, SIGNAL(clicked()), this, SLOT(clearButtonClicked())); layout->addWidget(m_clearButton, line, 7); } void SearchField::setupLabels(QGridLayout* layout, int line) { m_label->setObjectName(QLatin1String("SearchField_MainLabel")); m_detailLabel->setObjectName(QLatin1String("SearchField_DetailLabel")); layout->addWidget(m_label, line, 1); layout->addWidget(m_detailLabel, line, 2); } void SearchField::setFieldName(const QString& fieldName) { m_name = fieldName; } void SearchField::setText(const QString& label, const QString& detailLabel) { m_label->setText(label); m_detailLabel->setText(detailLabel); } bool SearchField::supportsField(const QString& fieldName) { return m_name == fieldName; } void SearchField::setVisible(bool visible) { m_label->setVisible(visible && m_categoryLabelVisible); m_detailLabel->setVisible(visible); m_clearButton->setShallBeShown(visible); setValueWidgetsVisible(visible); } bool SearchField::isVisible() { // the detail label is considered representative for all widgets return m_detailLabel->isVisible(); } void SearchField::setCategoryLabelVisible(bool visible) { if (m_categoryLabelVisible == visible) { return; } m_categoryLabelVisible = visible; // update status: compare setVisible() and isVisible() m_label->setVisible(m_detailLabel->isVisible() && m_categoryLabelVisible); } void SearchField::setCategoryLabelVisibleFromPreviousField(SearchField* previousField) { if (previousField->m_label->text() == m_label->text()) { setCategoryLabelVisible(false); } else { setCategoryLabelVisible(true); } } QList SearchField::widgetRects(WidgetRectType type) const { QList rects; if (type == LabelAndValueWidgetRects) { rects << m_label->geometry(); rects << m_detailLabel->geometry(); } rects += valueWidgetRects(); return rects; } void SearchField::clearButtonClicked() { reset(); } void SearchField::setValidValueState(bool valueIsValid) { if (valueIsValid != m_valueIsValid) { m_valueIsValid = valueIsValid; // Note: setVisible visibility is independent from animateVisible visibility! m_clearButton->animateVisible(m_valueIsValid); } } // ------------------------------------------------------------------------- SearchFieldText::SearchFieldText(QObject* const parent) : SearchField(parent), m_edit(0) { } void SearchFieldText::setupValueWidgets(QGridLayout* layout, int row, int column) { m_edit = new QLineEdit; layout->addWidget(m_edit, row, column, 1, 3); connect(m_edit, SIGNAL(textChanged(QString)), this, SLOT(valueChanged(QString))); } void SearchFieldText::read(SearchXmlCachingReader& reader) { QString value = reader.value(); m_edit->setText(value); } void SearchFieldText::write(SearchXmlWriter& writer) { QString value = m_edit->text(); if (!value.isEmpty()) { writer.writeField(m_name, SearchXml::Like); writer.writeValue(value); writer.finishField(); } } void SearchFieldText::reset() { m_edit->setText(QString()); } void SearchFieldText::setValueWidgetsVisible(bool visible) { m_edit->setVisible(visible); } QList SearchFieldText::valueWidgetRects() const { QList rects; rects << m_edit->geometry(); return rects; } void SearchFieldText::valueChanged(const QString& text) { setValidValueState(!text.isEmpty()); } // ------------------------------------------------------------------------- SearchFieldKeyword::SearchFieldKeyword(QObject* const parent) : SearchFieldText(parent) { } void SearchFieldKeyword::read(SearchXmlCachingReader& reader) { QString keyword = reader.value(); m_edit->setText(KeywordSearch::merge(m_edit->text(), keyword)); } void SearchFieldKeyword::write(SearchXmlWriter& writer) { QStringList keywordList = KeywordSearch::split(m_edit->text()); foreach(const QString& keyword, keywordList) { if (!keyword.isEmpty()) { writer.writeField(m_name, SearchXml::Like); writer.writeValue(keyword); writer.finishField(); } } } // ------------------------------------------------------------------------- SearchFieldRangeDate::SearchFieldRangeDate(QObject* const parent, Type type) : SearchField(parent), m_firstTimeEdit(0), m_firstDateEdit(0), m_secondTimeEdit(0), m_secondDateEdit(0), m_type(type) { m_betweenLabel = new QLabel; } void SearchFieldRangeDate::setupValueWidgets(QGridLayout* layout, int row, int column) { // QHBoxLayout *hbox = new QHBoxLayout; // layout->addLayout(hbox, row, column, 1, 3); m_firstDateEdit = new DDateEdit; m_secondDateEdit = new DDateEdit; if (m_type == DateOnly) { layout->addWidget(m_firstDateEdit, row, column); layout->addWidget(m_betweenLabel, row, column + 1, Qt::AlignHCenter); layout->addWidget(m_secondDateEdit, row, column + 2); } else { QHBoxLayout* const hbox1 = new QHBoxLayout; QHBoxLayout* const hbox2 = new QHBoxLayout; m_firstTimeEdit = new QTimeEdit; m_secondTimeEdit = new QTimeEdit; hbox1->addWidget(m_firstDateEdit); hbox1->addWidget(m_firstTimeEdit); hbox2->addWidget(m_secondDateEdit); hbox2->addWidget(m_secondTimeEdit); layout->addLayout(hbox1, row, column); layout->addWidget(m_betweenLabel, row, column + 1, Qt::AlignHCenter); layout->addLayout(hbox2, row, column + 2); } connect(m_firstDateEdit, SIGNAL(dateChanged(QDate)), this, SLOT(valueChanged())); connect(m_secondDateEdit, SIGNAL(dateChanged(QDate)), this, SLOT(valueChanged())); } void SearchFieldRangeDate::setBetweenText(const QString& between) { m_betweenLabel->setText(between); } void SearchFieldRangeDate::read(SearchXmlCachingReader& reader) { SearchXml::Relation relation = reader.fieldRelation(); if (relation == SearchXml::Interval || relation == SearchXml::IntervalOpen) { QList dates = reader.valueToDateTimeList(); if (dates.size() != 2) { return; } if (m_type == DateTime) { m_firstDateEdit->setDate(dates.first().date()); m_firstTimeEdit->setTime(dates.first().time()); m_secondDateEdit->setDate(dates.last().date()); m_secondTimeEdit->setTime(dates.last().time()); } else { if (relation == SearchXml::Interval) { dates.last() = dates.last().addDays(-1); } m_firstDateEdit->setDate(dates.first().date()); m_secondDateEdit->setDate(dates.last().date()); } } else { QDateTime dt = reader.valueToDateTime(); if (m_type == DateTime) { if (relation == SearchXml::Equal) { m_firstDateEdit->setDate(dt.date()); m_firstTimeEdit->setTime(dt.time()); m_secondDateEdit->setDate(dt.date()); m_secondTimeEdit->setTime(dt.time()); } else if (relation == SearchXml::GreaterThanOrEqual || relation == SearchXml::GreaterThan) { m_firstDateEdit->setDate(dt.date()); m_firstTimeEdit->setTime(dt.time()); } { m_secondDateEdit->setDate(dt.date()); m_secondTimeEdit->setTime(dt.time()); } } else { // In DateOnly mode, we always assume dealing with the beginning of the day, QTime(0,0,0). // In the UI, we show the date only (including the whole day). // The difference between ...Than and ...ThanOrEqual is only one second, ignored. if (relation == SearchXml::Equal) { m_firstDateEdit->setDate(dt.date()); m_secondDateEdit->setDate(dt.date()); } else if (relation == SearchXml::GreaterThanOrEqual || relation == SearchXml::GreaterThan) { m_firstDateEdit->setDate(dt.date()); } else if (relation == SearchXml::LessThanOrEqual || relation == SearchXml::LessThan) { dt = dt.addDays(-1); m_secondDateEdit->setDate(dt.date()); } } } valueChanged(); } void SearchFieldRangeDate::write(SearchXmlWriter& writer) { if (m_firstDateEdit->date().isValid() && m_secondDateEdit->date().isValid()) { QDateTime firstDate(m_firstDateEdit->date()); if (m_type == DateTime) { firstDate.setTime(m_firstTimeEdit->time()); } QDateTime secondDate(m_secondDateEdit->date()); if (m_type == DateTime) { secondDate.setTime(m_secondTimeEdit->time()); } if (firstDate == secondDate) { writer.writeField(m_name, SearchXml::Equal); writer.writeValue(firstDate); writer.finishField(); } else { if (m_type == DateOnly) { secondDate = secondDate.addDays(1); } writer.writeField(m_name, SearchXml::Interval); writer.writeValue(QList() << firstDate << secondDate); writer.finishField(); } } else { QDate date = m_firstDateEdit->date(); if (date.isValid()) { writer.writeField(m_name, SearchXml::GreaterThanOrEqual); QDateTime dt(date); if (m_type == DateTime) { dt.setTime(m_firstTimeEdit->time()); } writer.writeValue(dt); writer.finishField(); } date = m_secondDateEdit->date(); if (date.isValid()) { writer.writeField(m_name, SearchXml::LessThan); QDateTime dt(date); if (m_type == DateTime) { dt.setTime(m_secondTimeEdit->time()); } else { dt = dt.addDays(1); // include whole day } writer.writeValue(dt); writer.finishField(); } } } void SearchFieldRangeDate::reset() { m_firstDateEdit->setDate(QDate()); if (m_type == DateTime) { m_firstTimeEdit->setTime(QTime(0, 0, 0, 0)); } m_secondDateEdit->setDate(QDate()); if (m_type == DateTime) { m_secondTimeEdit->setTime(QTime(0, 0, 0, 0)); } valueChanged(); } void SearchFieldRangeDate::setBoundary(const QDateTime& min, const QDateTime& max) { //something here? Q_UNUSED(min); Q_UNUSED(max); } void SearchFieldRangeDate::setValueWidgetsVisible(bool visible) { m_firstDateEdit->setVisible(visible); if (m_firstTimeEdit) { m_firstTimeEdit->setVisible(visible); } m_secondDateEdit->setVisible(visible); if (m_secondTimeEdit) { m_secondTimeEdit->setVisible(visible); } m_betweenLabel->setVisible(visible); } QList SearchFieldRangeDate::valueWidgetRects() const { QList rects; rects << m_firstDateEdit->geometry(); if (m_firstTimeEdit) { rects << m_firstTimeEdit->geometry(); } rects << m_secondDateEdit->geometry(); if (m_secondTimeEdit) { rects << m_secondTimeEdit->geometry(); } return rects; } void SearchFieldRangeDate::valueChanged() { setValidValueState(m_firstDateEdit->date().isValid() || m_secondDateEdit->date().isValid()); } // ------------------------------------------------------------------------- SearchFieldRangeInt::SearchFieldRangeInt(QObject* const parent) : SearchField(parent), m_min(0), m_max(100), m_reciprocal(false), m_firstBox(0), m_secondBox(0) { m_betweenLabel = new QLabel; m_firstBox = new CustomStepsIntSpinBox; m_secondBox = new CustomStepsIntSpinBox; } void SearchFieldRangeInt::setupValueWidgets(QGridLayout* layout, int row, int column) { // QHBoxLayout *hbox = new QHBoxLayout; // layout->addLayout(hbox, row, column); m_firstBox->setSpecialValueText(QLatin1String(" ")); m_secondBox->setSpecialValueText(QLatin1String(" ")); // hbox->addWidget(m_firstBox); // hbox->addWidget(m_betweenLabel); // hbox->addWidget(m_secondBox); // hbox->addStretch(1); layout->addWidget(m_firstBox, row, column); layout->addWidget(m_betweenLabel, row, column + 1, Qt::AlignHCenter); layout->addWidget(m_secondBox, row, column + 2); connect(m_firstBox, SIGNAL(valueChanged(int)), this, SLOT(valueChanged())); connect(m_secondBox, SIGNAL(valueChanged(int)), this, SLOT(valueChanged())); } void SearchFieldRangeInt::read(SearchXmlCachingReader& reader) { SearchXml::Relation relation = reader.fieldRelation(); if (m_reciprocal) { switch (relation) { case SearchXml::LessThanOrEqual: case SearchXml::LessThan: m_firstBox->setFractionMagicValue(reader.valueToDouble()); break; case SearchXml::GreaterThanOrEqual: case SearchXml::GreaterThan: m_secondBox->setFractionMagicValue(reader.valueToDouble()); break; case SearchXml::Equal: m_firstBox->setFractionMagicValue(reader.valueToDouble()); m_secondBox->setFractionMagicValue(reader.valueToDouble()); break; case SearchXml::Interval: case SearchXml::IntervalOpen: { QList list = reader.valueToDoubleList(); if (list.size() != 2) { return; } m_secondBox->setFractionMagicValue(list.first()); m_firstBox->setFractionMagicValue(list.last()); break; } default: break; } } else { switch (relation) { case SearchXml::GreaterThanOrEqual: m_firstBox->setValue(reader.valueToInt()); break; case SearchXml::GreaterThan: m_firstBox->setValue(reader.valueToInt() - 1); break; case SearchXml::LessThanOrEqual: m_secondBox->setValue(reader.valueToInt()); break; case SearchXml::LessThan: m_secondBox->setValue(reader.valueToInt() + 1); break; case SearchXml::Equal: m_firstBox->setValue(reader.valueToInt()); m_secondBox->setValue(reader.valueToInt()); break; case SearchXml::Interval: case SearchXml::IntervalOpen: { QList list = reader.valueToIntList(); if (list.size() != 2) { return; } m_firstBox->setValue(list.first()); m_secondBox->setValue(list.last()); break; } default: break; } } } void SearchFieldRangeInt::write(SearchXmlWriter& writer) { if (m_firstBox->value() != m_firstBox->minimum() && m_secondBox->value() != m_secondBox->minimum()) { if (m_firstBox->value() != m_secondBox->value()) { writer.writeField(m_name, SearchXml::Interval); if (m_reciprocal) { writer.writeValue(QList() << m_secondBox->fractionMagicValue() << m_firstBox->fractionMagicValue()); } else { writer.writeValue(QList() << m_firstBox->value() << m_secondBox->value()); } writer.finishField(); } else { /** * @todo : This condition is never met. * Right value is either displayed empty (minimum, greater than left) * or one step larger than left */ writer.writeField(m_name, SearchXml::Equal); if (m_reciprocal) { writer.writeValue(m_firstBox->fractionMagicValue()); } else { writer.writeValue(m_firstBox->value()); } writer.finishField(); } } else { if (m_firstBox->value() != m_firstBox->minimum()) { if (m_reciprocal) { writer.writeField(m_name, SearchXml::LessThanOrEqual); writer.writeValue(m_firstBox->fractionMagicValue()); } else { writer.writeField(m_name, SearchXml::GreaterThanOrEqual); writer.writeValue(m_firstBox->value()); } writer.finishField(); } if (m_secondBox->value() != m_secondBox->minimum()) { if (m_reciprocal) { writer.writeField(m_name, SearchXml::GreaterThanOrEqual); writer.writeValue(m_secondBox->fractionMagicValue()); } else { writer.writeField(m_name, SearchXml::LessThanOrEqual); writer.writeValue(m_secondBox->value()); } writer.finishField(); } } } void SearchFieldRangeInt::setBetweenText(const QString& text) { m_betweenLabel->setText(text); } void SearchFieldRangeInt::setNumberPrefixAndSuffix(const QString& prefix, const QString& suffix) { m_firstBox->setPrefix(prefix); m_secondBox->setPrefix(prefix); m_firstBox->setSuffix(suffix); m_secondBox->setSuffix(suffix); } void SearchFieldRangeInt::setBoundary(int min, int max, int step) { if (m_reciprocal) { m_min = max; m_max = min; } else { m_min = min; m_max = max; } m_firstBox->setRange(m_min, m_max); m_firstBox->setSingleStep(step); m_firstBox->setValue(m_min); m_secondBox->setRange(m_min, m_max); m_secondBox->setSingleStep(step); m_secondBox->setValue(m_min); } void SearchFieldRangeInt::enableFractionMagic(const QString& prefix) { m_reciprocal = true; m_firstBox->enableFractionMagic(prefix); m_firstBox->setInvertStepping(true); m_secondBox->enableFractionMagic(prefix); m_secondBox->setInvertStepping(true); } void SearchFieldRangeInt::setSuggestedValues(const QList& values) { m_firstBox->setSuggestedValues(values); m_secondBox->setSuggestedValues(values); } void SearchFieldRangeInt::setSuggestedInitialValue(int value) { m_firstBox->setSuggestedInitialValue(value); m_secondBox->setSuggestedInitialValue(value); } void SearchFieldRangeInt::setSingleSteps(int smaller, int larger) { m_firstBox->setSingleSteps(smaller, larger); m_secondBox->setSingleSteps(smaller, larger); } void SearchFieldRangeInt::setInvertStepping(bool invert) { m_firstBox->setInvertStepping(invert); m_secondBox->setInvertStepping(invert); } void SearchFieldRangeInt::valueChanged() { bool validValue = false; if (m_reciprocal) { bool firstAtMinimum = (m_firstBox->value() == m_firstBox->minimum()); bool secondAtMinimum = (m_secondBox->value() == m_secondBox->minimum()); if (!secondAtMinimum) { m_firstBox->setRange(m_secondBox->value() - 1, m_max); validValue = true; } if (!firstAtMinimum) { m_secondBox->setRange(m_min - 1, m_firstBox->value()); if (secondAtMinimum) { m_firstBox->setRange(m_min, m_max); m_secondBox->setValue(m_secondBox->minimum()); } validValue = true; } if (firstAtMinimum && secondAtMinimum) { m_firstBox->setRange(m_min, m_max); m_secondBox->setRange(m_min, m_max); } } else { bool firstAtMinimum = (m_firstBox->value() == m_firstBox->minimum()); bool secondAtMinimum = (m_secondBox->value() == m_secondBox->minimum()); if (!secondAtMinimum) { m_firstBox->setRange(m_min, m_secondBox->value()); validValue = true; } if (!firstAtMinimum) { m_secondBox->setRange(m_firstBox->value() - 1, m_max); if (secondAtMinimum) { m_firstBox->setRange(m_min, m_max); m_secondBox->setValue(m_secondBox->minimum()); } validValue = true; } if (firstAtMinimum && secondAtMinimum) { m_firstBox->setRange(m_min, m_max); m_secondBox->setRange(m_min, m_max); } } setValidValueState(validValue); } void SearchFieldRangeInt::reset() { m_firstBox->setRange(m_min, m_max); m_secondBox->setRange(m_min, m_max); m_firstBox->reset(); m_secondBox->reset(); } void SearchFieldRangeInt::setValueWidgetsVisible(bool visible) { m_firstBox->setVisible(visible); m_secondBox->setVisible(visible); m_betweenLabel->setVisible(visible); } QList SearchFieldRangeInt::valueWidgetRects() const { QList rects; rects << m_firstBox->geometry(); rects << m_secondBox->geometry(); return rects; } // ------------------------------------------------------------------------- SearchFieldRangeDouble::SearchFieldRangeDouble(QObject* const parent) : SearchField(parent), m_min(0), m_max(100), m_factor(1), m_firstBox(0), m_secondBox(0) { m_betweenLabel = new QLabel; m_firstBox = new CustomStepsDoubleSpinBox; m_secondBox = new CustomStepsDoubleSpinBox; } void SearchFieldRangeDouble::setupValueWidgets(QGridLayout* layout, int row, int column) { // QHBoxLayout *hbox = new QHBoxLayout; // layout->addLayout(hbox, row, column); m_firstBox->setSpecialValueText(QLatin1String(" ")); m_secondBox->setSpecialValueText(QLatin1String(" ")); /* hbox->addWidget(m_firstBox); hbox->addWidget(m_betweenLabel); hbox->addWidget(m_secondBox); hbox->addStretch(1);*/ layout->addWidget(m_firstBox, row, column); layout->addWidget(m_betweenLabel, row, column + 1, Qt::AlignHCenter); layout->addWidget(m_secondBox, row, column + 2); connect(m_firstBox, SIGNAL(valueChanged(double)), this, SLOT(valueChanged())); connect(m_secondBox, SIGNAL(valueChanged(double)), this, SLOT(valueChanged())); } void SearchFieldRangeDouble::read(SearchXmlCachingReader& reader) { SearchXml::Relation relation = reader.fieldRelation(); if (relation == SearchXml::GreaterThanOrEqual || relation == SearchXml::GreaterThan) { m_firstBox->setValue(reader.valueToDouble() / m_factor); } else if (relation == SearchXml::LessThanOrEqual || relation == SearchXml::LessThan) { m_secondBox->setValue(reader.valueToDouble() / m_factor); } else if (relation == SearchXml::Interval || relation == SearchXml::IntervalOpen) { QList list = reader.valueToDoubleList(); if (list.size() != 2) { return; } m_firstBox->setValue(list.first() / m_factor); m_secondBox->setValue(list.last() / m_factor); } } void SearchFieldRangeDouble::write(SearchXmlWriter& writer) { if (m_firstBox->value() != m_firstBox->minimum() && m_secondBox->value() != m_secondBox->minimum()) { if (m_firstBox->value() != m_secondBox->value()) { writer.writeField(m_name, SearchXml::Interval); writer.writeValue(QList() << (m_firstBox->value() * m_factor) << (m_secondBox->value() * m_factor)); writer.finishField(); } else { //TODO: See SearchFieldRangeInt writer.writeField(m_name, SearchXml::Equal); writer.writeValue(m_firstBox->value() * m_factor); writer.finishField(); } } else { if (m_firstBox->value() != m_firstBox->minimum()) { writer.writeField(m_name, SearchXml::GreaterThanOrEqual); writer.writeValue(m_firstBox->value() * m_factor); writer.finishField(); } if (m_secondBox->value() != m_secondBox->minimum()) { writer.writeField(m_name, SearchXml::LessThanOrEqual); writer.writeValue(m_secondBox->value() * m_factor); writer.finishField(); } } } void SearchFieldRangeDouble::setBetweenText(const QString& text) { m_betweenLabel->setText(text); } void SearchFieldRangeDouble::setNoValueText(const QString& text) { m_firstBox->setSpecialValueText(text); m_secondBox->setSpecialValueText(text); } void SearchFieldRangeDouble::setNumberPrefixAndSuffix(const QString& prefix, const QString& suffix) { m_firstBox->setPrefix(prefix); m_secondBox->setPrefix(prefix); m_firstBox->setSuffix(suffix); m_secondBox->setSuffix(suffix); } void SearchFieldRangeDouble::setBoundary(double min, double max, int decimals, double step) { m_min = min; m_max = max; m_firstBox->setRange(min, max); m_firstBox->setSingleStep(step); m_firstBox->setDecimals(decimals); m_firstBox->setValue(min); m_secondBox->setRange(min, max); m_secondBox->setSingleStep(step); m_secondBox->setDecimals(decimals); m_secondBox->setValue(min); } void SearchFieldRangeDouble::setFactor(double factor) { m_factor = factor; } void SearchFieldRangeDouble::setSuggestedValues(const QList& values) { m_firstBox->setSuggestedValues(values); m_secondBox->setSuggestedValues(values); } void SearchFieldRangeDouble::setSuggestedInitialValue(double value) { m_firstBox->setSuggestedInitialValue(value); m_secondBox->setSuggestedInitialValue(value); } void SearchFieldRangeDouble::setSingleSteps(double smaller, double larger) { m_firstBox->setSingleSteps(smaller, larger); m_secondBox->setSingleSteps(smaller, larger); } void SearchFieldRangeDouble::setInvertStepping(bool invert) { m_firstBox->setInvertStepping(invert); m_secondBox->setInvertStepping(invert); } void SearchFieldRangeDouble::valueChanged() { bool validValue = false; bool firstAtMinimum = (m_firstBox->value() == m_firstBox->minimum()); bool secondAtMinimum = (m_secondBox->value() == m_secondBox->minimum()); if (!secondAtMinimum) { m_firstBox->setRange(m_min, m_secondBox->value()); validValue = true; } if (!firstAtMinimum) { m_secondBox->setRange(m_firstBox->value() - 0.1, m_max); if (secondAtMinimum) { m_firstBox->setRange(m_min, m_max); m_secondBox->setValue(m_secondBox->minimum()); } validValue = true; } if (firstAtMinimum && secondAtMinimum) { m_firstBox->setRange(m_min, m_max); m_secondBox->setRange(m_min, m_max); } setValidValueState(validValue); } void SearchFieldRangeDouble::reset() { m_firstBox->setRange(m_min, m_max); m_secondBox->setRange(m_min, m_max); m_firstBox->reset(); m_secondBox->reset(); } void SearchFieldRangeDouble::setValueWidgetsVisible(bool visible) { m_firstBox->setVisible(visible); m_secondBox->setVisible(visible); m_betweenLabel->setVisible(visible); } QList SearchFieldRangeDouble::valueWidgetRects() const { QList rects; rects << m_firstBox->geometry(); rects << m_secondBox->geometry(); return rects; } // ------------------------------------------------------------------------- SearchFieldChoice::SearchFieldChoice(QObject* const parent) : SearchField(parent), m_comboBox(0), m_type(QVariant::Invalid) { m_model = new ChoiceSearchModel(this); m_anyText = i18n("Any"); } void SearchFieldChoice::setupValueWidgets(QGridLayout* layout, int row, int column) { m_comboBox = new ChoiceSearchComboBox; m_comboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); connect(m_model, SIGNAL(checkStateChanged(QVariant,bool)), this, SLOT(checkStateChanged())); m_comboBox->setModel(m_model); // set object name for style sheet m_comboBox->setObjectName(QLatin1String("SearchFieldChoice_ComboBox")); // label is created only after setting the model m_comboBox->label()->setObjectName(QLatin1String("SearchFieldChoice_ClickLabel")); updateComboText(); layout->addWidget(m_comboBox, row, column, 1, 3); } void SearchFieldChoice::setChoice(const QMap& map) { m_type = QVariant::Int; m_model->setChoice(map); } void SearchFieldChoice::setChoice(const QStringList& choice) { m_type = QVariant::String; m_model->setChoice(choice); } void SearchFieldChoice::setAnyText(const QString& anyText) { m_anyText = anyText; } void SearchFieldChoice::checkStateChanged() { updateComboText(); } void SearchFieldChoice::updateComboText() { QStringList checkedChoices = m_model->checkedDisplayTexts(); if (checkedChoices.isEmpty()) { m_comboBox->setLabelText(m_anyText); setValidValueState(false); } else if (checkedChoices.count() == 1) { m_comboBox->setLabelText(checkedChoices.first()); setValidValueState(true); } else { m_comboBox->setLabelText(i18n("Any of: %1", checkedChoices.join(QLatin1String(", ")))); setValidValueState(true); } } void SearchFieldChoice::read(SearchXmlCachingReader& reader) { SearchXml::Relation relation = reader.fieldRelation(); QList values; if (relation == SearchXml::OneOf) { if (m_type == QVariant::Int) { m_model->setChecked(reader.valueToIntList()); } else if (m_type == QVariant::String) { m_model->setChecked(reader.valueToStringList()); } } else { if (m_type == QVariant::Int) { m_model->setChecked(reader.valueToInt(), relation); } else if (m_type == QVariant::String) { // The testRelation magic only really makes sense for integers. "Like" is not implemented. //m_model->setChecked(reader.value(), relation); m_model->setChecked(reader.value()); } } } void SearchFieldChoice::write(SearchXmlWriter& writer) { if (m_type == QVariant::Int) { QList v = m_model->checkedKeys(); if (!v.isEmpty()) { if (v.size() == 1) { writer.writeField(m_name, SearchXml::Equal); writer.writeValue(v.first()); writer.finishField(); } else { writer.writeField(m_name, SearchXml::OneOf); writer.writeValue(v); writer.finishField(); } } } else if (m_type == QVariant::String) { QList v = m_model->checkedKeys(); if (!v.isEmpty()) { if (v.size() == 1) { // For choice string fields, we have the possibility to specify the wildcard // position with the position of *. if (v.first().contains(QLatin1String("*"))) { writer.writeField(m_name, SearchXml::Like); } else { writer.writeField(m_name, SearchXml::Equal); } writer.writeValue(v.first()); writer.finishField(); } else { // OneOf handles wildcards automatically writer.writeField(m_name, SearchXml::OneOf); writer.writeValue(v); writer.finishField(); } } } } void SearchFieldChoice::reset() { m_model->resetChecked(); } void SearchFieldChoice::setValueWidgetsVisible(bool visible) { m_comboBox->setVisible(visible); } QList SearchFieldChoice::valueWidgetRects() const { QList rects; rects << m_comboBox->geometry(); return rects; } /* class SearchFieldChoice : public SearchField { // Note: Someone added this space on purpose (Marcel?) // It seems that automoc4 is not recognizing this macro to be in a comment // block and therefore will fail when parsing this file. Adding a space to the // macro name will fix this issue. When uncommenting this block again, make sure to // fix the macro name of course. Q_ OBJECT public: SearchFieldChoice(SearchFieldGroup *parent); virtual void read(SearchXmlCachingReader &reader); virtual void write(SearchXmlWriter &writer); virtual void reset(); void setChoice(const QMap &map); void setAnyText(const QString& string); virtual void setupValueWidgets(QGridLayout *layout, int row, int column); virtual void setValueWidgetsVisible(bool visible); protected Q_SLOTS: void slotClicked(); void slotUpdateLabel(); protected: void setValues(const QList &values); void setValues(int value, SearchXml::Relation relation); QList values() const; QString valueText() const; virtual void setupChoiceWidgets(); protected: QString m_anyText; SqueezedClickLabel *m_label; QVBoxLayout *m_vbox; QMap m_choiceMap; QMap m_widgetMap; VisibilityController *m_controller; }; SearchFieldChoice::SearchFieldChoice(SearchFieldGroup *parent) : SearchField(parent), m_vbox(0) { m_anyText = i18n("Any"); m_label = new SqueezedClickLabel; m_label->setObjectName(QLatin1String("SearchFieldChoice_ClickLabel")); m_controller = new VisibilityController(this); m_controller->setContainerWidget(parent); } void SearchFieldChoice::setupValueWidgets(QGridLayout *layout, int row, int column) { m_vbox = new QVBoxLayout; layout->addLayout(m_vbox, row, column, 1, 3); m_label->setElideMode(Qt::ElideRight); m_vbox->addWidget(m_label); connect(m_label, SIGNAL(activated()), this, SLOT(slotClicked())); setupChoiceWidgets(); slotUpdateLabel(); } void SearchFieldChoice::slotClicked() { m_controller->triggerVisibility(); } void SearchFieldChoice::slotUpdateLabel() { QString text = valueText(); if (text.isNull()) text = m_anyText; m_label->setText(text); } void SearchFieldChoice::setValueWidgetsVisible(bool visible) { m_label->setVisible(visible); if (!visible) m_controller->hide(); } void SearchFieldChoice::setupChoiceWidgets() { QGroupBox *groupbox = new QGroupBox; m_vbox->addWidget(groupbox); m_controller->addWidget(groupbox); QVBoxLayout *vbox = new QVBoxLayout; QMap::const_iterator it; for (it = m_choiceMap.begin(); it != m_choiceMap.end(); ++it) { QCheckBox *box = new QCheckBox; box->setText(it.value()); vbox->addWidget(box); m_controller->addWidget(box); m_widgetMap[box] = it.key(); connect(box, SIGNAL(stateChanged(int)), this, SLOT(slotUpdateLabel())); } groupbox->setLayout(vbox); } QString SearchFieldChoice::valueText() const { QStringList list; QMap::const_iterator it; for (it = m_widgetMap.begin(); it != m_widgetMap.end(); ++it) { if (it.key()->isChecked()) list << it.key()->text(); } if (list.isEmpty()) return QString(); else if (list.size() == 1) { return list.first(); } else { return i18n("Either of: %1", list.join(", ")); } } void SearchFieldChoice::read(SearchXmlCachingReader& reader) { SearchXml::Relation relation = reader.fieldRelation(); QList values; if (relation == SearchXml::OneOf) { setValues(reader.valueToIntList()); } else { setValues(reader.valueToInt(), relation); } } void SearchFieldChoice::write(SearchXmlWriter& writer) { QList v = values(); if (!v.isEmpty()) { if (v.size() == 1) { writer.writeField(m_name, SearchXml::Equal); writer.writeValue(v.first()); writer.finishField(); } else { writer.writeField(m_name, SearchXml::OneOf); writer.writeValue(v); writer.finishField(); } } } void SearchFieldChoice::reset() { setValues(QList()); } void SearchFieldChoice::setChoice(const QMap& map) { m_choiceMap = map; } void SearchFieldChoice::setValues(const QList& values) { QMap::const_iterator it; for (it = m_widgetMap.begin(); it != m_widgetMap.end(); ++it) { it.key()->setChecked(values.contains(it.value())); } } void SearchFieldChoice::setValues(int value, SearchXml::Relation relation) { QMap::const_iterator it; for (it = m_widgetMap.begin(); it != m_widgetMap.end(); ++it) { it.key()->setChecked(SearchXml::testRelation(it.value(), value, relation)); } } QList SearchFieldChoice::values() const { QList list; QMap::const_iterator it; for (it = m_widgetMap.begin(); it != m_widgetMap.end(); ++it) { if (it.key()->isChecked()) list << it.value(); } return list; } */ // ------------------------------------------------------------------------- SearchFieldAlbum::SearchFieldAlbum(QObject* const parent, Type type) : SearchField(parent), m_wrapperBox(0), m_albumComboBox(0), m_tagComboBox(0), m_operation(0), m_type(type), m_model(0) { } void SearchFieldAlbum::setupValueWidgets(QGridLayout* layout, int row, int column) { if (m_type == TypeAlbum) { m_albumComboBox = new AlbumTreeViewSelectComboBox; m_wrapperBox = m_albumComboBox; m_albumComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); m_albumComboBox->setDefaultModel(); m_albumComboBox->setNoSelectionText(i18n("Any Album")); m_albumComboBox->addCheckUncheckContextMenuActions(); m_model = m_albumComboBox->model(); layout->addWidget(m_wrapperBox, row, column, 1, 3); } else if (m_type == TypeTag) { m_wrapperBox = new DHBox(0); m_tagComboBox = new TagTreeViewSelectComboBox(m_wrapperBox); m_operation = new SqueezedComboBox(m_wrapperBox); m_operation->addSqueezedItem(i18nc("@label:listbox", "In All"), Operation::All); m_operation->addSqueezedItem(i18nc("@label:listbox", "In One of"), Operation::OneOf); m_tagComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); m_tagComboBox->setDefaultModel(); m_tagComboBox->setNoSelectionText(i18n("Any Tag")); m_tagComboBox->addCheckUncheckContextMenuActions(); m_model = m_tagComboBox->model(); layout->addWidget(m_wrapperBox, row, column, 1, 3); } connect(m_model, SIGNAL(checkStateChanged(Album*,Qt::CheckState)), this, SLOT(updateState())); updateState(); } void SearchFieldAlbum::updateState() { setValidValueState(!m_model->checkedAlbums().isEmpty()); } void SearchFieldAlbum::read(SearchXmlCachingReader& reader) { QList ids = reader.valueToIntOrIntList(); Album* a = 0; if (m_type == TypeAlbum) { foreach(int id, ids) { a = AlbumManager::instance()->findPAlbum(id); if (!a) { qCDebug(DIGIKAM_GENERAL_LOG) << "Search: Did not find album for ID" << id << "given in Search XML"; return; } m_model->setChecked(a, true); } } else if (m_type == TypeTag) { if (reader.fieldRelation() == SearchXml::AllOf) { m_operation->setCurrentIndex(Operation::All); } else { m_operation->setCurrentIndex(Operation::OneOf); } foreach(int id, ids) { a = AlbumManager::instance()->findTAlbum(id); // Ignore internal tags here. if (a && TagsCache::instance()->isInternalTag(a->id())) { a = 0; } if (!a) { qCDebug(DIGIKAM_GENERAL_LOG) << "Search: Did not find album for ID" << id << "given in Search XML"; return; } m_model->setChecked(a, true); } } } void SearchFieldAlbum::write(SearchXmlWriter& writer) { AlbumList checkedAlbums = m_model->checkedAlbums(); if (checkedAlbums.isEmpty()) { return; } QList albumIds; foreach(Album* const album, checkedAlbums) { albumIds << album->id(); } SearchXml::Relation relation = SearchXml::OneOf; if (m_operation) { if (m_operation->itemData(m_operation->currentIndex()).toInt() == Operation::All) { relation = SearchXml::AllOf; } } if (albumIds.size() > 1) { writer.writeField(m_name, relation); writer.writeValue(albumIds); } else { writer.writeField(m_name, SearchXml::Equal); writer.writeValue(albumIds.first()); } writer.finishField(); } void SearchFieldAlbum::reset() { m_model->resetCheckedAlbums(); } void SearchFieldAlbum::setValueWidgetsVisible(bool visible) { m_wrapperBox->setVisible(visible); } QList SearchFieldAlbum::valueWidgetRects() const { QList rects; rects << m_wrapperBox->geometry(); return rects; } // ------------------------------------------------------------------------- SearchFieldRating::SearchFieldRating(QObject* const parent) : SearchField(parent) { m_betweenLabel = new QLabel; m_firstBox = new RatingComboBox; m_secondBox = new RatingComboBox; } void SearchFieldRating::setupValueWidgets(QGridLayout* layout, int row, int column) { layout->addWidget(m_firstBox, row, column); layout->addWidget(m_betweenLabel, row, column + 1, Qt::AlignHCenter); layout->addWidget(m_secondBox, row, column + 2); connect(m_firstBox, SIGNAL(ratingValueChanged(int)), this, SLOT(firstValueChanged())); connect(m_secondBox, SIGNAL(ratingValueChanged(int)), this, SLOT(secondValueChanged())); } void SearchFieldRating::read(SearchXmlCachingReader& reader) { SearchXml::Relation relation = reader.fieldRelation(); switch (relation) { case SearchXml::GreaterThanOrEqual: m_firstBox->setRatingValue((RatingComboBox::RatingValue)reader.valueToInt()); break; case SearchXml::GreaterThan: m_firstBox->setRatingValue((RatingComboBox::RatingValue)(reader.valueToInt() - 1)); break; case SearchXml::LessThanOrEqual: m_secondBox->setRatingValue((RatingComboBox::RatingValue)reader.valueToInt()); break; case SearchXml::LessThan: m_secondBox->setRatingValue((RatingComboBox::RatingValue)(reader.valueToInt() + 1)); break; case SearchXml::Equal: m_firstBox->setRatingValue((RatingComboBox::RatingValue)reader.valueToInt()); m_secondBox->setRatingValue((RatingComboBox::RatingValue)reader.valueToInt()); break; case SearchXml::Interval: case SearchXml::IntervalOpen: { QList list = reader.valueToIntList(); if (list.size() != 2) { return; } m_firstBox->setRatingValue((RatingComboBox::RatingValue)list.first()); m_secondBox->setRatingValue((RatingComboBox::RatingValue)list.last()); break; } default: break; } } void SearchFieldRating::write(SearchXmlWriter& writer) { RatingComboBox::RatingValue first = m_firstBox->ratingValue(); RatingComboBox::RatingValue second = m_secondBox->ratingValue(); if (first == RatingComboBox::NoRating) { writer.writeField(m_name, SearchXml::Equal); writer.writeValue(-1); writer.finishField(); } else if (first != RatingComboBox::Null && first == second) { writer.writeField(m_name, SearchXml::Equal); writer.writeValue(first); writer.finishField(); } else if (first != RatingComboBox::Null && second != RatingComboBox::Null) { writer.writeField(m_name, SearchXml::Interval); writer.writeValue(QList() << first << second); writer.finishField(); } else { if (first != RatingComboBox::Null) { writer.writeField(m_name, SearchXml::GreaterThanOrEqual); writer.writeValue(first); writer.finishField(); } if (second != RatingComboBox::Null) { writer.writeField(m_name, SearchXml::LessThanOrEqual); writer.writeValue(second); writer.finishField(); } } } void SearchFieldRating::setBetweenText(const QString& text) { m_betweenLabel->setText(text); } void SearchFieldRating::firstValueChanged() { RatingComboBox::RatingValue first = m_firstBox->ratingValue(); RatingComboBox::RatingValue second = m_secondBox->ratingValue(); if (first == RatingComboBox::NoRating) { m_secondBox->setRatingValue(RatingComboBox::Null); m_secondBox->setEnabled(false); } else { m_secondBox->setEnabled(true); } if (first >= RatingComboBox::Rating0 && first <= RatingComboBox::Rating5) { if (first > second) { m_secondBox->setRatingValue(RatingComboBox::Null); } } setValidValueState(first != RatingComboBox::Null || second != RatingComboBox::Null); } void SearchFieldRating::secondValueChanged() { RatingComboBox::RatingValue first = m_firstBox->ratingValue(); RatingComboBox::RatingValue second = m_secondBox->ratingValue(); // NoRating is not possible for the second box if (second >= RatingComboBox::Rating0 && second <= RatingComboBox::Rating5) { if (first > second) { m_firstBox->setRatingValue(second); } } setValidValueState(first != RatingComboBox::Null || second != RatingComboBox::Null); } void SearchFieldRating::reset() { m_firstBox->setRatingValue(RatingComboBox::Null); m_secondBox->setRatingValue(RatingComboBox::Null); } void SearchFieldRating::setValueWidgetsVisible(bool visible) { m_firstBox->setVisible(visible); m_secondBox->setVisible(visible); m_betweenLabel->setVisible(visible); } QList SearchFieldRating::valueWidgetRects() const { QList rects; rects << m_firstBox->geometry(); rects << m_secondBox->geometry(); return rects; } // ------------------------------------------------------------------------- SearchFieldComboBox::SearchFieldComboBox(QObject* const parent) : SearchField(parent), m_comboBox(0) { } void SearchFieldComboBox::setupValueWidgets(QGridLayout* layout, int row, int column) { m_comboBox = new QComboBox; m_comboBox->setEditable(false); layout->addWidget(m_comboBox, row, column, 1, 3); connect(m_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(indexChanged(int))); } void SearchFieldComboBox::write(SearchXmlWriter& writer) { int index = m_comboBox->currentIndex(); if (index != -1) { QVariant bits = m_comboBox->itemData(index); if (!bits.isNull()) { writer.writeField(m_name, SearchXml::Equal); writer.writeValue(bits.toInt()); writer.finishField(); } } } void SearchFieldComboBox::setValueWidgetsVisible(bool visible) { m_comboBox->setVisible(visible); } void SearchFieldComboBox::reset() { m_comboBox->setCurrentIndex(0); } QList SearchFieldComboBox::valueWidgetRects() const { QList rects; rects << m_comboBox->geometry(); return rects; } void SearchFieldComboBox::indexChanged(int index) { setValidValueState(index != 0); } // ------------------------------------------------------------------------- SearchFieldCheckBox::SearchFieldCheckBox(QObject* const parent) : SearchField(parent), m_checkBox(0) { } void SearchFieldCheckBox::setupValueWidgets(QGridLayout* layout, int row, int column) { m_checkBox = new QCheckBox(m_text); layout->addWidget(m_checkBox, row, column, 1, 3); connect(m_checkBox, SIGNAL(toggled(bool)), this, SLOT(slotToggled(bool))); } void SearchFieldCheckBox::setLabel(const QString& text) { m_text = text; if (m_checkBox) { m_checkBox->setText(m_text); } } void SearchFieldCheckBox::write(SearchXmlWriter& writer) { if (m_checkBox->isChecked()) { writer.writeField(m_name, SearchXml::Equal); writer.finishField(); } } void SearchFieldCheckBox::read(SearchXmlCachingReader& reader) { SearchXml::Relation relation = reader.fieldRelation(); reader.readToEndOfElement(); if (relation == SearchXml::Equal) { m_checkBox->setChecked(true); } } void SearchFieldCheckBox::setValueWidgetsVisible(bool visible) { m_checkBox->setVisible(visible); } void SearchFieldCheckBox::reset() { m_checkBox->setChecked(false); } QList SearchFieldCheckBox::valueWidgetRects() const { QList rects; rects << m_checkBox->geometry(); return rects; } void SearchFieldCheckBox::slotToggled(bool checked) { setValidValueState(checked); } // ------------------------------------------------------------------------- SearchFieldColorDepth::SearchFieldColorDepth(QObject* const parent) : SearchFieldComboBox(parent) { } void SearchFieldColorDepth::setupValueWidgets(QGridLayout* layout, int row, int column) { SearchFieldComboBox::setupValueWidgets(layout, row, column); m_comboBox->addItem(i18n("any color depth")); m_comboBox->addItem(i18n("8 bits per channel"), 8); m_comboBox->addItem(i18n("16 bits per channel"), 16); m_comboBox->setCurrentIndex(0); } void SearchFieldColorDepth::read(SearchXmlCachingReader& reader) { SearchXml::Relation relation = reader.fieldRelation(); if (relation == SearchXml::Equal) { int bits = reader.valueToInt(); if (bits == 8) { m_comboBox->setCurrentIndex(1); } else if (bits == 16) { m_comboBox->setCurrentIndex(2); } } } // ------------------------------------------------------------------------- SearchFieldPageOrientation::SearchFieldPageOrientation(QObject* const parent) : SearchFieldComboBox(parent) { } void SearchFieldPageOrientation::setupValueWidgets(QGridLayout* layout, int row, int column) { SearchFieldComboBox::setupValueWidgets(layout, row, column); m_comboBox->addItem(i18n("Any Orientation")); m_comboBox->addItem(i18n("Landscape Orientation"), 1); m_comboBox->addItem(i18n("Portrait orientation"), 2); m_comboBox->setCurrentIndex(0); } void SearchFieldPageOrientation::read(SearchXmlCachingReader& reader) { SearchXml::Relation relation = reader.fieldRelation(); if (relation == SearchXml::Equal) { int value = reader.valueToInt(); if (value == 1) { m_comboBox->setCurrentIndex(1); } else if (value == 2) { m_comboBox->setCurrentIndex(2); } } } // ------------------------------------------------------------------------- SearchFieldLabels::SearchFieldLabels(QObject* const parent) : SearchField(parent), m_pickLabelFilter(0), m_colorLabelFilter(0) { } void SearchFieldLabels::setupValueWidgets(QGridLayout* layout, int row, int column) { QHBoxLayout* const hbox = new QHBoxLayout; m_pickLabelFilter = new PickLabelFilter; m_colorLabelFilter = new ColorLabelFilter; hbox->addWidget(m_pickLabelFilter); hbox->addStretch(10); hbox->addWidget(m_colorLabelFilter); connect(m_pickLabelFilter, SIGNAL(signalPickLabelSelectionChanged(QList)), this, SLOT(updateState())); connect(m_colorLabelFilter, SIGNAL(signalColorLabelSelectionChanged(QList)), this, SLOT(updateState())); updateState(); layout->addLayout(hbox, row, column, 1, 3); } void SearchFieldLabels::updateState() { setValidValueState(!m_colorLabelFilter->colorLabels().isEmpty()); } void SearchFieldLabels::read(SearchXmlCachingReader& reader) { TAlbum* a = 0; QList ids = reader.valueToIntOrIntList(); QList clabels; QList plabels; foreach(int id, ids) { a = AlbumManager::instance()->findTAlbum(id); if (!a) { qCDebug(DIGIKAM_GENERAL_LOG) << "Search: Did not find Label album for ID" << id << "given in Search XML"; } else { int cl = TagsCache::instance()->colorLabelForTag(a->id()); if (cl != -1) { clabels.append((ColorLabel)cl); } else { int pl = TagsCache::instance()->pickLabelForTag(a->id()); if (pl != -1) { plabels.append((PickLabel)pl); } } } } m_colorLabelFilter->setColorLabels(clabels); m_pickLabelFilter->setPickLabels(plabels); } void SearchFieldLabels::write(SearchXmlWriter& writer) { QList albumIds; QList clAlbums = m_colorLabelFilter->getCheckedColorLabelTags(); if (!clAlbums.isEmpty()) { foreach(TAlbum* const album, clAlbums) { albumIds << album->id(); } } QList plAlbums = m_pickLabelFilter->getCheckedPickLabelTags(); if (!plAlbums.isEmpty()) { foreach(TAlbum* const album, plAlbums) { albumIds << album->id(); } } if (albumIds.isEmpty()) { return; } // NOTE: As Color Labels are internal tags, we trig database on "tagid" // with "labels" in ImageQueryBuilder::buildField(). writer.writeField(m_name, SearchXml::InTree); if (albumIds.size() > 1) { writer.writeValue(albumIds); } else { writer.writeValue(albumIds.first()); } writer.finishField(); } void SearchFieldLabels::reset() { m_colorLabelFilter->reset(); m_pickLabelFilter->reset(); } void SearchFieldLabels::setValueWidgetsVisible(bool visible) { m_colorLabelFilter->setVisible(visible); m_pickLabelFilter->setVisible(visible); } QList SearchFieldLabels::valueWidgetRects() const { QList rects; rects << m_pickLabelFilter->geometry(); rects << m_colorLabelFilter->geometry(); return rects; } } // namespace Digikam