diff options
Diffstat (limited to 'src/views')
24 files changed, 3431 insertions, 2 deletions
diff --git a/src/views/additionalinfoaccessor.cpp b/src/views/additionalinfoaccessor.cpp new file mode 100644 index 000000000..1b445cc99 --- /dev/null +++ b/src/views/additionalinfoaccessor.cpp @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2010 by Peter Penz <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "additionalinfoaccessor.h" + +#include "dolphinmodel.h" +#include <kglobal.h> +#include <klocale.h> + +class AdditionalInfoAccessorSingleton +{ +public: + AdditionalInfoAccessor instance; +}; +K_GLOBAL_STATIC(AdditionalInfoAccessorSingleton, s_additionalInfoManager) + +AdditionalInfoAccessor& AdditionalInfoAccessor::instance() +{ + return s_additionalInfoManager->instance; +} + +KFileItemDelegate::InformationList AdditionalInfoAccessor::keys() const +{ + return m_informations; +} + +KFileItemDelegate::Information AdditionalInfoAccessor::keyForColumn(int columnIndex) const +{ + KFileItemDelegate::Information info = KFileItemDelegate::NoInformation; + + switch (columnIndex) { + case DolphinModel::Size: info = KFileItemDelegate::Size; break; + case DolphinModel::ModifiedTime: info = KFileItemDelegate::ModificationTime; break; + case DolphinModel::Permissions: info = KFileItemDelegate::Permissions; break; + case DolphinModel::Owner: info = KFileItemDelegate::Owner; break; + case DolphinModel::Group: info = KFileItemDelegate::OwnerAndGroup; break; + case DolphinModel::Type: info = KFileItemDelegate::FriendlyMimeType; break; + case DolphinModel::LinkDest: info = KFileItemDelegate::LinkDest; break; + case DolphinModel::LocalPathOrUrl: info = KFileItemDelegate::LocalPathOrUrl; break; + default: break; + } + + return info; +} + +QString AdditionalInfoAccessor::actionCollectionName(KFileItemDelegate::Information info, + ActionCollectionType type) const +{ + QString name; + switch (type) { + case SortByType: + name = QLatin1String("sort_by_") + QLatin1String(m_map[info]->actionCollectionName); + break; + + case AdditionalInfoType: + name = QLatin1String("show_") + QLatin1String(m_map[info]->actionCollectionName); + break; + } + + return name; +} + +QString AdditionalInfoAccessor::translation(KFileItemDelegate::Information info) const +{ + return i18nc(m_map[info]->context, m_map[info]->translation); +} + +DolphinView::Sorting AdditionalInfoAccessor::sorting(KFileItemDelegate::Information info) const +{ + return m_map[info]->sorting; +} + +int AdditionalInfoAccessor::bitValue(KFileItemDelegate::Information info) const +{ + return m_map[info]->bitValue; +} + +AdditionalInfoAccessor::AdditionalInfoAccessor() : + m_informations(), + m_map() +{ + static const AdditionalInfoAccessor::AdditionalInfo additionalInfos[] = { + { "size", I18N_NOOP2_NOSTRIP("@label", "Size"), DolphinView::SortBySize, 1 }, + { "date", I18N_NOOP2_NOSTRIP("@label", "Date"), DolphinView::SortByDate, 2 }, + { "permissions", I18N_NOOP2_NOSTRIP("@label", "Permissions"), DolphinView::SortByPermissions, 4 }, + { "owner", I18N_NOOP2_NOSTRIP("@label", "Owner"), DolphinView::SortByOwner, 8 }, + { "group", I18N_NOOP2_NOSTRIP("@label", "Group"), DolphinView::SortByGroup, 16 }, + { "type", I18N_NOOP2_NOSTRIP("@label", "Type"), DolphinView::SortByType, 32 }, + { "destination", I18N_NOOP2_NOSTRIP("@label", "Link Destination"), DolphinView::SortByDestination, 64 }, + { "path", I18N_NOOP2_NOSTRIP("@label", "Path"), DolphinView::SortByPath, 128 } + }; + + m_map.insert(KFileItemDelegate::Size, &additionalInfos[0]); + m_map.insert(KFileItemDelegate::ModificationTime, &additionalInfos[1]); + m_map.insert(KFileItemDelegate::Permissions, &additionalInfos[2]); + m_map.insert(KFileItemDelegate::Owner, &additionalInfos[3]); + m_map.insert(KFileItemDelegate::OwnerAndGroup, &additionalInfos[4]); + m_map.insert(KFileItemDelegate::FriendlyMimeType, &additionalInfos[5]); + m_map.insert(KFileItemDelegate::LinkDest, &additionalInfos[6]); + m_map.insert(KFileItemDelegate::LocalPathOrUrl, &additionalInfos[7]); + + // The m_informations list defines all available keys and the sort order + // (don't use m_informations = m_map.keys(), as the order is undefined). + m_informations.append(KFileItemDelegate::Size); + m_informations.append(KFileItemDelegate::ModificationTime); + m_informations.append(KFileItemDelegate::Permissions); + m_informations.append(KFileItemDelegate::Owner); + m_informations.append(KFileItemDelegate::OwnerAndGroup); + m_informations.append(KFileItemDelegate::FriendlyMimeType); + m_informations.append(KFileItemDelegate::LinkDest); + m_informations.append(KFileItemDelegate::LocalPathOrUrl); +} + +AdditionalInfoAccessor::~AdditionalInfoAccessor() +{ +} diff --git a/src/views/additionalinfoaccessor.h b/src/views/additionalinfoaccessor.h new file mode 100644 index 000000000..0a3d51459 --- /dev/null +++ b/src/views/additionalinfoaccessor.h @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright (C) 2010 by Peter Penz <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef ADDITIONALINFOACCESSOR_H +#define ADDITIONALINFOACCESSOR_H + +#include <libdolphin_export.h> +#include <kfileitemdelegate.h> +#include <views/dolphinview.h> + +#include <QList> +#include <QMap> + +/** + * @brief Allows to access the information that is available by KFileItemDelegate::Information. + * + * The information that is available by KFileItemDelegate::Information will be shown + * in Dolphin the following way: + * - As additional columns in the details view + * - As additional lines in the icons view + * - As menu entries in the "Sort By" and "Additional Information" groups + * - As popup menu entries in the details view header popup + * - As checkable entries in the View Properties dialog + * + * The AdditionalInfoAccessor provides a central place to get all available keys, + * the corresponding translations, action names, etc., so that modifications or + * extensions in KFileItemDelegate only require adjustments in the implementation + * of this class. + */ +class LIBDOLPHINPRIVATE_EXPORT AdditionalInfoAccessor +{ +public: + enum ActionCollectionType { + /// Action collection from "View -> Sort By" + SortByType, + /// Action collection from "View -> Additional Information" + AdditionalInfoType + }; + + static AdditionalInfoAccessor& instance(); + + /** + * @return List of all available information entries of KFileItemDelegate. + * All entries of this list are keys for accessing the corresponding + * data (see actionCollectionName(), translation(), bitValue()). + */ + KFileItemDelegate::InformationList keys() const; + + /** + * @return Key for the model column with the index \p columnIndex. + */ + KFileItemDelegate::Information keyForColumn(int columnIndex) const; + + QString actionCollectionName(KFileItemDelegate::Information info, ActionCollectionType type) const; + + QString translation(KFileItemDelegate::Information info) const; + + DolphinView::Sorting sorting(KFileItemDelegate::Information info) const; + + /** + * @return Bitvalue for \p info that is stored in a ViewProperties instance. + */ + int bitValue(KFileItemDelegate::Information info) const; + +protected: + AdditionalInfoAccessor(); + virtual ~AdditionalInfoAccessor(); + friend class AdditionalInfoAccessorSingleton; + +private: + struct AdditionalInfo { + const char* const actionCollectionName; + const char* const context; + const char* const translation; + const DolphinView::Sorting sorting; + const int bitValue; + }; + + KFileItemDelegate::InformationList m_informations; + QMap<KFileItemDelegate::Information, const AdditionalInfo*> m_map; +}; + +#endif + diff --git a/src/views/dolphindirlister.cpp b/src/views/dolphindirlister.cpp new file mode 100644 index 000000000..2eac5522d --- /dev/null +++ b/src/views/dolphindirlister.cpp @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Peter Penz <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphindirlister.h" +#include "klocale.h" +#include <kio/jobclasses.h> + +DolphinDirLister::DolphinDirLister() : + KDirLister() +{ + setAutoErrorHandlingEnabled(false, 0); +} + +DolphinDirLister::~DolphinDirLister() +{ +} + +void DolphinDirLister::handleError(KIO::Job* job) +{ + if (job->error() == KIO::ERR_IS_FILE) { + emit urlIsFileError(url()); + } else { + const QString errorString = job->errorString(); + if (errorString.isEmpty()) { + emit errorMessage(i18nc("@info:status", "Unknown error.")); + } else { + emit errorMessage(errorString); + } + } +} + +#include "dolphindirlister.moc" diff --git a/src/views/dolphindirlister.h b/src/views/dolphindirlister.h new file mode 100644 index 000000000..629c335d3 --- /dev/null +++ b/src/views/dolphindirlister.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2006-2009 by Peter Penz <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHINDIRLISTER_H +#define DOLPHINDIRLISTER_H + +#include <kdirlister.h> + +/** + * @brief Extends the class KDirLister by emitting a signal when an + * error occurred instead of showing an error dialog. + */ +class DolphinDirLister : public KDirLister +{ + Q_OBJECT + +public: + DolphinDirLister(); + virtual ~DolphinDirLister(); + +signals: + /** Is emitted whenever an error has occurred. */ + void errorMessage(const QString& msg); + + /** Is emitted when the URL of the directory lister represents a file. */ + void urlIsFileError(const KUrl& url); + +protected: + virtual void handleError(KIO::Job* job); +}; + +#endif diff --git a/src/views/dolphinfileitemdelegate.cpp b/src/views/dolphinfileitemdelegate.cpp index 17447d8cb..6b706c88b 100644 --- a/src/views/dolphinfileitemdelegate.cpp +++ b/src/views/dolphinfileitemdelegate.cpp @@ -19,7 +19,7 @@ #include "dolphinfileitemdelegate.h" -#include <dolphinmodel.h> +#include "dolphinmodel.h" #include <kfileitem.h> #include <kicon.h> #include <kiconloader.h> diff --git a/src/views/dolphinfileitemdelegate.h b/src/views/dolphinfileitemdelegate.h index 405f24916..0777571f7 100644 --- a/src/views/dolphinfileitemdelegate.h +++ b/src/views/dolphinfileitemdelegate.h @@ -20,8 +20,8 @@ #ifndef DOLPHINFILEITEMDELEGATE_H #define DOLPHINFILEITEMDELEGATE_H -#include <dolphinmodel.h> #include <kfileitemdelegate.h> +#include <views/dolphinmodel.h> class QAbstractProxyModel; diff --git a/src/views/dolphinmodel.cpp b/src/views/dolphinmodel.cpp new file mode 100644 index 000000000..137274e0d --- /dev/null +++ b/src/views/dolphinmodel.cpp @@ -0,0 +1,445 @@ +/** + * This file is part of the KDE project + * Copyright (C) 2007 Rafael Fernández López <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "dolphinmodel.h" + +#include "dolphinsortfilterproxymodel.h" + +#include "kcategorizedview.h" + +#include <kdatetime.h> +#include <kdirmodel.h> +#include <kfileitem.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kurl.h> +#include <kuser.h> +#include <kmimetype.h> +#include <kstandarddirs.h> + +#include <QList> +#include <QSortFilterProxyModel> +#include <QPainter> +#include <QPersistentModelIndex> +#include <QDir> +#include <QFileInfo> + +const char* const DolphinModel::m_others = I18N_NOOP2("@title:group Name", "Others"); + +DolphinModel::DolphinModel(QObject* parent) : + KDirModel(parent), + m_hasVersionData(false), + m_revisionHash() +{ + setJobTransfersVisible(true); +} + +DolphinModel::~DolphinModel() +{ +} + +bool DolphinModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if ((index.column() == DolphinModel::Version) && (role == Qt::DecorationRole)) { + // TODO: remove data again when items are deleted... + + const QPersistentModelIndex key = index; + const KVersionControlPlugin::VersionState state = static_cast<KVersionControlPlugin::VersionState>(value.toInt()); + if (m_revisionHash.value(key, KVersionControlPlugin::UnversionedVersion) != state) { + if (!m_hasVersionData) { + connect(this, SIGNAL(rowsRemoved (const QModelIndex&, int, int)), + this, SLOT(slotRowsRemoved(const QModelIndex&, int, int))); + m_hasVersionData = true; + } + + m_revisionHash.insert(key, state); + emit dataChanged(index, index); + return true; + } + } + + return KDirModel::setData(index, value, role); +} + +QVariant DolphinModel::data(const QModelIndex& index, int role) const +{ + switch (role) { + case KCategorizedSortFilterProxyModel::CategoryDisplayRole: + return displayRoleData(index); + + case KCategorizedSortFilterProxyModel::CategorySortRole: + return sortRoleData(index); + + case Qt::DecorationRole: + if (index.column() == DolphinModel::Version) { + return m_revisionHash.value(index, KVersionControlPlugin::UnversionedVersion); + } + break; + + case Qt::DisplayRole: + switch (index.column()) { + case DolphinModel::LinkDest: { + const KDirModel *dirModel = qobject_cast<const KDirModel*>(index.model()); + const KFileItem item = dirModel->itemForIndex(index); + return item.linkDest(); + } + + case DolphinModel::LocalPathOrUrl: + const KDirModel *dirModel = qobject_cast<const KDirModel*>(index.model()); + const KFileItem item = dirModel->itemForIndex(index); + return item.localPath(); + break; + } + break; + + default: + break; + } + + return KDirModel::data(index, role); +} + +QVariant DolphinModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if ((orientation == Qt::Horizontal) && (role == Qt::DisplayRole)) { + switch (section) { + case DolphinModel::LinkDest: + return i18nc("@title::column", "Link Destination"); + case DolphinModel::LocalPathOrUrl: + return i18nc("@title::column", "Path"); + default: + return KDirModel::headerData(section, orientation, role); + } + } + return QVariant(); +} + +int DolphinModel::columnCount(const QModelIndex& parent) const +{ + return KDirModel::columnCount(parent) + (ExtraColumnCount - ColumnCount); +} + +void DolphinModel::clearVersionData() +{ + m_revisionHash.clear(); + m_hasVersionData = false; +} + +bool DolphinModel::hasVersionData() const +{ + return m_hasVersionData; +} + +void DolphinModel::slotRowsRemoved(const QModelIndex& parent, int start, int end) +{ + if (m_hasVersionData) { + const int column = parent.column(); + for (int row = start; row <= end; ++row) { + m_revisionHash.remove(parent.child(row, column)); + } + } +} + +QVariant DolphinModel::displayRoleData(const QModelIndex& index) const +{ + QString retString; + + if (!index.isValid()) { + return retString; + } + + const KDirModel *dirModel = qobject_cast<const KDirModel*>(index.model()); + KFileItem item = dirModel->itemForIndex(index); + + switch (index.column()) { + case KDirModel::Name: { + // KDirModel checks columns to know to which role are + // we talking about + const QModelIndex nameIndex = index.model()->index(index.row(), KDirModel::Name, index.parent()); + if (!nameIndex.isValid()) { + return retString; + } + const QVariant data = nameIndex.model()->data(nameIndex, Qt::DisplayRole); + const QString name = data.toString(); + if (!name.isEmpty()) { + if (!item.isHidden() && name.at(0).isLetter()) + retString = name.at(0).toUpper(); + else if (item.isHidden()) { + if (name.at(0) == '.') { + if (name.size() > 1 && name.at(1).isLetter()) { + retString = name.at(1).toUpper(); + } else { + retString = i18nc("@title:group Name", m_others); + } + } else { + retString = name.at(0).toUpper(); + } + } else { + bool validCategory = false; + + const QString str(name.toUpper()); + const QChar* currA = str.unicode(); + while (!currA->isNull() && !validCategory) { + if (currA->isLetter()) { + validCategory = true; + } else if (currA->isDigit()) { + return i18nc("@title:group Name", m_others); + } else { + ++currA; + } + } + + retString = validCategory ? *currA : i18nc("@title:group Name", m_others); + } + } + break; + } + + case KDirModel::Size: { + const KIO::filesize_t fileSize = !item.isNull() ? item.size() : ~0U; + if (!item.isNull() && item.isDir()) { + retString = i18nc("@title:group Size", "Folders"); + } else if (fileSize < 5242880) { + retString = i18nc("@title:group Size", "Small"); + } else if (fileSize < 10485760) { + retString = i18nc("@title:group Size", "Medium"); + } else { + retString = i18nc("@title:group Size", "Big"); + } + break; + } + + case KDirModel::ModifiedTime: { + KDateTime modifiedTime = item.time(KFileItem::ModificationTime); + modifiedTime = modifiedTime.toLocalZone(); + + const QDate currentDate = KDateTime::currentLocalDateTime().date(); + const QDate modifiedDate = modifiedTime.date(); + + const int daysDistance = modifiedDate.daysTo(currentDate); + + int yearForCurrentWeek = 0; + int currentWeek = currentDate.weekNumber(&yearForCurrentWeek); + if (yearForCurrentWeek == currentDate.year() + 1) { + currentWeek = 53; + } + + int yearForModifiedWeek = 0; + int modifiedWeek = modifiedDate.weekNumber(&yearForModifiedWeek); + if (yearForModifiedWeek == modifiedDate.year() + 1) { + modifiedWeek = 53; + } + + if (currentDate.year() == modifiedDate.year() && currentDate.month() == modifiedDate.month()) { + if (modifiedWeek > currentWeek) { + // use case: modified date = 2010-01-01, current date = 2010-01-22 + // modified week = 53, current week = 3 + modifiedWeek = 0; + } + switch (currentWeek - modifiedWeek) { + case 0: + switch (daysDistance) { + case 0: retString = i18nc("@title:group Date", "Today"); break; + case 1: retString = i18nc("@title:group Date", "Yesterday"); break; + default: retString = modifiedTime.toString(i18nc("@title:group The week day name: %A", "%A")); + } + break; + case 1: + retString = i18nc("@title:group Date", "Last Week"); + break; + case 2: + retString = i18nc("@title:group Date", "Two Weeks Ago"); + break; + case 3: + retString = i18nc("@title:group Date", "Three Weeks Ago"); + break; + case 4: + case 5: + retString = i18nc("@title:group Date", "Earlier this Month"); + break; + default: + Q_ASSERT(false); + } + } else { + const QDate lastMonthDate = currentDate.addMonths(-1); + if (lastMonthDate.year() == modifiedDate.year() && lastMonthDate.month() == modifiedDate.month()) { + if (daysDistance == 1) { + retString = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Yesterday (%B, %Y)")); + } else if (daysDistance <= 7) { + retString = modifiedTime.toString(i18nc("@title:group The week day name: %A, %B is full month name in current locale, and %Y is full year number", "%A (%B, %Y)")); + } else if (daysDistance <= 7 * 2) { + retString = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Last Week (%B, %Y)")); + } else if (daysDistance <= 7 * 3) { + retString = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Two Weeks Ago (%B, %Y)")); + } else if (daysDistance <= 7 * 4) { + retString = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Three Weeks Ago (%B, %Y)")); + } else { + retString = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Earlier on %B, %Y")); + } + } else { + retString = modifiedTime.toString(i18nc("@title:group The month and year: %B is full month name in current locale, and %Y is full year number", "%B, %Y")); + } + } + break; + } + + case KDirModel::Permissions: { + QString user; + QString group; + QString others; + + QFileInfo info(item.url().pathOrUrl()); + + // set user string + if (info.permission(QFile::ReadUser)) { + user = i18nc("@item:intext Access permission, concatenated", "Read, "); + } + if (info.permission(QFile::WriteUser)) { + user += i18nc("@item:intext Access permission, concatenated", "Write, "); + } + if (info.permission(QFile::ExeUser)) { + user += i18nc("@item:intext Access permission, concatenated", "Execute, "); + } + user = user.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : user.mid(0, user.count() - 2); + + // set group string + if (info.permission(QFile::ReadGroup)) { + group = i18nc("@item:intext Access permission, concatenated", "Read, "); + } + if (info.permission(QFile::WriteGroup)) { + group += i18nc("@item:intext Access permission, concatenated", "Write, "); + } + if (info.permission(QFile::ExeGroup)) { + group += i18nc("@item:intext Access permission, concatenated", "Execute, "); + } + group = group.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : group.mid(0, group.count() - 2); + + // set permission string + if (info.permission(QFile::ReadOther)) { + others = i18nc("@item:intext Access permission, concatenated", "Read, "); + } + if (info.permission(QFile::WriteOther)) { + others += i18nc("@item:intext Access permission, concatenated", "Write, "); + } + if (info.permission(QFile::ExeOther)) { + others += i18nc("@item:intext Access permission, concatenated", "Execute, "); + } + others = others.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : others.mid(0, others.count() - 2); + + retString = i18nc("@title:group Files and folders by permissions", "(User: %1) (Group: %2) (Others: %3)", user, group, others); + break; + } + + case KDirModel::Owner: + retString = item.user(); + break; + + case KDirModel::Group: + retString = item.group(); + break; + + case KDirModel::Type: + retString = item.mimeComment(); + break; + + case DolphinModel::Version: + retString = "test"; + break; + } + + return retString; +} + +QVariant DolphinModel::sortRoleData(const QModelIndex& index) const +{ + QVariant retVariant; + + if (!index.isValid()) { + return retVariant; + } + + const KDirModel *dirModel = qobject_cast<const KDirModel*>(index.model()); + KFileItem item = dirModel->itemForIndex(index); + + switch (index.column()) { + case KDirModel::Name: { + retVariant = data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole); + if (retVariant == i18nc("@title:group Name", m_others)) { + // assure that the "Others" group is always the last categorization + retVariant = QString('Z').append(QChar::ReplacementCharacter); + } + break; + } + + case KDirModel::Size: { + const KIO::filesize_t fileSize = !item.isNull() ? item.size() : ~0U; + if (item.isDir()) { + retVariant = 0; + } else if (fileSize < 5242880) { + retVariant = 1; + } else if (fileSize < 10485760) { + retVariant = 2; + } else { + retVariant = 3; + } + break; + } + + case KDirModel::ModifiedTime: { + KDateTime modifiedTime = item.time(KFileItem::ModificationTime); + modifiedTime = modifiedTime.toLocalZone(); + + const QDate currentDate = KDateTime::currentLocalDateTime().date(); + const QDate modifiedDate = modifiedTime.date(); + + retVariant = -modifiedDate.daysTo(currentDate); + break; + } + + case KDirModel::Permissions: { + QFileInfo info(item.url().pathOrUrl()); + + retVariant = -KDirSortFilterProxyModel::pointsForPermissions(info); + break; + } + + case KDirModel::Owner: + retVariant = item.user(); + break; + + case KDirModel::Group: + retVariant = item.group(); + break; + + case KDirModel::Type: + if (item.isDir()) { + // when sorting we want folders to be placed first + retVariant = QString(); // krazy:exclude=nullstrassign + } else { + retVariant = item.mimeComment(); + } + break; + + default: + break; + } + + return retVariant; +} diff --git a/src/views/dolphinmodel.h b/src/views/dolphinmodel.h new file mode 100644 index 000000000..6f1b00c75 --- /dev/null +++ b/src/views/dolphinmodel.h @@ -0,0 +1,68 @@ +/* + * This file is part of the KDE project + * Copyright (C) 2007 Rafael Fernández López <[email protected]> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#ifndef DOLPHINMODEL_H +#define DOLPHINMODEL_H + +#include <kdirmodel.h> +#include <kversioncontrolplugin.h> +#include <libdolphin_export.h> + +#include <QHash> +#include <QPersistentModelIndex> + +class LIBDOLPHINPRIVATE_EXPORT DolphinModel : public KDirModel +{ + Q_OBJECT + +public: + enum AdditionalColumns { + Version = KDirModel::ColumnCount, + LinkDest, + LocalPathOrUrl, + ExtraColumnCount // Mandatory last entry + }; + + DolphinModel(QObject* parent = 0); + virtual ~DolphinModel(); + + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual int columnCount(const QModelIndex& parent = QModelIndex()) const; + + void clearVersionData(); + bool hasVersionData() const; + +private slots: + void slotRowsRemoved(const QModelIndex& parent, int start, int end); + +private: + QVariant displayRoleData(const QModelIndex& index) const; + QVariant sortRoleData(const QModelIndex& index) const; + +private: + bool m_hasVersionData; + QHash<QPersistentModelIndex, KVersionControlPlugin::VersionState> m_revisionHash; + + static const char* const m_others; +}; + +#endif // DOLPHINMODEL_H diff --git a/src/views/dolphinnewmenuobserver.cpp b/src/views/dolphinnewmenuobserver.cpp new file mode 100644 index 000000000..def510c53 --- /dev/null +++ b/src/views/dolphinnewmenuobserver.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphinnewmenuobserver.h" + +#include <kglobal.h> +#include <knewmenu.h> + +class DolphinNewMenuObserverSingleton +{ +public: + DolphinNewMenuObserver instance; +}; +K_GLOBAL_STATIC(DolphinNewMenuObserverSingleton, s_dolphinNewMenuObserver) + +DolphinNewMenuObserver& DolphinNewMenuObserver::instance() +{ + return s_dolphinNewMenuObserver->instance; +} + +void DolphinNewMenuObserver::attach(const KNewFileMenu* menu) +{ + connect(menu, SIGNAL(fileCreated(const KUrl&)), + this, SIGNAL(itemCreated(const KUrl&))); + connect(menu, SIGNAL(directoryCreated(const KUrl&)), + this, SIGNAL(itemCreated(const KUrl&))); +} + +void DolphinNewMenuObserver::detach(const KNewFileMenu* menu) +{ + disconnect(menu, SIGNAL(fileCreated(const KUrl&)), + this, SIGNAL(itemCreated(const KUrl&))); + disconnect(menu, SIGNAL(directoryCreated(const KUrl&)), + this, SIGNAL(itemCreated(const KUrl&))); +} + +DolphinNewMenuObserver::DolphinNewMenuObserver() : + QObject(0) +{ +} + +DolphinNewMenuObserver::~DolphinNewMenuObserver() +{ +} + +#include "dolphinnewmenuobserver.moc" diff --git a/src/views/dolphinnewmenuobserver.h b/src/views/dolphinnewmenuobserver.h new file mode 100644 index 000000000..dc9010a43 --- /dev/null +++ b/src/views/dolphinnewmenuobserver.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2009 by Peter Penz <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHINNEWMENUOBSERVER_H +#define DOLPHINNEWMENUOBSERVER_H + +#include <QObject> + +#include "libdolphin_export.h" + +class KNewFileMenu; +class KUrl; + +/** + * @brief Allows to observe new file items that have been created + * by a DolphinNewMenu instance. + * + * As soon as a DolphinNewMenu instance created a new item, + * the observer will emit the signal itemCreated(). + */ +class LIBDOLPHINPRIVATE_EXPORT DolphinNewMenuObserver : public QObject +{ + Q_OBJECT + +public: + static DolphinNewMenuObserver& instance(); + void attach(const KNewFileMenu* menu); + void detach(const KNewFileMenu* menu); + +signals: + void itemCreated(const KUrl& url); + +private: + DolphinNewMenuObserver(); + virtual ~DolphinNewMenuObserver(); + + friend class DolphinNewMenuObserverSingleton; +}; + +#endif diff --git a/src/views/dolphinremoteencoding.cpp b/src/views/dolphinremoteencoding.cpp new file mode 100644 index 000000000..397eaccc0 --- /dev/null +++ b/src/views/dolphinremoteencoding.cpp @@ -0,0 +1,241 @@ +/*************************************************************************** + * Copyright (C) 2009 by Rahman Duran <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + + /* + * This code is largely based on the kremoteencodingplugin + * Copyright (c) 2003 Thiago Macieira <[email protected]> + * Distributed under the same terms. + */ + +#include "dolphinremoteencoding.h" +#include "dolphinviewactionhandler.h" + +#include <kdebug.h> +#include <kactionmenu.h> +#include <kactioncollection.h> +#include <kicon.h> +#include <klocale.h> +#include <kglobal.h> +#include <kmimetype.h> +#include <kconfig.h> +#include <kcharsets.h> +#include <kmenu.h> +#include <kprotocolinfo.h> +#include <kprotocolmanager.h> +#include <kio/slaveconfig.h> +#include <kio/scheduler.h> +#include <kconfiggroup.h> + +#define DATA_KEY QLatin1String("Charset") + +DolphinRemoteEncoding::DolphinRemoteEncoding(QObject* parent, DolphinViewActionHandler* actionHandler) + :QObject(parent), + m_actionHandler(actionHandler), + m_loaded(false), + m_idDefault(0) +{ + m_menu = new KActionMenu(KIcon("character-set"), i18n("Select Remote Charset"), this); + m_actionHandler->actionCollection()->addAction("change_remote_encoding", m_menu); + connect(m_menu->menu(), SIGNAL(aboutToShow()), + this, SLOT(slotAboutToShow())); + + m_menu->setEnabled(false); + m_menu->setDelayed(false); +} + +DolphinRemoteEncoding::~DolphinRemoteEncoding() +{ +} + +void DolphinRemoteEncoding::slotReload() +{ + loadSettings(); +} + +void DolphinRemoteEncoding::loadSettings() +{ + m_loaded = true; + m_encodingDescriptions = KGlobal::charsets()->descriptiveEncodingNames(); + + fillMenu(); +} + +void DolphinRemoteEncoding::slotAboutToOpenUrl() +{ + KUrl oldURL = m_currentURL; + m_currentURL = m_actionHandler->currentView()->url(); + + if (m_currentURL.protocol() != oldURL.protocol()) { + // This plugin works on ftp, fish, etc. + // everything whose type is T_FILESYSTEM except for local files + if (!m_currentURL.isLocalFile() && + KProtocolManager::outputType(m_currentURL) == KProtocolInfo::T_FILESYSTEM) { + + m_menu->setEnabled(true); + loadSettings(); + } else { + m_menu->setEnabled(false); + } + return; + } + + if (m_currentURL.host() != oldURL.host()) { + updateMenu(); + } +} + +void DolphinRemoteEncoding::fillMenu() +{ + KMenu* menu = m_menu->menu(); + menu->clear(); + + + for (int i = 0; i < m_encodingDescriptions.size();i++) { + QAction* action = new QAction(m_encodingDescriptions.at(i), this); + action->setCheckable(true); + action->setData(i); + menu->addAction(action); + } + menu->addSeparator(); + + menu->addAction(i18n("Reload"), this, SLOT(slotReload()), 0); + menu->addAction(i18n("Default"), this, SLOT(slotDefault()), 0)->setCheckable(true); + m_idDefault = m_encodingDescriptions.size() + 2; + + connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(slotItemSelected(QAction*))); +} + +void DolphinRemoteEncoding::updateMenu() +{ + if (!m_loaded) { + loadSettings(); + } + + // uncheck everything + for (int i = 0; i < m_menu->menu()->actions().count(); i++) { + m_menu->menu()->actions().at(i)->setChecked(false); + } + + QString charset = KIO::SlaveConfig::self()->configData(m_currentURL.protocol(), + m_currentURL.host(), DATA_KEY); + + if (!charset.isEmpty()) { + int id = 0; + bool isFound = false; + for (int i = 0; i < m_encodingDescriptions.size(); i++) { + if (m_encodingDescriptions.at(i).contains(charset)) { + isFound = true; + id = i; + break; + } + } + + kDebug() << "URL=" << m_currentURL << " charset=" << charset; + + if (!isFound) { + kWarning() << "could not find entry for charset=" << charset ; + } else { + m_menu->menu()->actions().at(id)->setChecked(true); + } + } else { + m_menu->menu()->actions().at(m_idDefault)->setChecked(true); + } + +} + +void DolphinRemoteEncoding::slotAboutToShow() +{ + if (!m_loaded) { + loadSettings(); + } + updateMenu(); +} + +void DolphinRemoteEncoding::slotItemSelected(QAction* action) +{ + if (action != 0) { + int id = action->data().toInt(); + + KConfig config(("kio_" + m_currentURL.protocol() + "rc").toLatin1()); + QString host = m_currentURL.host(); + if (m_menu->menu()->actions().at(id)->isChecked()) { + QString charset = KGlobal::charsets()->encodingForName(m_encodingDescriptions.at(id)); + KConfigGroup cg(&config, host); + cg.writeEntry(DATA_KEY, charset); + config.sync(); + + // Update the io-slaves... + updateView(); + } + } +} + +void DolphinRemoteEncoding::slotDefault() +{ + // We have no choice but delete all higher domain level + // settings here since it affects what will be matched. + KConfig config(("kio_" + m_currentURL.protocol() + "rc").toLatin1()); + + QStringList partList = m_currentURL.host().split('.', QString::SkipEmptyParts); + if (!partList.isEmpty()) { + partList.erase(partList.begin()); + + QStringList domains; + // Remove the exact name match... + domains << m_currentURL.host(); + + while (partList.count()) { + if (partList.count() == 2) { + if (partList[0].length() <= 2 && partList[1].length() == 2) { + break; + } + } + + if (partList.count() == 1) { + break; + } + + domains << partList.join("."); + partList.erase(partList.begin()); + } + + for (QStringList::const_iterator it = domains.constBegin(); it != domains.constEnd();++it) { + kDebug() << "Domain to remove: " << *it; + if (config.hasGroup(*it)) { + config.deleteGroup(*it); + } else if (config.group("").hasKey(*it)) { + config.group("").deleteEntry(*it); //don't know what group name is supposed to be XXX + } + } + } + config.sync(); + + // Update the io-slaves. + updateView(); +} + +void DolphinRemoteEncoding::updateView() +{ + KIO::Scheduler::emitReparseSlaveConfiguration(); + // Reload the page with the new charset + m_actionHandler->currentView()->setUrl(m_currentURL); + m_actionHandler->currentView()->reload(); +} + +#include "dolphinremoteencoding.moc" diff --git a/src/views/dolphinremoteencoding.h b/src/views/dolphinremoteencoding.h new file mode 100644 index 000000000..54499f78b --- /dev/null +++ b/src/views/dolphinremoteencoding.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2009 by Rahman Duran <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHINREMOTEENCODING_H +#define DOLPHINREMOTEENCODING_H + +#include <QStringList> +#include <QtGui/QAction> +#include <kurl.h> +#include "libdolphin_export.h" + + +class KActionMenu; +class DolphinViewActionHandler; + +/** + * @brief Allows to chnage character encoding for remote urls like ftp. + * + * When browsing remote url, its possible to change encoding from Tools Menu. + */ + +class LIBDOLPHINPRIVATE_EXPORT DolphinRemoteEncoding: public QObject +{ + Q_OBJECT +public: + DolphinRemoteEncoding(QObject* parent, DolphinViewActionHandler* actionHandler); + ~DolphinRemoteEncoding(); + +public Q_SLOTS: + void slotAboutToOpenUrl(); + void slotItemSelected(QAction* action); + void slotReload(); + void slotDefault(); + +private Q_SLOTS: + void slotAboutToShow(); + +private: + void updateView(); + void loadSettings(); + void fillMenu(); + void updateMenu(); + + KActionMenu* m_menu; + QStringList m_encodingDescriptions; + KUrl m_currentURL; + DolphinViewActionHandler* m_actionHandler; + + bool m_loaded; + int m_idDefault; +}; + +#endif diff --git a/src/views/dolphinsortfilterproxymodel.cpp b/src/views/dolphinsortfilterproxymodel.cpp new file mode 100644 index 000000000..70256f156 --- /dev/null +++ b/src/views/dolphinsortfilterproxymodel.cpp @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Peter Penz <[email protected]> * + * Copyright (C) 2006 by Dominic Battre <[email protected]> * + * Copyright (C) 2006 by Martin Pool <[email protected]> * + * Copyright (C) 2007 by Rafael Fernández López <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphinsortfilterproxymodel.h" + +DolphinSortFilterProxyModel::DolphinSortFilterProxyModel(QObject* parent) : + KDirSortFilterProxyModel(parent), + m_sorting(DolphinView::SortByName), + m_sortOrder(Qt::AscendingOrder) +{ +} + +DolphinSortFilterProxyModel::~DolphinSortFilterProxyModel() +{ +} + +void DolphinSortFilterProxyModel::setSorting(DolphinView::Sorting sorting) +{ + m_sorting = sorting; + KDirSortFilterProxyModel::sort(static_cast<int>(m_sorting), m_sortOrder); +} + +void DolphinSortFilterProxyModel::setSortOrder(Qt::SortOrder sortOrder) +{ + m_sortOrder = sortOrder; + KDirSortFilterProxyModel::sort(static_cast<int>(m_sorting), m_sortOrder); +} + +void DolphinSortFilterProxyModel::setSortFoldersFirst(bool foldersFirst) +{ + if (foldersFirst != sortFoldersFirst()) { + KDirSortFilterProxyModel::setSortFoldersFirst(foldersFirst); + + // We need to make sure that the files and folders are really resorted. + // Without the following two lines, QSortFilterProxyModel::sort(int column, Qt::SortOrder order) + // would do nothing because neither the column nor the sort order have changed. + // TODO: remove this hack if we find a better way to force the ProxyModel to re-sort the data. + const Qt::SortOrder tmpSortOrder = (m_sortOrder == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder); + KDirSortFilterProxyModel::sort(static_cast<int>(m_sorting), tmpSortOrder); + + // Now comes the real sorting with the old column and sort order + KDirSortFilterProxyModel::sort(static_cast<int>(m_sorting), m_sortOrder); + } +} + +void DolphinSortFilterProxyModel::sort(int column, Qt::SortOrder sortOrder) +{ + m_sorting = sortingForColumn(column); + m_sortOrder = sortOrder; + + emit sortingRoleChanged(); + KDirSortFilterProxyModel::sort(static_cast<int>(m_sorting), sortOrder); +} + +DolphinView::Sorting DolphinSortFilterProxyModel::sortingForColumn(int column) +{ + Q_ASSERT(column >= 0); + Q_ASSERT(column <= DolphinView::MaxSortingEnum); + return static_cast<DolphinView::Sorting>(column); +} + +#include "dolphinsortfilterproxymodel.moc" diff --git a/src/views/dolphinsortfilterproxymodel.h b/src/views/dolphinsortfilterproxymodel.h new file mode 100644 index 000000000..3ae8a059c --- /dev/null +++ b/src/views/dolphinsortfilterproxymodel.h @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Peter Penz <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DOLPHINSORTFILTERPROXYMODEL_H +#define DOLPHINSORTFILTERPROXYMODEL_H + +#include <views/dolphinview.h> +#include <kdirsortfilterproxymodel.h> +#include <libdolphin_export.h> + +/** + * @brief Acts as proxy model for DolphinModel to sort and filter + * KFileItems. + * + * Per default a natural sorting is done. This means that items like: + * - item_10.png + * - item_1.png + * - item_2.png + * are sorted like + * - item_1.png + * - item_2.png + * - item_10.png + */ +class LIBDOLPHINPRIVATE_EXPORT DolphinSortFilterProxyModel : public KDirSortFilterProxyModel +{ + Q_OBJECT + +public: + DolphinSortFilterProxyModel(QObject* parent = 0); + virtual ~DolphinSortFilterProxyModel(); + + void setSorting(DolphinView::Sorting sorting); + DolphinView::Sorting sorting() const; + + void setSortOrder(Qt::SortOrder sortOrder); + Qt::SortOrder sortOrder() const; + + void setSortFoldersFirst(bool foldersFirst); + + /** @reimplemented */ + virtual void sort(int column, + Qt::SortOrder order = Qt::AscendingOrder); + + /** + * Helper method to get the DolphinView::Sorting type for a given + * column \a column. If the column is smaller 0 or greater than the + * available columns, DolphinView::SortByName is returned. + */ + static DolphinView::Sorting sortingForColumn(int column); + +signals: + void sortingRoleChanged(); + +private: + DolphinView::Sorting m_sorting:16; + Qt::SortOrder m_sortOrder:16; +}; + +inline DolphinView::Sorting DolphinSortFilterProxyModel::sorting() const +{ + return m_sorting; +} + +inline Qt::SortOrder DolphinSortFilterProxyModel::sortOrder() const +{ + return m_sortOrder; +} + +#endif diff --git a/src/views/dolphinviewactionhandler.cpp b/src/views/dolphinviewactionhandler.cpp new file mode 100644 index 000000000..d2eaa4d51 --- /dev/null +++ b/src/views/dolphinviewactionhandler.cpp @@ -0,0 +1,518 @@ +/*************************************************************************** + * Copyright (C) 2008 by David Faure <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "dolphinviewactionhandler.h" + +#include "additionalinfoaccessor.h" +#include "settings/viewpropertiesdialog.h" +#include "views/dolphinview.h" +#include "views/zoomlevelinfo.h" +#include <konq_operations.h> + +#include <kaction.h> +#include <kactioncollection.h> +#include <kactionmenu.h> +#include <kfileitemdelegate.h> +#include <klocale.h> +#include <knewmenu.h> +#include <kselectaction.h> +#include <ktoggleaction.h> +#include <krun.h> +#include <kpropertiesdialog.h> + + +DolphinViewActionHandler::DolphinViewActionHandler(KActionCollection* collection, QObject* parent) + : QObject(parent), + m_actionCollection(collection), + m_currentView(0) +{ + Q_ASSERT(m_actionCollection); + createActions(); +} + +void DolphinViewActionHandler::setCurrentView(DolphinView* view) +{ + Q_ASSERT(view); + + if (m_currentView) + disconnect(m_currentView, 0, this, 0); + + m_currentView = view; + + connect(view, SIGNAL(modeChanged()), + this, SLOT(updateViewActions())); + connect(view, SIGNAL(showPreviewChanged()), + this, SLOT(slotShowPreviewChanged())); + connect(view, SIGNAL(sortOrderChanged(Qt::SortOrder)), + this, SLOT(slotSortOrderChanged(Qt::SortOrder))); + connect(view, SIGNAL(sortFoldersFirstChanged(bool)), + this, SLOT(slotSortFoldersFirstChanged(bool))); + connect(view, SIGNAL(additionalInfoChanged()), + this, SLOT(slotAdditionalInfoChanged())); + connect(view, SIGNAL(categorizedSortingChanged()), + this, SLOT(slotCategorizedSortingChanged())); + connect(view, SIGNAL(showHiddenFilesChanged()), + this, SLOT(slotShowHiddenFilesChanged())); + connect(view, SIGNAL(sortingChanged(DolphinView::Sorting)), + this, SLOT(slotSortingChanged(DolphinView::Sorting))); + connect(view, SIGNAL(zoomLevelChanged(int)), + this, SLOT(slotZoomLevelChanged(int))); +} + +DolphinView* DolphinViewActionHandler::currentView() +{ + return m_currentView; +} + +void DolphinViewActionHandler::createActions() +{ + // This action doesn't appear in the GUI, it's for the shortcut only. + // KNewFileMenu takes care of the GUI stuff. + KAction* newDirAction = m_actionCollection->addAction("create_dir"); + newDirAction->setText(i18nc("@action", "Create Folder...")); + newDirAction->setShortcut(Qt::Key_F10); + newDirAction->setIcon(KIcon("folder-new")); + connect(newDirAction, SIGNAL(triggered()), this, SIGNAL(createDirectory())); + + // File menu + + KAction* rename = m_actionCollection->addAction("rename"); + rename->setText(i18nc("@action:inmenu File", "Rename...")); + rename->setShortcut(Qt::Key_F2); + rename->setIcon(KIcon("edit-rename")); + connect(rename, SIGNAL(triggered()), this, SLOT(slotRename())); + + KAction* moveToTrash = m_actionCollection->addAction("move_to_trash"); + moveToTrash->setText(i18nc("@action:inmenu File", "Move to Trash")); + moveToTrash->setIcon(KIcon("user-trash")); + moveToTrash->setShortcut(QKeySequence::Delete); + connect(moveToTrash, SIGNAL(triggered(Qt::MouseButtons, Qt::KeyboardModifiers)), + this, SLOT(slotTrashActivated(Qt::MouseButtons, Qt::KeyboardModifiers))); + + KAction* deleteAction = m_actionCollection->addAction("delete"); + deleteAction->setIcon(KIcon("edit-delete")); + deleteAction->setText(i18nc("@action:inmenu File", "Delete")); + deleteAction->setShortcut(Qt::SHIFT | Qt::Key_Delete); + connect(deleteAction, SIGNAL(triggered()), this, SLOT(slotDeleteItems())); + + // This action is useful for being enabled when "move_to_trash" should be + // disabled and "delete" is enabled (e.g. non-local files), so that Key_Del + // can be used for deleting the file (#76016). It needs to be a separate action + // so that the Edit menu isn't affected. + KAction* deleteWithTrashShortcut = m_actionCollection->addAction("delete_shortcut"); + // The descriptive text is just for the shortcuts editor. + deleteWithTrashShortcut->setText(i18nc("@action \"Move to Trash\" for non-local files, etc.", "Delete (using shortcut for Trash)")); + deleteWithTrashShortcut->setShortcut(QKeySequence::Delete); + deleteWithTrashShortcut->setEnabled(false); + connect(deleteWithTrashShortcut, SIGNAL(triggered()), this, SLOT(slotDeleteItems())); + + KAction *propertiesAction = m_actionCollection->addAction( "properties" ); + // Well, it's the File menu in dolphinmainwindow and the Edit menu in dolphinpart... :) + propertiesAction->setText( i18nc("@action:inmenu File", "Properties") ); + propertiesAction->setIcon(KIcon("document-properties")); + propertiesAction->setShortcut(Qt::ALT | Qt::Key_Return); + connect(propertiesAction, SIGNAL(triggered()), SLOT(slotProperties())); + + // View menu + KToggleAction* iconsAction = iconsModeAction(); + KToggleAction* detailsAction = detailsModeAction(); + KToggleAction* columnsAction = columnsModeAction(); + + KSelectAction* viewModeActions = m_actionCollection->add<KSelectAction>("view_mode"); + viewModeActions->setText(i18nc("@action:intoolbar", "View Mode")); + viewModeActions->addAction(iconsAction); + viewModeActions->addAction(detailsAction); + viewModeActions->addAction(columnsAction); + viewModeActions->setToolBarMode(KSelectAction::MenuMode); + connect(viewModeActions, SIGNAL(triggered(QAction*)), this, SLOT(slotViewModeActionTriggered(QAction*))); + + KStandardAction::zoomIn(this, + SLOT(zoomIn()), + m_actionCollection); + + KStandardAction::zoomOut(this, + SLOT(zoomOut()), + m_actionCollection); + + KToggleAction* showPreview = m_actionCollection->add<KToggleAction>("show_preview"); + showPreview->setText(i18nc("@action:intoolbar", "Preview")); + showPreview->setToolTip(i18nc("@info", "Show preview of files and folders")); + showPreview->setIcon(KIcon("view-preview")); + connect(showPreview, SIGNAL(triggered(bool)), this, SLOT(togglePreview(bool))); + + KToggleAction* sortDescending = m_actionCollection->add<KToggleAction>("descending"); + sortDescending->setText(i18nc("@action:inmenu Sort", "Descending")); + connect(sortDescending, SIGNAL(triggered()), this, SLOT(toggleSortOrder())); + + KToggleAction* sortFoldersFirst = m_actionCollection->add<KToggleAction>("folders_first"); + sortFoldersFirst->setText(i18nc("@action:inmenu Sort", "Folders First")); + connect(sortFoldersFirst, SIGNAL(triggered()), this, SLOT(toggleSortFoldersFirst())); + + // View -> Sort By + QActionGroup* sortByActionGroup = createSortByActionGroup(); + connect(sortByActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotSortTriggered(QAction*))); + + KActionMenu* sortByActionMenu = m_actionCollection->add<KActionMenu>("sort"); + sortByActionMenu->setText(i18nc("@action:inmenu View", "Sort By")); + sortByActionMenu->setDelayed(false); + + foreach (QAction* action, sortByActionGroup->actions()) { + sortByActionMenu->addAction(action); + } + sortByActionMenu->addSeparator(); + sortByActionMenu->addAction(sortDescending); + sortByActionMenu->addAction(sortFoldersFirst); + + // View -> Additional Information + QActionGroup* additionalInfoGroup = createAdditionalInformationActionGroup(); + connect(additionalInfoGroup, SIGNAL(triggered(QAction*)), this, SLOT(toggleAdditionalInfo(QAction*))); + + KActionMenu* additionalInfoMenu = m_actionCollection->add<KActionMenu>("additional_info"); + additionalInfoMenu->setText(i18nc("@action:inmenu View", "Additional Information")); + additionalInfoMenu->setDelayed(false); + foreach (QAction* action, additionalInfoGroup->actions()) { + additionalInfoMenu->addAction(action); + } + + KToggleAction* showInGroups = m_actionCollection->add<KToggleAction>("show_in_groups"); + showInGroups->setText(i18nc("@action:inmenu View", "Show in Groups")); + connect(showInGroups, SIGNAL(triggered(bool)), this, SLOT(toggleSortCategorization(bool))); + + KToggleAction* showHiddenFiles = m_actionCollection->add<KToggleAction>("show_hidden_files"); + showHiddenFiles->setText(i18nc("@action:inmenu View", "Show Hidden Files")); + showHiddenFiles->setShortcuts(QList<QKeySequence>() << Qt::ALT + Qt::Key_Period << Qt::Key_F8); + connect(showHiddenFiles, SIGNAL(triggered(bool)), this, SLOT(toggleShowHiddenFiles(bool))); + + KAction* adjustViewProps = m_actionCollection->addAction("view_properties"); + adjustViewProps->setText(i18nc("@action:inmenu View", "Adjust View Properties...")); + connect(adjustViewProps, SIGNAL(triggered()), this, SLOT(slotAdjustViewProperties())); +} + +QActionGroup* DolphinViewActionHandler::createAdditionalInformationActionGroup() +{ + QActionGroup* additionalInfoGroup = new QActionGroup(m_actionCollection); + additionalInfoGroup->setExclusive(false); + + KActionMenu* showInformationMenu = m_actionCollection->add<KActionMenu>("additional_info"); + showInformationMenu->setText(i18nc("@action:inmenu View", "Additional Information")); + showInformationMenu->setDelayed(false); + + const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); + + const KFileItemDelegate::InformationList infos = infoAccessor.keys(); + foreach (KFileItemDelegate::Information info, infos) { + const QString name = infoAccessor.actionCollectionName(info, AdditionalInfoAccessor::AdditionalInfoType); + KToggleAction* action = m_actionCollection->add<KToggleAction>(name); + action->setText(infoAccessor.translation(info)); + action->setData(info); + action->setActionGroup(additionalInfoGroup); + } + + return additionalInfoGroup; +} + +Q_DECLARE_METATYPE(DolphinView::Sorting) + +QActionGroup* DolphinViewActionHandler::createSortByActionGroup() +{ + QActionGroup* sortByActionGroup = new QActionGroup(m_actionCollection); + sortByActionGroup->setExclusive(true); + + KToggleAction* sortByName = m_actionCollection->add<KToggleAction>("sort_by_name"); + sortByName->setText(i18nc("@action:inmenu Sort By", "Name")); + sortByName->setData(QVariant::fromValue(DolphinView::SortByName)); + sortByActionGroup->addAction(sortByName); + + const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); + const KFileItemDelegate::InformationList infos = infoAccessor.keys(); + foreach (KFileItemDelegate::Information info, infos) { + const QString name = infoAccessor.actionCollectionName(info, AdditionalInfoAccessor::SortByType); + KToggleAction* action = m_actionCollection->add<KToggleAction>(name); + action->setText(infoAccessor.translation(info)); + const DolphinView::Sorting sorting = infoAccessor.sorting(info); + action->setData(QVariant::fromValue(sorting)); + sortByActionGroup->addAction(action); + } + + return sortByActionGroup; +} + +void DolphinViewActionHandler::slotViewModeActionTriggered(QAction* action) +{ + const DolphinView::Mode mode = action->data().value<DolphinView::Mode>(); + m_currentView->setMode(mode); + + QAction* viewModeMenu = m_actionCollection->action("view_mode"); + viewModeMenu->setIcon(KIcon(action->icon())); +} + +void DolphinViewActionHandler::slotRename() +{ + emit actionBeingHandled(); + m_currentView->renameSelectedItems(); +} + +void DolphinViewActionHandler::slotTrashActivated(Qt::MouseButtons, Qt::KeyboardModifiers modifiers) +{ + emit actionBeingHandled(); + // Note: kde3's konq_mainwindow.cpp used to check + // reason == KAction::PopupMenuActivation && ... + // but this isn't supported anymore + if (modifiers & Qt::ShiftModifier) { + m_currentView->deleteSelectedItems(); + } else { + m_currentView->trashSelectedItems(); + } +} + +void DolphinViewActionHandler::slotDeleteItems() +{ + emit actionBeingHandled(); + m_currentView->deleteSelectedItems(); +} + +void DolphinViewActionHandler::togglePreview(bool show) +{ + emit actionBeingHandled(); + m_currentView->setShowPreview(show); +} + +void DolphinViewActionHandler::slotShowPreviewChanged() +{ + // It is not enough to update the 'Show Preview' action, also + // the 'Zoom In' and 'Zoom Out' actions must be adapted. + updateViewActions(); +} + +QString DolphinViewActionHandler::currentViewModeActionName() const +{ + switch (m_currentView->mode()) { + case DolphinView::IconsView: + return "icons"; + case DolphinView::DetailsView: + return "details"; + case DolphinView::ColumnView: + return "columns"; + } + return QString(); // can't happen +} + +KActionCollection* DolphinViewActionHandler::actionCollection() +{ + return m_actionCollection; +} + +void DolphinViewActionHandler::updateViewActions() +{ + QAction* viewModeAction = m_actionCollection->action(currentViewModeActionName()); + if (viewModeAction != 0) { + viewModeAction->setChecked(true); + + QAction* viewModeMenu = m_actionCollection->action("view_mode"); + viewModeMenu->setIcon(KIcon(viewModeAction->icon())); + } + + QAction* showPreviewAction = m_actionCollection->action("show_preview"); + showPreviewAction->setChecked(m_currentView->showPreview()); + + slotSortOrderChanged(m_currentView->sortOrder()); + slotSortFoldersFirstChanged(m_currentView->sortFoldersFirst()); + slotAdditionalInfoChanged(); + slotCategorizedSortingChanged(); + slotSortingChanged(m_currentView->sorting()); + slotZoomLevelChanged(m_currentView->zoomLevel()); + + QAction* showHiddenFilesAction = m_actionCollection->action("show_hidden_files"); + showHiddenFilesAction->setChecked(m_currentView->showHiddenFiles()); +} + +void DolphinViewActionHandler::zoomIn() +{ + const int level = m_currentView->zoomLevel(); + m_currentView->setZoomLevel(level + 1); + updateViewActions(); +} + +void DolphinViewActionHandler::zoomOut() +{ + const int level = m_currentView->zoomLevel(); + m_currentView->setZoomLevel(level - 1); + updateViewActions(); +} + +void DolphinViewActionHandler::toggleSortOrder() +{ + m_currentView->toggleSortOrder(); +} + +void DolphinViewActionHandler::toggleSortFoldersFirst() +{ + m_currentView->toggleSortFoldersFirst(); +} + +void DolphinViewActionHandler::slotSortOrderChanged(Qt::SortOrder order) +{ + QAction* descending = m_actionCollection->action("descending"); + const bool sortDescending = (order == Qt::DescendingOrder); + descending->setChecked(sortDescending); +} + +void DolphinViewActionHandler::slotSortFoldersFirstChanged(bool foldersFirst) +{ + m_actionCollection->action("folders_first")->setChecked(foldersFirst); +} + +void DolphinViewActionHandler::toggleAdditionalInfo(QAction* action) +{ + emit actionBeingHandled(); + m_currentView->toggleAdditionalInfo(action); +} + +void DolphinViewActionHandler::slotAdditionalInfoChanged() +{ + m_currentView->updateAdditionalInfoActions(m_actionCollection); +} + +void DolphinViewActionHandler::toggleSortCategorization(bool categorizedSorting) +{ + m_currentView->setCategorizedSorting(categorizedSorting); +} + +void DolphinViewActionHandler::slotCategorizedSortingChanged() +{ + QAction* showInGroupsAction = m_actionCollection->action("show_in_groups"); + showInGroupsAction->setChecked(m_currentView->categorizedSorting()); + showInGroupsAction->setEnabled(m_currentView->supportsCategorizedSorting()); +} + +void DolphinViewActionHandler::toggleShowHiddenFiles(bool show) +{ + emit actionBeingHandled(); + m_currentView->setShowHiddenFiles(show); +} + +void DolphinViewActionHandler::slotShowHiddenFilesChanged() +{ + QAction* showHiddenFilesAction = m_actionCollection->action("show_hidden_files"); + showHiddenFilesAction->setChecked(m_currentView->showHiddenFiles()); +} + + +KToggleAction* DolphinViewActionHandler::iconsModeAction() +{ + KToggleAction* iconsView = m_actionCollection->add<KToggleAction>("icons"); + iconsView->setText(i18nc("@action:inmenu View Mode", "Icons")); + iconsView->setToolTip(i18nc("@info", "Icons view mode")); + iconsView->setShortcut(Qt::CTRL | Qt::Key_1); + iconsView->setIcon(KIcon("view-list-icons")); + iconsView->setData(QVariant::fromValue(DolphinView::IconsView)); + return iconsView; +} + +KToggleAction* DolphinViewActionHandler::detailsModeAction() +{ + KToggleAction* detailsView = m_actionCollection->add<KToggleAction>("details"); + detailsView->setText(i18nc("@action:inmenu View Mode", "Details")); + detailsView->setToolTip(i18nc("@info", "Details view mode")); + detailsView->setShortcut(Qt::CTRL | Qt::Key_2); + detailsView->setIcon(KIcon("view-list-details")); + detailsView->setData(QVariant::fromValue(DolphinView::DetailsView)); + return detailsView; +} + +KToggleAction* DolphinViewActionHandler::columnsModeAction() +{ + KToggleAction* columnView = m_actionCollection->add<KToggleAction>("columns"); + columnView->setText(i18nc("@action:inmenu View Mode", "Columns")); + columnView->setToolTip(i18nc("@info", "Columns view mode")); + columnView->setShortcut(Qt::CTRL | Qt::Key_3); + columnView->setIcon(KIcon("view-file-columns")); + columnView->setData(QVariant::fromValue(DolphinView::ColumnView)); + return columnView; +} + +void DolphinViewActionHandler::slotSortingChanged(DolphinView::Sorting sorting) +{ + const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); + const KFileItemDelegate::InformationList infos = infoAccessor.keys(); + + QAction* action = 0; + if (sorting == DolphinView::SortByName) { + action = m_actionCollection->action("sort_by_name"); + } else { + foreach (const KFileItemDelegate::Information info, infos) { + if (sorting == infoAccessor.sorting(info)) { + const QString name = infoAccessor.actionCollectionName(info, AdditionalInfoAccessor::SortByType); + action = m_actionCollection->action(name); + break; + } + } + } + + if (action != 0) { + action->setChecked(true); + + QAction* sortByMenu = m_actionCollection->action("sort"); + sortByMenu->setIcon(KIcon(action->icon())); + } +} + +void DolphinViewActionHandler::slotZoomLevelChanged(int level) +{ + QAction* zoomInAction = m_actionCollection->action(KStandardAction::name(KStandardAction::ZoomIn)); + if (zoomInAction != 0) { + zoomInAction->setEnabled(level < ZoomLevelInfo::maximumLevel()); + } + + QAction* zoomOutAction = m_actionCollection->action(KStandardAction::name(KStandardAction::ZoomOut)); + if (zoomOutAction != 0) { + zoomOutAction->setEnabled(level > ZoomLevelInfo::minimumLevel()); + } +} + +void DolphinViewActionHandler::slotSortTriggered(QAction* action) +{ + const DolphinView::Sorting sorting = action->data().value<DolphinView::Sorting>(); + m_currentView->setSorting(sorting); +} + +void DolphinViewActionHandler::slotAdjustViewProperties() +{ + emit actionBeingHandled(); + QPointer<ViewPropertiesDialog> dialog = new ViewPropertiesDialog(m_currentView); + dialog->exec(); + delete dialog; +} + +void DolphinViewActionHandler::slotProperties() +{ + KPropertiesDialog* dialog = 0; + const KFileItemList list = m_currentView->selectedItems(); + if (list.isEmpty()) { + const KUrl url = m_currentView->url(); + dialog = new KPropertiesDialog(url, m_currentView); + } else { + dialog = new KPropertiesDialog(list, m_currentView); + } + + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->show(); + dialog->raise(); + dialog->activateWindow(); +} diff --git a/src/views/dolphinviewactionhandler.h b/src/views/dolphinviewactionhandler.h new file mode 100644 index 000000000..05339ce37 --- /dev/null +++ b/src/views/dolphinviewactionhandler.h @@ -0,0 +1,252 @@ +/*************************************************************************** + * Copyright (C) 2008 by David Faure <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + + +#ifndef DOLPHINVIEWACTIONHANDLER_H +#define DOLPHINVIEWACTIONHANDLER_H + +#include "libdolphin_export.h" +#include <kactionmenu.h> +#include <kselectaction.h> +#include "views/dolphinview.h" +#include <QtCore/QObject> + +class KToggleAction; +class QAction; +class QActionGroup; +class DolphinView; +class KActionCollection; + +/** + * @short Handles all actions for DolphinView + * + * The action handler owns all the actions and slots related to DolphinView, + * but can the view that is acts upon can be switched to another one + * (this is used in the case of split views). + * + * The purpose of this class is also to share this code between DolphinMainWindow + * and DolphinPart. + * + * @see DolphinView + * @see DolphinMainWindow + * @see DolphinPart + */ +class LIBDOLPHINPRIVATE_EXPORT DolphinViewActionHandler : public QObject +{ + Q_OBJECT + +public: + explicit DolphinViewActionHandler(KActionCollection* collection, QObject* parent); + + /** + * Sets the view that this action handler should work on. + */ + void setCurrentView(DolphinView* view); + + /** + * Returns the view that this action handler should work on. + */ + DolphinView* currentView(); + + /** + * Returns the name of the action for the current viewmode + */ + QString currentViewModeActionName() const; + + /** + * Returns m_actionCollection + */ + KActionCollection* actionCollection(); + +public Q_SLOTS: + /** + * Update all actions in the 'View' menu, i.e. those that depend on the + * settings in the current view. + */ + void updateViewActions(); + +Q_SIGNALS: + /** + * Emitted by DolphinViewActionHandler when the user triggered an action. + * This is only used for clearining the statusbar in DolphinMainWindow. + */ + void actionBeingHandled(); + + /** + * Emitted if the user requested creating a new directory by the F10 key. + * The receiver of the signal (DolphinMainWindow or DolphinPart) invokes + * the method createDirectory of their KNewFileMenu instance. + */ + void createDirectory(); + +private Q_SLOTS: + /** + * Emitted when the user requested a change of view mode + */ + void slotViewModeActionTriggered(QAction*); + + /** + * Let the user input a name for the selected item(s) and trigger + * a renaming afterwards. + */ + void slotRename(); + + /** + * Moves the selected items of the active view to the trash. + * This methods adds "shift means del" handling. + */ + void slotTrashActivated(Qt::MouseButtons, Qt::KeyboardModifiers); + + /** + * Deletes the selected items of the active view. + */ + void slotDeleteItems(); + + /** + * Switches between showing a preview of the file content and showing the icon. + */ + void togglePreview(bool); + + /** Updates the state of the 'Show preview' menu action. */ + void slotShowPreviewChanged(); + + /** Increases the size of the current set view mode. */ + void zoomIn(); + + /** Decreases the size of the current set view mode. */ + void zoomOut(); + + /** Switches between an ascending and descending sorting order. */ + void toggleSortOrder(); + + /** Switches between a separate sorting and a mixed sorting of files and folders. */ + void toggleSortFoldersFirst(); + + /** + * Updates the state of the 'Sort Ascending/Descending' action. + */ + void slotSortOrderChanged(Qt::SortOrder order); + + /** + * Updates the state of the 'Sort Folders First' action. + */ + void slotSortFoldersFirstChanged(bool foldersFirst); + + /** + * Updates the state of the 'Sort by' actions. + */ + void slotSortingChanged(DolphinView::Sorting sorting); + + /** + * Updates the state of the 'Zoom In' and 'Zoom Out' actions. + */ + void slotZoomLevelChanged(int level); + + /** + * Switches on or off the displaying of additional information + * as specified by \a action. + */ + void toggleAdditionalInfo(QAction* action); + + /** + * Changes the sorting of the current view. + */ + void slotSortTriggered(QAction*); + + /** + * Updates the state of the 'Additional Information' actions. + */ + void slotAdditionalInfoChanged(); + + /** + * Switches between sorting by categories or not. + */ + void toggleSortCategorization(bool); + + /** + * Updates the state of the 'Categorized sorting' menu action. + */ + void slotCategorizedSortingChanged(); + + /** + * Switches between showing and hiding of hidden marked files + */ + void toggleShowHiddenFiles(bool); + + /** + * Updates the state of the 'Show hidden files' menu action. + */ + void slotShowHiddenFilesChanged(); + + /** + * Opens the view properties dialog, which allows to modify the properties + * of the currently active view. + */ + void slotAdjustViewProperties(); + + /** + * Connected to the "properties" action. + * Opens the properties dialog for the selected items of the + * active view. The properties dialog shows information + * like name, size and permissions. + */ + void slotProperties(); + +private: + /** + * Create all the actions. + * This is called only once (by the constructor) + */ + void createActions(); + + /** + * Creates an action group with all the "show additional information" actions in it. + * Helper method for createActions(); + */ + QActionGroup* createAdditionalInformationActionGroup(); + + /** + * Creates an action group with all the "sort by" actions in it. + * Helper method for createActions(); + */ + QActionGroup* createSortByActionGroup(); + + /** + * Returns the "switch to icons mode" action. + * Helper method for createActions(); + */ + KToggleAction* iconsModeAction(); + + /** + * Returns the "switch to details mode" action. + * Helper method for createActions(); + */ + KToggleAction* detailsModeAction(); + + /** + * Returns the "switch to columns mode" action. + * Helper method for createActions(); + */ + KToggleAction* columnsModeAction(); + + KActionCollection* m_actionCollection; + DolphinView* m_currentView; +}; + +#endif /* DOLPHINVIEWACTIONHANDLER_H */ diff --git a/src/views/draganddrophelper.cpp b/src/views/draganddrophelper.cpp new file mode 100644 index 000000000..0cca59ac4 --- /dev/null +++ b/src/views/draganddrophelper.cpp @@ -0,0 +1,189 @@ +/*************************************************************************** + * Copyright (C) 2007 by Peter Penz <[email protected]> * + * Copyright (C) 2007 by David Faure <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "draganddrophelper.h" + +#include <kdirmodel.h> +#include <kfileitem.h> +#include <kicon.h> +#include <klocale.h> +#include <konq_operations.h> + +#include "views/dolphiniconsview.h" +#include "views/dolphinviewcontroller.h" + +#include <QAbstractItemView> +#include <QAbstractProxyModel> +#include <QtDBus> +#include <QDrag> +#include <QPainter> + +class DragAndDropHelperSingleton +{ +public: + DragAndDropHelper instance; +}; +K_GLOBAL_STATIC(DragAndDropHelperSingleton, s_dragAndDropHelper) + +DragAndDropHelper& DragAndDropHelper::instance() +{ + return s_dragAndDropHelper->instance; +} + +bool DragAndDropHelper::isMimeDataSupported(const QMimeData* mimeData) const +{ + return mimeData->hasUrls() || mimeData->hasFormat("application/x-kde-dndextract"); +} + +void DragAndDropHelper::startDrag(QAbstractItemView* itemView, + Qt::DropActions supportedActions, + DolphinViewController* dolphinViewController) +{ + // Do not start a new drag until the previous one has been finished. + // This is a (possibly temporary) fix for bug #187884. + static bool isDragging = false; + if (isDragging) { + return; + } + isDragging = true; + + const QModelIndexList indexes = itemView->selectionModel()->selectedIndexes(); + if (!indexes.isEmpty()) { + QMimeData *data = itemView->model()->mimeData(indexes); + if (data == 0) { + return; + } + + if (dolphinViewController != 0) { + dolphinViewController->requestToolTipHiding(); + } + + QDrag* drag = new QDrag(itemView); + drag->setPixmap(createDragPixmap(itemView)); + drag->setMimeData(data); + + m_dragSource = itemView; + drag->exec(supportedActions, Qt::IgnoreAction); + m_dragSource = 0; + } + isDragging = false; +} + +bool DragAndDropHelper::isDragSource(QAbstractItemView* itemView) const +{ + return (m_dragSource != 0) && (m_dragSource == itemView); +} + +void DragAndDropHelper::dropUrls(const KFileItem& destItem, + const KUrl& destPath, + QDropEvent* event, + QWidget* widget) +{ + const bool dropToItem = !destItem.isNull() && (destItem.isDir() || destItem.isDesktopFile()); + const KUrl destination = dropToItem ? destItem.url() : destPath; + + const QMimeData* mimeData = event->mimeData(); + if (mimeData->hasFormat("application/x-kde-dndextract")) { + QString remoteDBusClient = mimeData->data("application/x-kde-dndextract"); + QDBusMessage message = QDBusMessage::createMethodCall(remoteDBusClient, "/DndExtract", + "org.kde.DndExtract", "extractSelectedFilesTo"); + message.setArguments(QVariantList() << destination.path()); + QDBusConnection::sessionBus().call(message); + } else { + const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData()); + const int urlsCount = urls.count(); + if (urlsCount == 0) { + // TODO: handle dropping of other data + } else if ((urlsCount == 1) && (urls.first() == destination)) { + emit errorMessage(i18nc("@info:status", "A folder cannot be dropped into itself")); + } else if (dropToItem) { + KonqOperations::doDrop(destItem, destination, event, widget); + } else { + KonqOperations::doDrop(KFileItem(), destination, event, widget); + } + } +} + +DragAndDropHelper::DragAndDropHelper() + : m_dragSource(0) +{ +} + +QPixmap DragAndDropHelper::createDragPixmap(QAbstractItemView* itemView) const +{ + const QModelIndexList selectedIndexes = itemView->selectionModel()->selectedIndexes(); + Q_ASSERT(!selectedIndexes.isEmpty()); + + QAbstractProxyModel* proxyModel = static_cast<QAbstractProxyModel*>(itemView->model()); + KDirModel* dirModel = static_cast<KDirModel*>(proxyModel->sourceModel()); + + const int itemCount = selectedIndexes.count(); + + // If more than one item is dragged, align the items inside a + // rectangular grid. The maximum grid size is limited to 5 x 5 items. + int xCount = 3; + int size = KIconLoader::SizeMedium; + if (itemCount > 16) { + xCount = 5; + size = KIconLoader::SizeSmall; + } else if (itemCount > 9) { + xCount = 4; + size = KIconLoader::SizeSmallMedium; + } + + if (itemCount < xCount) { + xCount = itemCount; + } + + int yCount = itemCount / xCount; + if (itemCount % xCount != 0) { + ++yCount; + } + if (yCount > xCount) { + yCount = xCount; + } + + // Draw the selected items into the grid cells + QPixmap dragPixmap(xCount * size + xCount - 1, yCount * size + yCount - 1); + dragPixmap.fill(Qt::transparent); + + QPainter painter(&dragPixmap); + int x = 0; + int y = 0; + foreach (const QModelIndex& selectedIndex, selectedIndexes) { + const QModelIndex index = proxyModel->mapToSource(selectedIndex); + const KFileItem item = dirModel->itemForIndex(index); + const QPixmap pixmap = item.pixmap(size, size); + painter.drawPixmap(x, y, pixmap); + + x += size + 1; + if (x >= dragPixmap.width()) { + x = 0; + y += size + 1; + } + if (y >= dragPixmap.height()) { + break; + } + } + + return dragPixmap; +} + +#include "draganddrophelper.moc" diff --git a/src/views/draganddrophelper.h b/src/views/draganddrophelper.h new file mode 100644 index 000000000..3cb506c43 --- /dev/null +++ b/src/views/draganddrophelper.h @@ -0,0 +1,102 @@ +/*************************************************************************** + * Copyright (C) 2007 by Peter Penz <[email protected]> * + * Copyright (C) 2007 by David Faure <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef DRAGANDDROPHELPER_H +#define DRAGANDDROPHELPER_H + +#include "libdolphin_export.h" +#include <QObject> +#include <QPixmap> + +class DolphinViewController; +class KFileItem; +class KUrl; +class QDropEvent; +class QAbstractItemView; +class QMimeData; +class QWidget; + +/** + * @brief Helper class for having a common drag and drop behavior. + * + * The class is used by DolphinIconsView, DolphinDetailsView, + * DolphinColumnView and PanelTreeView to have a consistent + * drag and drop behavior between all views. + */ +class LIBDOLPHINPRIVATE_EXPORT DragAndDropHelper : public QObject +{ + Q_OBJECT + +public: + static DragAndDropHelper& instance(); + + /** + * Returns true, if Dolphin supports the dragging of + * the given mime data. + */ + bool isMimeDataSupported(const QMimeData* mimeData) const; + + /** + * Creates a drag object for the view \a itemView for all selected items. + */ + void startDrag(QAbstractItemView* itemView, + Qt::DropActions supportedActions, + DolphinViewController* dolphinViewController = 0); + + /** + * Returns true if and only if the view \a itemView was the last view to + * be passed to startDrag(...), and that drag is still in progress. + */ + bool isDragSource(QAbstractItemView* itemView) const; + + /** + * Handles the dropping of URLs to the given + * destination. A context menu with the options + * 'Move Here', 'Copy Here', 'Link Here' and + * 'Cancel' is offered to the user. + * @param destItem Item of the destination (can be null, see KFileItem::isNull()). + * @param destPath Path of the destination. + * @param event Drop event. + * @param widget Source widget where the dragging has been started. + */ + void dropUrls(const KFileItem& destItem, + const KUrl& destPath, + QDropEvent* event, + QWidget* widget); +signals: + void errorMessage(const QString& msg); + +private: + DragAndDropHelper(); + + /** + * Creates a pixmap the contains the all icons of the items + * that are dragged. + */ + QPixmap createDragPixmap(QAbstractItemView* itemView) const; + + // The last view passed in startDrag(...), or 0 if + // no startDrag(...) initiated drag is in progress. + QAbstractItemView *m_dragSource; + + friend class DragAndDropHelperSingleton; +}; + +#endif diff --git a/src/views/folderexpander.cpp b/src/views/folderexpander.cpp new file mode 100644 index 000000000..a2dfb137b --- /dev/null +++ b/src/views/folderexpander.cpp @@ -0,0 +1,141 @@ +/*************************************************************************** + * Copyright (C) 2008 by Simon St James <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "folderexpander.h" + +#include <QtCore/QTimer> +#include <QtGui/QAbstractItemView> +#include <QtGui/QTreeView> +#include <QtGui/QScrollBar> + +#include <QtCore/QEvent> +#include <QtGui/QDragMoveEvent> + +#include <QtGui/QSortFilterProxyModel> + +#include <kdirmodel.h> + +FolderExpander::FolderExpander(QAbstractItemView *view, QSortFilterProxyModel *proxyModel) : + QObject(view), + m_enabled(true), + m_view(view), + m_proxyModel(proxyModel), + m_autoExpandTriggerTimer(0), + m_autoExpandPos() +{ + if (m_view == 0) { + return; + } + if (m_proxyModel == 0) { + return; + } + KDirModel *m_dirModel = qobject_cast<KDirModel*>(m_proxyModel->sourceModel()); + if (m_dirModel == 0) { + return; + } + + // Initialise auto-expand timer. + m_autoExpandTriggerTimer = new QTimer(this); + m_autoExpandTriggerTimer->setSingleShot(true); + connect(m_autoExpandTriggerTimer, SIGNAL(timeout()), + this, SLOT(autoExpandTimeout())); + + // The view scrolling complicates matters, so we want to + // be informed if they occur. + connect(m_view->horizontalScrollBar(), SIGNAL(valueChanged(int)), + this, SLOT(viewScrolled())); + connect(m_view->verticalScrollBar(), SIGNAL(valueChanged(int)), + this, SLOT(viewScrolled())); + + // "Dragging" events are sent to the QAbstractItemView's viewport. + m_view->viewport()->installEventFilter(this); +} + +void FolderExpander::setEnabled(bool enabled) +{ + m_enabled = enabled; +} + +bool FolderExpander::enabled() const +{ + return m_enabled; +} + +FolderExpander::~FolderExpander() +{ +} + +void FolderExpander::viewScrolled() +{ + if (m_autoExpandTriggerTimer->isActive()) { + m_autoExpandTriggerTimer->start(AUTO_EXPAND_DELAY); + } +} + +void FolderExpander::autoExpandTimeout() +{ + if (!m_enabled) { + return; + } + + // We want to find whether the file currently being hovered over is a + // directory. TODO - is there a simpler way, preferably without + // needing to pass in m_proxyModel that has a KDirModel as its sourceModel() ... ? + QModelIndex proxyIndexToExpand = m_view->indexAt(m_autoExpandPos); + QModelIndex indexToExpand = m_proxyModel->mapToSource(proxyIndexToExpand); + KDirModel* m_dirModel = qobject_cast< KDirModel* >(m_proxyModel->sourceModel()); + Q_ASSERT(m_dirModel != 0); + KFileItem itemToExpand = m_dirModel->itemForIndex(indexToExpand); + + if (itemToExpand.isNull() || itemToExpand == m_dirModel->itemForIndex(QModelIndex())) { + // The second clause occurs when we are expanding the folder represented + // by the view, which is a case we should ignore (#182618). + return; + } + + if (itemToExpand.isDir()) { + QTreeView* treeView = qobject_cast<QTreeView*>(m_view); + if ((treeView != 0) && treeView->itemsExpandable()) { + // Toggle expanded state of this directory. + treeView->setExpanded(proxyIndexToExpand, !treeView->isExpanded(proxyIndexToExpand)); + } + else { + emit enterDir(proxyIndexToExpand); + } + } +} + +bool FolderExpander::eventFilter(QObject* watched, QEvent* event) +{ + Q_UNUSED(watched); + // We're interested in reading Drag* events, but not filtering them, + // so always return false. + // We just store the position of the hover, here; actually working out + // what the hovered item is and whether it is expandable is done in + // autoExpandTimeout. + if (event->type() == QEvent::DragMove) { + QDragMoveEvent *dragMoveEvent = static_cast<QDragMoveEvent*>(event); + // (Re-)set the timer while we're still moving and dragging. + m_autoExpandTriggerTimer->start(AUTO_EXPAND_DELAY); + m_autoExpandPos = dragMoveEvent->pos(); + } else if (event->type() == QEvent::DragLeave || event->type() == QEvent::Drop) { + m_autoExpandTriggerTimer->stop(); + } + return false; +} diff --git a/src/views/folderexpander.h b/src/views/folderexpander.h new file mode 100644 index 000000000..63de57f4a --- /dev/null +++ b/src/views/folderexpander.h @@ -0,0 +1,89 @@ +/*************************************************************************** + * Copyright (C) 2008 by Simon St James <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef FOLDEREXPANDER_H +#define FOLDEREXPANDER_H + +// Needs to be exported as FoldersPanel uses it. +#include "libdolphin_export.h" + +#include <QObject> +#include <QPoint> + +class QAbstractItemView; +class QTreeView; +class QTimer; +class QSortFilterProxyModel; +class QModelIndex; + +/** + * Grants auto expanding functionality to the provided item view. + * Qt has its own auto expand mechanism, but this works only + * for QTreeView. Auto expanding of folders is turned on + * per default. + * + * If the provided view is an instance of the class QTreeView, the + * expansion of the directory is automatically done on hover. Otherwise + * the enterDir() signal is emitted and the caller needs to ensure that + * the requested directory is entered. + * + * The FolderExpander becomes a child of the provided view. + */ +class LIBDOLPHINPRIVATE_EXPORT FolderExpander : public QObject +{ + Q_OBJECT + +public: + FolderExpander(QAbstractItemView* view, QSortFilterProxyModel* proxyModel); + virtual ~FolderExpander(); + + void setEnabled(bool enabled); + bool enabled() const; + +signals: + /** + * Is emitted if the directory \a dirModelIndex should be entered. The + * signal is not emitted when a QTreeView is used, as the entering of + * the directory is already provided by expanding the tree node. + */ + void enterDir(const QModelIndex& dirModelIndex); + + +private slots: + void viewScrolled(); + void autoExpandTimeout(); + +private: + bool m_enabled; + + QAbstractItemView* m_view; + QSortFilterProxyModel* m_proxyModel; + + QTimer* m_autoExpandTriggerTimer; + QPoint m_autoExpandPos; + + static const int AUTO_EXPAND_DELAY = 700; + + /** + * Watchs the drag/move events for the view to decide + * whether auto expanding of a folder should be triggered. + */ + bool eventFilter(QObject* watched, QEvent* event); +}; +#endif diff --git a/src/views/renamedialog.cpp b/src/views/renamedialog.cpp new file mode 100644 index 000000000..39e61c464 --- /dev/null +++ b/src/views/renamedialog.cpp @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz ([email protected]) * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "renamedialog.h" + +#include <kfileitem.h> +#include <klineedit.h> +#include <klocale.h> + +#include <QtGui/QLabel> +#include <QtGui/QBoxLayout> + +RenameDialog::RenameDialog(QWidget *parent, const KFileItemList& items) : + KDialog(parent), + m_renameOneItem(false) +{ + const QSize minSize = minimumSize(); + setMinimumSize(QSize(320, minSize.height())); + + const int itemCount = items.count(); + Q_ASSERT(itemCount >= 1); + m_renameOneItem = (itemCount == 1); + + setCaption(m_renameOneItem ? + i18nc("@title:window", "Rename Item") : + i18nc("@title:window", "Rename Items")); + setButtons(Ok | Cancel); + setDefaultButton(Ok); + + setButtonGuiItem(Ok, KGuiItem(i18nc("@action:button", "&Rename"), "dialog-ok-apply")); + + QWidget* page = new QWidget(this); + setMainWidget(page); + + QVBoxLayout* topLayout = new QVBoxLayout(page); + + QLabel* editLabel = 0; + if (m_renameOneItem) { + m_newName = items.first().name(); + editLabel = new QLabel(i18nc("@label:textbox", "Rename the item <filename>%1</filename> to:", m_newName), + page); + } else { + m_newName = i18nc("@info:status", "New name #"); + editLabel = new QLabel(i18ncp("@label:textbox", + "Rename the %1 selected item to:", + "Rename the %1 selected items to:", itemCount), + page); + } + + m_lineEdit = new KLineEdit(page); + + QString fileName = items[0].url().prettyUrl(); + QString extension = KMimeType::extractKnownExtension(fileName.toLower()); + if (!extension.isEmpty()) { + extension.insert(0, '.'); + // The first item seems to have a extension (e. g. '.jpg' or '.txt'). Now + // check whether all other URLs have the same extension. If this is the + // case, add this extension to the name suggestion. + for (int i = 1; i < itemCount; ++i) { + fileName = items[i].url().prettyUrl().toLower(); + if (!fileName.endsWith(extension)) { + // at least one item does not have the same extension + extension.truncate(0); + break; + } + } + } + + int selectionLength = m_newName.length(); + if (!m_renameOneItem) { + --selectionLength; // don't select the # character + } + + const int extensionLength = extension.length(); + if (extensionLength > 0) { + if (m_renameOneItem) { + selectionLength -= extensionLength; + } else { + m_newName.append(extension); + } + } + + m_lineEdit->setText(m_newName); + m_lineEdit->setSelection(0, selectionLength); + m_lineEdit->setFocus(); + + topLayout->addWidget(editLabel); + topLayout->addWidget(m_lineEdit); + + if (!m_renameOneItem) { + QLabel* infoLabel = new QLabel(i18nc("@info", "(# will be replaced by ascending numbers)"), page); + topLayout->addWidget(infoLabel); + } +} + +RenameDialog::~RenameDialog() +{ +} + +void RenameDialog::slotButtonClicked(int button) +{ + if (button == Ok) { + m_newName = m_lineEdit->text(); + if (m_newName.isEmpty()) { + m_errorString = i18nc("@info:status", + "The new name is empty. A name with at least one character must be entered."); + } else if (!m_renameOneItem && (m_newName.count('#') == 0)) { + m_newName.truncate(0); + m_errorString = i18nc("@info:status", "The name must contain at least one # character."); + } + } + + KDialog::slotButtonClicked(button); +} + +#include "renamedialog.moc" diff --git a/src/views/renamedialog.h b/src/views/renamedialog.h new file mode 100644 index 000000000..eca5f4d06 --- /dev/null +++ b/src/views/renamedialog.h @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (C) 2006 by Peter Penz ([email protected]) * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ +#ifndef RENAMEDIALOG_H +#define RENAMEDIALOG_H + +#include "libdolphin_export.h" + +#include <kdialog.h> +#include <kurl.h> + +class KFileItem; +class KFileItemList; +class KLineEdit; + +#include <QString> + +/** + * @brief Dialog for renaming a variable number of files. + * + * The renaming is not done by the dialog, the invoker + * must do this itself: + * \code + * RenameDialog dialog(...); + * if (dialog.exec() == QDialog::Accepted) { + * const QString& newName = dialog.newName(); + * if (newName.isEmpty()) { + * // an invalid name has been chosen, use + * // dialog.errorString() to tell the user about this + * } + * else { + * // rename items corresponding to the new name + * } + * } + * \endcode + */ +class LIBDOLPHINPRIVATE_EXPORT RenameDialog : public KDialog +{ + Q_OBJECT + +public: + explicit RenameDialog(QWidget *parent, const KFileItemList& items); + virtual ~RenameDialog(); + + /** + * Returns the new name of the items. If more than one + * item should be renamed, then it is assured that the # character + * is part of the returned string. If the returned string is empty, + * then RenameDialog::errorString() should be used to show the reason + * of having an empty string (e. g. if the # character has + * been deleted by the user, although more than one item should be + * renamed). + */ + QString newName() const; + + /** + * Returns the error string, if Dialog::newName() returned an empty string. + */ + QString errorString() const; + +protected slots: + virtual void slotButtonClicked(int button); + +private: + bool m_renameOneItem; + KLineEdit* m_lineEdit; + QString m_newName; + QString m_errorString; + + friend class RenameDialogTest; // allow access for unit testing +}; + +inline QString RenameDialog::newName() const +{ + return m_newName; +} + +inline QString RenameDialog::errorString() const +{ + return m_errorString; +} + +#endif diff --git a/src/views/viewproperties.cpp b/src/views/viewproperties.cpp new file mode 100644 index 000000000..3e08358f7 --- /dev/null +++ b/src/views/viewproperties.cpp @@ -0,0 +1,310 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Peter Penz <[email protected]> * + * Copyright (C) 2006 by Aaron J. Seigo <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "viewproperties.h" + +#include "additionalinfoaccessor.h" +#include "dolphin_directoryviewpropertysettings.h" +#include "dolphin_generalsettings.h" + +#include <kcomponentdata.h> +#include <klocale.h> +#include <kstandarddirs.h> +#include <kurl.h> + +#include <QDate> +#include <QFile> +#include <QFileInfo> + +#include "settings/dolphinsettings.h" + +ViewProperties::ViewProperties(const KUrl& url) : + m_changedProps(false), + m_autoSave(true), + m_node(0) +{ + // We try and save it to the file .directory in the directory being viewed. + // If the directory is not writable by the user or the directory is not local, + // we store the properties information in a local file. + GeneralSettings* settings = DolphinSettings::instance().generalSettings(); + const bool useGlobalViewProps = settings->globalViewProps(); + if (useGlobalViewProps) { + m_filePath = destinationDir("global"); + } else if (url.isLocalFile()) { + m_filePath = url.toLocalFile(); + const QFileInfo info(m_filePath); + if (!info.isWritable()) { + m_filePath = destinationDir("local") + m_filePath; + } + } else { + m_filePath = destinationDir("remote") + m_filePath; + } + + const QString file = m_filePath + QDir::separator() + QLatin1String(".directory"); + m_node = new ViewPropertySettings(KSharedConfig::openConfig(file)); + + const bool useDefaultProps = !useGlobalViewProps && + (!QFileInfo(file).exists() || + (m_node->timestamp() < settings->viewPropsTimestamp())); + if (useDefaultProps) { + // If the .directory file does not exist or the timestamp is too old, + // use the values from the global .directory file instead, which acts + // as default view for new folders in this case. + settings->setGlobalViewProps(true); + + ViewProperties defaultProps(url); + setDirProperties(defaultProps); + + settings->setGlobalViewProps(false); + m_changedProps = false; + } +} + +ViewProperties::~ViewProperties() +{ + if (m_changedProps && m_autoSave) { + save(); + } + + delete m_node; + m_node = 0; +} + +void ViewProperties::setViewMode(DolphinView::Mode mode) +{ + if (m_node->viewMode() != mode) { + m_node->setViewMode(mode); + updateTimeStamp(); + } +} + +DolphinView::Mode ViewProperties::viewMode() const +{ + return static_cast<DolphinView::Mode>(m_node->viewMode()); +} + +void ViewProperties::setShowPreview(bool show) +{ + if (m_node->showPreview() != show) { + m_node->setShowPreview(show); + updateTimeStamp(); + } +} + +bool ViewProperties::showPreview() const +{ + return m_node->showPreview(); +} + +void ViewProperties::setShowHiddenFiles(bool show) +{ + if (m_node->showHiddenFiles() != show) { + m_node->setShowHiddenFiles(show); + updateTimeStamp(); + } +} + +void ViewProperties::setCategorizedSorting(bool categorized) +{ + if (m_node->categorizedSorting() != categorized) { + m_node->setCategorizedSorting(categorized); + updateTimeStamp(); + } +} + +bool ViewProperties::categorizedSorting() const +{ + return m_node->categorizedSorting(); +} + +bool ViewProperties::showHiddenFiles() const +{ + return m_node->showHiddenFiles(); +} + +void ViewProperties::setSorting(DolphinView::Sorting sorting) +{ + if (m_node->sorting() != sorting) { + m_node->setSorting(sorting); + updateTimeStamp(); + } +} + +DolphinView::Sorting ViewProperties::sorting() const +{ + return static_cast<DolphinView::Sorting>(m_node->sorting()); +} + +void ViewProperties::setSortOrder(Qt::SortOrder sortOrder) +{ + if (m_node->sortOrder() != sortOrder) { + m_node->setSortOrder(sortOrder); + updateTimeStamp(); + } +} + +Qt::SortOrder ViewProperties::sortOrder() const +{ + return static_cast<Qt::SortOrder>(m_node->sortOrder()); +} + +void ViewProperties::setSortFoldersFirst(bool foldersFirst) +{ + if (m_node->sortFoldersFirst() != foldersFirst) { + m_node->setSortFoldersFirst(foldersFirst); + updateTimeStamp(); + } +} + +bool ViewProperties::sortFoldersFirst() const +{ + return m_node->sortFoldersFirst(); +} + +void ViewProperties::setAdditionalInfo(const KFileItemDelegate::InformationList& list) +{ + AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); + + int infoMask = 0; + foreach (KFileItemDelegate::Information currentInfo, list) { + infoMask = infoMask | infoAccessor.bitValue(currentInfo); + } + + const int encodedInfo = encodedAdditionalInfo(infoMask); + if (m_node->additionalInfo() != encodedInfo) { + m_node->setAdditionalInfo(encodedInfo); + updateTimeStamp(); + } +} + +KFileItemDelegate::InformationList ViewProperties::additionalInfo() const +{ + KFileItemDelegate::InformationList usedInfos; + + const int decodedInfo = decodedAdditionalInfo(); + + AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); + const KFileItemDelegate::InformationList infos = infoAccessor.keys(); + + foreach (const KFileItemDelegate::Information info, infos) { + if (decodedInfo & infoAccessor.bitValue(info)) { + usedInfos.append(info); + } + } + + return usedInfos; +} + + +void ViewProperties::setDirProperties(const ViewProperties& props) +{ + setViewMode(props.viewMode()); + setShowPreview(props.showPreview()); + setShowHiddenFiles(props.showHiddenFiles()); + setCategorizedSorting(props.categorizedSorting()); + setSorting(props.sorting()); + setSortOrder(props.sortOrder()); + setSortFoldersFirst(props.sortFoldersFirst()); + setAdditionalInfo(props.additionalInfo()); +} + +void ViewProperties::setAutoSaveEnabled(bool autoSave) +{ + m_autoSave = autoSave; +} + +bool ViewProperties::isAutoSaveEnabled() const +{ + return m_autoSave; +} + +void ViewProperties::updateTimeStamp() +{ + m_changedProps = true; + m_node->setTimestamp(QDateTime::currentDateTime()); +} + +void ViewProperties::save() +{ + KStandardDirs::makeDir(m_filePath); + m_node->writeConfig(); + m_changedProps = false; +} + +KUrl ViewProperties::mirroredDirectory() +{ + QString basePath = KGlobal::mainComponent().componentName(); + basePath.append("/view_properties/"); + return KUrl(KStandardDirs::locateLocal("data", basePath)); +} + +QString ViewProperties::destinationDir(const QString& subDir) const +{ + QString basePath = KGlobal::mainComponent().componentName(); + basePath.append("/view_properties/").append(subDir); + return KStandardDirs::locateLocal("data", basePath); +} + +int ViewProperties::encodedAdditionalInfo(int info) const +{ + int encodedInfo = m_node->additionalInfo(); + + switch (viewMode()) { + case DolphinView::DetailsView: + encodedInfo = (encodedInfo & 0xFFFF00) | info; + break; + case DolphinView::IconsView: + encodedInfo = (encodedInfo & 0xFF00FF) | (info << 8); + break; + case DolphinView::ColumnView: + encodedInfo = (encodedInfo & 0x00FFFF) | (info << 16); + break; + default: break; + } + + return encodedInfo; +} + +int ViewProperties::decodedAdditionalInfo() const +{ + int decodedInfo = m_node->additionalInfo(); + + switch (viewMode()) { + case DolphinView::DetailsView: + decodedInfo = decodedInfo & 0xFF; + if (decodedInfo == 0) { + // A details view without any additional info makes no sense, hence + // provide at least a size-info and date-info as fallback + AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); + decodedInfo = infoAccessor.bitValue(KFileItemDelegate::Size) | + infoAccessor.bitValue(KFileItemDelegate::ModificationTime); + } + break; + case DolphinView::IconsView: + decodedInfo = (decodedInfo >> 8) & 0xFF; + break; + case DolphinView::ColumnView: + decodedInfo = (decodedInfo >> 16) & 0xFF; + break; + default: break; + } + + return decodedInfo; +} diff --git a/src/views/viewproperties.h b/src/views/viewproperties.h new file mode 100644 index 000000000..bb476dc44 --- /dev/null +++ b/src/views/viewproperties.h @@ -0,0 +1,163 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Peter Penz <[email protected]> * + * Copyright (C) 2006 by Aaron J. Seigo <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef VIEWPROPERTIES_H +#define VIEWPROPERTIES_H + +#include <views/dolphinview.h> +#include <kurl.h> +#include <libdolphin_export.h> + +class ViewPropertySettings; +/** + * @brief Maintains the view properties like 'view mode' or + * 'show hidden files' for a directory. + * + * The view properties are automatically stored as part of the file + * .directory inside the corresponding path. To read out the view properties + * just construct an instance by passing the path of the directory: + * + * \code + * ViewProperties props(KUrl("/home/peter/Documents")); + * const DolphinView::Mode mode = props.viewMode(); + * const bool showHiddenFiles = props.isShowHiddenFilesEnabled(); + * \endcode + * + * When modifying a view property, the '.directory' file is automatically updated + * inside the destructor. + * + * If no .directory file is available or the global view mode is turned on + * (see GeneralSettings::globalViewMode()), the values from the global .directory file + * are used for initialization. + */ +class LIBDOLPHINPRIVATE_EXPORT ViewProperties +{ +public: + explicit ViewProperties(const KUrl& url); + virtual ~ViewProperties(); + + void setViewMode(DolphinView::Mode mode); + DolphinView::Mode viewMode() const; + + void setShowPreview(bool show); + bool showPreview() const; + + void setShowHiddenFiles(bool show); + bool showHiddenFiles() const; + + void setCategorizedSorting(bool categorized); + bool categorizedSorting() const; + + void setSorting(DolphinView::Sorting sorting); + DolphinView::Sorting sorting() const; + + void setSortOrder(Qt::SortOrder sortOrder); + Qt::SortOrder sortOrder() const; + + void setSortFoldersFirst(bool foldersFirst); + bool sortFoldersFirst() const; + + /** + * Sets the additional information for the current set view-mode. + * Note that the additional-info property is the only property where + * the value is dependent from another property (in this case the view-mode). + */ + void setAdditionalInfo(const KFileItemDelegate::InformationList& info); + + /** + * Returns the additional information for the current set view-mode. + * Note that the additional-info property is the only property where + * the value is dependent from another property (in this case the view-mode). + */ + KFileItemDelegate::InformationList additionalInfo() const; + + /** + * Sets the directory properties view mode, show preview, + * show hidden files, sorting and sort order like + * set in \a props. + */ + void setDirProperties(const ViewProperties& props); + + /** + * If \a autoSave is true, the properties are automatically + * saved when the destructor is called. Per default autosaving + * is enabled. + */ + void setAutoSaveEnabled(bool autoSave); + bool isAutoSaveEnabled() const; + + void updateTimeStamp(); + + /** + * Saves the view properties for the directory specified + * in the constructor. The method is automatically + * invoked in the destructor, if + * ViewProperties::isAutoSaveEnabled() returns true and + * at least one property has been changed. + */ + void save(); + + /** + * Returns the URL of the directory, where the mirrored view properties + * are stored into. Mirrored view properties are used if: + * - there is no write access for storing the view properties into + * the original directory + * - for non local directories + */ + static KUrl mirroredDirectory(); + +private: + /** + * Returns the destination directory path where the view + * properties are stored. \a subDir specifies the used sub + * directory. + */ + QString destinationDir(const QString& subDir) const; + + /** + * Returns the encoded additional information that can be stored + * in the .directory file. See ViewProperties::decodedAdditionalInfo() + * for the coding format. + * @param info Additional information for the current view mode. + */ + int encodedAdditionalInfo(int info) const; + + /** + * Returns the decoded additional information from the .directory + * file by respecting the current set view mode. The additional + * information from the .directory file is an integer value, where: + * - Byte 0 stores the additional info for the details view + * - Byte 1 stores the additional info for the icons view + * - Byte 2 stores the additional info for the column view + * The additional information property is the only property that is + * dependent from another property (in this case the view-mode). + */ + int decodedAdditionalInfo() const; + + Q_DISABLE_COPY(ViewProperties) + +private: + bool m_changedProps; + bool m_autoSave; + QString m_filePath; + ViewPropertySettings* m_node; +}; + +#endif |
