#include "kitemviews/kitemlistcontroller.h"
#include "kitemviews/kitemlistheader.h"
#include "kitemviews/kitemlistselectionmanager.h"
+#include "kitemviews/private/kitemlistroleeditor.h"
#include "versioncontrol/versioncontrolobserver.h"
#include "viewproperties.h"
#include "views/tooltips/tooltipmanager.h"
#include <KProtocolManager>
#include <QAbstractItemView>
+#include <QActionGroup>
#include <QApplication>
#include <QClipboard>
#include <QDropEvent>
m_assureVisibleCurrentIndex(false),
m_isFolderWritable(true),
m_dragging(false),
+ m_loading(false),
m_url(url),
m_viewPropertiesContext(),
m_mode(DolphinView::IconsView),
m_markFirstNewlySelectedItemAsCurrent(false),
m_versionControlObserver(nullptr),
m_twoClicksRenamingTimer(nullptr),
- m_placeholderLabel(nullptr)
+ m_placeholderLabel(nullptr),
+ m_showLoadingPlaceholderTimer(nullptr)
{
m_topLayout = new QVBoxLayout(this);
m_topLayout->setSpacing(0);
connect(m_container->horizontalScrollBar(), &QScrollBar::valueChanged, this, [=] { hideToolTip(); });
connect(m_container->verticalScrollBar(), &QScrollBar::valueChanged, this, [=] { hideToolTip(); });
+ m_showLoadingPlaceholderTimer = new QTimer(this);
+ m_showLoadingPlaceholderTimer->setInterval(500);
+ m_showLoadingPlaceholderTimer->setSingleShot(true);
+ connect(m_showLoadingPlaceholderTimer, &QTimer::timeout, this, &DolphinView::showLoadingPlaceholder);
+
// Show some placeholder text for empty folders
// This is made using a heavily-modified QLabel rather than a KTitleWidget
// because KTitleWidget can't be told to turn off mouse-selectable text
m_placeholderLabel->setGraphicsEffect(effect);
// Set initial text and visibility
updatePlaceholderLabel();
- // Add a new layout to hold it and put it in the layout
- auto *centeringLayout = new QVBoxLayout(this);
- m_container->setLayout(centeringLayout);
+
+ auto *centeringLayout = new QVBoxLayout(m_container);
centeringLayout->addWidget(m_placeholderLabel);
centeringLayout->setAlignment(m_placeholderLabel, Qt::AlignCenter);
connect(m_model, &KFileItemModel::directoryLoadingStarted, this, &DolphinView::slotDirectoryLoadingStarted);
connect(m_model, &KFileItemModel::directoryLoadingCompleted, this, &DolphinView::slotDirectoryLoadingCompleted);
- connect(m_model, &KFileItemModel::directoryLoadingCanceled, this, &DolphinView::directoryLoadingCanceled);
+ connect(m_model, &KFileItemModel::directoryLoadingCanceled, this, &DolphinView::slotDirectoryLoadingCanceled);
connect(m_model, &KFileItemModel::directoryLoadingProgress, this, &DolphinView::directoryLoadingProgress);
connect(m_model, &KFileItemModel::directorySortingProgress, this, &DolphinView::directorySortingProgress);
connect(m_model, &KFileItemModel::itemsChanged,
connect(m_model, &KFileItemModel::errorMessage, this, &DolphinView::errorMessage);
connect(m_model, &KFileItemModel::directoryRedirection, this, &DolphinView::slotDirectoryRedirection);
connect(m_model, &KFileItemModel::urlIsFileError, this, &DolphinView::urlIsFileError);
+ connect(m_model, &KFileItemModel::fileItemsChanged, this, &DolphinView::fileItemsChanged);
connect(this, &DolphinView::itemCountChanged,
this, &DolphinView::updatePlaceholderLabel);
- connect(this, &DolphinView::urlChanged,
- this, &DolphinView::updatePlaceholderLabel);
m_view->installEventFilter(this);
connect(m_view, &DolphinItemListView::sortOrderChanged,
return m_model->mimeTypeFilters();
}
-QString DolphinView::statusBarText() const
+void DolphinView::requestStatusBarText()
{
- QString summary;
- QString foldersText;
- QString filesText;
-
- int folderCount = 0;
- int fileCount = 0;
- KIO::filesize_t totalFileSize = 0;
+ if (m_statJobForStatusBarText) {
+ // Kill the pending request.
+ m_statJobForStatusBarText->kill();
+ }
if (m_container->controller()->selectionManager()->hasSelection()) {
+ int folderCount = 0;
+ int fileCount = 0;
+ KIO::filesize_t totalFileSize = 0;
+
// Give a summary of the status of the selected files
const KFileItemList list = selectedItems();
for (const KFileItem& item : list) {
if (folderCount + fileCount == 1) {
// If only one item is selected, show info about it
- return list.first().getStatusBarInfo();
+ Q_EMIT statusBarTextChanged(list.first().getStatusBarInfo());
} else {
// At least 2 items are selected
- foldersText = i18ncp("@info:status", "1 Folder selected", "%1 Folders selected", folderCount);
- filesText = i18ncp("@info:status", "1 File selected", "%1 Files selected", fileCount);
+ emitStatusBarText(folderCount, fileCount, totalFileSize, HasSelection);
}
+ } else { // has no selection
+ if (!m_model->rootItem().url().isValid()) {
+ return;
+ }
+
+ m_statJobForStatusBarText = KIO::statDetails(m_model->rootItem().url(),
+ KIO::StatJob::SourceSide, KIO::StatRecursiveSize, KIO::HideProgressInfo);
+ connect(m_statJobForStatusBarText, &KJob::result,
+ this, &DolphinView::slotStatJobResult);
+ m_statJobForStatusBarText->start();
+ }
+}
+
+void DolphinView::emitStatusBarText(const int folderCount, const int fileCount,
+ KIO::filesize_t totalFileSize, const Selection selection)
+{
+ QString foldersText;
+ QString filesText;
+ QString summary;
+
+ if (selection == HasSelection) {
+ // At least 2 items are selected because the case of 1 selected item is handled in
+ // DolphinView::requestStatusBarText().
+ foldersText = i18ncp("@info:status", "1 Folder selected", "%1 Folders selected", folderCount);
+ filesText = i18ncp("@info:status", "1 File selected", "%1 Files selected", fileCount);
} else {
- calculateItemCount(fileCount, folderCount, totalFileSize);
foldersText = i18ncp("@info:status", "1 Folder", "%1 Folders", folderCount);
filesText = i18ncp("@info:status", "1 File", "%1 Files", fileCount);
}
} else {
summary = i18nc("@info:status", "0 Folders, 0 Files");
}
-
- return summary;
+ Q_EMIT statusBarTextChanged(summary);
}
QList<QAction*> DolphinView::versionControlActions(const KFileItemList& items) const
if (items.count() == 1 && GeneralSettings::renameInline()) {
const int index = m_model->index(items.first());
- m_view->editRole(index, "text");
- hideToolTip();
+ QMetaObject::Connection * const connection = new QMetaObject::Connection;
+ *connection = connect(m_view, &KItemListView::scrollingStopped, this, [=](){
+ QObject::disconnect(*connection);
+ delete connection;
+
+ m_view->editRole(index, "text");
+
+ hideToolTip();
+
+ connect(m_view, &DolphinItemListView::roleEditingFinished,
+ this, &DolphinView::slotRoleEditingFinished);
+ });
+ m_view->scrollToItem(index);
- connect(m_view, &DolphinItemListView::roleEditingFinished,
- this, &DolphinView::slotRoleEditingFinished);
} else {
KIO::RenameFileDialog* dialog = new KIO::RenameFileDialog(items, this);
connect(dialog, &KIO::RenameFileDialog::renamingFinished,
const QUrl& url = openItemAsFolderUrl(item);
if (!url.isEmpty()) { // Open folders in new tabs
- Q_EMIT tabRequested(url, DolphinTabWidget::AfterLastTab);
+ Q_EMIT tabRequested(url);
} else {
items.append(item);
}
const KFileItem& item = m_model->fileItem(index);
const QUrl& url = openItemAsFolderUrl(item);
if (!url.isEmpty()) {
- Q_EMIT tabRequested(url, DolphinTabWidget::AfterCurrentTab);
+ Q_EMIT tabRequested(url);
} else if (isTabsForFilesEnabled()) {
- Q_EMIT tabRequested(item.url(), DolphinTabWidget::AfterCurrentTab);
+ Q_EMIT tabRequested(item.url());
}
}
Q_EMIT selectionChanged(selectedItems());
}
+void DolphinView::slotStatJobResult(KJob *job)
+{
+ int folderCount = 0;
+ int fileCount = 0;
+ KIO::filesize_t totalFileSize = 0;
+ bool countFileSize = true;
+
+ const auto entry = static_cast<KIO::StatJob *>(job)->statResult();
+ if (entry.contains(KIO::UDSEntry::UDS_RECURSIVE_SIZE)) {
+ // We have a precomputed value.
+ totalFileSize = static_cast<KIO::filesize_t>(
+ entry.numberValue(KIO::UDSEntry::UDS_RECURSIVE_SIZE));
+ countFileSize = false;
+ }
+
+ const int itemCount = m_model->count();
+ for (int i = 0; i < itemCount; ++i) {
+ const KFileItem item = m_model->fileItem(i);
+ if (item.isDir()) {
+ ++folderCount;
+ } else {
+ ++fileCount;
+ if (countFileSize) {
+ totalFileSize += item.size();
+ }
+ }
+ }
+ emitStatusBarText(folderCount, fileCount, totalFileSize, NoSelection);
+}
+
void DolphinView::updateSortRole(const QByteArray& role)
{
ViewProperties props(viewPropertiesUrl());
#endif
}
-void DolphinView::calculateItemCount(int& fileCount,
- int& folderCount,
- KIO::filesize_t& totalFileSize) const
-{
- const int itemCount = m_model->count();
-
- bool countFileSize = true;
-
- if (!m_model->rootItem().url().isValid()) {
- return;
- }
-
- // In case we have a precomputed value
- const auto job = KIO::statDetails(m_model->rootItem().url(), KIO::StatJob::SourceSide, KIO::StatRecursiveSize, KIO::HideProgressInfo);
- job->exec();
- const auto entry = job->statResult();
- if (entry.contains(KIO::UDSEntry::UDS_RECURSIVE_SIZE)) {
- totalFileSize = static_cast<KIO::filesize_t>(entry.numberValue(KIO::UDSEntry::UDS_RECURSIVE_SIZE));
- countFileSize = false;
- }
-
- for (int i = 0; i < itemCount; ++i) {
- const KFileItem item = m_model->fileItem(i);
- if (item.isDir()) {
- ++folderCount;
- } else {
- ++fileCount;
- if (countFileSize) {
- totalFileSize += item.size();
- }
- }
- }
-}
-
void DolphinView::slotTwoClicksRenamingTimerTimeout()
{
const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager();
void DolphinView::slotDirectoryLoadingStarted()
{
- // We don't want the placeholder label to flicker while the folder is loading
- m_placeholderLabel->setVisible(false);
+ m_loading = true;
+ updatePlaceholderLabel();
// Disable the writestate temporary until it can be determined in a fast way
// in DolphinView::slotDirectoryLoadingCompleted()
void DolphinView::slotDirectoryLoadingCompleted()
{
+ m_loading = false;
+
// Update the view-state. This has to be done asynchronously
// because the view might not be in its final state yet.
QTimer::singleShot(0, this, &DolphinView::updateViewState);
updateWritableState();
}
+void DolphinView::slotDirectoryLoadingCanceled()
+{
+ m_loading = false;
+
+ updatePlaceholderLabel();
+
+ Q_EMIT directoryLoadingCanceled();
+}
+
void DolphinView::slotItemsChanged()
{
m_assureVisibleCurrentIndex = false;
disconnect(m_view, &DolphinItemListView::roleEditingFinished,
this, &DolphinView::slotRoleEditingFinished);
- if (index < 0 || index >= m_model->count()) {
+ const KFileItemList items = selectedItems();
+ if (items.count() != 1) {
return;
}
if (role == "text") {
- const KFileItem oldItem = m_model->fileItem(index);
- const QString newName = value.toString();
+ const KFileItem oldItem = items.first();
+ const EditResult retVal = value.value<EditResult>();
+ const QString newName = retVal.newName;
if (!newName.isEmpty() && newName != oldItem.text() && newName != QLatin1Char('.') && newName != QLatin1String("..")) {
const QUrl oldUrl = oldItem.url();
#endif
const bool newNameExistsAlready = (m_model->index(newUrl) >= 0);
- if (!newNameExistsAlready) {
+ if (!newNameExistsAlready && m_model->index(oldUrl) == index) {
// Only change the data in the model if no item with the new name
// is in the model yet. If there is an item with the new name
// already, calling KIO::CopyJob will open a dialog
// asking for a new name, and KFileItemModel will update the
// data when the dir lister signals that the file name has changed.
QHash<QByteArray, QVariant> data;
- data.insert(role, value);
+ data.insert(role, retVal.newName);
m_model->setData(index, data);
}
connect(job, &KJob::result, this, &DolphinView::slotRenamingResult);
}
}
+ if (retVal.direction != EditDone) {
+ const short indexShift = retVal.direction == EditNext ? 1 : -1;
+ m_container->controller()->selectionManager()->setSelected(index, 1, KItemListSelectionManager::Deselect);
+ m_container->controller()->selectionManager()->setSelected(index + indexShift, 1,
+ KItemListSelectionManager::Select);
+ renameSelectedItems();
+ }
}
}
Q_EMIT goUpRequested();
}
+void DolphinView::showLoadingPlaceholder()
+{
+ m_placeholderLabel->setText(i18n("Loading..."));
+ m_placeholderLabel->setVisible(true);
+}
+
void DolphinView::updatePlaceholderLabel()
{
+ m_showLoadingPlaceholderTimer->stop();
if (itemsCount() > 0) {
m_placeholderLabel->setVisible(false);
return;
}
+ if (m_loading) {
+ m_placeholderLabel->setVisible(false);
+ m_showLoadingPlaceholderTimer->start();
+ return;
+ }
+
if (!nameFilter().isEmpty()) {
m_placeholderLabel->setText(i18n("No items matching the filter"));
} else if (m_url.scheme() == QLatin1String("baloosearch") || m_url.scheme() == QLatin1String("filenamesearch")) {
m_placeholderLabel->setText(i18n("No items matching the search"));
- } else if (m_url.scheme() == QLatin1String("trash")) {
+ } else if (m_url.scheme() == QLatin1String("trash") && m_url.path() == QLatin1String("/")) {
m_placeholderLabel->setText(i18n("Trash is empty"));
} else if (m_url.scheme() == QLatin1String("tags")) {
m_placeholderLabel->setText(i18n("No tags"));
m_placeholderLabel->setText(i18n("No shared folders found"));
} else if (m_url.scheme() == QLatin1String("network")) {
m_placeholderLabel->setText(i18n("No relevant network resources found"));
- } else if (m_url.scheme() == QLatin1String("mtp")) {
+ } else if (m_url.scheme() == QLatin1String("mtp") && m_url.path() == QLatin1String("/")) {
m_placeholderLabel->setText(i18n("No MTP-compatible devices found"));
} else if (m_url.scheme() == QLatin1String("bluetooth")) {
m_placeholderLabel->setText(i18n("No Bluetooth devices found"));