X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/77a576335ca8ef6a8921b766c43b07b6eb65e80f..a26eda8d3809159615a5fe95b1cf8eb154b1374b:/src/dolphinview.cpp diff --git a/src/dolphinview.cpp b/src/dolphinview.cpp index 53d8acae5..51e2a0a57 100644 --- a/src/dolphinview.cpp +++ b/src/dolphinview.cpp @@ -19,6 +19,8 @@ ***************************************************************************/ #include "dolphinview.h" +#include +#include #include #include @@ -27,14 +29,15 @@ #include #include #include +#include #include #include #include #include #include +#include #include -#include #include #include #include @@ -59,6 +62,7 @@ DolphinView::DolphinView(QWidget* parent, DolphinSortFilterProxyModel* proxyModel) : QWidget(parent), m_active(true), + m_showPreview(false), m_loadingDirectory(false), m_storedCategorizedSorting(false), m_mode(DolphinView::IconsView), @@ -88,16 +92,26 @@ DolphinView::DolphinView(QWidget* parent, m_controller = new DolphinController(this); m_controller->setUrl(url); + + // Receiver of the DolphinView signal 'urlChanged()' don't need + // to care whether the internal controller changed the URL already or whether + // the controller just requested an URL change and will be updated later. + // In both cases the URL has been changed: connect(m_controller, SIGNAL(urlChanged(const KUrl&)), this, SIGNAL(urlChanged(const KUrl&))); + connect(m_controller, SIGNAL(requestUrlChange(const KUrl&)), + this, SIGNAL(urlChanged(const KUrl&))); + connect(m_controller, SIGNAL(requestContextMenu(const QPoint&)), this, SLOT(openContextMenu(const QPoint&))); - connect(m_controller, SIGNAL(urlsDropped(const KUrl::List&, const KUrl&, const QModelIndex&, QWidget*)), - this, SLOT(dropUrls(const KUrl::List&, const KUrl&, const QModelIndex&, QWidget*))); + connect(m_controller, SIGNAL(urlsDropped(const KUrl::List&, const KUrl&, const KFileItem&)), + this, SLOT(dropUrls(const KUrl::List&, const KUrl&, const KFileItem&))); connect(m_controller, SIGNAL(sortingChanged(DolphinView::Sorting)), this, SLOT(updateSorting(DolphinView::Sorting))); connect(m_controller, SIGNAL(sortOrderChanged(Qt::SortOrder)), this, SLOT(updateSortOrder(Qt::SortOrder))); + connect(m_controller, SIGNAL(additionalInfoChanged(const KFileItemDelegate::InformationList&)), + this, SLOT(updateAdditionalInfo(const KFileItemDelegate::InformationList&))); connect(m_controller, SIGNAL(itemTriggered(const KFileItem&)), this, SLOT(triggerItem(const KFileItem&))); connect(m_controller, SIGNAL(activated()), @@ -133,12 +147,29 @@ void DolphinView::setActive(bool active) m_active = active; - updateViewportColor(); + QColor color = KColorScheme(QPalette::Active, KColorScheme::View).background().color(); + if (active) { + // TODO: emitting urlChanged() is a hack, as the URL hasn't really changed. It + // bypasses the problem when having a split view and changing the active view to + // update the some URL dependent states. A nicer approach should be no big deal... + emit urlChanged(url()); + emit selectionChanged(selectedItems()); + } else { + color.setAlpha(150); + } + + QWidget* viewport = itemView()->viewport(); + QPalette palette; + palette.setColor(viewport->backgroundRole(), color); + viewport->setPalette(palette); + update(); if (active) { emit activated(); } + + m_controller->indicateActivationChange(active); } bool DolphinView::isActive() const @@ -159,16 +190,28 @@ void DolphinView::setMode(Mode mode) // to go back to the root URL of the column view automatically. // Otherwise there it would not be possible to turn off the column view // without focusing the first column. - setUrl(m_dirLister->url()); - m_controller->setUrl(m_dirLister->url()); + const KUrl root = rootUrl(); + setUrl(root); + m_controller->setUrl(root); } + deleteView(); + + // It is important to read the view properties _after_ deleting the view, + // as e. g. the detail view might adjust the additional information properties + // after getting closed: const KUrl viewPropsUrl = viewPropertiesUrl(); ViewProperties props(viewPropsUrl); props.setViewMode(m_mode); createView(); + // the file item delegate has been recreated, apply the current + // additional information manually + const KFileItemDelegate::InformationList infoList = props.additionalInfo(); + m_fileItemDelegate->setShowInformation(infoList); + emit additionalInfoChanged(infoList); + // Not all view modes support categorized sorting. Adjust the sorting model // if changing the view mode results in a change of the categorized sorting // capabilities. @@ -176,7 +219,6 @@ void DolphinView::setMode(Mode mode) const bool categorized = m_storedCategorizedSorting && supportsCategorizedSorting(); if (categorized != m_proxyModel->isCategorizedModel()) { m_proxyModel->setCategorizedModel(categorized); - m_proxyModel->sort(m_proxyModel->sortColumn(), m_proxyModel->sortOrder()); emit categorizedSortingChanged(); } @@ -192,11 +234,16 @@ DolphinView::Mode DolphinView::mode() const void DolphinView::setShowPreview(bool show) { + if (m_showPreview == show) { + return; + } + const KUrl viewPropsUrl = viewPropertiesUrl(); ViewProperties props(viewPropsUrl); props.setShowPreview(show); - m_controller->setShowPreview(show); + m_showPreview = show; + emit showPreviewChanged(); loadDirectory(viewPropsUrl, true); @@ -204,7 +251,7 @@ void DolphinView::setShowPreview(bool show) bool DolphinView::showPreview() const { - return m_controller->showPreview(); + return m_showPreview; } void DolphinView::setShowHiddenFiles(bool show) @@ -218,7 +265,6 @@ void DolphinView::setShowHiddenFiles(bool show) props.setShowHiddenFiles(show); m_dirLister->setShowingDotFiles(show); - m_controller->setShowHiddenFiles(show); emit showHiddenFilesChanged(); loadDirectory(viewPropsUrl, true); @@ -245,7 +291,6 @@ void DolphinView::setCategorizedSorting(bool categorized) m_storedCategorizedSorting = categorized; m_proxyModel->setCategorizedModel(categorized); - m_proxyModel->sort(m_proxyModel->sortColumn(), m_proxyModel->sortOrder()); emit categorizedSortingChanged(); } @@ -411,12 +456,15 @@ void DolphinView::setAdditionalInfo(KFileItemDelegate::InformationList info) const KUrl viewPropsUrl = viewPropertiesUrl(); ViewProperties props(viewPropsUrl); props.setAdditionalInfo(info); - - m_controller->setAdditionalInfoCount(info.count()); m_fileItemDelegate->setShowInformation(info); emit additionalInfoChanged(info); - loadDirectory(viewPropsUrl, true); + + if (itemView() != m_detailsView) { + // the details view requires no reloading of the directory, as it maps + // the file item delegate info to its columns internally + loadDirectory(viewPropsUrl, true); + } } KFileItemDelegate::InformationList DolphinView::additionalInfo() const @@ -432,10 +480,14 @@ void DolphinView::reload() void DolphinView::refresh() { + const bool oldActivationState = m_active; + m_active = true; + createView(); applyViewProperties(m_controller->url()); reload(); - updateViewportColor(); + + setActive(oldActivationState); } void DolphinView::updateView(const KUrl& url, const KUrl& rootUrl) @@ -444,18 +496,11 @@ void DolphinView::updateView(const KUrl& url, const KUrl& rootUrl) return; } - const bool restoreColumnView = !rootUrl.isEmpty() - && !rootUrl.equals(url, KUrl::CompareWithoutTrailingSlash) - && rootUrl.isParentOf(url); - m_controller->setUrl(url); // emits urlChanged, which we forward - if (restoreColumnView) { + if (!rootUrl.isEmpty() && rootUrl.isParentOf(url)) { applyViewProperties(rootUrl); loadDirectory(rootUrl); - // Restoring the column view relies on the URL-history. It might be possible - // that the view properties have been changed or deleted in the meantime, so - // it cannot be asserted that really a column view has been created: if (itemView() == m_columnView) { m_columnView->setRootUrl(rootUrl); m_columnView->showColumn(url); @@ -465,11 +510,31 @@ void DolphinView::updateView(const KUrl& url, const KUrl& rootUrl) loadDirectory(url); } - itemView()->setFocus(); - emit startedPathLoading(url); } +void DolphinView::setNameFilter(const QString& nameFilter) +{ + m_proxyModel->setFilterRegExp(nameFilter); + + if (isColumnViewActive()) { + // adjusting the directory lister is not enough in the case of the + // column view, as each column has its own directory lister internally... + m_columnView->setNameFilter(nameFilter); + } +} + +void DolphinView::calculateItemCount(int& fileCount, int& folderCount) +{ + foreach (KFileItem item, m_dirLister->items()) { + if (item.isDir()) { + ++folderCount; + } else { + ++fileCount; + } + } +} + void DolphinView::setUrl(const KUrl& url) { updateView(url, KUrl()); @@ -503,7 +568,7 @@ void DolphinView::triggerItem(const KFileItem& item) void DolphinView::generatePreviews(const KFileItemList& items) { - if (m_controller->showPreview()) { + if (m_controller->dolphinView()->showPreview()) { KIO::PreviewJob* job = KIO::filePreview(items, 128); connect(job, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)), this, SLOT(showPreview(const KFileItem&, const QPixmap&))); @@ -601,7 +666,6 @@ void DolphinView::applyViewProperties(const KUrl& url) const bool showHiddenFiles = props.showHiddenFiles(); if (showHiddenFiles != m_dirLister->showingDotFiles()) { m_dirLister->setShowingDotFiles(showHiddenFiles); - m_controller->setShowHiddenFiles(showHiddenFiles); emit showHiddenFilesChanged(); } @@ -609,7 +673,6 @@ void DolphinView::applyViewProperties(const KUrl& url) const bool categorized = m_storedCategorizedSorting && supportsCategorizedSorting(); if (categorized != m_proxyModel->isCategorizedModel()) { m_proxyModel->setCategorizedModel(categorized); - m_proxyModel->sort(m_proxyModel->sortColumn(), m_proxyModel->sortOrder()); emit categorizedSortingChanged(); } @@ -627,14 +690,13 @@ void DolphinView::applyViewProperties(const KUrl& url) KFileItemDelegate::InformationList info = props.additionalInfo(); if (info != m_fileItemDelegate->showInformation()) { - m_controller->setAdditionalInfoCount(info.count()); m_fileItemDelegate->setShowInformation(info); emit additionalInfoChanged(info); } const bool showPreview = props.showPreview(); - if (showPreview != m_controller->showPreview()) { - m_controller->setShowPreview(showPreview); + if (showPreview != m_showPreview) { + m_showPreview = showPreview; emit showPreviewChanged(); } } @@ -665,7 +727,7 @@ void DolphinView::openContextMenu(const QPoint& pos) KFileItem item; const QModelIndex index = itemView()->indexAt(pos); - if (isValidNameIndex(index)) { + if (index.isValid() && (index.column() == DolphinModel::Name)) { item = fileItem(index); } @@ -674,28 +736,14 @@ void DolphinView::openContextMenu(const QPoint& pos) void DolphinView::dropUrls(const KUrl::List& urls, const KUrl& destPath, - const QModelIndex& destIndex, - QWidget* source) + const KFileItem& destItem) { - KFileItem directory; - if (isValidNameIndex(destIndex)) { - KFileItem item = fileItem(destIndex); - Q_ASSERT(!item.isNull()); - if (item.isDir()) { - // the URLs are dropped above a directory - directory = item; - } + const KUrl& destination = !destItem.isNull() && destItem.isDir() ? + destItem.url() : destPath; + const KUrl sourceDir = KUrl(urls.first().directory()); + if (sourceDir != destination) { + dropUrls(urls, destination); } - - if ((directory.isNull()) && (source == itemView())) { - // The dropping is done into the same viewport where - // the dragging has been started. Just ignore this... - return; - } - - const KUrl& destination = (directory.isNull()) ? - destPath : directory.url(); - dropUrls(urls, destination); } void DolphinView::dropUrls(const KUrl::List& urls, @@ -724,6 +772,18 @@ void DolphinView::updateSortOrder(Qt::SortOrder order) emit sortOrderChanged(order); } +void DolphinView::updateAdditionalInfo(const KFileItemDelegate::InformationList& info) +{ + ViewProperties props(viewPropertiesUrl()); + props.setAdditionalInfo(info); + props.save(); + + m_fileItemDelegate->setShowInformation(info); + + emit additionalInfoChanged(info); + +} + void DolphinView::emitContentsMoved() { // only emit the contents moved signal if: @@ -773,29 +833,12 @@ void DolphinView::clearHoverInformation() void DolphinView::createView() { - KFileItemDelegate::InformationList infoList; - if (m_fileItemDelegate != 0) { - infoList = m_fileItemDelegate->showInformation(); - } - - // delete current view - QAbstractItemView* view = itemView(); - if (view != 0) { - m_topLayout->removeWidget(view); - view->close(); - view->deleteLater(); - view = 0; - m_iconsView = 0; - m_detailsView = 0; - m_columnView = 0; - m_fileItemDelegate = 0; - } - + deleteView(); Q_ASSERT(m_iconsView == 0); Q_ASSERT(m_detailsView == 0); Q_ASSERT(m_columnView == 0); - // ... and recreate it representing the current mode + QAbstractItemView* view = 0; switch (m_mode) { case IconsView: { m_iconsView = new DolphinIconsView(this, m_controller); @@ -817,7 +860,6 @@ void DolphinView::createView() Q_ASSERT(view != 0); m_fileItemDelegate = new KFileItemDelegate(view); - m_fileItemDelegate->setShowInformation(infoList); view->setItemDelegate(m_fileItemDelegate); view->setModel(m_proxyModel); @@ -832,7 +874,21 @@ void DolphinView::createView() this, SLOT(emitContentsMoved())); connect(view->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(emitContentsMoved())); - view->setFocus(); +} + +void DolphinView::deleteView() +{ + QAbstractItemView* view = itemView(); + if (view != 0) { + m_topLayout->removeWidget(view); + view->close(); + view->deleteLater(); + view = 0; + m_iconsView = 0; + m_detailsView = 0; + m_columnView = 0; + m_fileItemDelegate = 0; + } } QAbstractItemView* DolphinView::itemView() const @@ -846,11 +902,6 @@ QAbstractItemView* DolphinView::itemView() const return m_iconsView; } -bool DolphinView::isValidNameIndex(const QModelIndex& index) const -{ - return index.isValid() && (index.column() == DolphinModel::Name); -} - bool DolphinView::isCutItem(const KFileItem& item) const { const QMimeData* mimeData = QApplication::clipboard()->mimeData(); @@ -883,8 +934,6 @@ void DolphinView::applyCutItemEffect() const KFileItem item = *it; if (isCutItem(item)) { const QModelIndex index = m_dolphinModel->indexForItem(item); - // Huh? the item is already known - //const KFileItem item = m_dolphinModel->itemForIndex(index); const QVariant value = m_dolphinModel->data(index, Qt::DecorationRole); if (value.type() == QVariant::Icon) { const QIcon icon(qvariant_cast(value)); @@ -907,20 +956,261 @@ void DolphinView::applyCutItemEffect() } } -void DolphinView::updateViewportColor() +KToggleAction* DolphinView::iconsModeAction(KActionCollection* actionCollection) { - QColor color = KColorScheme(QPalette::Active, KColorScheme::View).background().color(); - if (m_active) { - emit urlChanged(url()); // Hmm, this is a hack; the url hasn't really changed. - emit selectionChanged(selectedItems()); + KToggleAction* iconsView = actionCollection->add("icons"); + iconsView->setText(i18nc("@action:inmenu View Mode", "Icons")); + iconsView->setShortcut(Qt::CTRL | Qt::Key_1); + iconsView->setIcon(KIcon("fileview-icon")); + iconsView->setData(QVariant::fromValue(IconsView)); + return iconsView; +} + +KToggleAction* DolphinView::detailsModeAction(KActionCollection* actionCollection) +{ + KToggleAction* detailsView = actionCollection->add("details"); + detailsView->setText(i18nc("@action:inmenu View Mode", "Details")); + detailsView->setShortcut(Qt::CTRL | Qt::Key_2); + detailsView->setIcon(KIcon("fileview-detailed")); + detailsView->setData(QVariant::fromValue(DetailsView)); + return detailsView; +} + +KToggleAction* DolphinView::columnsModeAction(KActionCollection* actionCollection) +{ + KToggleAction* columnView = actionCollection->add("columns"); + columnView->setText(i18nc("@action:inmenu View Mode", "Columns")); + columnView->setShortcut(Qt::CTRL | Qt::Key_3); + columnView->setIcon(KIcon("fileview-column")); + columnView->setData(QVariant::fromValue(ColumnView)); + return columnView; +} + +QString DolphinView::currentViewModeActionName() const +{ + switch (m_mode) { + case DolphinView::IconsView: + return "icons"; + case DolphinView::DetailsView: + return "details"; + case DolphinView::ColumnView: + return "columns"; + } + return QString(); // can't happen +} + +void DolphinView::renameSelectedItems() +{ + const KFileItemList items = selectedItems(); + if (items.count() > 1) { + // More than one item has been selected for renaming. Open + // a rename dialog and rename all items afterwards. + RenameDialog dialog(this, items); + if (dialog.exec() == QDialog::Rejected) { + return; + } + + const QString newName = dialog.newName(); + if (newName.isEmpty()) { + emit errorMessage(dialog.errorString()); + } else { + // TODO: check how this can be integrated into KonqFileUndoManager/KonqOperations + // as one operation instead of n rename operations like it is done now... + Q_ASSERT(newName.contains('#')); + + // iterate through all selected items and rename them... + const int replaceIndex = newName.indexOf('#'); + Q_ASSERT(replaceIndex >= 0); + int index = 1; + + KFileItemList::const_iterator it = items.begin(); + const KFileItemList::const_iterator end = items.end(); + while (it != end) { + const KUrl& oldUrl = (*it).url(); + QString number; + number.setNum(index++); + + QString name(newName); + name.replace(replaceIndex, 1, number); + + if (oldUrl.fileName() != name) { + KUrl newUrl = oldUrl; + newUrl.setFileName(name); + KonqOperations::rename(this, oldUrl, newUrl); + emit doingOperation(KonqFileUndoManager::RENAME); + } + ++it; + } + } } else { - color.setAlpha(0); + // Only one item has been selected for renaming. Use the custom + // renaming mechanism from the views. + Q_ASSERT(items.count() == 1); + + // TODO: Think about using KFileItemDelegate as soon as it supports editing. + // Currently the RenameDialog is used, but I'm not sure whether inline renaming + // is a benefit for the user at all -> let's wait for some input first... + RenameDialog dialog(this, items); + if (dialog.exec() == QDialog::Rejected) { + return; + } + + const QString& newName = dialog.newName(); + if (newName.isEmpty()) { + emit errorMessage(dialog.errorString()); + } else { + const KUrl& oldUrl = items.first().url(); + KUrl newUrl = oldUrl; + newUrl.setFileName(newName); + KonqOperations::rename(this, oldUrl, newUrl); + emit doingOperation(KonqFileUndoManager::RENAME); + } } +} - QWidget* viewport = itemView()->viewport(); - QPalette palette; - palette.setColor(viewport->backgroundRole(), color); - viewport->setPalette(palette); +void DolphinView::trashSelectedItems() +{ + emit doingOperation(KonqFileUndoManager::TRASH); + KonqOperations::del(this, KonqOperations::TRASH, selectedUrls()); +} + +void DolphinView::deleteSelectedItems() +{ + const KUrl::List list = selectedUrls(); + const bool del = KonqOperations::askDeleteConfirmation(list, + KonqOperations::DEL, + KonqOperations::DEFAULT_CONFIRMATION, + this); + + if (del) { + KIO::Job* job = KIO::del(list); + connect(job, SIGNAL(result(KJob*)), + this, SLOT(slotDeleteFileFinished(KJob*))); + } +} + +void DolphinView::slotDeleteFileFinished(KJob* job) +{ + if (job->error() == 0) { + emit operationCompletedMessage(i18nc("@info:status", "Delete operation completed.")); + } else { + emit errorMessage(job->errorString()); + } +} + +void DolphinView::cutSelectedItems() +{ + QMimeData* mimeData = new QMimeData(); + const KUrl::List kdeUrls = selectedUrls(); + const KUrl::List mostLocalUrls; + KonqMimeData::populateMimeData(mimeData, kdeUrls, mostLocalUrls, true); + QApplication::clipboard()->setMimeData(mimeData); +} + +void DolphinView::copySelectedItems() +{ + QMimeData* mimeData = new QMimeData(); + const KUrl::List kdeUrls = selectedUrls(); + const KUrl::List mostLocalUrls; + KonqMimeData::populateMimeData(mimeData, kdeUrls, mostLocalUrls, false); + QApplication::clipboard()->setMimeData(mimeData); +} + +void DolphinView::paste() +{ + QClipboard* clipboard = QApplication::clipboard(); + const QMimeData* mimeData = clipboard->mimeData(); + + const KUrl::List sourceUrls = KUrl::List::fromMimeData(mimeData); + + // per default the pasting is done into the current Url of the view + KUrl destUrl(url()); + + // check whether the pasting should be done into a selected directory + const KUrl::List selectedUrls = this->selectedUrls(); + if (selectedUrls.count() == 1) { + const KFileItem fileItem(S_IFDIR, + KFileItem::Unknown, + selectedUrls.first(), + true); + if (fileItem.isDir()) { + // only one item is selected which is a directory, hence paste + // into this directory + destUrl = selectedUrls.first(); + } + } + + if (KonqMimeData::decodeIsCutSelection(mimeData)) { + KonqOperations::copy(this, KonqOperations::MOVE, sourceUrls, destUrl); + emit doingOperation(KonqFileUndoManager::MOVE); + clipboard->clear(); + } else { + KonqOperations::copy(this, KonqOperations::COPY, sourceUrls, destUrl); + emit doingOperation(KonqFileUndoManager::COPY); + } +} + +QPair DolphinView::pasteInfo() const +{ + QPair ret; + QClipboard* clipboard = QApplication::clipboard(); + const QMimeData* mimeData = clipboard->mimeData(); + + KUrl::List urls = KUrl::List::fromMimeData(mimeData); + if (!urls.isEmpty()) { + ret.first = true; + ret.second = i18ncp("@action:inmenu", "Paste One File", "Paste %1 Files", urls.count()); + } else { + ret.first = false; + ret.second = i18nc("@action:inmenu", "Paste"); + } + + if (ret.first) { + const KUrl::List urls = selectedUrls(); + const uint count = urls.count(); + if (count > 1) { + // pasting should not be allowed when more than one file + // is selected + ret.first = false; + } else if (count == 1) { + // Only one file is selected. Pasting is only allowed if this + // file is a directory. + // TODO: this doesn't work with remote protocols; instead we need a + // m_activeViewContainer->selectedFileItems() to get the real KFileItems + const KFileItem fileItem(S_IFDIR, + KFileItem::Unknown, + urls.first(), + true); + ret.first = fileItem.isDir(); + } + } + return ret; +} + +KAction* DolphinView::createRenameAction(KActionCollection* collection) +{ + KAction* rename = collection->addAction("rename"); + rename->setText(i18nc("@action:inmenu File", "Rename...")); + rename->setShortcut(Qt::Key_F2); + return rename; +} + +KAction* DolphinView::createMoveToTrashAction(KActionCollection* collection) +{ + KAction* moveToTrash = collection->addAction("move_to_trash"); + moveToTrash->setText(i18nc("@action:inmenu File", "Move to Trash")); + moveToTrash->setIcon(KIcon("user-trash")); + moveToTrash->setShortcut(QKeySequence::Delete); + return moveToTrash; +} + +KAction* DolphinView::createDeleteAction(KActionCollection* collection) +{ + KAction* deleteAction = collection->addAction("delete"); + deleteAction->setIcon(KIcon("edit-delete")); + deleteAction->setText(i18nc("@action:inmenu File", "Delete")); + deleteAction->setShortcut(Qt::SHIFT | Qt::Key_Delete); + return deleteAction; } #include "dolphinview.moc"