#include <kfileitemdelegate.h>
#include <klocale.h>
#include <kiconeffect.h>
+#include <kio/deletejob.h>
#include <kio/netaccess.h>
-#include <kio/renamedialog.h>
#include <kio/previewjob.h>
+#include <kjob.h>
+#include <kmenu.h>
#include <kmimetyperesolver.h>
#include <konqmimedata.h>
#include <konq_operations.h>
#include <kurl.h>
+#include "dolphindropcontroller.h"
#include "dolphinmodel.h"
#include "dolphincolumnview.h"
#include "dolphincontroller.h"
DolphinSortFilterProxyModel* proxyModel) :
QWidget(parent),
m_active(true),
+ m_showPreview(false),
m_loadingDirectory(false),
m_storedCategorizedSorting(false),
m_mode(DolphinView::IconsView),
m_detailsView(0),
m_columnView(0),
m_fileItemDelegate(0),
+ m_selectionModel(0),
m_dolphinModel(dolphinModel),
m_dirLister(dirLister),
- m_proxyModel(proxyModel)
+ m_proxyModel(proxyModel),
+ m_previewJob(0)
{
setFocusPolicy(Qt::StrongFocus);
m_topLayout = new QVBoxLayout(this);
connect(m_controller, SIGNAL(requestContextMenu(const QPoint&)),
this, SLOT(openContextMenu(const QPoint&)));
- connect(m_controller, SIGNAL(urlsDropped(const KUrl::List&, const KUrl&, const KFileItem&, QWidget*)),
- this, SLOT(dropUrls(const KUrl::List&, const KUrl&, const KFileItem&, 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)),
DolphinView::~DolphinView()
{
+ if (m_previewJob != 0) {
+ m_previewJob->kill();
+ m_previewJob = 0;
+ }
}
const KUrl& DolphinView::url() const
}
m_active = active;
+ m_selectionModel->clearSelection();
QColor color = KColorScheme(QPalette::Active, KColorScheme::View).background().color();
if (active) {
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
emit categorizedSortingChanged();
}
- loadDirectory(viewPropsUrl);
-
emit modeChanged();
}
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);
bool DolphinView::showPreview() const
{
- return m_controller->showPreview();
+ return m_showPreview;
}
void DolphinView::setShowHiddenFiles(bool show)
void DolphinView::selectAll()
{
- itemView()->selectAll();
+ QAbstractItemView* view = itemView();
+ // TODO: there seems to be a bug in QAbstractItemView::selectAll(); if
+ // the Ctrl-key is pressed (e. g. for Ctrl+A), selectAll() inverts the
+ // selection instead of selecting all items. This is bypassed for KDE 4.0
+ // by invoking clearSelection() first.
+ view->clearSelection();
+ view->selectAll();
}
void DolphinView::invertSelection()
void DolphinView::generatePreviews(const KFileItemList& items)
{
- if (m_controller->showPreview()) {
- KIO::PreviewJob* job = KIO::filePreview(items, 128);
- connect(job, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)),
- this, SLOT(showPreview(const KFileItem&, const QPixmap&)));
+ if (m_controller->dolphinView()->showPreview()) {
+ if (m_previewJob != 0) {
+ m_previewJob->kill();
+ m_previewJob = 0;
+ }
+
+ m_previewJob = KIO::filePreview(items, 128);
+ connect(m_previewJob, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)),
+ this, SLOT(replaceIcon(const KFileItem&, const QPixmap&)));
+ connect(m_previewJob, SIGNAL(finished(KJob*)),
+ this, SLOT(slotPreviewJobFinished(KJob*)));
}
}
-void DolphinView::showPreview(const KFileItem& item, const QPixmap& pixmap)
+void DolphinView::replaceIcon(const KFileItem& item, const QPixmap& pixmap)
{
Q_ASSERT(!item.isNull());
- if (item.url().directory() != m_dirLister->url().path()) {
- // the preview job is still working on items of an older URL, hence
+ if (!m_showPreview || (item.url().directory() != m_dirLister->url().path())) {
+ // the preview has been deactivated in the meanwhile or the preview
+ // job is still working on items of an older URL, hence
// the item is not part of the directory model anymore
return;
}
}
const bool showPreview = props.showPreview();
- if (showPreview != m_controller->showPreview()) {
- m_controller->setShowPreview(showPreview);
+ if (showPreview != m_showPreview) {
+ m_showPreview = showPreview;
emit showPreviewChanged();
}
}
void DolphinView::dropUrls(const KUrl::List& urls,
const KUrl& destPath,
- const KFileItem& destItem,
- QWidget* source)
-{
- bool dropAboveDir = false;
- if (!destItem.isNull()) {
- dropAboveDir = destItem.isDir();
- if (!dropAboveDir) {
- // the dropping is done above a file
- return;
- }
- } else if (source == itemView()) {
- // the dropping is done into the same viewport where the dragging
- // has been started
- return;
+ const KFileItem& destItem)
+{
+ Q_ASSERT(!urls.isEmpty());
+ const KUrl& destination = !destItem.isNull() && destItem.isDir() ?
+ destItem.url() : destPath;
+ const KUrl sourceDir = KUrl(urls.first().directory());
+ if (sourceDir != destination) {
+ dropUrls(urls, destination);
}
-
- const KUrl& destination = dropAboveDir ? destItem.url() : destPath;
- dropUrls(urls, destination);
}
void DolphinView::dropUrls(const KUrl::List& urls,
const KUrl& destination)
{
- emit urlsDropped(urls, destination);
+ DolphinDropController dropController(this);
+ // forward doingOperation signal up to the mainwindow
+ connect(&dropController, SIGNAL(doingOperation(KonqFileUndoManager::CommandType)),
+ this, SIGNAL(doingOperation(KonqFileUndoManager::CommandType)));
+ dropController.dropUrls(urls, destination);
}
void DolphinView::updateSorting(DolphinView::Sorting sorting)
{
ViewProperties props(viewPropertiesUrl());
props.setAdditionalInfo(info);
+ props.save();
m_fileItemDelegate->setShowInformation(info);
void DolphinView::showHoverInformation(const KFileItem& item)
{
- if (hasSelection()) {
+ if (hasSelection() || !m_active) {
return;
}
void DolphinView::clearHoverInformation()
{
- emit requestItemInfo(KFileItem());
+ if (m_active) {
+ emit requestItemInfo(KFileItem());
+ }
}
void DolphinView::createView()
{
- // 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);
view->setItemDelegate(m_fileItemDelegate);
view->setModel(m_proxyModel);
+ if (m_selectionModel != 0) {
+ view->setSelectionModel(m_selectionModel);
+ } else {
+ m_selectionModel = view->selectionModel();
+ }
+
+ // reparent the selection model, as it should not be deleted
+ // when deleting the model
+ m_selectionModel->setParent(this);
+
view->setSelectionMode(QAbstractItemView::ExtendedSelection);
new KMimeTypeResolver(view, m_dolphinModel);
this, SLOT(emitContentsMoved()));
}
+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
{
if (m_detailsView != 0) {
KToggleAction* iconsView = actionCollection->add<KToggleAction>("icons");
iconsView->setText(i18nc("@action:inmenu View Mode", "Icons"));
iconsView->setShortcut(Qt::CTRL | Qt::Key_1);
- iconsView->setIcon(KIcon("fileview-icon"));
+ iconsView->setIcon(KIcon("view-list-icons"));
iconsView->setData(QVariant::fromValue(IconsView));
return iconsView;
}
KToggleAction* detailsView = actionCollection->add<KToggleAction>("details");
detailsView->setText(i18nc("@action:inmenu View Mode", "Details"));
detailsView->setShortcut(Qt::CTRL | Qt::Key_2);
- detailsView->setIcon(KIcon("fileview-detailed"));
+ detailsView->setIcon(KIcon("view-list-details"));
detailsView->setData(QVariant::fromValue(DetailsView));
return detailsView;
}
KToggleAction* columnView = actionCollection->add<KToggleAction>("columns");
columnView->setText(i18nc("@action:inmenu View Mode", "Columns"));
columnView->setShortcut(Qt::CTRL | Qt::Key_3);
- columnView->setIcon(KIcon("fileview-column"));
+ columnView->setIcon(KIcon("view-file-columns"));
columnView->setData(QVariant::fromValue(ColumnView));
return columnView;
}
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 {
+ // 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);
+ }
+ }
+}
+
+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::slotPreviewJobFinished(KJob* job)
+{
+ Q_ASSERT(job == m_previewJob);
+ m_previewJob = 0;
+}
+
+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<bool, QString> DolphinView::pasteInfo() const
+{
+ QPair<bool, QString> 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 KFileItemList items = selectedItems();
+ const uint count = items.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.
+ ret.first = items.first().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"