diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 89180b780..58af19ebd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,407 +1,408 @@ include(ECMAddAppIcon) configure_file(config-baloo.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-baloo.h) configure_file(config-dolphin.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-dolphin.h) add_definitions( -DTRANSLATION_DOMAIN=\"dolphin\" -DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS ) remove_definitions( -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_URL_CAST_FROM_STRING -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII ) ########################################## set(dolphinvcs_LIB_SRCS views/versioncontrol/kversioncontrolplugin.cpp ) add_library(dolphinvcs ${dolphinvcs_LIB_SRCS}) generate_export_header(dolphinvcs BASE_NAME dolphinvcs) target_link_libraries( dolphinvcs PUBLIC Qt5::Widgets ) set_target_properties(dolphinvcs PROPERTIES VERSION ${DOLPHINVCS_VERSION_STRING} SOVERSION ${DOLPHINVCS_SOVERSION} EXPORT_NAME DolphinVcs ) ecm_generate_headers(dolphinvcs_LIB_HEADERS HEADER_NAMES KVersionControlPlugin RELATIVE "views/versioncontrol" REQUIRED_HEADERS dolphinvcs_LIB_HEADERS ) install(TARGETS dolphinvcs EXPORT DolphinVcsTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES views/versioncontrol/fileviewversioncontrolplugin.desktop DESTINATION ${KDE_INSTALL_KSERVICETYPES5DIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dolphinvcs_export.h DESTINATION ${KDE_INSTALL_INCLUDEDIR} COMPONENT Devel) install(FILES ${dolphinvcs_LIB_HEADERS} DESTINATION "${KDE_INSTALL_INCLUDEDIR}/Dolphin" COMPONENT Devel) ########### next target ############### set(dolphinprivate_LIB_SRCS kitemviews/kfileitemlistview.cpp kitemviews/kfileitemlistwidget.cpp kitemviews/kfileitemmodel.cpp kitemviews/kfileitemmodelrolesupdater.cpp kitemviews/kitemlistcontainer.cpp kitemviews/kitemlistcontroller.cpp kitemviews/kitemlistgroupheader.cpp kitemviews/kitemlistheader.cpp kitemviews/kitemlistselectionmanager.cpp kitemviews/kitemliststyleoption.cpp kitemviews/kitemlistview.cpp kitemviews/kitemlistviewaccessible.cpp kitemviews/kitemlistwidget.cpp kitemviews/kitemmodelbase.cpp kitemviews/kitemset.cpp kitemviews/kstandarditem.cpp kitemviews/kstandarditemlistgroupheader.cpp kitemviews/kstandarditemlistwidget.cpp kitemviews/kstandarditemlistview.cpp kitemviews/kstandarditemmodel.cpp kitemviews/private/kdirectorycontentscounter.cpp kitemviews/private/kdirectorycontentscounterworker.cpp kitemviews/private/kfileitemclipboard.cpp kitemviews/private/kfileitemmodeldirlister.cpp kitemviews/private/kfileitemmodelfilter.cpp kitemviews/private/kitemlistheaderwidget.cpp kitemviews/private/kitemlistkeyboardsearchmanager.cpp kitemviews/private/kitemlistroleeditor.cpp kitemviews/private/kitemlistrubberband.cpp kitemviews/private/kitemlistselectiontoggle.cpp kitemviews/private/kitemlistsizehintresolver.cpp kitemviews/private/kitemlistsmoothscroller.cpp kitemviews/private/kitemlistviewanimation.cpp kitemviews/private/kitemlistviewlayouter.cpp kitemviews/private/kpixmapmodifier.cpp settings/additionalinfodialog.cpp settings/applyviewpropsjob.cpp settings/viewmodes/viewmodesettings.cpp settings/viewpropertiesdialog.cpp settings/viewpropsprogressinfo.cpp views/dolphinfileitemlistwidget.cpp views/dolphinitemlistview.cpp views/dolphinnewfilemenuobserver.cpp views/dolphinremoteencoding.cpp views/dolphinview.cpp views/dolphinviewactionhandler.cpp views/draganddrophelper.cpp views/renamedialog.cpp views/tooltips/dolphinfilemetadatawidget.cpp views/tooltips/tooltipmanager.cpp views/versioncontrol/updateitemstatesthread.cpp views/versioncontrol/versioncontrolobserver.cpp views/viewmodecontroller.cpp views/viewproperties.cpp views/zoomlevelinfo.cpp dolphinremoveaction.cpp middleclickactioneventfilter.cpp dolphinnewfilemenu.cpp dolphindebug.cpp ) if(HAVE_BALOO) set(dolphinprivate_LIB_SRCS ${dolphinprivate_LIB_SRCS} kitemviews/private/kbaloorolesprovider.cpp ) endif() kconfig_add_kcfg_files(dolphinprivate_LIB_SRCS GENERATE_MOC settings/dolphin_compactmodesettings.kcfgc settings/dolphin_directoryviewpropertysettings.kcfgc settings/dolphin_detailsmodesettings.kcfgc settings/dolphin_iconsmodesettings.kcfgc settings/dolphin_generalsettings.kcfgc settings/dolphin_versioncontrolsettings.kcfgc ) add_library(dolphinprivate ${dolphinprivate_LIB_SRCS}) generate_export_header(dolphinprivate BASE_NAME dolphin) target_link_libraries( dolphinprivate PUBLIC dolphinvcs Qt5::Concurrent Qt5::Gui KF5::Crash KF5::I18n KF5::IconThemes KF5::KIOCore KF5::KIOWidgets KF5::KIOFileWidgets KF5::Completion KF5::TextWidgets KF5::ConfigCore KF5::NewStuff KF5::Parts ) if(HAVE_BALOO) target_link_libraries( dolphinprivate PUBLIC KF5::FileMetaData KF5::Baloo KF5::BalooWidgets ) else() target_link_libraries( dolphinprivate PUBLIC KF5::KDELibs4Support # for KFileMetaDataWidget ) endif() set_target_properties(dolphinprivate PROPERTIES VERSION ${DOLPHINPRIVATE_VERSION_STRING} SOVERSION ${DOLPHINPRIVATE_SOVERSION} ) install(TARGETS dolphinprivate ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_SKIP) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dolphin_export.h DESTINATION ${KDE_INSTALL_INCLUDEDIR} COMPONENT Devel) ########################################## set(dolphinpart_SRCS dolphinpart.cpp dolphinpart_ext.cpp dolphindebug.cpp ) qt5_add_resources(dolphinpart_SRCS dolphinpart.qrc) add_library(dolphinpart MODULE ${dolphinpart_SRCS}) target_link_libraries(dolphinpart dolphinprivate ) install(TARGETS dolphinpart DESTINATION ${KDE_INSTALL_PLUGINDIR}) install(FILES dolphinpart.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) ########################################## set(dolphinstatic_SRCS dolphindockwidget.cpp dolphinmainwindow.cpp dolphinviewcontainer.cpp dolphincontextmenu.cpp dolphintabbar.cpp dolphinrecenttabsmenu.cpp dolphintabpage.cpp dolphintabwidget.cpp + trash/dolphintrash.cpp filterbar/filterbar.cpp main.cpp panels/information/filemetadataconfigurationdialog.cpp panels/information/informationpanel.cpp panels/information/informationpanelcontent.cpp panels/information/pixmapviewer.cpp panels/information/phononwidget.cpp panels/places/placespanel.cpp panels/places/placesitem.cpp panels/places/placesitemeditdialog.cpp panels/places/placesitemlistgroupheader.cpp panels/places/placesitemlistwidget.cpp panels/places/placesitemmodel.cpp panels/places/placesitemsignalhandler.cpp panels/places/placesview.cpp panels/panel.cpp panels/folders/foldersitemlistwidget.cpp panels/folders/treeviewcontextmenu.cpp panels/folders/folderspanel.cpp panels/terminal/terminalpanel.cpp search/dolphinfacetswidget.cpp search/dolphinsearchbox.cpp settings/general/behaviorsettingspage.cpp settings/general/configurepreviewplugindialog.cpp settings/general/confirmationssettingspage.cpp settings/general/generalsettingspage.cpp settings/general/previewssettingspage.cpp settings/general/statusbarsettingspage.cpp settings/dolphinsettingsdialog.cpp settings/navigation/navigationsettingspage.cpp settings/services/servicessettingspage.cpp settings/settingspagebase.cpp settings/serviceitemdelegate.cpp settings/servicemodel.cpp settings/startup/startupsettingspage.cpp settings/trash/trashsettingspage.cpp settings/viewmodes/dolphinfontrequester.cpp settings/viewmodes/viewsettingspage.cpp settings/viewmodes/viewmodesettings.cpp settings/viewmodes/viewsettingstab.cpp statusbar/dolphinstatusbar.cpp statusbar/mountpointobserver.cpp statusbar/mountpointobservercache.cpp statusbar/spaceinfoobserver.cpp statusbar/statusbarspaceinfo.cpp views/zoomlevelinfo.cpp dolphindebug.cpp global.cpp ) kconfig_add_kcfg_files(dolphinstatic_SRCS GENERATE_MOC panels/folders/dolphin_folderspanelsettings.kcfgc panels/information/dolphin_informationpanelsettings.kcfgc panels/places/dolphin_placespanelsettings.kcfgc settings/dolphin_compactmodesettings.kcfgc settings/dolphin_detailsmodesettings.kcfgc settings/dolphin_generalsettings.kcfgc settings/dolphin_iconsmodesettings.kcfgc search/dolphin_searchsettings.kcfgc settings/dolphin_versioncontrolsettings.kcfgc ) qt5_add_resources(dolphinstatic_SRCS dolphin.qrc) add_library(dolphinstatic STATIC ${dolphinstatic_SRCS}) target_include_directories(dolphinstatic PRIVATE ${PHONON_INCLUDES}) target_link_libraries(dolphinstatic dolphinprivate KF5::KCMUtils KF5::DBusAddons KF5::Notifications Phonon::phonon4qt5 ) if (KF5Activities_FOUND) target_link_libraries( dolphinstatic KF5::Activities ) endif() set(dolphin_SRCS dbusinterface.cpp main.cpp ) # Sets the icon on Windows and OSX file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/icons/*system-file-manager.png") ecm_add_app_icon(dolphin_SRCS ICONS ${ICONS_SRCS}) kf5_add_kdeinit_executable(dolphin ${dolphin_SRCS}) target_link_libraries(kdeinit_dolphin dolphinstatic dolphinprivate ) include(DbusInterfaceMacros) generate_and_install_dbus_interface( kdeinit_dolphin dbusinterface.h org.freedesktop.FileManager1.xml OPTIONS -a ) install(TARGETS kdeinit_dolphin ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(TARGETS dolphin ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) ########################################## set(kcm_dolphinviewmodes_PART_SRCS settings/kcm/kcmdolphinviewmodes.cpp settings/viewmodes/dolphinfontrequester.cpp settings/viewmodes/viewmodesettings.cpp settings/viewmodes/viewsettingstab.cpp views/zoomlevelinfo.cpp) set(kcm_dolphinnavigation_PART_SRCS settings/kcm/kcmdolphinnavigation.cpp settings/navigation/navigationsettingspage.cpp settings/settingspagebase.cpp) set(kcm_dolphinservices_PART_SRCS settings/kcm/kcmdolphinservices.cpp settings/services/servicessettingspage.cpp settings/settingspagebase.cpp settings/serviceitemdelegate.cpp settings/servicemodel.cpp) set(kcm_dolphingeneral_PART_SRCS settings/kcm/kcmdolphingeneral.cpp settings/general/behaviorsettingspage.cpp settings/general/previewssettingspage.cpp settings/general/configurepreviewplugindialog.cpp settings/general/confirmationssettingspage.cpp settings/settingspagebase.cpp settings/serviceitemdelegate.cpp settings/servicemodel.cpp) kconfig_add_kcfg_files(kcm_dolphinviewmodes_PART_SRCS settings/dolphin_compactmodesettings.kcfgc settings/dolphin_directoryviewpropertysettings.kcfgc settings/dolphin_detailsmodesettings.kcfgc settings/dolphin_iconsmodesettings.kcfgc settings/dolphin_generalsettings.kcfgc settings/dolphin_versioncontrolsettings.kcfgc ) kconfig_add_kcfg_files(kcm_dolphinnavigation_PART_SRCS settings/dolphin_generalsettings.kcfgc) kconfig_add_kcfg_files(kcm_dolphinservices_PART_SRCS settings/dolphin_generalsettings.kcfgc settings/dolphin_versioncontrolsettings.kcfgc) kconfig_add_kcfg_files(kcm_dolphingeneral_PART_SRCS settings/dolphin_generalsettings.kcfgc) add_library(kcm_dolphinviewmodes MODULE ${kcm_dolphinviewmodes_PART_SRCS}) add_library(kcm_dolphinnavigation MODULE ${kcm_dolphinnavigation_PART_SRCS}) add_library(kcm_dolphinservices MODULE ${kcm_dolphinservices_PART_SRCS}) add_library(kcm_dolphingeneral MODULE ${kcm_dolphingeneral_PART_SRCS}) target_link_libraries(kcm_dolphinviewmodes dolphinprivate) target_link_libraries(kcm_dolphinnavigation dolphinprivate) target_link_libraries(kcm_dolphinservices dolphinprivate) target_link_libraries(kcm_dolphingeneral dolphinprivate) install(TARGETS kcm_dolphinviewmodes DESTINATION ${KDE_INSTALL_PLUGINDIR} ) install(TARGETS kcm_dolphinnavigation DESTINATION ${KDE_INSTALL_PLUGINDIR} ) install(TARGETS kcm_dolphinservices DESTINATION ${KDE_INSTALL_PLUGINDIR} ) install(TARGETS kcm_dolphingeneral DESTINATION ${KDE_INSTALL_PLUGINDIR} ) ########### install files ############### install( PROGRAMS org.kde.dolphin.desktop DESTINATION ${KDE_INSTALL_APPDIR} ) install( FILES settings/dolphin_directoryviewpropertysettings.kcfg settings/dolphin_generalsettings.kcfg settings/dolphin_compactmodesettings.kcfg settings/dolphin_iconsmodesettings.kcfg settings/dolphin_detailsmodesettings.kcfg settings/dolphin_versioncontrolsettings.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR} ) install( FILES org.kde.dolphin.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR} ) install( FILES settings/kcm/kcmdolphinviewmodes.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) install( FILES settings/kcm/kcmdolphinnavigation.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) install( FILES settings/kcm/kcmdolphinservices.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) install( FILES settings/kcm/kcmdolphingeneral.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) install( FILES settings/services/servicemenu.knsrc DESTINATION ${KDE_INSTALL_CONFDIR} ) install( PROGRAMS settings/services/servicemenuinstallation DESTINATION ${KDE_INSTALL_BINDIR} ) install( PROGRAMS settings/services/servicemenudeinstallation DESTINATION ${KDE_INSTALL_BINDIR} ) if(BUILD_TESTING) find_package(Qt5Test ${QT_MIN_VERSION} CONFIG REQUIRED) add_subdirectory(tests) endif() diff --git a/src/dolphincontextmenu.cpp b/src/dolphincontextmenu.cpp index c35f442cb..c4a66c19a 100644 --- a/src/dolphincontextmenu.cpp +++ b/src/dolphincontextmenu.cpp @@ -1,499 +1,493 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz (peter.penz@gmx.at) and * * Cvetoslav Ludmiloff * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * 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. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "dolphincontextmenu.h" #include "dolphin_generalsettings.h" #include "dolphinmainwindow.h" #include "dolphinnewfilemenu.h" #include "dolphinremoveaction.h" #include "dolphinviewcontainer.h" #include "panels/places/placesitem.h" #include "panels/places/placesitemmodel.h" +#include "trash/dolphintrash.h" #include "views/dolphinview.h" #include "views/viewmodecontroller.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include DolphinContextMenu::DolphinContextMenu(DolphinMainWindow* parent, const QPoint& pos, const KFileItem& fileInfo, const QUrl& baseUrl) : QMenu(parent), m_pos(pos), m_mainWindow(parent), m_fileInfo(fileInfo), m_baseUrl(baseUrl), m_baseFileItem(nullptr), m_selectedItems(), m_selectedItemsProperties(nullptr), m_context(NoContext), m_copyToMenu(parent), m_customActions(), m_command(None), m_removeAction(nullptr) { // The context menu either accesses the URLs of the selected items // or the items itself. To increase the performance both lists are cached. const DolphinView* view = m_mainWindow->activeViewContainer()->view(); m_selectedItems = view->selectedItems(); } DolphinContextMenu::~DolphinContextMenu() { delete m_selectedItemsProperties; m_selectedItemsProperties = nullptr; } void DolphinContextMenu::setCustomActions(const QList& actions) { m_customActions = actions; } DolphinContextMenu::Command DolphinContextMenu::open() { // get the context information if (m_baseUrl.scheme() == QLatin1String("trash")) { m_context |= TrashContext; } if (!m_fileInfo.isNull() && !m_selectedItems.isEmpty()) { m_context |= ItemContext; // TODO: handle other use cases like devices + desktop files } // open the corresponding popup for the context if (m_context & TrashContext) { if (m_context & ItemContext) { openTrashItemContextMenu(); } else { openTrashContextMenu(); } } else if (m_context & ItemContext) { openItemContextMenu(); } else { Q_ASSERT(m_context == NoContext); openViewportContextMenu(); } return m_command; } void DolphinContextMenu::keyPressEvent(QKeyEvent *ev) { if (m_removeAction && ev->key() == Qt::Key_Shift) { m_removeAction->update(DolphinRemoveAction::ShiftState::Pressed); } QMenu::keyPressEvent(ev); } void DolphinContextMenu::keyReleaseEvent(QKeyEvent *ev) { if (m_removeAction && ev->key() == Qt::Key_Shift) { m_removeAction->update(DolphinRemoveAction::ShiftState::Released); } QMenu::keyReleaseEvent(ev); } void DolphinContextMenu::openTrashContextMenu() { Q_ASSERT(m_context & TrashContext); QAction* emptyTrashAction = new QAction(QIcon::fromTheme(QStringLiteral("trash-empty")), i18nc("@action:inmenu", "Empty Trash"), this); - KConfig trashConfig(QStringLiteral("trashrc"), KConfig::SimpleConfig); - emptyTrashAction->setEnabled(!trashConfig.group("Status").readEntry("Empty", true)); + emptyTrashAction->setEnabled(!Trash::isEmpty()); addAction(emptyTrashAction); addCustomActions(); QAction* propertiesAction = m_mainWindow->actionCollection()->action(QStringLiteral("properties")); addAction(propertiesAction); addShowMenuBarAction(); if (exec(m_pos) == emptyTrashAction) { - KIO::JobUiDelegate uiDelegate; - uiDelegate.setWindow(m_mainWindow); - if (uiDelegate.askDeleteConfirmation(QList(), KIO::JobUiDelegate::EmptyTrash, KIO::JobUiDelegate::DefaultConfirmation)) { - KIO::Job* job = KIO::emptyTrash(); - KJobWidgets::setWindow(job, m_mainWindow); - job->uiDelegate()->setAutoErrorHandlingEnabled(true); - } + Trash::empty(m_mainWindow); } } void DolphinContextMenu::openTrashItemContextMenu() { Q_ASSERT(m_context & TrashContext); Q_ASSERT(m_context & ItemContext); QAction* restoreAction = new QAction(i18nc("@action:inmenu", "Restore"), m_mainWindow); addAction(restoreAction); QAction* deleteAction = m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::DeleteFile)); addAction(deleteAction); QAction* propertiesAction = m_mainWindow->actionCollection()->action(QStringLiteral("properties")); addAction(propertiesAction); if (exec(m_pos) == restoreAction) { QList selectedUrls; selectedUrls.reserve(m_selectedItems.count()); foreach (const KFileItem &item, m_selectedItems) { selectedUrls.append(item.url()); } KIO::RestoreJob *job = KIO::restoreFromTrash(selectedUrls); KJobWidgets::setWindow(job, m_mainWindow); job->uiDelegate()->setAutoErrorHandlingEnabled(true); } } void DolphinContextMenu::openItemContextMenu() { Q_ASSERT(!m_fileInfo.isNull()); QAction* openParentAction = nullptr; QAction* openParentInNewWindowAction = nullptr; QAction* openParentInNewTabAction = nullptr; QAction* addToPlacesAction = nullptr; const KFileItemListProperties& selectedItemsProps = selectedItemsProperties(); if (m_selectedItems.count() == 1) { if (m_fileInfo.isDir()) { // setup 'Create New' menu DolphinNewFileMenu* newFileMenu = new DolphinNewFileMenu(m_mainWindow->actionCollection(), m_mainWindow); const DolphinView* view = m_mainWindow->activeViewContainer()->view(); newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown()); newFileMenu->checkUpToDate(); newFileMenu->setPopupFiles(m_fileInfo.url()); newFileMenu->setEnabled(selectedItemsProps.supportsWriting()); connect(newFileMenu, &DolphinNewFileMenu::fileCreated, newFileMenu, &DolphinNewFileMenu::deleteLater); connect(newFileMenu, &DolphinNewFileMenu::directoryCreated, newFileMenu, &DolphinNewFileMenu::deleteLater); QMenu* menu = newFileMenu->menu(); menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New")); menu->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); addMenu(menu); addSeparator(); // insert 'Open in new window' and 'Open in new tab' entries addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_window"))); addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_tab"))); // insert 'Add to Places' entry if (!placeExists(m_fileInfo.url())) { addToPlacesAction = addAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), i18nc("@action:inmenu Add selected folder to places", "Add to Places")); } addSeparator(); } else if (m_baseUrl.scheme().contains(QStringLiteral("search")) || m_baseUrl.scheme().contains(QStringLiteral("timeline"))) { openParentAction = new QAction(QIcon::fromTheme(QStringLiteral("document-open-folder")), i18nc("@action:inmenu", "Open Path"), this); addAction(openParentAction); openParentInNewWindowAction = new QAction(QIcon::fromTheme(QStringLiteral("window-new")), i18nc("@action:inmenu", "Open Path in New Window"), this); addAction(openParentInNewWindowAction); openParentInNewTabAction = new QAction(QIcon::fromTheme(QStringLiteral("tab-new")), i18nc("@action:inmenu", "Open Path in New Tab"), this); addAction(openParentInNewTabAction); addSeparator(); } else if (!DolphinView::openItemAsFolderUrl(m_fileInfo).isEmpty()) { // insert 'Open in new window' and 'Open in new tab' entries addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_window"))); addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_tab"))); addSeparator(); } } else { bool selectionHasOnlyDirs = true; foreach (const KFileItem& item, m_selectedItems) { const QUrl& url = DolphinView::openItemAsFolderUrl(item); if (url.isEmpty()) { selectionHasOnlyDirs = false; break; } } if (selectionHasOnlyDirs) { // insert 'Open in new tab' entry addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_tabs"))); addSeparator(); } } insertDefaultItemActions(selectedItemsProps); addSeparator(); KFileItemActions fileItemActions; fileItemActions.setItemListProperties(selectedItemsProps); addServiceActions(fileItemActions); fileItemActions.addPluginActionsTo(this); addVersionControlPluginActions(); // insert 'Copy To' and 'Move To' sub menus if (GeneralSettings::showCopyMoveMenu()) { m_copyToMenu.setUrls(m_selectedItems.urlList()); m_copyToMenu.setReadOnly(!selectedItemsProps.supportsWriting()); m_copyToMenu.setAutoErrorHandlingEnabled(true); m_copyToMenu.addActionsTo(this); } // insert 'Properties...' entry QAction* propertiesAction = m_mainWindow->actionCollection()->action(QStringLiteral("properties")); addAction(propertiesAction); QAction* activatedAction = exec(m_pos); if (activatedAction) { if (activatedAction == addToPlacesAction) { const QUrl selectedUrl(m_fileInfo.url()); if (selectedUrl.isValid()) { PlacesItemModel model; const QString text = selectedUrl.fileName(); model.createPlacesItem(text, selectedUrl, KIO::iconNameForUrl(selectedUrl)); } } else if (activatedAction == openParentAction) { m_command = OpenParentFolder; } else if (activatedAction == openParentInNewWindowAction) { m_command = OpenParentFolderInNewWindow; } else if (activatedAction == openParentInNewTabAction) { m_command = OpenParentFolderInNewTab; } } } void DolphinContextMenu::openViewportContextMenu() { // setup 'Create New' menu KNewFileMenu* newFileMenu = m_mainWindow->newFileMenu(); const DolphinView* view = m_mainWindow->activeViewContainer()->view(); newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown()); newFileMenu->checkUpToDate(); newFileMenu->setPopupFiles(m_baseUrl); addMenu(newFileMenu->menu()); addSeparator(); // Insert 'New Window' and 'New Tab' entries. Don't use "open_in_new_window" and // "open_in_new_tab" here, as the current selection should get ignored. addAction(m_mainWindow->actionCollection()->action(QStringLiteral("new_window"))); addAction(m_mainWindow->actionCollection()->action(QStringLiteral("new_tab"))); // Insert 'Add to Places' entry if exactly one item is selected QAction* addToPlacesAction = nullptr; if (!placeExists(m_mainWindow->activeViewContainer()->url())) { addToPlacesAction = addAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), i18nc("@action:inmenu Add current folder to places", "Add to Places")); } addSeparator(); QAction* pasteAction = createPasteAction(); addAction(pasteAction); addSeparator(); // Insert service actions const KFileItemListProperties baseUrlProperties(KFileItemList() << baseFileItem()); KFileItemActions fileItemActions; fileItemActions.setItemListProperties(baseUrlProperties); addServiceActions(fileItemActions); fileItemActions.addPluginActionsTo(this); addVersionControlPluginActions(); addCustomActions(); QAction* propertiesAction = m_mainWindow->actionCollection()->action(QStringLiteral("properties")); addAction(propertiesAction); addShowMenuBarAction(); QAction* action = exec(m_pos); if (addToPlacesAction && (action == addToPlacesAction)) { const DolphinViewContainer* container = m_mainWindow->activeViewContainer(); const QUrl url = container->url(); if (url.isValid()) { PlacesItemModel model; QString icon; if (container->isSearchModeEnabled()) { icon = QStringLiteral("folder-saved-search-symbolic"); } else { icon = KIO::iconNameForUrl(url); } model.createPlacesItem(container->placesText(), url, icon); } } } void DolphinContextMenu::insertDefaultItemActions(const KFileItemListProperties& properties) { const KActionCollection* collection = m_mainWindow->actionCollection(); // Insert 'Cut', 'Copy' and 'Paste' addAction(collection->action(KStandardAction::name(KStandardAction::Cut))); addAction(collection->action(KStandardAction::name(KStandardAction::Copy))); addAction(createPasteAction()); addSeparator(); // Insert 'Rename' addAction(collection->action(KStandardAction::name(KStandardAction::RenameFile))); // Insert 'Move to Trash' and/or 'Delete' if (properties.supportsDeleting()) { const bool showDeleteAction = (KSharedConfig::openConfig()->group("KDE").readEntry("ShowDeleteCommand", false) || !properties.isLocal()); const bool showMoveToTrashAction = (properties.isLocal() && properties.supportsMoving()); if (showDeleteAction && showMoveToTrashAction) { delete m_removeAction; m_removeAction = nullptr; addAction(m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::MoveToTrash))); addAction(m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::DeleteFile))); } else if (showDeleteAction && !showMoveToTrashAction) { addAction(m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::DeleteFile))); } else { if (!m_removeAction) { m_removeAction = new DolphinRemoveAction(this, m_mainWindow->actionCollection()); } addAction(m_removeAction); m_removeAction->update(); } } } void DolphinContextMenu::addShowMenuBarAction() { const KActionCollection* ac = m_mainWindow->actionCollection(); QAction* showMenuBar = ac->action(KStandardAction::name(KStandardAction::ShowMenubar)); if (!m_mainWindow->menuBar()->isVisible() && !m_mainWindow->toolBar()->isVisible()) { addSeparator(); addAction(showMenuBar); } } bool DolphinContextMenu::placeExists(const QUrl& url) const { Q_UNUSED(url) // Creating up a PlacesItemModel to find out if 'url' is one of the Places // can be expensive because the model asks Solid for the devices which are // available, which can take some time. // TODO: Consider restoring this check if the handling of Places and devices // will be decoupled in the future. return false; } QAction* DolphinContextMenu::createPasteAction() { QAction* action = nullptr; const bool isDir = !m_fileInfo.isNull() && m_fileInfo.isDir(); if (isDir && (m_selectedItems.count() == 1)) { const QMimeData *mimeData = QApplication::clipboard()->mimeData(); bool canPaste; const QString text = KIO::pasteActionText(mimeData, &canPaste, m_fileInfo); action = new QAction(QIcon::fromTheme(QStringLiteral("edit-paste")), text, this); action->setEnabled(canPaste); connect(action, &QAction::triggered, m_mainWindow, &DolphinMainWindow::pasteIntoFolder); } else { action = m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Paste)); } return action; } KFileItemListProperties& DolphinContextMenu::selectedItemsProperties() const { if (!m_selectedItemsProperties) { m_selectedItemsProperties = new KFileItemListProperties(m_selectedItems); } return *m_selectedItemsProperties; } KFileItem DolphinContextMenu::baseFileItem() { if (!m_baseFileItem) { m_baseFileItem = new KFileItem(m_baseUrl); } return *m_baseFileItem; } void DolphinContextMenu::addServiceActions(KFileItemActions& fileItemActions) { fileItemActions.setParentWidget(m_mainWindow); // insert 'Open With...' action or sub menu fileItemActions.addOpenWithActionsTo(this, QStringLiteral("DesktopEntryName != 'dolphin'")); // insert 'Actions' sub menu fileItemActions.addServiceActionsTo(this); } void DolphinContextMenu::addVersionControlPluginActions() { const DolphinView* view = m_mainWindow->activeViewContainer()->view(); const QList versionControlActions = view->versionControlActions(m_selectedItems); if (!versionControlActions.isEmpty()) { addActions(versionControlActions); addSeparator(); } } void DolphinContextMenu::addCustomActions() { addActions(m_customActions); } diff --git a/src/dolphinviewcontainer.cpp b/src/dolphinviewcontainer.cpp index 1620f3c36..6c5edc13f 100644 --- a/src/dolphinviewcontainer.cpp +++ b/src/dolphinviewcontainer.cpp @@ -1,699 +1,700 @@ /*************************************************************************** * Copyright (C) 2007 by Peter Penz * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * 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. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "dolphinviewcontainer.h" #include "dolphin_generalsettings.h" #include "dolphindebug.h" #include "filterbar/filterbar.h" #include "global.h" #include "search/dolphinsearchbox.h" #include "statusbar/dolphinstatusbar.h" +#include "trash/dolphintrash.h" #include "views/viewmodecontroller.h" #include "views/viewproperties.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) : QWidget(parent), m_topLayout(nullptr), m_urlNavigator(nullptr), m_searchBox(nullptr), m_messageWidget(nullptr), m_view(nullptr), m_filterBar(nullptr), m_statusBar(nullptr), m_statusBarTimer(nullptr), m_statusBarTimestamp(), m_autoGrabFocus(true) #ifdef KActivities_FOUND , m_activityResourceInstance(0) #endif { hide(); m_topLayout = new QVBoxLayout(this); m_topLayout->setSpacing(0); m_topLayout->setMargin(0); m_urlNavigator = new KUrlNavigator(new KFilePlacesModel(this), url, this); connect(m_urlNavigator, &KUrlNavigator::activated, this, &DolphinViewContainer::activate); connect(m_urlNavigator->editor(), &KUrlComboBox::completionModeChanged, this, &DolphinViewContainer::saveUrlCompletionMode); const GeneralSettings* settings = GeneralSettings::self(); m_urlNavigator->setUrlEditable(settings->editableUrl()); m_urlNavigator->setShowFullPath(settings->showFullPath()); m_urlNavigator->setHomeUrl(Dolphin::homeUrl()); KUrlComboBox* editor = m_urlNavigator->editor(); editor->setCompletionMode(KCompletion::CompletionMode(settings->urlCompletionMode())); m_searchBox = new DolphinSearchBox(this); m_searchBox->hide(); connect(m_searchBox, &DolphinSearchBox::activated, this, &DolphinViewContainer::activate); connect(m_searchBox, &DolphinSearchBox::closeRequest, this, &DolphinViewContainer::closeSearchBox); connect(m_searchBox, &DolphinSearchBox::searchRequest, this, &DolphinViewContainer::startSearching); connect(m_searchBox, &DolphinSearchBox::returnPressed, this, &DolphinViewContainer::requestFocus); m_messageWidget = new KMessageWidget(this); m_messageWidget->setCloseButtonVisible(true); m_messageWidget->hide(); m_view = new DolphinView(url, this); connect(m_view, &DolphinView::urlChanged, m_urlNavigator, &KUrlNavigator::setLocationUrl); connect(m_view, &DolphinView::urlChanged, m_messageWidget, &KMessageWidget::hide); connect(m_view, &DolphinView::writeStateChanged, this, &DolphinViewContainer::writeStateChanged); connect(m_view, &DolphinView::requestItemInfo, this, &DolphinViewContainer::showItemInfo); connect(m_view, &DolphinView::itemActivated, this, &DolphinViewContainer::slotItemActivated); connect(m_view, &DolphinView::itemsActivated, this, &DolphinViewContainer::slotItemsActivated); connect(m_view, &DolphinView::redirection, this, &DolphinViewContainer::redirect); connect(m_view, &DolphinView::directoryLoadingStarted, this, &DolphinViewContainer::slotDirectoryLoadingStarted); connect(m_view, &DolphinView::directoryLoadingCompleted, this, &DolphinViewContainer::slotDirectoryLoadingCompleted); connect(m_view, &DolphinView::directoryLoadingCanceled, this, &DolphinViewContainer::slotDirectoryLoadingCanceled); connect(m_view, &DolphinView::itemCountChanged, this, &DolphinViewContainer::delayedStatusBarUpdate); connect(m_view, &DolphinView::directoryLoadingProgress, this, &DolphinViewContainer::updateDirectoryLoadingProgress); connect(m_view, &DolphinView::directorySortingProgress, this, &DolphinViewContainer::updateDirectorySortingProgress); connect(m_view, &DolphinView::selectionChanged, this, &DolphinViewContainer::delayedStatusBarUpdate); connect(m_view, &DolphinView::errorMessage, this, &DolphinViewContainer::showErrorMessage); connect(m_view, &DolphinView::urlIsFileError, this, &DolphinViewContainer::slotUrlIsFileError); connect(m_view, &DolphinView::activated, this, &DolphinViewContainer::activate); connect(m_urlNavigator, &KUrlNavigator::urlAboutToBeChanged, this, &DolphinViewContainer::slotUrlNavigatorLocationAboutToBeChanged); connect(m_urlNavigator, &KUrlNavigator::urlChanged, this, &DolphinViewContainer::slotUrlNavigatorLocationChanged); connect(m_urlNavigator, &KUrlNavigator::urlSelectionRequested, this, &DolphinViewContainer::slotUrlSelectionRequested); connect(m_urlNavigator, &KUrlNavigator::returnPressed, this, &DolphinViewContainer::slotReturnPressed); connect(m_urlNavigator, &KUrlNavigator::urlsDropped, this, [=](const QUrl &destination, QDropEvent *event) { #if KIO_VERSION >= QT_VERSION_CHECK(5, 37, 0) m_view->dropUrls(destination, event, m_urlNavigator->dropWidget()); #else // TODO: remove as soon as we can hard-depend of KF5 >= 5.37 m_view->dropUrls(destination, event, m_view); #endif }); // Initialize status bar m_statusBar = new DolphinStatusBar(this); m_statusBar->setUrl(m_view->url()); m_statusBar->setZoomLevel(m_view->zoomLevel()); connect(m_view, &DolphinView::urlChanged, m_statusBar, &DolphinStatusBar::setUrl); connect(m_view, &DolphinView::zoomLevelChanged, m_statusBar, &DolphinStatusBar::setZoomLevel); connect(m_view, &DolphinView::infoMessage, m_statusBar, &DolphinStatusBar::setText); connect(m_view, &DolphinView::operationCompletedMessage, m_statusBar, &DolphinStatusBar::setText); connect(m_statusBar, &DolphinStatusBar::stopPressed, this, &DolphinViewContainer::stopDirectoryLoading); connect(m_statusBar, &DolphinStatusBar::zoomLevelChanged, this, &DolphinViewContainer::slotStatusBarZoomLevelChanged); m_statusBarTimer = new QTimer(this); m_statusBarTimer->setSingleShot(true); m_statusBarTimer->setInterval(300); connect(m_statusBarTimer, &QTimer::timeout, this, &DolphinViewContainer::updateStatusBar); KIO::FileUndoManager* undoManager = KIO::FileUndoManager::self(); connect(undoManager, &KIO::FileUndoManager::jobRecordingFinished, this, &DolphinViewContainer::delayedStatusBarUpdate); // Initialize filter bar m_filterBar = new FilterBar(this); m_filterBar->setVisible(settings->filterBar()); connect(m_filterBar, &FilterBar::filterChanged, this, &DolphinViewContainer::setNameFilter); connect(m_filterBar, &FilterBar::closeRequest, this, &DolphinViewContainer::closeFilterBar); connect(m_filterBar, &FilterBar::focusViewRequest, this, &DolphinViewContainer::requestFocus); connect(m_view, &DolphinView::urlChanged, m_filterBar, &FilterBar::slotUrlChanged); m_topLayout->addWidget(m_urlNavigator); m_topLayout->addWidget(m_searchBox); m_topLayout->addWidget(m_messageWidget); m_topLayout->addWidget(m_view); m_topLayout->addWidget(m_filterBar); m_topLayout->addWidget(m_statusBar); setSearchModeEnabled(isSearchUrl(url)); // Initialize kactivities resource instance #ifdef KActivities_FOUND m_activityResourceInstance = new KActivities::ResourceInstance( window()->winId(), url); m_activityResourceInstance->setParent(this); #endif } DolphinViewContainer::~DolphinViewContainer() { } QUrl DolphinViewContainer::url() const { return m_view->url(); } void DolphinViewContainer::setActive(bool active) { m_searchBox->setActive(active); m_urlNavigator->setActive(active); m_view->setActive(active); #ifdef KActivities_FOUND if (active) { m_activityResourceInstance->notifyFocusedIn(); } else { m_activityResourceInstance->notifyFocusedOut(); } #endif } bool DolphinViewContainer::isActive() const { Q_ASSERT(m_view->isActive() == m_urlNavigator->isActive()); return m_view->isActive(); } void DolphinViewContainer::setAutoGrabFocus(bool grab) { m_autoGrabFocus = grab; } bool DolphinViewContainer::autoGrabFocus() const { return m_autoGrabFocus; } QString DolphinViewContainer::currentSearchText() const { return m_searchBox->text(); } const DolphinStatusBar* DolphinViewContainer::statusBar() const { return m_statusBar; } DolphinStatusBar* DolphinViewContainer::statusBar() { return m_statusBar; } const KUrlNavigator* DolphinViewContainer::urlNavigator() const { return m_urlNavigator; } KUrlNavigator* DolphinViewContainer::urlNavigator() { return m_urlNavigator; } const DolphinView* DolphinViewContainer::view() const { return m_view; } DolphinView* DolphinViewContainer::view() { return m_view; } void DolphinViewContainer::showMessage(const QString& msg, MessageType type) { if (msg.isEmpty()) { return; } m_messageWidget->setText(msg); switch (type) { case Information: m_messageWidget->setMessageType(KMessageWidget::Information); break; case Warning: m_messageWidget->setMessageType(KMessageWidget::Warning); break; case Error: m_messageWidget->setMessageType(KMessageWidget::Error); break; default: Q_ASSERT(false); break; } m_messageWidget->setWordWrap(false); const int unwrappedWidth = m_messageWidget->sizeHint().width(); m_messageWidget->setWordWrap(unwrappedWidth > size().width()); if (m_messageWidget->isVisible()) { m_messageWidget->hide(); } m_messageWidget->animatedShow(); } void DolphinViewContainer::readSettings() { if (GeneralSettings::modifiedStartupSettings()) { // The startup settings should only get applied if they have been // modified by the user. Otherwise keep the (possibly) different current // settings of the URL navigator and the filterbar. m_urlNavigator->setUrlEditable(GeneralSettings::editableUrl()); m_urlNavigator->setShowFullPath(GeneralSettings::showFullPath()); m_urlNavigator->setHomeUrl(Dolphin::homeUrl()); setFilterBarVisible(GeneralSettings::filterBar()); } m_view->readSettings(); m_statusBar->readSettings(); } bool DolphinViewContainer::isFilterBarVisible() const { return m_filterBar->isVisible(); } void DolphinViewContainer::setSearchModeEnabled(bool enabled) { if (enabled == isSearchModeEnabled()) { if (enabled && !m_searchBox->hasFocus()) { m_searchBox->setFocus(); m_searchBox->selectAll(); } return; } m_searchBox->setVisible(enabled); m_urlNavigator->setVisible(!enabled); if (enabled) { const QUrl& locationUrl = m_urlNavigator->locationUrl(); m_searchBox->fromSearchUrl(locationUrl); } else { m_view->setViewPropertiesContext(QString()); // Restore the URL for the URL navigator. If Dolphin has been // started with a search-URL, the home URL is used as fallback. QUrl url = m_searchBox->searchPath(); if (url.isEmpty() || !url.isValid() || isSearchUrl(url)) { url = Dolphin::homeUrl(); } m_urlNavigator->setLocationUrl(url); } } bool DolphinViewContainer::isSearchModeEnabled() const { return m_searchBox->isVisible(); } QString DolphinViewContainer::placesText() const { QString text; if (isSearchModeEnabled()) { text = i18n("Search for %1 in %2", m_searchBox->text(), m_searchBox->searchPath().fileName()); } else { text = url().fileName(); if (text.isEmpty()) { text = url().host(); } if (text.isEmpty()) { text = url().scheme(); } } return text; } void DolphinViewContainer::reload() { view()->reload(); m_messageWidget->hide(); } void DolphinViewContainer::setUrl(const QUrl& newUrl) { if (newUrl != m_urlNavigator->locationUrl()) { m_urlNavigator->setLocationUrl(newUrl); } #ifdef KActivities_FOUND m_activityResourceInstance->setUri(newUrl); #endif } void DolphinViewContainer::setFilterBarVisible(bool visible) { Q_ASSERT(m_filterBar); if (visible) { m_filterBar->show(); m_filterBar->setFocus(); m_filterBar->selectAll(); } else { closeFilterBar(); } } void DolphinViewContainer::delayedStatusBarUpdate() { if (m_statusBarTimer->isActive() && (m_statusBarTimestamp.elapsed() > 2000)) { // No update of the statusbar has been done during the last 2 seconds, // although an update has been requested. Trigger an immediate update. m_statusBarTimer->stop(); updateStatusBar(); } else { // Invoke updateStatusBar() with a small delay. This assures that // when a lot of delayedStatusBarUpdates() are done in a short time, // no bottleneck is given. m_statusBarTimer->start(); } } void DolphinViewContainer::updateStatusBar() { m_statusBarTimestamp.start(); const QString text = m_view->statusBarText(); m_statusBar->setDefaultText(text); m_statusBar->resetToDefaultText(); } void DolphinViewContainer::updateDirectoryLoadingProgress(int percent) { if (m_statusBar->progressText().isEmpty()) { m_statusBar->setProgressText(i18nc("@info:progress", "Loading folder...")); } m_statusBar->setProgress(percent); } void DolphinViewContainer::updateDirectorySortingProgress(int percent) { if (m_statusBar->progressText().isEmpty()) { m_statusBar->setProgressText(i18nc("@info:progress", "Sorting...")); } m_statusBar->setProgress(percent); } void DolphinViewContainer::slotDirectoryLoadingStarted() { if (isSearchUrl(url())) { // Search KIO-slaves usually don't provide any progress information. Give // a hint to the user that a searching is done: updateStatusBar(); m_statusBar->setProgressText(i18nc("@info", "Searching...")); m_statusBar->setProgress(-1); } else { // Trigger an undetermined progress indication. The progress // information in percent will be triggered by the percent() signal // of the directory lister later. updateDirectoryLoadingProgress(-1); } } void DolphinViewContainer::slotDirectoryLoadingCompleted() { if (!m_statusBar->progressText().isEmpty()) { m_statusBar->setProgressText(QString()); m_statusBar->setProgress(100); } if (isSearchUrl(url()) && m_view->itemsCount() == 0) { // The dir lister has been completed on a Baloo-URI and no items have been found. Instead // of showing the default status bar information ("0 items") a more helpful information is given: m_statusBar->setText(i18nc("@info:status", "No items found.")); } else { updateStatusBar(); } } void DolphinViewContainer::slotDirectoryLoadingCanceled() { if (!m_statusBar->progressText().isEmpty()) { m_statusBar->setProgressText(QString()); m_statusBar->setProgress(100); } m_statusBar->setText(QString()); } void DolphinViewContainer::slotUrlIsFileError(const QUrl& url) { const KFileItem item(url); // Find out if the file can be opened in the view (for example, this is the // case if the file is an archive). The mime type must be known for that. item.determineMimeType(); const QUrl& folderUrl = DolphinView::openItemAsFolderUrl(item, true); if (!folderUrl.isEmpty()) { setUrl(folderUrl); } else { slotItemActivated(item); } } void DolphinViewContainer::slotItemActivated(const KFileItem& item) { // It is possible to activate items on inactive views by // drag & drop operations. Assure that activating an item always // results in an active view. m_view->setActive(true); const QUrl& url = DolphinView::openItemAsFolderUrl(item, GeneralSettings::browseThroughArchives()); if (!url.isEmpty()) { setUrl(url); return; } KRun *run = new KRun(item.targetUrl(), this); run->setShowScriptExecutionPrompt(true); } void DolphinViewContainer::slotItemsActivated(const KFileItemList& items) { Q_ASSERT(items.count() >= 2); KFileItemActions fileItemActions(this); fileItemActions.runPreferredApplications(items, QString()); } void DolphinViewContainer::showItemInfo(const KFileItem& item) { if (item.isNull()) { m_statusBar->resetToDefaultText(); } else { m_statusBar->setText(item.getStatusBarInfo()); } } void DolphinViewContainer::closeFilterBar() { m_filterBar->closeFilterBar(); m_view->setFocus(); emit showFilterBarChanged(false); } void DolphinViewContainer::setNameFilter(const QString& nameFilter) { m_view->setNameFilter(nameFilter); delayedStatusBarUpdate(); } void DolphinViewContainer::activate() { setActive(true); } void DolphinViewContainer::slotUrlNavigatorLocationAboutToBeChanged(const QUrl&) { saveViewState(); } void DolphinViewContainer::slotUrlNavigatorLocationChanged(const QUrl& url) { slotReturnPressed(); if (KProtocolManager::supportsListing(url)) { setSearchModeEnabled(isSearchUrl(url)); m_view->setUrl(url); tryRestoreViewState(); if (m_autoGrabFocus && isActive() && !isSearchUrl(url)) { // When an URL has been entered, the view should get the focus. // The focus must be requested asynchronously, as changing the URL might create // a new view widget. QTimer::singleShot(0, this, &DolphinViewContainer::requestFocus); } } else if (KProtocolManager::isSourceProtocol(url)) { QString app = QStringLiteral("konqueror"); if (url.scheme().startsWith(QLatin1String("http"))) { showMessage(i18nc("@info:status", // krazy:exclude=qmethods "Dolphin does not support web pages, the web browser has been launched"), Information); const KConfigGroup config(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), "General"); const QString browser = config.readEntry("BrowserApplication"); if (!browser.isEmpty()) { app = browser; if (app.startsWith('!')) { // a literal command has been configured, remove the '!' prefix app = app.mid(1); } } } else { showMessage(i18nc("@info:status", "Protocol not supported by Dolphin, Konqueror has been launched"), Information); } const QString secureUrl = KShell::quoteArg(url.toDisplayString(QUrl::PreferLocalFile)); const QString command = app + ' ' + secureUrl; KRun::runCommand(command, app, app, this); } else { showMessage(i18nc("@info:status", "Invalid protocol"), Error); } } void DolphinViewContainer::slotUrlSelectionRequested(const QUrl& url) { qCDebug(DolphinDebug) << "slotUrlSelectionRequested: " << url; m_view->markUrlsAsSelected({url}); m_view->markUrlAsCurrent(url); // makes the item scroll into view } void DolphinViewContainer::redirect(const QUrl& oldUrl, const QUrl& newUrl) { Q_UNUSED(oldUrl); const bool block = m_urlNavigator->signalsBlocked(); m_urlNavigator->blockSignals(true); // Assure that the location state is reset for redirection URLs. This // allows to skip redirection URLs when going back or forward in the // URL history. m_urlNavigator->saveLocationState(QByteArray()); m_urlNavigator->setLocationUrl(newUrl); setSearchModeEnabled(isSearchUrl(newUrl)); m_urlNavigator->blockSignals(block); } void DolphinViewContainer::requestFocus() { m_view->setFocus(); } void DolphinViewContainer::saveUrlCompletionMode(KCompletion::CompletionMode completion) { GeneralSettings::setUrlCompletionMode(completion); } void DolphinViewContainer::slotReturnPressed() { if (!GeneralSettings::editableUrl()) { m_urlNavigator->setUrlEditable(false); } } void DolphinViewContainer::startSearching() { const QUrl url = m_searchBox->urlForSearching(); if (url.isValid() && !url.isEmpty()) { m_view->setViewPropertiesContext(QStringLiteral("search")); m_urlNavigator->setLocationUrl(url); } } void DolphinViewContainer::closeSearchBox() { setSearchModeEnabled(false); } void DolphinViewContainer::stopDirectoryLoading() { m_view->stopLoading(); m_statusBar->setProgress(100); } void DolphinViewContainer::slotStatusBarZoomLevelChanged(int zoomLevel) { m_view->setZoomLevel(zoomLevel); } void DolphinViewContainer::showErrorMessage(const QString& msg) { showMessage(msg, Error); } bool DolphinViewContainer::isSearchUrl(const QUrl& url) const { return url.scheme().contains(QStringLiteral("search")); } void DolphinViewContainer::saveViewState() { QByteArray locationState; QDataStream stream(&locationState, QIODevice::WriteOnly); m_view->saveState(stream); m_urlNavigator->saveLocationState(locationState); } void DolphinViewContainer::tryRestoreViewState() { QByteArray locationState = m_urlNavigator->locationState(); if (!locationState.isEmpty()) { QDataStream stream(&locationState, QIODevice::ReadOnly); m_view->restoreState(stream); } } diff --git a/src/panels/places/placesitem.cpp b/src/panels/places/placesitem.cpp index 20c19e152..ff81e8e63 100644 --- a/src/panels/places/placesitem.cpp +++ b/src/panels/places/placesitem.cpp @@ -1,290 +1,276 @@ /*************************************************************************** * Copyright (C) 2012 by Peter Penz * * * * Based on KFilePlacesItem from kdelibs: * * Copyright (C) 2007 Kevin Ottens * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * 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. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "placesitem.h" +#include "trash/dolphintrash.h" #include "dolphindebug.h" #include "placesitemsignalhandler.h" #include #include #include PlacesItem::PlacesItem(const KBookmark& bookmark, PlacesItem* parent) : KStandardItem(parent), m_device(), m_access(), m_volume(), m_disc(), m_mtp(), m_signalHandler(nullptr), m_trashDirLister(nullptr), m_bookmark() { m_signalHandler = new PlacesItemSignalHandler(this); setBookmark(bookmark); } PlacesItem::~PlacesItem() { delete m_signalHandler; delete m_trashDirLister; } void PlacesItem::setUrl(const QUrl &url) { // The default check in KStandardItem::setDataValue() // for equal values does not work with a custom value // like QUrl. Hence do a manual check to prevent that // setting an equal URL results in an itemsChanged() // signal. if (dataValue("url").toUrl() != url) { delete m_trashDirLister; if (url.scheme() == QLatin1String("trash")) { - // The trash icon must always be updated dependent on whether - // the trash is empty or not. We use a KDirLister that automatically - // watches for changes if the number of items has been changed. - // The update of the icon is handled in onTrashDirListerCompleted(). - m_trashDirLister = new KDirLister(); - m_trashDirLister->setAutoErrorHandlingEnabled(false, nullptr); - m_trashDirLister->setDelayedMimeTypes(true); - QObject::connect(m_trashDirLister.data(), static_cast(&KDirLister::completed), - m_signalHandler.data(), &PlacesItemSignalHandler::onTrashDirListerCompleted); - m_trashDirLister->openUrl(url); + QObject::connect(&Trash::instance(), &Trash::emptinessChanged, [this](bool isTrashEmpty){ + setIcon(isTrashEmpty ? QStringLiteral("user-trash") : QStringLiteral("user-trash-full")); + }); } setDataValue("url", url); } } QUrl PlacesItem::url() const { return dataValue("url").toUrl(); } void PlacesItem::setUdi(const QString& udi) { setDataValue("udi", udi); } QString PlacesItem::udi() const { return dataValue("udi").toString(); } void PlacesItem::setHidden(bool hidden) { setDataValue("isHidden", hidden); } bool PlacesItem::isHidden() const { return dataValue("isHidden").toBool(); } bool PlacesItem::isGroupHidden() const { return dataValue("isGroupHidden").toBool(); } void PlacesItem::setGroupHidden(bool hidden) { setDataValue("isGroupHidden", hidden); } void PlacesItem::setSystemItem(bool isSystemItem) { setDataValue("isSystemItem", isSystemItem); } bool PlacesItem::isSystemItem() const { return dataValue("isSystemItem").toBool(); } Solid::Device PlacesItem::device() const { return m_device; } void PlacesItem::setBookmark(const KBookmark& bookmark) { const bool bookmarkDataChanged = !(bookmark == m_bookmark); // bookmark object must be updated to keep in sync with source model m_bookmark = bookmark; if (!bookmarkDataChanged) { return; } delete m_access; delete m_volume; delete m_disc; delete m_mtp; const QString udi = bookmark.metaDataItem(QStringLiteral("UDI")); if (udi.isEmpty()) { setIcon(bookmark.icon()); setText(i18nc("KFile System Bookmarks", bookmark.text().toUtf8().constData())); setUrl(bookmark.url()); setSystemItem(bookmark.metaDataItem(QStringLiteral("isSystemItem")) == QLatin1String("true")); } else { initializeDevice(udi); } setHidden(bookmark.metaDataItem(QStringLiteral("IsHidden")) == QLatin1String("true")); } KBookmark PlacesItem::bookmark() const { return m_bookmark; } bool PlacesItem::storageSetupNeeded() const { return m_access ? !m_access->isAccessible() : false; } bool PlacesItem::isSearchOrTimelineUrl() const { const QString urlScheme = url().scheme(); return (urlScheme.contains("search") || urlScheme.contains("timeline")); } void PlacesItem::onDataValueChanged(const QByteArray& role, const QVariant& current, const QVariant& previous) { Q_UNUSED(current); Q_UNUSED(previous); if (!m_bookmark.isNull()) { updateBookmarkForRole(role); } } void PlacesItem::onDataChanged(const QHash& current, const QHash& previous) { Q_UNUSED(previous); if (!m_bookmark.isNull()) { QHashIterator it(current); while (it.hasNext()) { it.next(); updateBookmarkForRole(it.key()); } } } void PlacesItem::initializeDevice(const QString& udi) { m_device = Solid::Device(udi); if (!m_device.isValid()) { return; } m_access = m_device.as(); m_volume = m_device.as(); m_disc = m_device.as(); m_mtp = m_device.as(); setText(m_device.description()); setIcon(m_device.icon()); setIconOverlays(m_device.emblems()); setUdi(udi); if (m_access) { setUrl(QUrl::fromLocalFile(m_access->filePath())); QObject::connect(m_access.data(), &Solid::StorageAccess::accessibilityChanged, m_signalHandler.data(), &PlacesItemSignalHandler::onAccessibilityChanged); QObject::connect(m_access.data(), &Solid::StorageAccess::teardownRequested, m_signalHandler.data(), &PlacesItemSignalHandler::onTearDownRequested); } else if (m_disc && (m_disc->availableContent() & Solid::OpticalDisc::Audio) != 0) { Solid::Block *block = m_device.as(); if (block) { const QString device = block->device(); setUrl(QUrl(QStringLiteral("audiocd:/?device=%1").arg(device))); } else { setUrl(QUrl(QStringLiteral("audiocd:/"))); } } else if (m_mtp) { setUrl(QUrl(QStringLiteral("mtp:udi=%1").arg(m_device.udi()))); } } void PlacesItem::onAccessibilityChanged() { setIconOverlays(m_device.emblems()); setUrl(QUrl::fromLocalFile(m_access->filePath())); } -void PlacesItem::onTrashDirListerCompleted() -{ - Q_ASSERT(url().scheme() == QLatin1String("trash")); - - const bool isTrashEmpty = m_trashDirLister->items().isEmpty(); - setIcon(isTrashEmpty ? QStringLiteral("user-trash") : QStringLiteral("user-trash-full")); -} - void PlacesItem::updateBookmarkForRole(const QByteArray& role) { Q_ASSERT(!m_bookmark.isNull()); if (role == "iconName") { m_bookmark.setIcon(icon()); } else if (role == "text") { // Only store the text in the KBookmark if it is not the translation of // the current text. This makes sure that the text is re-translated if // the user chooses another language, or the translation itself changes. // // NOTE: It is important to use "KFile System Bookmarks" as context // (see PlacesItemModel::createSystemBookmarks()). if (text() != i18nc("KFile System Bookmarks", m_bookmark.text().toUtf8().data())) { m_bookmark.setFullText(text()); } } else if (role == "url") { m_bookmark.setUrl(url()); } else if (role == "udi") { m_bookmark.setMetaDataItem(QStringLiteral("UDI"), udi()); } else if (role == "isSystemItem") { m_bookmark.setMetaDataItem(QStringLiteral("isSystemItem"), isSystemItem() ? QStringLiteral("true") : QStringLiteral("false")); } else if (role == "isHidden") { m_bookmark.setMetaDataItem(QStringLiteral("IsHidden"), isHidden() ? QStringLiteral("true") : QStringLiteral("false")); } } QString PlacesItem::generateNewId() { // The ID-generation must be different as done in KFilePlacesItem from kdelibs // to prevent identical IDs, because 'count' is of course not shared. We append a // " (V2)" to indicate that the ID has been generated by // a new version of the places view. static int count = 0; return QString::number(QDateTime::currentDateTimeUtc().toTime_t()) + '/' + QString::number(count++) + " (V2)"; } PlacesItemSignalHandler *PlacesItem::signalHandler() const { return m_signalHandler.data(); } diff --git a/src/panels/places/placesitem.h b/src/panels/places/placesitem.h index 680590dcd..3de626983 100644 --- a/src/panels/places/placesitem.h +++ b/src/panels/places/placesitem.h @@ -1,122 +1,116 @@ /*************************************************************************** * Copyright (C) 2012 by Peter Penz * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * 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. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #ifndef PLACESITEM_H #define PLACESITEM_H #include "kitemviews/kstandarditem.h" #include #include #include #include #include #include #include #include class KDirLister; class PlacesItemSignalHandler; /** * @brief Extends KStandardItem by places-specific properties. */ class PlacesItem : public KStandardItem { public: explicit PlacesItem(const KBookmark& bookmark, PlacesItem* parent = nullptr); ~PlacesItem() override; void setUrl(const QUrl& url); QUrl url() const; void setUdi(const QString& udi); QString udi() const; void setHidden(bool hidden); bool isHidden() const; void setGroupHidden(bool hidden); bool isGroupHidden() const; void setSystemItem(bool isSystemItem); bool isSystemItem() const; Solid::Device device() const; void setBookmark(const KBookmark& bookmark); KBookmark bookmark() const; bool storageSetupNeeded() const; bool isSearchOrTimelineUrl() const; PlacesItemSignalHandler* signalHandler() const; protected: void onDataValueChanged(const QByteArray& role, const QVariant& current, const QVariant& previous) override; void onDataChanged(const QHash& current, const QHash& previous) override; private: PlacesItem(const PlacesItem& item); void initializeDevice(const QString& udi); /** * Is invoked if the accessibility of the storage access * m_access has been changed and updates the emblem. */ void onAccessibilityChanged(); - /** - * Is invoked if the listing of the trash has been completed. - * Updates the state of the trash-icon to be empty or full. - */ - void onTrashDirListerCompleted(); - /** * Applies the data-value from the role to m_bookmark. */ void updateBookmarkForRole(const QByteArray& role); static QString generateNewId(); private: Solid::Device m_device; QPointer m_access; QPointer m_volume; QPointer m_disc; QPointer m_mtp; QPointer m_signalHandler; QPointer m_trashDirLister; KBookmark m_bookmark; friend class PlacesItemSignalHandler; // Calls onAccessibilityChanged() }; #endif diff --git a/src/panels/places/placesitemsignalhandler.cpp b/src/panels/places/placesitemsignalhandler.cpp index 5531d1b63..c85c8336e 100644 --- a/src/panels/places/placesitemsignalhandler.cpp +++ b/src/panels/places/placesitemsignalhandler.cpp @@ -1,60 +1,53 @@ /*************************************************************************** * Copyright (C) 2012 by Peter Penz * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * 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. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "placesitemsignalhandler.h" #include "placesitem.h" PlacesItemSignalHandler::PlacesItemSignalHandler(PlacesItem* item, QObject* parent) : QObject(parent), m_item(item) { } PlacesItemSignalHandler::~PlacesItemSignalHandler() { } void PlacesItemSignalHandler::onAccessibilityChanged() { if (m_item) { m_item->onAccessibilityChanged(); } } -void PlacesItemSignalHandler::onTrashDirListerCompleted() -{ - if (m_item) { - m_item->onTrashDirListerCompleted(); - } -} - void PlacesItemSignalHandler::onTearDownRequested(const QString& udi) { Q_UNUSED(udi) if (m_item) { Solid::StorageAccess *tmp = m_item->device().as(); if (tmp) { QString mountPath = tmp->filePath(); emit tearDownExternallyRequested(mountPath); } } } diff --git a/src/panels/places/placesitemsignalhandler.h b/src/panels/places/placesitemsignalhandler.h index 49c8e43c9..6158d7180 100644 --- a/src/panels/places/placesitemsignalhandler.h +++ b/src/panels/places/placesitemsignalhandler.h @@ -1,73 +1,68 @@ /*************************************************************************** * Copyright (C) 2012 by Peter Penz * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * 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. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #ifndef PLACESITEMSIGNALHANDLER_H #define PLACESITEMSIGNALHANDLER_H #include class PlacesItem; /** * @brief Helper class for PlacesItem to be able to listen to signals * and performing a corresponding action. * * PlacesItem is derived from KStandardItem, which is no QObject-class * on purpose. To be able to internally listen to signals and performing a * corresponding action, PlacesItemSignalHandler is used. * * E.g. if the PlacesItem wants to react on accessibility-changes of a storage-access, * the signal-handler can be used like this: * * QObject::connect(storageAccess, SIGNAL(accessibilityChanged(bool,QString)), * signalHandler, SLOT(onAccessibilityChanged())); * * * The slot PlacesItemSignalHandler::onAccessibilityChanged() will call * the method PlacesItem::onAccessibilityChanged(). */ class PlacesItemSignalHandler: public QObject { Q_OBJECT public: explicit PlacesItemSignalHandler(PlacesItem* item, QObject* parent = nullptr); ~PlacesItemSignalHandler() override; public slots: /** * Calls PlacesItem::onAccessibilityChanged() */ void onAccessibilityChanged(); - /** - * Calls PlacesItem::onTrashDirListerCompleted() - */ - void onTrashDirListerCompleted(); - void onTearDownRequested(const QString& udi); signals: void tearDownExternallyRequested(const QString& udi); private: PlacesItem* m_item; }; #endif diff --git a/src/panels/places/placespanel.cpp b/src/panels/places/placespanel.cpp index 29acd034c..216b76038 100644 --- a/src/panels/places/placespanel.cpp +++ b/src/panels/places/placespanel.cpp @@ -1,542 +1,523 @@ /*************************************************************************** * Copyright (C) 2008-2012 by Peter Penz * * * * Based on KFilePlacesView from kdelibs: * * Copyright (C) 2007 Kevin Ottens * * Copyright (C) 2007 David Faure * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * 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. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "placespanel.h" #include "dolphin_generalsettings.h" #include "global.h" +#include "kitemviews/kitemlistcontainer.h" +#include "kitemviews/kitemlistcontroller.h" +#include "kitemviews/kitemlistselectionmanager.h" +#include "kitemviews/kstandarditem.h" #include "placesitem.h" #include "placesitemeditdialog.h" #include "placesitemlistgroupheader.h" #include "placesitemlistwidget.h" #include "placesitemmodel.h" #include "placesview.h" -#include "kitemviews/kitemlistcontainer.h" -#include "kitemviews/kitemlistcontroller.h" -#include "kitemviews/kitemlistselectionmanager.h" -#include "kitemviews/kstandarditem.h" +#include "trash/dolphintrash.h" #include "views/draganddrophelper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include PlacesPanel::PlacesPanel(QWidget* parent) : Panel(parent), m_controller(nullptr), m_model(nullptr), m_view(nullptr), m_storageSetupFailedUrl(), m_triggerStorageSetupButton(), m_itemDropEventIndex(-1), m_itemDropEventMimeData(nullptr), m_itemDropEvent(nullptr) { } PlacesPanel::~PlacesPanel() { } void PlacesPanel::proceedWithTearDown() { m_model->proceedWithTearDown(); } bool PlacesPanel::urlChanged() { if (!url().isValid() || url().scheme().contains(QStringLiteral("search"))) { // Skip results shown by a search, as possible identical // directory names are useless without parent-path information. return false; } if (m_controller) { selectClosestItem(); } return true; } void PlacesPanel::readSettings() { if (m_controller) { const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1; m_controller->setAutoActivationDelay(delay); } } void PlacesPanel::showEvent(QShowEvent* event) { if (event->spontaneous()) { Panel::showEvent(event); return; } if (!m_controller) { // Postpone the creating of the controller to the first show event. // This assures that no performance and memory overhead is given when the folders panel is not // used at all and stays invisible. m_model = new PlacesItemModel(this); m_model->setGroupedSorting(true); connect(m_model, &PlacesItemModel::errorMessage, this, &PlacesPanel::errorMessage); connect(m_model, &PlacesItemModel::storageTearDownRequested, this, &PlacesPanel::storageTearDownRequested); connect(m_model, &PlacesItemModel::storageTearDownExternallyRequested, this, &PlacesPanel::storageTearDownExternallyRequested); m_view = new PlacesView(); m_view->setWidgetCreator(new KItemListWidgetCreator()); m_view->setGroupHeaderCreator(new KItemListGroupHeaderCreator()); m_controller = new KItemListController(m_model, m_view, this); m_controller->setSelectionBehavior(KItemListController::SingleSelection); m_controller->setSingleClickActivationEnforced(true); readSettings(); connect(m_controller, &KItemListController::itemActivated, this, &PlacesPanel::slotItemActivated); connect(m_controller, &KItemListController::itemMiddleClicked, this, &PlacesPanel::slotItemMiddleClicked); connect(m_controller, &KItemListController::itemContextMenuRequested, this, &PlacesPanel::slotItemContextMenuRequested); connect(m_controller, &KItemListController::viewContextMenuRequested, this, &PlacesPanel::slotViewContextMenuRequested); connect(m_controller, &KItemListController::itemDropEvent, this, &PlacesPanel::slotItemDropEvent); connect(m_controller, &KItemListController::aboveItemDropEvent, this, &PlacesPanel::slotAboveItemDropEvent); KItemListContainer* container = new KItemListContainer(m_controller, this); container->setEnabledFrame(false); QVBoxLayout* layout = new QVBoxLayout(this); layout->setMargin(0); layout->addWidget(container); selectClosestItem(); } Panel::showEvent(event); } void PlacesPanel::slotItemActivated(int index) { triggerItem(index, Qt::LeftButton); } void PlacesPanel::slotItemMiddleClicked(int index) { triggerItem(index, Qt::MiddleButton); } void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos) { PlacesItem* item = m_model->placesItem(index); if (!item) { return; } QMenu menu(this); QAction* emptyTrashAction = nullptr; QAction* editAction = nullptr; QAction* teardownAction = nullptr; QAction* ejectAction = nullptr; const QString label = item->text(); const bool isDevice = !item->udi().isEmpty(); const bool isTrash = (item->url().scheme() == QLatin1String("trash")); if (isDevice) { ejectAction = m_model->ejectAction(index); if (ejectAction) { ejectAction->setParent(&menu); menu.addAction(ejectAction); } teardownAction = m_model->teardownAction(index); if (teardownAction) { teardownAction->setParent(&menu); menu.addAction(teardownAction); } if (teardownAction || ejectAction) { menu.addSeparator(); } } else { if (isTrash) { emptyTrashAction = menu.addAction(QIcon::fromTheme(QStringLiteral("trash-empty")), i18nc("@action:inmenu", "Empty Trash")); emptyTrashAction->setEnabled(item->icon() == QLatin1String("user-trash-full")); menu.addSeparator(); } } QAction* openInNewWindowAction = menu.addAction(QIcon::fromTheme("window-new"), i18nc("@item:inmenu", "Open in New Window")); QAction* openInNewTabAction = menu.addAction(QIcon::fromTheme("tab-new"), i18nc("@item:inmenu", "Open in New Tab")); if (!isDevice && !isTrash) { menu.addSeparator(); } if (!isDevice) { editAction = menu.addAction(QIcon::fromTheme("document-properties"), i18nc("@item:inmenu", "Edit...")); } QAction* removeAction = nullptr; if (!isDevice && !item->isSystemItem()) { removeAction = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@item:inmenu", "Remove")); } QAction* hideAction = menu.addAction(i18nc("@item:inmenu", "Hide")); hideAction->setCheckable(true); hideAction->setChecked(item->isHidden()); buildGroupContextMenu(&menu, index); QAction* action = menu.exec(pos.toPoint()); if (action) { if (action == emptyTrashAction) { - emptyTrash(); + Trash::empty(this); } else { // The index might have changed if devices were added/removed while // the context menu was open. index = m_model->index(item); if (index < 0) { // The item is not in the model any more, probably because it was an // external device that has been removed while the context menu was open. return; } if (action == editAction) { editEntry(index); } else if (action == removeAction) { m_model->deleteItem(index); } else if (action == hideAction) { item->setHidden(hideAction->isChecked()); } else if (action == openInNewWindowAction) { Dolphin::openNewWindow({KFilePlacesModel::convertedUrl(m_model->data(index).value("url").toUrl())}, this); } else if (action == openInNewTabAction) { // TriggerItem does set up the storage first and then it will // emit the slotItemMiddleClicked signal, because of Qt::MiddleButton. triggerItem(index, Qt::MiddleButton); } else if (action == teardownAction) { m_model->requestTearDown(index); } else if (action == ejectAction) { m_model->requestEject(index); } } } selectClosestItem(); } void PlacesPanel::slotViewContextMenuRequested(const QPointF& pos) { QMenu menu(this); QAction* addAction = menu.addAction(QIcon::fromTheme(QStringLiteral("document-new")), i18nc("@item:inmenu", "Add Entry...")); QAction* showAllAction = nullptr; if (m_model->hiddenCount() > 0) { showAllAction = menu.addAction(i18nc("@item:inmenu", "Show All Entries")); showAllAction->setCheckable(true); showAllAction->setChecked(m_model->hiddenItemsShown()); } buildGroupContextMenu(&menu, m_controller->indexCloseToMousePressedPosition()); QMenu* iconSizeSubMenu = new QMenu(i18nc("@item:inmenu", "Icon Size"), &menu); struct IconSizeInfo { int size; const char* context; const char* text; }; const int iconSizeCount = 4; static const IconSizeInfo iconSizes[iconSizeCount] = { {KIconLoader::SizeSmall, I18N_NOOP2_NOSTRIP("Small icon size", "Small (%1x%2)")}, {KIconLoader::SizeSmallMedium, I18N_NOOP2_NOSTRIP("Medium icon size", "Medium (%1x%2)")}, {KIconLoader::SizeMedium, I18N_NOOP2_NOSTRIP("Large icon size", "Large (%1x%2)")}, {KIconLoader::SizeLarge, I18N_NOOP2_NOSTRIP("Huge icon size", "Huge (%1x%2)")} }; QMap iconSizeActionMap; QActionGroup* iconSizeGroup = new QActionGroup(iconSizeSubMenu); for (int i = 0; i < iconSizeCount; ++i) { const int size = iconSizes[i].size; const QString text = i18nc(iconSizes[i].context, iconSizes[i].text, size, size); QAction* action = iconSizeSubMenu->addAction(text); iconSizeActionMap.insert(action, size); action->setActionGroup(iconSizeGroup); action->setCheckable(true); action->setChecked(m_view->iconSize() == size); } menu.addMenu(iconSizeSubMenu); menu.addSeparator(); foreach (QAction* action, customContextMenuActions()) { menu.addAction(action); } QAction* action = menu.exec(pos.toPoint()); if (action) { if (action == addAction) { addEntry(); } else if (action == showAllAction) { m_model->setHiddenItemsShown(showAllAction->isChecked()); } else if (iconSizeActionMap.contains(action)) { m_view->setIconSize(iconSizeActionMap.value(action)); } } selectClosestItem(); } QAction *PlacesPanel::buildGroupContextMenu(QMenu *menu, int index) { if (index == -1) { return nullptr; } KFilePlacesModel::GroupType groupType = m_model->groupType(index); QAction *hideGroupAction = menu->addAction(i18nc("@item:inmenu", "Hide Section '%1'", m_model->item(index)->group())); hideGroupAction->setCheckable(true); hideGroupAction->setChecked(m_model->isGroupHidden(groupType)); connect(hideGroupAction, &QAction::triggered, this, [this, groupType, hideGroupAction]{ m_model->setGroupHidden(groupType, hideGroupAction->isChecked()); }); return hideGroupAction; } void PlacesPanel::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event) { if (index < 0) { return; } const PlacesItem* destItem = m_model->placesItem(index); if (destItem->isSearchOrTimelineUrl()) { return; } if (m_model->storageSetupNeeded(index)) { connect(m_model, &PlacesItemModel::storageSetupDone, this, &PlacesPanel::slotItemDropEventStorageSetupDone); m_itemDropEventIndex = index; // Make a full copy of the Mime-Data m_itemDropEventMimeData = new QMimeData; m_itemDropEventMimeData->setText(event->mimeData()->text()); m_itemDropEventMimeData->setHtml(event->mimeData()->html()); m_itemDropEventMimeData->setUrls(event->mimeData()->urls()); m_itemDropEventMimeData->setImageData(event->mimeData()->imageData()); m_itemDropEventMimeData->setColorData(event->mimeData()->colorData()); m_itemDropEvent = new QDropEvent(event->pos().toPoint(), event->possibleActions(), m_itemDropEventMimeData, event->buttons(), event->modifiers()); m_model->requestStorageSetup(index); return; } QUrl destUrl = destItem->url(); QDropEvent dropEvent(event->pos().toPoint(), event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers()); slotUrlsDropped(destUrl, &dropEvent, this); } void PlacesPanel::slotItemDropEventStorageSetupDone(int index, bool success) { disconnect(m_model, &PlacesItemModel::storageSetupDone, this, &PlacesPanel::slotItemDropEventStorageSetupDone); if ((index == m_itemDropEventIndex) && m_itemDropEvent && m_itemDropEventMimeData) { if (success) { QUrl destUrl = m_model->placesItem(index)->url(); slotUrlsDropped(destUrl, m_itemDropEvent, this); } delete m_itemDropEventMimeData; delete m_itemDropEvent; m_itemDropEventIndex = -1; m_itemDropEventMimeData = nullptr; m_itemDropEvent = nullptr; } } void PlacesPanel::slotAboveItemDropEvent(int index, QGraphicsSceneDragDropEvent* event) { m_model->dropMimeDataBefore(index, event->mimeData()); } void PlacesPanel::slotUrlsDropped(const QUrl& dest, QDropEvent* event, QWidget* parent) { KIO::DropJob *job = DragAndDropHelper::dropUrls(dest, event, parent); if (job) { connect(job, &KIO::DropJob::result, this, [this](KJob *job) { if (job->error()) emit errorMessage(job->errorString()); }); } } -void PlacesPanel::slotTrashUpdated(KJob* job) -{ - if (job->error()) { - emit errorMessage(job->errorString()); - } - // as long as KIO doesn't do this, do it ourselves - KNotification::event(QStringLiteral("Trash: emptied"), QString(), QPixmap(), nullptr, KNotification::DefaultEvent); -} - void PlacesPanel::slotStorageSetupDone(int index, bool success) { disconnect(m_model, &PlacesItemModel::storageSetupDone, this, &PlacesPanel::slotStorageSetupDone); if (m_triggerStorageSetupButton == Qt::NoButton) { return; } if (success) { Q_ASSERT(!m_model->storageSetupNeeded(index)); triggerItem(index, m_triggerStorageSetupButton); m_triggerStorageSetupButton = Qt::NoButton; } else { setUrl(m_storageSetupFailedUrl); m_storageSetupFailedUrl = QUrl(); } } -void PlacesPanel::emptyTrash() -{ - KIO::JobUiDelegate uiDelegate; - uiDelegate.setWindow(window()); - if (uiDelegate.askDeleteConfirmation(QList(), KIO::JobUiDelegate::EmptyTrash, KIO::JobUiDelegate::DefaultConfirmation)) { - KIO::Job* job = KIO::emptyTrash(); - KJobWidgets::setWindow(job, window()); - connect(job, &KIO::Job::result, this, &PlacesPanel::slotTrashUpdated); - } -} - void PlacesPanel::addEntry() { const int index = m_controller->selectionManager()->currentItem(); const QUrl url = m_model->data(index).value("url").toUrl(); QPointer dialog = new PlacesItemEditDialog(this); dialog->setWindowTitle(i18nc("@title:window", "Add Places Entry")); dialog->setAllowGlobal(true); dialog->setUrl(url); if (dialog->exec() == QDialog::Accepted) { m_model->createPlacesItem(dialog->text(), dialog->url(), dialog->icon()); } delete dialog; } void PlacesPanel::editEntry(int index) { QHash data = m_model->data(index); QPointer dialog = new PlacesItemEditDialog(this); dialog->setWindowTitle(i18nc("@title:window", "Edit Places Entry")); dialog->setIcon(data.value("iconName").toString()); dialog->setText(data.value("text").toString()); dialog->setUrl(data.value("url").toUrl()); dialog->setAllowGlobal(true); if (dialog->exec() == QDialog::Accepted) { PlacesItem* oldItem = m_model->placesItem(index); if (oldItem) { oldItem->setText(dialog->text()); oldItem->setUrl(dialog->url()); oldItem->setIcon(dialog->icon()); m_model->refresh(); } } delete dialog; } void PlacesPanel::selectClosestItem() { const int index = m_model->closestItem(url()); KItemListSelectionManager* selectionManager = m_controller->selectionManager(); selectionManager->setCurrentItem(index); selectionManager->clearSelection(); selectionManager->setSelected(index); } void PlacesPanel::triggerItem(int index, Qt::MouseButton button) { const PlacesItem* item = m_model->placesItem(index); if (!item) { return; } if (m_model->storageSetupNeeded(index)) { m_triggerStorageSetupButton = button; m_storageSetupFailedUrl = url(); connect(m_model, &PlacesItemModel::storageSetupDone, this, &PlacesPanel::slotStorageSetupDone); m_model->requestStorageSetup(index); } else { m_triggerStorageSetupButton = Qt::NoButton; const QUrl url = m_model->data(index).value("url").toUrl(); if (!url.isEmpty()) { if (button == Qt::MiddleButton) { emit placeMiddleClicked(KFilePlacesModel::convertedUrl(url)); } else { emit placeActivated(KFilePlacesModel::convertedUrl(url)); } } } } diff --git a/src/panels/places/placespanel.h b/src/panels/places/placespanel.h index 7159357f8..d62697940 100644 --- a/src/panels/places/placespanel.h +++ b/src/panels/places/placespanel.h @@ -1,102 +1,100 @@ /*************************************************************************** * Copyright (C) 2008-2012 by Peter Penz * * Copyright (C) 2010 by Christian Muehlhaeuser * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * 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. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #ifndef PLACESPANEL_H #define PLACESPANEL_H #include "panels/panel.h" #include #include class KItemListController; class PlacesItem; class PlacesItemModel; class PlacesView; class QGraphicsSceneDragDropEvent; class KJob; class QMenu; /** * @brief Combines bookmarks and mounted devices as list. */ class PlacesPanel : public Panel { Q_OBJECT public: explicit PlacesPanel(QWidget* parent); ~PlacesPanel() override; void proceedWithTearDown(); signals: void placeActivated(const QUrl& url); void placeMiddleClicked(const QUrl& url); void errorMessage(const QString& error); void storageTearDownRequested(const QString& mountPath); void storageTearDownExternallyRequested(const QString& mountPath); protected: bool urlChanged() override; void showEvent(QShowEvent* event) override; public slots: void readSettings() override; private slots: void slotItemActivated(int index); void slotItemMiddleClicked(int index); void slotItemContextMenuRequested(int index, const QPointF& pos); void slotViewContextMenuRequested(const QPointF& pos); void slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event); void slotItemDropEventStorageSetupDone(int index, bool success); void slotAboveItemDropEvent(int index, QGraphicsSceneDragDropEvent* event); void slotUrlsDropped(const QUrl& dest, QDropEvent* event, QWidget* parent); - void slotTrashUpdated(KJob* job); void slotStorageSetupDone(int index, bool success); private: - void emptyTrash(); void addEntry(); void editEntry(int index); /** * Selects the item that has the closest URL for the URL set * for the panel (see Panel::setUrl()). */ void selectClosestItem(); void triggerItem(int index, Qt::MouseButton button); QAction* buildGroupContextMenu(QMenu* menu, int index); private: KItemListController* m_controller; PlacesItemModel* m_model; PlacesView* m_view; QUrl m_storageSetupFailedUrl; Qt::MouseButton m_triggerStorageSetupButton; int m_itemDropEventIndex; QMimeData* m_itemDropEventMimeData; QDropEvent* m_itemDropEvent; }; #endif // PLACESPANEL_H diff --git a/src/trash/dolphintrash.cpp b/src/trash/dolphintrash.cpp new file mode 100644 index 000000000..10d9badf5 --- /dev/null +++ b/src/trash/dolphintrash.cpp @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz * + * Copyright (C) 2018 by Roman Inflianskas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * 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. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphintrash.h" + +#include +#include +#include +#include +#include +#include + + +Trash::Trash() + : m_trashDirLister(new KDirLister()) +{ + // The trash icon must always be updated dependent on whether + // the trash is empty or not. We use a KDirLister that automatically + // watches for changes if the number of items has been changed. + m_trashDirLister->setAutoErrorHandlingEnabled(false, nullptr); + m_trashDirLister->setDelayedMimeTypes(true); + auto trashDirContentChanged = [this]() { + bool isTrashEmpty = m_trashDirLister->items().isEmpty(); + emit emptinessChanged(isTrashEmpty); + }; + connect(m_trashDirLister, static_cast(&KDirLister::completed), trashDirContentChanged); + m_trashDirLister->openUrl(QStringLiteral("trash:/")); +} + +Trash::~Trash() +{ + delete m_trashDirLister; +} + +Trash &Trash::instance() +{ + static Trash result; + return result; +} + +KIO::Job *Trash::empty(QWidget *window) +{ + KIO::JobUiDelegate uiDelegate; + uiDelegate.setWindow(window); + bool confirmed = uiDelegate.askDeleteConfirmation(QList(), KIO::JobUiDelegate::EmptyTrash, KIO::JobUiDelegate::DefaultConfirmation); + if (confirmed) { + KIO::Job* job = KIO::emptyTrash(); + KJobWidgets::setWindow(job, window); + job->uiDelegate()->setAutoErrorHandlingEnabled(true); + // as long as KIO doesn't do this, do it ourselves + connect(job, &KIO::Job::result, [](){ + KNotification::event(QStringLiteral("Trash: emptied"), QString(), QPixmap(), nullptr, KNotification::DefaultEvent); + }); + return job; + } + return nullptr; +} + +bool Trash::isEmpty() +{ + KConfig trashConfig(QStringLiteral("trashrc"), KConfig::SimpleConfig); + return (trashConfig.group("Status").readEntry("Empty", true)); +} + diff --git a/src/panels/places/placesitemsignalhandler.cpp b/src/trash/dolphintrash.h similarity index 61% copy from src/panels/places/placesitemsignalhandler.cpp copy to src/trash/dolphintrash.h index 5531d1b63..76da4a5e0 100644 --- a/src/panels/places/placesitemsignalhandler.cpp +++ b/src/trash/dolphintrash.h @@ -1,60 +1,54 @@ /*************************************************************************** * Copyright (C) 2012 by Peter Penz * + * Copyright (C) 2018 by Roman Inflianskas * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * 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. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ -#include "placesitemsignalhandler.h" +#ifndef DOLPHINTRASH_H +#define DOLPHINTRASH_H -#include "placesitem.h" +#include -PlacesItemSignalHandler::PlacesItemSignalHandler(PlacesItem* item, - QObject* parent) : - QObject(parent), - m_item(item) -{ -} +#include +#include -PlacesItemSignalHandler::~PlacesItemSignalHandler() +class Trash: public QObject { -} + Q_OBJECT -void PlacesItemSignalHandler::onAccessibilityChanged() -{ - if (m_item) { - m_item->onAccessibilityChanged(); - } -} +public: + // delete copy and move constructors and assign operators + Trash(Trash const&) = delete; + Trash(Trash&&) = delete; + Trash& operator=(Trash const&) = delete; + Trash& operator=(Trash &&) = delete; -void PlacesItemSignalHandler::onTrashDirListerCompleted() -{ - if (m_item) { - m_item->onTrashDirListerCompleted(); - } -} + static Trash& instance(); + static KIO::Job* empty(QWidget *window); + static bool isEmpty(); -void PlacesItemSignalHandler::onTearDownRequested(const QString& udi) -{ - Q_UNUSED(udi) - if (m_item) { - Solid::StorageAccess *tmp = m_item->device().as(); - if (tmp) { - QString mountPath = tmp->filePath(); - emit tearDownExternallyRequested(mountPath); - } - } -} +signals: + void emptinessChanged(bool isEmpty); + +private: + KDirLister *m_trashDirLister; + + Trash(); + ~Trash(); +}; +#endif // DOLPHINTRASH_H