+ ViewProperties props(viewPropertiesUrl());
+
+ QPointer<QMenu> menu = new QMenu(QApplication::activeWindow());
+
+ KItemListView* view = m_container->controller()->view();
+ const QList<QByteArray> visibleRolesSet = view->visibleRoles();
+
+ bool indexingEnabled = false;
+#ifdef HAVE_BALOO
+ Baloo::IndexerConfig config;
+ indexingEnabled = config.fileIndexingEnabled();
+#endif
+
+ QString groupName;
+ QMenu* groupMenu = nullptr;
+
+ // Add all roles to the menu that can be shown or hidden by the user
+ const QList<KFileItemModel::RoleInfo> rolesInfo = KFileItemModel::rolesInformation();
+ for (const KFileItemModel::RoleInfo& info : rolesInfo) {
+ if (info.role == "text") {
+ // It should not be possible to hide the "text" role
+ continue;
+ }
+
+ const QString text = m_model->roleDescription(info.role);
+ QAction* action = nullptr;
+ if (info.group.isEmpty()) {
+ action = menu->addAction(text);
+ } else {
+ if (!groupMenu || info.group != groupName) {
+ groupName = info.group;
+ groupMenu = menu->addMenu(groupName);
+ }
+
+ action = groupMenu->addAction(text);
+ }
+
+ action->setCheckable(true);
+ action->setChecked(visibleRolesSet.contains(info.role));
+ action->setData(info.role);
+
+ const bool enable = (!info.requiresBaloo && !info.requiresIndexer) ||
+ (info.requiresBaloo) ||
+ (info.requiresIndexer && indexingEnabled);
+ action->setEnabled(enable);
+ }
+
+ menu->addSeparator();
+
+ QActionGroup* widthsGroup = new QActionGroup(menu);
+ const bool autoColumnWidths = props.headerColumnWidths().isEmpty();
+
+ QAction* toggleLeadingPaddingAction = menu->addAction(i18nc("@action:inmenu", "Leading Column Padding"));
+ toggleLeadingPaddingAction->setCheckable(true);
+ toggleLeadingPaddingAction->setChecked(view->header()->leadingPadding() > 0);
+
+ QAction* autoAdjustWidthsAction = menu->addAction(i18nc("@action:inmenu", "Automatic Column Widths"));
+ autoAdjustWidthsAction->setCheckable(true);
+ autoAdjustWidthsAction->setChecked(autoColumnWidths);
+ autoAdjustWidthsAction->setActionGroup(widthsGroup);
+
+ QAction* customWidthsAction = menu->addAction(i18nc("@action:inmenu", "Custom Column Widths"));
+ customWidthsAction->setCheckable(true);
+ customWidthsAction->setChecked(!autoColumnWidths);
+ customWidthsAction->setActionGroup(widthsGroup);
+
+ QAction* action = menu->exec(pos.toPoint());
+ if (menu && action) {
+ KItemListHeader* header = view->header();
+
+ if (action == autoAdjustWidthsAction) {
+ // Clear the column-widths from the viewproperties and turn on
+ // the automatic resizing of the columns
+ props.setHeaderColumnWidths(QList<int>());
+ header->setAutomaticColumnResizing(true);
+ } else if (action == customWidthsAction) {
+ // Apply the current column-widths as custom column-widths and turn
+ // off the automatic resizing of the columns
+ QList<int> columnWidths;
+ const auto visibleRoles = view->visibleRoles();
+ columnWidths.reserve(visibleRoles.count());
+ for (const QByteArray& role : visibleRoles) {
+ columnWidths.append(header->columnWidth(role));
+ }
+ props.setHeaderColumnWidths(columnWidths);
+ header->setAutomaticColumnResizing(false);
+ } else if (action == toggleLeadingPaddingAction) {
+ header->setLeadingPadding(toggleLeadingPaddingAction->isChecked() ? 20 : 0);
+ } else {
+ // Show or hide the selected role
+ const QByteArray selectedRole = action->data().toByteArray();
+
+ QList<QByteArray> visibleRoles = view->visibleRoles();
+ if (action->isChecked()) {
+ visibleRoles.append(selectedRole);
+ } else {
+ visibleRoles.removeOne(selectedRole);
+ }
+
+ view->setVisibleRoles(visibleRoles);
+ props.setVisibleRoles(visibleRoles);
+
+ QList<int> columnWidths;
+ if (!header->automaticColumnResizing()) {
+ const auto visibleRoles = view->visibleRoles();
+ columnWidths.reserve(visibleRoles.count());
+ for (const QByteArray& role : visibleRoles) {
+ columnWidths.append(header->columnWidth(role));
+ }
+ }
+ props.setHeaderColumnWidths(columnWidths);
+ }
+ }
+
+ delete menu;
+}
+
+void DolphinView::slotHeaderColumnWidthChangeFinished(const QByteArray& role, qreal current)
+{
+ const QList<QByteArray> visibleRoles = m_view->visibleRoles();
+
+ ViewProperties props(viewPropertiesUrl());
+ QList<int> columnWidths = props.headerColumnWidths();
+ if (columnWidths.count() != visibleRoles.count()) {
+ columnWidths.clear();
+ columnWidths.reserve(visibleRoles.count());
+ const KItemListHeader* header = m_view->header();
+ for (const QByteArray& role : visibleRoles) {
+ const int width = header->columnWidth(role);
+ columnWidths.append(width);
+ }
+ }
+
+ const int roleIndex = visibleRoles.indexOf(role);
+ Q_ASSERT(roleIndex >= 0 && roleIndex < columnWidths.count());
+ columnWidths[roleIndex] = current;
+
+ props.setHeaderColumnWidths(columnWidths);
+}
+
+void DolphinView::slotLeadingPaddingWidthChanged(qreal width)
+{
+ ViewProperties props(viewPropertiesUrl());
+ DetailsModeSettings::setLeadingPadding(int(width));
+ m_view->writeSettings();
+}
+
+void DolphinView::slotItemHovered(int index)
+{
+ const KFileItem item = m_model->fileItem(index);
+
+ if (GeneralSettings::showToolTips() && !m_dragging) {
+ QRectF itemRect = m_container->controller()->view()->itemContextRect(index);
+ const QPoint pos = m_container->mapToGlobal(itemRect.topLeft().toPoint());
+ itemRect.moveTo(pos);
+
+#ifdef HAVE_BALOO
+ m_toolTipManager->showToolTip(item, itemRect, nativeParentWidget()->windowHandle());
+#endif
+ }
+
+ Q_EMIT requestItemInfo(item);
+}
+
+void DolphinView::slotItemUnhovered(int index)
+{
+ Q_UNUSED(index)
+ hideToolTip();
+ Q_EMIT requestItemInfo(KFileItem());
+}
+
+void DolphinView::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event)
+{
+ QUrl destUrl;
+ KFileItem destItem = m_model->fileItem(index);
+ if (destItem.isNull() || (!destItem.isDir() && !destItem.isDesktopFile())) {
+ // Use the URL of the view as drop target if the item is no directory
+ // or desktop-file
+ destItem = m_model->rootItem();
+ destUrl = url();
+ } else {
+ // The item represents a directory or desktop-file
+ destUrl = destItem.mostLocalUrl();
+ }
+
+ QDropEvent dropEvent(event->pos().toPoint(),
+ event->possibleActions(),
+ event->mimeData(),
+ event->buttons(),
+ event->modifiers());
+ dropUrls(destUrl, &dropEvent, this);
+
+ setActive(true);
+}
+
+void DolphinView::dropUrls(const QUrl &destUrl, QDropEvent *dropEvent, QWidget *dropWidget)
+{
+ KIO::DropJob* job = DragAndDropHelper::dropUrls(destUrl, dropEvent, dropWidget);
+
+ if (job) {
+ connect(job, &KIO::DropJob::result, this, &DolphinView::slotJobResult);
+
+ if (destUrl == url()) {
+ // Mark the dropped urls as selected.
+ m_clearSelectionBeforeSelectingNewItems = true;
+ m_markFirstNewlySelectedItemAsCurrent = true;
+ connect(job, &KIO::DropJob::itemCreated, this, &DolphinView::slotItemCreated);
+ }
+ }
+}
+
+void DolphinView::slotModelChanged(KItemModelBase* current, KItemModelBase* previous)
+{
+ if (previous != nullptr) {
+ Q_ASSERT(qobject_cast<KFileItemModel*>(previous));
+ KFileItemModel* fileItemModel = static_cast<KFileItemModel*>(previous);
+ disconnect(fileItemModel, &KFileItemModel::directoryLoadingCompleted, this, &DolphinView::slotDirectoryLoadingCompleted);
+ m_versionControlObserver->setModel(nullptr);
+ }
+
+ if (current) {
+ Q_ASSERT(qobject_cast<KFileItemModel*>(current));
+ KFileItemModel* fileItemModel = static_cast<KFileItemModel*>(current);
+ connect(fileItemModel, &KFileItemModel::directoryLoadingCompleted, this, &DolphinView::slotDirectoryLoadingCompleted);
+ m_versionControlObserver->setModel(fileItemModel);
+ }
+}
+
+void DolphinView::slotMouseButtonPressed(int itemIndex, Qt::MouseButtons buttons)
+{
+ Q_UNUSED(itemIndex)
+
+ hideToolTip();
+
+ if (buttons & Qt::BackButton) {
+ Q_EMIT goBackRequested();
+ } else if (buttons & Qt::ForwardButton) {
+ Q_EMIT goForwardRequested();
+ }
+}
+
+void DolphinView::slotSelectedItemTextPressed(int index)
+{
+ if (GeneralSettings::renameInline() && !m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) {
+ const KFileItem item = m_model->fileItem(index);
+ const KFileItemListProperties capabilities(KFileItemList() << item);
+ if (capabilities.supportsMoving()) {
+ m_twoClicksRenamingItemUrl = item.url();
+ m_twoClicksRenamingTimer->start(QApplication::doubleClickInterval());
+ }
+ }
+}
+
+void DolphinView::slotCopyingDone(KIO::Job *, const QUrl &, const QUrl &to)
+{
+ slotItemCreated(to);
+}
+
+void DolphinView::slotItemCreated(const QUrl& url)
+{
+ if (m_markFirstNewlySelectedItemAsCurrent) {
+ markUrlAsCurrent(url);
+ m_markFirstNewlySelectedItemAsCurrent = false;
+ }
+ m_selectedUrls << url;
+}
+
+void DolphinView::slotJobResult(KJob *job)
+{
+ if (job->error()) {
+ Q_EMIT errorMessage(job->errorString());
+ }
+ if (!m_selectedUrls.isEmpty()) {
+ m_selectedUrls = KDirModel::simplifiedUrlList(m_selectedUrls);
+ }
+}
+
+void DolphinView::slotSelectionChanged(const KItemSet& current, const KItemSet& previous)
+{
+ const int currentCount = current.count();
+ const int previousCount = previous.count();
+ const bool selectionStateChanged = (currentCount == 0 && previousCount > 0) ||
+ (currentCount > 0 && previousCount == 0);
+
+ // If nothing has been selected before and something got selected (or if something
+ // was selected before and now nothing is selected) the selectionChangedSignal must
+ // be emitted asynchronously as fast as possible to update the edit-actions.
+ m_selectionChangedTimer->setInterval(selectionStateChanged ? 0 : 300);
+ m_selectionChangedTimer->start();
+}
+
+void DolphinView::emitSelectionChangedSignal()
+{
+ m_selectionChangedTimer->stop();
+ 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());
+ props.setSortRole(role);
+
+ KItemModelBase* model = m_container->controller()->model();
+ model->setSortRole(role);
+
+ Q_EMIT sortRoleChanged(role);
+}
+
+void DolphinView::updateSortOrder(Qt::SortOrder order)
+{
+ ViewProperties props(viewPropertiesUrl());
+ props.setSortOrder(order);
+
+ m_model->setSortOrder(order);
+
+ Q_EMIT sortOrderChanged(order);
+}
+
+void DolphinView::updateSortFoldersFirst(bool foldersFirst)
+{
+ ViewProperties props(viewPropertiesUrl());
+ props.setSortFoldersFirst(foldersFirst);
+
+ m_model->setSortDirectoriesFirst(foldersFirst);
+
+ Q_EMIT sortFoldersFirstChanged(foldersFirst);
+}
+
+void DolphinView::updateSortHiddenLast(bool hiddenLast)
+{
+ ViewProperties props(viewPropertiesUrl());
+ props.setSortHiddenLast(hiddenLast);
+
+ m_model->setSortHiddenLast(hiddenLast);
+
+ Q_EMIT sortHiddenLastChanged(hiddenLast);
+}
+
+
+QPair<bool, QString> DolphinView::pasteInfo() const
+{
+ const QMimeData *mimeData = QApplication::clipboard()->mimeData();
+ QPair<bool, QString> info;
+ info.second = KIO::pasteActionText(mimeData, &info.first, rootItem());
+ return info;
+}
+
+void DolphinView::setTabsForFilesEnabled(bool tabsForFiles)
+{
+ m_tabsForFiles = tabsForFiles;
+}
+
+bool DolphinView::isTabsForFilesEnabled() const
+{
+ return m_tabsForFiles;
+}
+
+bool DolphinView::itemsExpandable() const
+{
+ return m_mode == DetailsView;
+}
+
+void DolphinView::restoreState(QDataStream& stream)
+{
+ // Read the version number of the view state and check if the version is supported.
+ quint32 version = 0;
+ stream >> version;
+ if (version != 1) {
+ // The version of the view state isn't supported, we can't restore it.
+ return;
+ }
+
+ // Restore the current item that had the keyboard focus
+ stream >> m_currentItemUrl;
+
+ // Restore the previously selected items
+ stream >> m_selectedUrls;