#include <KFileItemListProperties>
#include <KFormat>
#include <KIO/CopyJob>
-#include <KIO/DeleteJob>
+#include <KIO/DeleteOrTrashJob>
#include <KIO/DropJob>
#include <KIO/JobUiDelegate>
#include <KIO/Paste>
#include <kwidgetsaddons_version.h>
-#include <kio_version.h>
-#if KIO_VERSION >= QT_VERSION_CHECK(5, 100, 0)
-#include <KIO/DeleteOrTrashJob>
-#endif
-
#include <QAbstractItemView>
#include <QActionGroup>
#include <QApplication>
, m_assureVisibleCurrentIndex(false)
, m_isFolderWritable(true)
, m_dragging(false)
+ , m_selectNextItem(false)
, m_url(url)
, m_viewPropertiesContext()
, m_mode(DolphinView::IconsView)
, m_view(nullptr)
, m_container(nullptr)
, m_toolTipManager(nullptr)
- , m_selectNextItem(false)
, m_selectionChangedTimer(nullptr)
, m_currentItemUrl()
, m_scrollToCurrentItem(false)
, m_restoredContentsPosition()
+ , m_controlWheelAccumulatedDelta(0)
, m_selectedUrls()
, m_clearSelectionBeforeSelectingNewItems(false)
, m_markFirstNewlySelectedItemAsCurrent(false)
m_container = new KItemListContainer(controller, this);
m_container->installEventFilter(this);
+#ifndef QT_NO_ACCESSIBILITY
+ m_view->setAccessibleParentsObject(m_container);
+#endif
setFocusProxy(m_container);
connect(m_container->horizontalScrollBar(), &QScrollBar::valueChanged, this, [=] {
hideToolTip();
connect(m_view, &DolphinItemListView::sortRoleChanged, this, &DolphinView::slotSortRoleChangedByHeader);
connect(m_view, &DolphinItemListView::visibleRolesChanged, this, &DolphinView::slotVisibleRolesChangedByHeader);
connect(m_view, &DolphinItemListView::roleEditingCanceled, this, &DolphinView::slotRoleEditingCanceled);
+
+ connect(m_view, &DolphinItemListView::columnHovered, this, [this](int columnIndex) {
+ m_hoveredColumnHeaderIndex = columnIndex;
+ });
+ connect(m_view, &DolphinItemListView::columnUnHovered, this, [this](int /* columnIndex */) {
+ m_hoveredColumnHeaderIndex = std::nullopt;
+ });
connect(m_view->header(), &KItemListHeader::columnWidthChangeFinished, this, &DolphinView::slotHeaderColumnWidthChangeFinished);
connect(m_view->header(), &KItemListHeader::sidePaddingChanged, this, &DolphinView::slotSidePaddingWidthChanged);
if (active) {
m_container->setFocus();
Q_EMIT activated();
- Q_EMIT writeStateChanged(m_isFolderWritable);
}
}
void DolphinView::setViewMode(Mode mode)
{
if (mode != m_mode) {
+ // Reset scrollbars before changing the view mode.
+ m_container->horizontalScrollBar()->setValue(0);
+ m_container->verticalScrollBar()->setValue(0);
+
ViewProperties props(viewPropertiesUrl());
props.setViewMode(mode);
void DolphinView::setSelectionModeEnabled(const bool enabled)
{
if (enabled) {
- m_proxyStyle = std::make_unique<SelectionMode::SingleClickSelectionProxyStyle>();
+ if (!m_proxyStyle) {
+ m_proxyStyle = std::make_unique<SelectionMode::SingleClickSelectionProxyStyle>();
+ }
setStyle(m_proxyStyle.get());
m_view->setStyle(m_proxyStyle.get());
m_view->setEnabledSelectionToggles(DolphinItemListView::SelectionTogglesEnabled::False);
void DolphinView::markUrlsAsSelected(const QList<QUrl> &urls)
{
m_selectedUrls = urls;
+ m_selectJobCreatedItems = false;
}
void DolphinView::markUrlAsCurrent(const QUrl &url)
return;
}
- m_statJobForStatusBarText = KIO::statDetails(m_model->rootItem().url(), KIO::StatJob::SourceSide, KIO::StatRecursiveSize, KIO::HideProgressInfo);
+ m_statJobForStatusBarText = KIO::stat(m_model->rootItem().url(), KIO::StatJob::SourceSide, KIO::StatRecursiveSize, KIO::HideProgressInfo);
connect(m_statJobForStatusBarText, &KJob::result, this, &DolphinView::slotStatJobResult);
m_statJobForStatusBarText->start();
}
void DolphinView::clearSelection()
{
+ m_selectJobCreatedItems = false;
m_selectedUrls.clear();
m_container->controller()->selectionManager()->clearSelection();
}
{
const QList<QUrl> list = simplifiedSelectedUrls();
-#if KIO_VERSION >= QT_VERSION_CHECK(5, 100, 0)
using Iface = KIO::AskUserActionInterface;
auto *trashJob = new KIO::DeleteOrTrashJob(list, Iface::Trash, Iface::DefaultConfirmation, this);
connect(trashJob, &KJob::result, this, &DolphinView::slotTrashFileFinished);
m_selectNextItem = true;
trashJob->start();
-#else
- KIO::JobUiDelegate uiDelegate;
- uiDelegate.setWindow(window());
- if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation)) {
- KIO::Job *job = KIO::trash(list);
- KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Trash, list, QUrl(QStringLiteral("trash:/")), job);
- KJobWidgets::setWindow(job, this);
- connect(job, &KIO::Job::result, this, &DolphinView::slotTrashFileFinished);
- }
-#endif
}
void DolphinView::deleteSelectedItems()
{
const QList<QUrl> list = simplifiedSelectedUrls();
-#if KIO_VERSION >= QT_VERSION_CHECK(5, 100, 0)
using Iface = KIO::AskUserActionInterface;
auto *trashJob = new KIO::DeleteOrTrashJob(list, Iface::Delete, Iface::DefaultConfirmation, this);
connect(trashJob, &KJob::result, this, &DolphinView::slotTrashFileFinished);
m_selectNextItem = true;
trashJob->start();
-#else
- KIO::JobUiDelegate uiDelegate;
- uiDelegate.setWindow(window());
- if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation)) {
- KIO::Job *job = KIO::del(list);
- KJobWidgets::setWindow(job, this);
- connect(job, &KIO::Job::result, this, &DolphinView::slotDeleteFileFinished);
- }
-#endif
}
void DolphinView::cutSelectedItemsToClipboard()
void DolphinView::copySelectedItems(const KFileItemList &selection, const QUrl &destinationUrl)
{
+ if (selection.isEmpty() || !destinationUrl.isValid()) {
+ return;
+ }
+
+ m_clearSelectionBeforeSelectingNewItems = true;
+ m_markFirstNewlySelectedItemAsCurrent = true;
+ m_selectJobCreatedItems = true;
+
KIO::CopyJob *job = KIO::copy(selection.urlList(), destinationUrl, KIO::DefaultFlags);
KJobWidgets::setWindow(job, this);
connect(job, &KIO::DropJob::result, this, &DolphinView::slotJobResult);
- connect(job, &KIO::CopyJob::copyingDone, this, &DolphinView::slotCopyingDone);
+ connect(job, &KIO::CopyJob::copying, this, &DolphinView::slotItemCreatedFromJob);
+ connect(job, &KIO::CopyJob::copyingDone, this, &DolphinView::slotItemCreatedFromJob);
KIO::FileUndoManager::self()->recordCopyJob(job);
}
void DolphinView::moveSelectedItems(const KFileItemList &selection, const QUrl &destinationUrl)
{
+ if (selection.isEmpty() || !destinationUrl.isValid()) {
+ return;
+ }
+
+ m_clearSelectionBeforeSelectingNewItems = true;
+ m_markFirstNewlySelectedItemAsCurrent = true;
+ m_selectJobCreatedItems = true;
+
KIO::CopyJob *job = KIO::move(selection.urlList(), destinationUrl, KIO::DefaultFlags);
KJobWidgets::setWindow(job, this);
connect(job, &KIO::DropJob::result, this, &DolphinView::slotJobResult);
- connect(job, &KIO::CopyJob::copyingDone, this, &DolphinView::slotCopyingDone);
+ connect(job, &KIO::CopyJob::moving, this, &DolphinView::slotItemCreatedFromJob);
+ connect(job, &KIO::CopyJob::copyingDone, this, &DolphinView::slotItemCreatedFromJob);
KIO::FileUndoManager::self()->recordCopyJob(job);
}
}
}
break;
+ case QEvent::KeyRelease:
+ if (static_cast<QKeyEvent *>(event)->key() == Qt::Key_Control) {
+ m_controlWheelAccumulatedDelta = 0;
+ }
+ break;
case QEvent::FocusIn:
if (watched == m_container) {
setActive(true);
}
break;
- case QEvent::ToolTip:
- tryShowNameToolTip(static_cast<QHelpEvent *>(event));
+ case QEvent::ToolTip: {
+ const auto helpEvent = static_cast<QHelpEvent *>(event);
+ if (tryShowNameToolTip(helpEvent)) {
+ return true;
+
+ } else if (m_hoveredColumnHeaderIndex) {
+ const auto rolesInfo = KFileItemModel::rolesInformation();
+ const auto visibleRole = m_visibleRoles.value(*m_hoveredColumnHeaderIndex);
+ for (const KFileItemModel::RoleInfo &info : rolesInfo) {
+ if (visibleRole == info.role) {
+ QToolTip::showText(helpEvent->globalPos(), info.tooltip, this);
+ return true;
+ }
+ }
+ }
+ break;
+ }
default:
break;
}
void DolphinView::wheelEvent(QWheelEvent *event)
{
if (event->modifiers().testFlag(Qt::ControlModifier)) {
- const QPoint numDegrees = event->angleDelta() / 8;
- const QPoint numSteps = numDegrees / 15;
+ m_controlWheelAccumulatedDelta += event->angleDelta().y();
+
+ if (m_controlWheelAccumulatedDelta <= -QWheelEvent::DefaultDeltasPerStep) {
+ slotDecreaseZoom();
+ m_controlWheelAccumulatedDelta += QWheelEvent::DefaultDeltasPerStep;
+ } else if (m_controlWheelAccumulatedDelta >= QWheelEvent::DefaultDeltasPerStep) {
+ slotIncreaseZoom();
+ m_controlWheelAccumulatedDelta -= QWheelEvent::DefaultDeltasPerStep;
+ }
- setZoomLevel(zoomLevel() + numSteps.y());
event->accept();
} else {
event->ignore();
if (indexes.count() > 5) {
QString question = i18np("Are you sure you want to open 1 item?", "Are you sure you want to open %1 items?", indexes.count());
-#if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
- const int answer = KMessageBox::warningTwoActions(
+ const int answer = KMessageBox::warningContinueCancel(
this,
question,
{},
-#else
- const int answer =
- KMessageBox::warningYesNo(this,
- question,
- {},
-#endif
KGuiItem(i18ncp("@action:button", "Open %1 Item", "Open %1 Items", indexes.count()), QStringLiteral("document-open")),
- KStandardGuiItem::cancel());
-#if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
- if (answer != KMessageBox::PrimaryAction) {
-#else
- if (answer != KMessageBox::Yes) {
-#endif
+ KStandardGuiItem::cancel(),
+ QStringLiteral("ConfirmOpenManyFolders"));
+ if (answer != KMessageBox::PrimaryAction && answer != KMessageBox::Continue) {
return;
}
}
const KFileItem &item = m_model->fileItem(index);
const QUrl &url = openItemAsFolderUrl(item);
const auto modifiers = QGuiApplication::keyboardModifiers();
+ const QString &archiveProtocol = KProtocolManager::protocolForArchiveMimetype(item.mimetype());
if (!url.isEmpty()) {
// keep in sync with KUrlNavigator::slotNavigatorButtonClicked
if (modifiers & Qt::ShiftModifier) {
} else {
Q_EMIT tabRequested(url);
}
- } else if (isTabsForFilesEnabled()) {
+ } else if (!archiveProtocol.isEmpty() && isTabsForFilesEnabled()) {
// keep in sync with KUrlNavigator::slotNavigatorButtonClicked
if (modifiers & Qt::ShiftModifier) {
Q_EMIT activeTabRequested(item.url());
} else {
Q_EMIT tabRequested(item.url());
}
+ } else {
+ Q_EMIT fileMiddleClickActivated(item);
}
}
action->setCheckable(true);
action->setChecked(visibleRolesSet.contains(info.role));
action->setData(info.role);
+ action->setToolTip(info.tooltip);
const bool enable = (!info.requiresBaloo && !info.requiresIndexer) || (info.requiresBaloo) || (info.requiresIndexer && indexingEnabled);
action->setEnabled(enable);
// Mark the dropped urls as selected.
m_clearSelectionBeforeSelectingNewItems = true;
m_markFirstNewlySelectedItemAsCurrent = true;
+ m_selectJobCreatedItems = true;
connect(job, &KIO::DropJob::itemCreated, this, &DolphinView::slotItemCreated);
+ connect(job, &KIO::DropJob::copyJobStarted, this, [this](const KIO::CopyJob *copyJob) {
+ connect(copyJob, &KIO::CopyJob::copying, this, &DolphinView::slotItemCreatedFromJob);
+ connect(copyJob, &KIO::CopyJob::moving, this, &DolphinView::slotItemCreatedFromJob);
+ connect(copyJob, &KIO::CopyJob::linking, this, [this](KIO::Job *job, const QString &src, const QUrl &dest) {
+ Q_UNUSED(job)
+ Q_UNUSED(src)
+ slotItemCreated(dest);
+ });
+ });
}
}
}
}
}
-void DolphinView::slotCopyingDone(KIO::Job *, const QUrl &, const QUrl &to)
+void DolphinView::slotItemCreatedFromJob(KIO::Job *, const QUrl &, const QUrl &to)
{
slotItemCreated(to);
}
markUrlAsCurrent(url);
m_markFirstNewlySelectedItemAsCurrent = false;
}
- m_selectedUrls << url;
+ if (m_selectJobCreatedItems && !m_selectedUrls.contains(url)) {
+ m_selectedUrls << url;
+ }
+}
+
+void DolphinView::onDirectoryLoadingCompleted()
+{
+ // the model should now contain all the items created by the job
+ updateSelectionState();
+ m_selectJobCreatedItems = false;
+ m_selectedUrls.clear();
}
void DolphinView::slotJobResult(KJob *job)
if (job->error() && job->error() != KIO::ERR_USER_CANCELED) {
Q_EMIT errorMessage(job->errorString());
}
+ if (!m_selectJobCreatedItems) {
+ m_selectedUrls.clear();
+ return;
+ }
if (!m_selectedUrls.isEmpty()) {
m_selectedUrls = KDirModel::simplifiedUrlList(m_selectedUrls);
+
+ updateSelectionState();
+ if (!m_selectedUrls.isEmpty()) {
+ // not all urls were found, the model may not be up to date
+ connect(m_model, &KFileItemModel::directoryLoadingCompleted, this, &DolphinView::onDirectoryLoadingCompleted, Qt::UniqueConnection);
+ } else {
+ m_selectJobCreatedItems = false;
+ m_selectedUrls.clear();
+ }
}
}
}
}
+void DolphinView::updateSelectionState()
+{
+ if (!m_selectedUrls.isEmpty()) {
+ KItemListSelectionManager *selectionManager = m_container->controller()->selectionManager();
+
+ // if there is a selection already, leave it that way
+ // unless some drop/paste job are in the process of creating items
+ if (!selectionManager->hasSelection() || m_selectJobCreatedItems) {
+ if (m_clearSelectionBeforeSelectingNewItems) {
+ selectionManager->clearSelection();
+ m_clearSelectionBeforeSelectingNewItems = false;
+ }
+
+ KItemSet selectedItems = selectionManager->selectedItems();
+
+ QList<QUrl>::iterator it = m_selectedUrls.begin();
+ while (it != m_selectedUrls.end()) {
+ const int index = m_model->index(*it);
+ if (index >= 0) {
+ selectedItems.insert(index);
+ it = m_selectedUrls.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ if (!selectedItems.isEmpty()) {
+ selectionManager->beginAnchoredSelection(selectionManager->currentItem());
+ selectionManager->setSelectedItems(selectedItems);
+ }
+ }
+ }
+}
+
void DolphinView::updateViewState()
{
if (m_currentItemUrl != QUrl()) {
// scroll to current item and reset the state
if (m_scrollToCurrentItem) {
- m_view->scrollToItem(currentIndex);
+ m_view->scrollToItem(currentIndex, KItemListView::ViewItemPosition::Middle);
m_scrollToCurrentItem = false;
}
m_currentItemUrl = QUrl();
m_container->verticalScrollBar()->setValue(y);
}
- if (!m_selectedUrls.isEmpty()) {
- KItemListSelectionManager *selectionManager = m_container->controller()->selectionManager();
-
- // if there is a selection already, leave it that way
- if (!selectionManager->hasSelection()) {
- if (m_clearSelectionBeforeSelectingNewItems) {
- selectionManager->clearSelection();
- m_clearSelectionBeforeSelectingNewItems = false;
- }
-
- KItemSet selectedItems = selectionManager->selectedItems();
-
- QList<QUrl>::iterator it = m_selectedUrls.begin();
- while (it != m_selectedUrls.end()) {
- const int index = m_model->index(*it);
- if (index >= 0) {
- selectedItems.insert(index);
- it = m_selectedUrls.erase(it);
- } else {
- ++it;
- }
- }
-
- if (!selectedItems.isEmpty()) {
- selectionManager->beginAnchoredSelection(selectionManager->currentItem());
- selectionManager->setSelectedItems(selectedItems);
- }
- }
- }
+ updateSelectionState();
}
void DolphinView::hideToolTip(const ToolTipManager::HideBehavior behavior)
}
}
+bool DolphinView::handleSpaceAsNormalKey() const
+{
+ return !m_container->hasFocus() || m_container->controller()->isSearchAsYouTypeActive();
+}
+
void DolphinView::slotTwoClicksRenamingTimerTimeout()
{
const KItemListSelectionManager *selectionManager = m_container->controller()->selectionManager();
void DolphinView::selectNextItem()
{
if (m_active && m_selectNextItem) {
- KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
+ KItemListSelectionManager *selectionManager = m_container->controller()->selectionManager();
if (selectedItems().isEmpty()) {
Q_ASSERT_X(false, "DolphinView", "Selecting the next item failed.");
return;
newUrl.setPath(newUrl.path() + KIO::encodeFileName(newName));
#ifndef Q_OS_WIN
- //Confirm hiding file/directory by renaming inline
+ // Confirm hiding file/directory by renaming inline
if (!hiddenFilesShown() && newName.startsWith(QLatin1Char('.')) && !oldItem.name().startsWith(QLatin1Char('.'))) {
KGuiItem yesGuiItem(i18nc("@action:button", "Rename and Hide"), QStringLiteral("view-hidden"));
-#if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
const auto code =
KMessageBox::questionTwoActions(this,
-#else
- const auto code =
- KMessageBox::questionYesNo(this,
-#endif
oldItem.isFile() ? i18n("Adding a dot to the beginning of this file's name will hide it from view.\n"
"Do you still want to rename it?")
: i18n("Adding a dot to the beginning of this folder's name will hide it from view.\n"
KStandardGuiItem::cancel(),
QStringLiteral("ConfirmHide"));
-#if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
if (code == KMessageBox::SecondaryAction) {
-#else
- if (code == KMessageBox::No) {
-#endif
return;
}
}
KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Rename, {oldUrl}, newUrl, job);
job->uiDelegate()->setAutoErrorHandlingEnabled(true);
- forceUrlsSelection(newUrl, {newUrl});
-
if (!newNameExistsAlready) {
+ forceUrlsSelection(newUrl, {newUrl});
+
// Only connect the result signal if there is no item with the new name
// in the model yet, see bug 328262.
connect(job, &KJob::result, this, &DolphinView::slotRenamingResult);
KJobWidgets::setWindow(job, this);
m_clearSelectionBeforeSelectingNewItems = true;
m_markFirstNewlySelectedItemAsCurrent = true;
+ m_selectJobCreatedItems = true;
+ // TODO KF6 use KIO::PasteJob::copyJobStarted to hook to earlier events copying/moving
connect(job, &KIO::PasteJob::itemCreated, this, &DolphinView::slotItemCreated);
connect(job, &KIO::PasteJob::result, this, &DolphinView::slotJobResult);
}
}
}
+bool DolphinView::isFolderWritable() const
+{
+ return m_isFolderWritable;
+}
+
QUrl DolphinView::viewPropertiesUrl() const
{
if (m_viewPropertiesContext.isEmpty()) {
if (clipboard == nullptr) {
return;
}
- clipboard->setText(path);
+ clipboard->setText(QDir::toNativeSeparators(path));
}
void DolphinView::slotIncreaseZoom()
void DolphinView::showLoadingPlaceholder()
{
- m_placeholderLabel->setText(i18n("Loading..."));
+ m_placeholderLabel->setText(i18n("Loading…"));
m_placeholderLabel->setVisible(true);
}
m_placeholderLabel->setVisible(true);
}
-void DolphinView::tryShowNameToolTip(QHelpEvent *event)
+bool DolphinView::tryShowNameToolTip(QHelpEvent *event)
{
if (!GeneralSettings::showToolTips() && m_mode == DolphinView::IconsView) {
const std::optional<int> index = m_view->itemAt(event->pos());
if (!index.has_value()) {
- return;
+ return false;
}
// Check whether the filename has been elided
const QString text = item.text();
const QPoint pos = mapToGlobal(event->pos());
QToolTip::showText(pos, text);
+ return true;
}
}
+ return false;
}
+
+#include "moc_dolphinview.cpp"