#include <QTimer>
#include <QScrollBar>
-#include <kactioncollection.h>
-#include <kcolorscheme.h>
-#include <kdirlister.h>
-#include <kiconeffect.h>
-#include <kfileitem.h>
-#include <klocale.h>
-#include <kio/deletejob.h>
-#include <kio/netaccess.h>
-#include <kio/previewjob.h>
-#include <kjob.h>
-#include <kmenu.h>
-#include <kmessagebox.h>
-#include <kmimetyperesolver.h>
+#include <KActionCollection>
+#include <KColorScheme>
+#include <KDirLister>
+#include <KIconEffect>
+#include <KFileItem>
+#include <KFileItemListProperties>
+#include <KLocale>
+#include <KIO/DeleteJob>
+#include <KIO/NetAccess>
+#include <KIO/PreviewJob>
+#include <KJob>
+#include <KMenu>
+#include <KMessageBox>
#include <konq_fileitemcapabilities.h>
#include <konq_operations.h>
#include <konqmimedata.h>
-#include <ktoggleaction.h>
-#include <kurl.h>
+#include <KToggleAction>
+#include <KUrl>
#include "additionalinfoaccessor.h"
+#include "dolphindirlister.h"
#include "dolphinmodel.h"
#include "dolphincolumnviewcontainer.h"
#include "dolphinviewcontroller.h"
#include "zoomlevelinfo.h"
#include "dolphindetailsviewexpander.h"
-DolphinView::DolphinView(QWidget* parent,
- const KUrl& url,
- DolphinSortFilterProxyModel* proxyModel) :
+DolphinView::DolphinView(const KUrl& url, QWidget* parent) :
QWidget(parent),
m_active(true),
m_showPreview(false),
m_tabsForFiles(false),
m_isContextMenuOpen(false),
m_assureVisibleCurrentIndex(false),
+ m_expanderActive(false),
+ m_isFolderWritable(true),
m_mode(DolphinView::IconsView),
m_topLayout(0),
m_dolphinViewController(0),
m_viewModeController(0),
- m_viewAccessor(proxyModel),
+ m_viewAccessor(),
m_selectionChangedTimer(0),
- m_rootUrl(),
m_activeItemUrl(),
m_restoredContentsPosition(),
m_createdItemUrl(),
connect(m_dolphinViewController, SIGNAL(viewportEntered()),
this, SLOT(clearHoverInformation()));
connect(m_dolphinViewController, SIGNAL(urlChangeRequested(KUrl)),
- m_viewModeController, SLOT(setUrl(KUrl)));
-
- KDirLister* dirLister = m_viewAccessor.dirLister();
- connect(dirLister, SIGNAL(redirection(KUrl,KUrl)),
- this, SLOT(slotRedirection(KUrl,KUrl)));
- connect(dirLister, SIGNAL(completed()),
- this, SLOT(slotDirListerCompleted()));
- connect(dirLister, SIGNAL(refreshItems(const QList<QPair<KFileItem,KFileItem>>&)),
- this, SLOT(slotRefreshItems()));
+ this, SLOT(slotUrlChangeRequested(KUrl)));
// When a new item has been created by the "Create New..." menu, the item should
// get selected and it must be assured that the item will get visible. As the
m_active = active;
QColor color = KColorScheme(QPalette::Active, KColorScheme::View).background().color();
- if (active) {
- emitSelectionChangedSignal();
- } else {
+ if (!active) {
color.setAlpha(150);
}
if (active) {
m_viewAccessor.itemView()->setFocus();
emit activated();
+ emitSelectionChangedSignal();
+ emit writeStateChanged(m_isFolderWritable);
}
m_viewModeController->indicateActivationChange(active);
// be restored after reloading the directory
m_selectedItems = selectedItems();
+ const bool hasFocus = m_viewAccessor.itemView()->hasFocus();
deleteView();
const KUrl viewPropsUrl = rootUrl();
props.setViewMode(m_mode);
createView();
+ if (hasFocus) {
+ m_viewAccessor.itemView()->setFocus();
+ }
+
// the file item delegate has been recreated, apply the current
// additional information manually
const KFileItemDelegate::InformationList infoList = props.additionalInfo();
return m_viewAccessor.supportsCategorizedSorting();
}
-bool DolphinView::hasSelection() const
+KFileItem DolphinView::rootItem() const
{
- const QAbstractItemView* view = m_viewAccessor.itemView();
- return (view != 0) && view->selectionModel()->hasSelection();
+ return m_viewAccessor.dirLister()->rootItem();
}
-void DolphinView::markUrlsAsSelected(const QList<KUrl>& urls)
+KFileItemList DolphinView::items() const
{
- foreach (const KUrl& url, urls) {
- KFileItem item(KFileItem::Unknown, KFileItem::Unknown, url);
- m_selectedItems.append(item);
- }
+ return m_viewAccessor.dirLister()->items();
}
KFileItemList DolphinView::selectedItems() const
return view->selectionModel()->selectedIndexes().count();
}
-QItemSelectionModel* DolphinView::selectionModel() const
+void DolphinView::markUrlsAsSelected(const QList<KUrl>& urls)
{
- return m_viewAccessor.itemView()->selectionModel();
+ foreach (const KUrl& url, urls) {
+ KFileItem item(KFileItem::Unknown, KFileItem::Unknown, url);
+ m_selectedItems.append(item);
+ }
+}
+
+void DolphinView::setItemSelectionEnabled(const QRegExp& pattern, bool enabled)
+{
+ const QItemSelection matchingIndexes = childrenMatchingPattern(QModelIndex(), pattern);
+ const QItemSelectionModel::SelectionFlags command = enabled
+ ? QItemSelectionModel::Select
+ : QItemSelectionModel::Deselect;
+ m_viewAccessor.itemView()->selectionModel()->select(matchingIndexes, command);
}
void DolphinView::setZoomLevel(int level)
m_viewModeController->setNameFilter(nameFilter);
}
+QString DolphinView::nameFilter() const
+{
+ return m_viewModeController->nameFilter();
+}
+
void DolphinView::calculateItemCount(int& fileCount,
int& folderCount,
KIO::filesize_t& totalFileSize) const
return;
}
- // The selection model might change in the case of the column view. Disconnect
- // from the current selection model and reconnect later after the URL switch.
const bool hadSelection = hasSelection();
- QAbstractItemView* view = m_viewAccessor.itemView();
- disconnect(view->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
- this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection)));
+
+ // The selection model and directory lister might change in the case of the column view:
+ disconnectViewAccessor();
m_newFileNames.clear();
m_viewModeController->setUrl(url); // emits urlChanged, which we forward
m_viewAccessor.prepareUrlChange(url);
applyViewProperties();
- loadDirectory(url);
// When changing the URL there is no need to keep the version
// data of the previous URL.
m_viewAccessor.dirModel()->clearVersionData();
- emit startedPathLoading(url);
+ // Reconnect to the (probably) new selection model and directory lister
+ connectViewAccessor();
+ loadDirectory(url);
- // Reconnect to the (probably) new selection model
- view = m_viewAccessor.itemView();
- connect(view->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
- this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection)));
if (hadSelection || hasSelection()) {
emitSelectionChangedSignal();
}
void DolphinView::invertSelection()
{
- QItemSelectionModel* selectionModel = m_viewAccessor.itemView()->selectionModel();
- const QAbstractItemModel* itemModel = selectionModel->model();
-
- const QModelIndex topLeft = itemModel->index(0, 0);
- const QModelIndex bottomRight = itemModel->index(itemModel->rowCount() - 1,
- itemModel->columnCount() - 1);
+ // Implementation note: Using selectionModel->select(selection, QItemSelectionModel::Toggle) does not
+ // work, as QItemSelectionModel::hasSelection() provides invalid values in this case. This might be a Qt-issue -
+ // when changing the implementation with an updated Qt-version don't forget to run the Dolphin-unit-tests that
+ // verify this usecase.
+ const KFileItemList selItems = selectedItems();
+ clearSelection();
+
+ QItemSelection invertedSelection;
+ foreach (const KFileItem& item, items()) {
+ if (!selItems.contains(item)) {
+ const QModelIndex index = m_viewAccessor.proxyModel()->mapFromSource(m_viewAccessor.dirModel()->indexForItem(item));
+ invertedSelection.select(index, index);
+ }
+ }
- const QItemSelection selection(topLeft, bottomRight);
- selectionModel->select(selection, QItemSelectionModel::Toggle);
+ QItemSelectionModel* selectionModel = m_viewAccessor.itemView()->selectionModel();
+ selectionModel->select(invertedSelection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current);
}
void DolphinView::clearSelection()
emit categorizedSortingChanged();
}
-void DolphinView::toggleSortOrder()
-{
- const Qt::SortOrder order = (sortOrder() == Qt::AscendingOrder) ?
- Qt::DescendingOrder :
- Qt::AscendingOrder;
- setSortOrder(order);
-}
-
-void DolphinView::toggleSortFoldersFirst()
-{
- setSortFoldersFirst(!sortFoldersFirst());
-}
-
-void DolphinView::toggleAdditionalInfo(QAction* action)
-{
- const KFileItemDelegate::Information info =
- static_cast<KFileItemDelegate::Information>(action->data().toInt());
-
- KFileItemDelegate::InformationList list = additionalInfo();
-
- const bool show = action->isChecked();
-
- const int index = list.indexOf(info);
- const bool containsInfo = (index >= 0);
- if (show && !containsInfo) {
- list.append(info);
- setAdditionalInfo(list);
- } else if (!show && containsInfo) {
- list.removeAt(index);
- setAdditionalInfo(list);
- Q_ASSERT(list.indexOf(info) < 0);
- }
-}
-
void DolphinView::mouseReleaseEvent(QMouseEvent* event)
{
QWidget::mouseReleaseEvent(event);
void DolphinView::restoreState(QDataStream& stream)
{
- // current item
+ // Restore the URL of the current item that had the keyboard focus
stream >> m_activeItemUrl;
- // view position
+ // Restore the root URL
+ KUrl rootUrl;
+ stream >> rootUrl;
+ m_viewAccessor.setRootUrl(rootUrl);
+
+ // Restore the view position
stream >> m_restoredContentsPosition;
- // expanded folders (only relevant for the details view - will be ignored by the view in other view modes)
+ // Restore expanded folders (only relevant for the details view - will be ignored by the view in other view modes)
QSet<KUrl> urlsToExpand;
stream >> urlsToExpand;
const DolphinDetailsViewExpander* expander = m_viewAccessor.setExpandedUrls(urlsToExpand);
void DolphinView::saveState(QDataStream& stream)
{
- // current item
+ // Save the URL of the current item that has the keyboard focus
KFileItem currentItem;
const QAbstractItemView* view = m_viewAccessor.itemView();
currentItem = m_viewAccessor.dirModel()->itemForIndex(dirModelIndex);
}
- KUrl currentUrl;
+ KUrl currentItemUrl;
if (!currentItem.isNull()) {
- currentUrl = currentItem.url();
+ currentItemUrl = currentItem.url();
}
- stream << currentUrl;
+ stream << currentItemUrl;
+
+ // Save the root URL
+ stream << m_viewAccessor.rootUrl();
- // view position
+ // Save view position
const int x = view->horizontalScrollBar()->value();
const int y = view->verticalScrollBar()->value();
stream << QPoint(x, y);
- // expanded folders (only relevant for the details view - the set will be empty in other view modes)
+ // Save expanded folders (only relevant for the details view - the set will be empty in other view modes)
stream << m_viewAccessor.expandedUrls();
}
+bool DolphinView::hasSelection() const
+{
+ const QAbstractItemView* view = m_viewAccessor.itemView();
+ return (view != 0) && view->selectionModel()->hasSelection();
+}
+
void DolphinView::observeCreatedItem(const KUrl& url)
{
m_createdItemUrl = url;
m_createdItemUrl = KUrl();
}
+void DolphinView::slotRedirection(const KUrl& oldUrl, const KUrl& newUrl)
+{
+ if (oldUrl.equals(url(), KUrl::CompareWithoutTrailingSlash)) {
+ emit redirection(oldUrl, newUrl);
+ m_viewModeController->redirectToUrl(newUrl); // #186947
+ }
+}
+
+void DolphinView::restoreContentsPosition()
+{
+ if (!m_restoredContentsPosition.isNull()) {
+ const int x = m_restoredContentsPosition.x();
+ const int y = m_restoredContentsPosition.y();
+ m_restoredContentsPosition = QPoint();
+
+ QAbstractItemView* view = m_viewAccessor.itemView();
+ Q_ASSERT(view != 0);
+ view->horizontalScrollBar()->setValue(x);
+ view->verticalScrollBar()->setValue(y);
+ }
+}
+
+void DolphinView::slotUrlChangeRequested(const KUrl& url)
+{
+ m_viewModeController->setUrl(url);
+ updateWritableState();
+}
+
void DolphinView::showHoverInformation(const KFileItem& item)
{
emit requestItemInfo(item);
}
}
+void DolphinView::slotDirListerStarted(const KUrl& url)
+{
+ // Disable the writestate temporary until it can be determined in a fast way
+ // in DolphinView::slotDirListerCompleted()
+ if (m_isFolderWritable) {
+ m_isFolderWritable = false;
+ emit writeStateChanged(m_isFolderWritable);
+ }
+
+ emit startedPathLoading(url);
+}
+
void DolphinView::slotDirListerCompleted()
{
if (!m_expanderActive) {
m_newFileNames.clear();
}
+
+ updateWritableState();
}
void DolphinView::slotLoadingCompleted()
foreach(const KFileItem& item, m_selectedItems) {
url = item.url().upUrl();
if (baseUrl.equals(url, KUrl::CompareWithoutTrailingSlash)) {
- QModelIndex index = m_viewAccessor.proxyModel()->mapFromSource(m_viewAccessor.dirModel()->indexForItem(item));
+ const QModelIndex index = m_viewAccessor.proxyModel()->mapFromSource(m_viewAccessor.dirModel()->indexForItem(item));
newSelection.select(index, index);
}
}
if (m_viewAccessor.itemView() == 0) {
createView();
}
+
Q_ASSERT(m_viewAccessor.itemView() != 0);
Q_ASSERT(m_viewAccessor.itemDelegate() != 0);
const int zoomLevel = ZoomLevelInfo::zoomLevelForIconSize(view->iconSize());
m_viewModeController->setZoomLevel(zoomLevel);
- connect(view->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
- this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection)));
+ connectViewAccessor();
setFocusProxy(m_viewAccessor.layoutTarget());
m_topLayout->insertWidget(1, m_viewAccessor.layoutTarget());
m_dolphinViewController->setItemView(0);
if (view != 0) {
- if (view->selectionModel() != 0) {
- disconnect(view->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
- this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection)));
+ disconnectViewAccessor();
+
+ if (hasFocus()) {
+ // It's important to set the keyboard focus to the parent
+ // before deleting the view: Otherwise when having a split
+ // view the other view will get the focus and will request
+ // an activation (see DolphinView::eventFilter()).
+ setFocusProxy(0);
+ setFocus();
}
- // It's important to set the keyboard focus to the parent
- // before deleting the view: Otherwise when having a split
- // view the other view will get the focus and will request
- // an activation (see DolphinView::eventFilter()).
- setFocusProxy(0);
- setFocus();
-
m_viewModeController->disconnect(view);
m_viewAccessor.deleteView();
}
}
-DolphinView::ViewAccessor::ViewAccessor(DolphinSortFilterProxyModel* proxyModel) :
+QItemSelection DolphinView::childrenMatchingPattern(const QModelIndex& parent, const QRegExp& pattern) const
+{
+ QItemSelection matchingIndexes;
+ const DolphinSortFilterProxyModel* proxyModel = m_viewAccessor.proxyModel();
+ const DolphinModel* dolphinModel = m_viewAccessor.dirModel();
+
+ const int rowCount = proxyModel->rowCount(parent);
+
+ for (int row = 0; row < rowCount; ++row) {
+ QModelIndex index = proxyModel->index(row, 0, parent);
+ QModelIndex sourceIndex = proxyModel->mapToSource(index);
+
+ if (sourceIndex.isValid() && pattern.exactMatch(dolphinModel->data(sourceIndex).toString())) {
+ matchingIndexes += QItemSelectionRange(index);
+ }
+
+ if (proxyModel->hasChildren(index)) {
+ matchingIndexes += childrenMatchingPattern(index, pattern);
+ }
+ }
+
+ return matchingIndexes;
+}
+
+void DolphinView::connectViewAccessor()
+{
+ KDirLister* dirLister = m_viewAccessor.dirLister();
+ connect(dirLister, SIGNAL(redirection(KUrl,KUrl)), this, SLOT(slotRedirection(KUrl,KUrl)));
+ connect(dirLister, SIGNAL(started(KUrl)), this, SLOT(slotDirListerStarted(KUrl)));
+ connect(dirLister, SIGNAL(completed()), this, SLOT(slotDirListerCompleted()));
+ connect(dirLister, SIGNAL(refreshItems(const QList<QPair<KFileItem,KFileItem>>&)),
+ this, SLOT(slotRefreshItems()));
+
+ connect(dirLister, SIGNAL(clear()), this, SIGNAL(itemCountChanged()));
+ connect(dirLister, SIGNAL(newItems(KFileItemList)), this, SIGNAL(itemCountChanged()));
+ connect(dirLister, SIGNAL(infoMessage(const QString&)), this, SIGNAL(infoMessage(const QString&)));
+ connect(dirLister, SIGNAL(errorMessage(const QString&)), this, SIGNAL(infoMessage(const QString&)));
+ connect(dirLister, SIGNAL(percent(int)), this, SIGNAL(pathLoadingProgress(int)));
+ connect(dirLister, SIGNAL(urlIsFileError(const KUrl&)), this, SIGNAL(urlIsFileError(const KUrl&)));
+ connect(dirLister, SIGNAL(itemsDeleted(const KFileItemList&)), this, SIGNAL(itemCountChanged()));
+
+ QAbstractItemView* view = m_viewAccessor.itemView();
+ connect(view->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
+ this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection)));
+}
+
+void DolphinView::disconnectViewAccessor()
+{
+ KDirLister* dirLister = m_viewAccessor.dirLister();
+ disconnect(dirLister, SIGNAL(redirection(KUrl,KUrl)), this, SLOT(slotRedirection(KUrl,KUrl)));
+ disconnect(dirLister, SIGNAL(started(KUrl)), this, SLOT(slotDirListerStarted(KUrl)));
+ disconnect(dirLister, SIGNAL(completed()), this, SLOT(slotDirListerCompleted()));
+ disconnect(dirLister, SIGNAL(refreshItems(const QList<QPair<KFileItem,KFileItem>>&)),
+ this, SLOT(slotRefreshItems()));
+
+ disconnect(dirLister, SIGNAL(clear()), this, SIGNAL(itemCountChanged()));
+ disconnect(dirLister, SIGNAL(newItems(KFileItemList)), this, SIGNAL(itemCountChanged()));
+ disconnect(dirLister, SIGNAL(infoMessage(const QString&)), this, SIGNAL(infoMessage(const QString&)));
+ disconnect(dirLister, SIGNAL(errorMessage(const QString&)), this, SIGNAL(errorMessage(const QString&)));
+ disconnect(dirLister, SIGNAL(percent(int)), this, SIGNAL(pathLoadingProgress(int)));
+ disconnect(dirLister, SIGNAL(urlIsFileError(const KUrl&)), this, SIGNAL(urlIsFileError(const KUrl&)));
+ disconnect(dirLister, SIGNAL(itemsDeleted(const KFileItemList&)), this, SIGNAL(itemCountChanged()));
+
+ QAbstractItemView* view = m_viewAccessor.itemView();
+ disconnect(view->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
+ this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection)));
+}
+
+void DolphinView::updateWritableState()
+{
+ const bool wasFolderWritable = m_isFolderWritable;
+ m_isFolderWritable = true;
+
+ const KFileItem item = m_viewAccessor.dirLister()->rootItem();
+ if (!item.isNull()) {
+ KFileItemListProperties capabilities(KFileItemList() << item);
+ m_isFolderWritable = capabilities.supportsWriting();
+ }
+ if (m_isFolderWritable != wasFolderWritable) {
+ emit writeStateChanged(m_isFolderWritable);
+ }
+}
+
+DolphinView::ViewAccessor::ViewAccessor() :
+ m_rootUrl(),
m_iconsView(0),
m_detailsView(0),
m_columnsContainer(0),
- m_proxyModel(proxyModel),
+ m_dolphinModel(0),
+ m_proxyModel(0),
m_dragSource(0)
{
+ DolphinDirLister* dirLister = new DolphinDirLister();
+ dirLister->setAutoUpdate(true);
+ dirLister->setDelayedMimeTypes(true);
+
+ m_dolphinModel = new DolphinModel();
+ m_dolphinModel->setDirLister(dirLister); // m_dolphinModel takes ownership of dirLister
+ m_dolphinModel->setDropsAllowed(DolphinModel::DropOnDirectory);
+
+ m_proxyModel = new DolphinSortFilterProxyModel();
+ m_proxyModel->setSourceModel(m_dolphinModel);
+ m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
}
DolphinView::ViewAccessor::~ViewAccessor()
{
+ delete m_proxyModel;
+ m_proxyModel = 0;
+
+ delete m_dolphinModel;
+ m_dolphinModel = 0;
+
delete m_dragSource;
m_dragSource = 0;
}
m_columnsContainer = new DolphinColumnViewContainer(parent,
dolphinViewController,
viewModeController);
+ if (!m_rootUrl.isEmpty() && m_rootUrl.isParentOf(viewModeController->url())) {
+ // The column-view must show several columns starting with m_rootUrl as
+ // first column and viewModeController->url() as last column.
+ m_columnsContainer->showColumn(m_rootUrl);
+ m_columnsContainer->showColumn(viewModeController->url());
+ }
break;
default:
}
}
-
void DolphinView::ViewAccessor::prepareUrlChange(const KUrl& url)
{
if (m_columnsContainer != 0) {
return itemView();
}
+void DolphinView::ViewAccessor::setRootUrl(const KUrl& rootUrl)
+{
+ m_rootUrl = rootUrl;
+}
+
KUrl DolphinView::ViewAccessor::rootUrl() const
{
- return (m_columnsContainer != 0) ? m_columnsContainer->rootUrl() : KUrl();
+ return (m_columnsContainer != 0) ? m_columnsContainer->rootUrl() : m_rootUrl;
}
bool DolphinView::ViewAccessor::supportsCategorizedSorting() const
return (m_detailsView != 0) && m_detailsView->itemsExpandable();
}
-
QSet<KUrl> DolphinView::ViewAccessor::expandedUrls() const
{
if (m_detailsView != 0) {
return dirModel()->dirLister();
}
-void DolphinView::slotRedirection(const KUrl& oldUrl, const KUrl& newUrl)
-{
- if (oldUrl.equals(url(), KUrl::CompareWithoutTrailingSlash)) {
- emit redirection(oldUrl, newUrl);
- m_viewModeController->redirectToUrl(newUrl); // #186947
- }
-}
-
-void DolphinView::restoreContentsPosition()
-{
- if (!m_restoredContentsPosition.isNull()) {
- const int x = m_restoredContentsPosition.x();
- const int y = m_restoredContentsPosition.y();
- m_restoredContentsPosition = QPoint();
-
- QAbstractItemView* view = m_viewAccessor.itemView();
- Q_ASSERT(view != 0);
- view->horizontalScrollBar()->setValue(x);
- view->verticalScrollBar()->setValue(y);
- }
-}
-
#include "dolphinview.moc"