Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Evergreen:11.1
kdebase4
plasma.diff
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File plasma.diff of Package kdebase4
Patch-rediff: rediff-plasma.sh --- applets/folderview/folderviewConfig.ui +++ applets/folderview/folderviewConfig.ui @@ -1,126 +0,0 @@ -<ui version="4.0" > - <class>folderviewConfig</class> - <widget class="QWidget" name="folderviewConfig" > - <property name="sizePolicy" > - <sizepolicy vsizetype="Expanding" hsizetype="Expanding" > - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <layout class="QGridLayout" name="gridLayout" > - <item row="0" column="0" colspan="3" > - <widget class="QRadioButton" name="showDesktopFolder" > - <property name="text" > - <string>Show the desktop folder</string> - </property> - </widget> - </item> - <item row="1" column="0" colspan="3" > - <widget class="QRadioButton" name="showCustomFolder" > - <property name="text" > - <string>Show a custom folder</string> - </property> - </widget> - </item> - <item row="2" column="0" > - <spacer name="spacer2" > - <property name="orientation" > - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType" > - <enum>QSizePolicy::Fixed</enum> - </property> - <property name="sizeHint" stdset="0" > - <size> - <width>21</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="2" column="1" > - <widget class="QLabel" name="selectLabel" > - <property name="sizePolicy" > - <sizepolicy vsizetype="Preferred" hsizetype="Minimum" > - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text" > - <string>Select:</string> - </property> - <property name="wordWrap" > - <bool>false</bool> - </property> - </widget> - </item> - <item row="2" column="2" > - <widget class="KUrlRequester" name="lineEdit" > - <property name="sizePolicy" > - <sizepolicy vsizetype="Preferred" hsizetype="Expanding" > - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - </widget> - </item> - <item row="3" column="1" > - <widget class="QLabel" name="filterLabel" > - <property name="text" > - <string>Filter</string> - </property> - </widget> - </item> - <item row="4" column="2" > - <spacer name="spacer1" > - <property name="orientation" > - <enum>Qt::Vertical</enum> - </property> - <property name="sizeType" > - <enum>QSizePolicy::Expanding</enum> - </property> - <property name="sizeHint" stdset="0" > - <size> - <width>20</width> - <height>81</height> - </size> - </property> - </spacer> - </item> - <item row="3" column="2" > - <widget class="KLineEdit" name="filterFiles" > - <property name="toolTip" > - <string>Space-separated list of extensions, e.g. *.txt * .od*</string> - </property> - <property name="whatsThis" > - <string>Space-separated list of extensions, e.g. *.txt * .od* to display only office- and text-files</string> - </property> - <property name="urlDropsEnabled" > - <bool>false</bool> - </property> - <property name="showClearButton" stdset="0" > - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </widget> - <customwidgets> - <customwidget> - <class>KLineEdit</class> - <extends>QLineEdit</extends> - <header>klineedit.h</header> - </customwidget> - <customwidget> - <class>KUrlRequester</class> - <extends>QFrame</extends> - <header>kurlrequester.h</header> - <container>1</container> - </customwidget> - </customwidgets> - <includes> - <include location="global" >KDE/KUrlRequester</include> - </includes> - <resources/> - <connections/> -</ui> --- applets/folderview/kabstractviewadapter_p.h +++ applets/folderview/kabstractviewadapter_p.h @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (C) 2008 by Fredrik Höglund <fredrik@kde.org> * + * * + * 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 KABSTRACTVIEWADAPTER_H +#define KABSTRACTVIEWADAPTER_H + +#include <QObject> + +class QAbstractItemModel; +class QModelIndex; +class QPalette; +class QRect; +class QSize; + +/* + * Interface used by KFilePreviewGenerator to generate previews + * for files. The interface allows KFilePreviewGenerator to be + * independent from the view implementation. + */ +class KAbstractViewAdapter : public QObject +{ +public: + enum Signal { ScrollBarValueChanged }; + + KAbstractViewAdapter(QObject *parent) : QObject(parent) {} + virtual ~KAbstractViewAdapter() {} + virtual QAbstractItemModel *model() const = 0; + virtual QSize iconSize() const = 0; + virtual QPalette palette() const = 0; + virtual QRect visibleArea() const = 0; + virtual QRect visualRect(const QModelIndex &index) const = 0; + virtual void connect(Signal signal, QObject *receiver, const char *slot) = 0; +}; + +#endif + --- applets/folderview/folderviewLocationConfig.ui +++ applets/folderview/folderviewLocationConfig.ui @@ -0,0 +1,97 @@ +<ui version="4.0" > + <class>folderviewLocationConfig</class> + <widget class="QWidget" name="folderviewLocationConfig" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout" > + <item row="0" column="0" colspan="3" > + <widget class="QRadioButton" name="showDesktopFolder" > + <property name="text" > + <string>Show the desktop folder</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="3" > + <widget class="QRadioButton" name="showCustomFolder" > + <property name="text" > + <string>Show a custom folder</string> + </property> + </widget> + </item> + <item row="2" column="0" > + <spacer name="horizontalSpacer" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType" > + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>25</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="1" > + <widget class="QLabel" name="selectLabel" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Minimum" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Select:</string> + </property> + <property name="wordWrap" > + <bool>false</bool> + </property> + </widget> + </item> + <item row="2" column="2" > + <widget class="KUrlRequester" name="lineEdit" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="3" column="2" > + <spacer name="verticalSpacer_3" > + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>20</width> + <height>183</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>KUrlRequester</class> + <extends>QFrame</extends> + <header>kurlrequester.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> --- applets/folderview/proxymodel.cpp +++ applets/folderview/proxymodel.cpp @@ -25,7 +25,7 @@ ProxyModel::ProxyModel(QObject *parent) - : QSortFilterProxyModel(parent) + : QSortFilterProxyModel(parent), m_sortDirsFirst(true) { } @@ -33,6 +33,38 @@ { } +void ProxyModel::setFilterMode(FilterMode filterMode) +{ + m_filterMode = filterMode; + invalidateFilter(); +} + +ProxyModel::FilterMode ProxyModel::filterMode() const +{ + return m_filterMode; +} + +void ProxyModel::setMimeTypeFilterList(const QStringList &mimeList) +{ + m_mimeList = mimeList; + invalidateFilter(); +} + +const QStringList &ProxyModel::mimeTypeFilterList() const +{ + return m_mimeList; +} + +void ProxyModel::setSortDirectoriesFirst(bool enable) +{ + m_sortDirsFirst = enable; +} + +bool ProxyModel::sortDirectoriesFirst() const +{ + return m_sortDirsFirst; +} + QModelIndex ProxyModel::indexForUrl(const KUrl &url) const { const KDirModel *dirModel = static_cast<KDirModel*>(sourceModel()); @@ -52,32 +84,66 @@ const KFileItem item2 = dirModel->itemForIndex(right); // Sort directories first - if (item1.isDir() && !item2.isDir()) - return true; + if (m_sortDirsFirst) { + if (item1.isDir() && !item2.isDir()) { + return true; + } + if (!item1.isDir() && item2.isDir()) { + return false; + } + } - if (!item1.isDir() && item2.isDir()) - return false; + const QString name1 = dirModel->data(left).toString(); + const QString name2 = dirModel->data(right).toString(); - return KStringHandler::naturalCompare(item1.name(), item2.name(), Qt::CaseInsensitive) < 0; + return KStringHandler::naturalCompare(name1, name2, Qt::CaseInsensitive) < 0; } +ProxyModel::FilterMode ProxyModel::filterModeFromInt(int filterMode) +{ + switch (filterMode) { + case 0: + return ProxyModel::NoFilter; + case 1: + return ProxyModel::FilterShowMatches; + default: + return ProxyModel::FilterHideMatches; + } +} + bool ProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { const KDirModel *dirModel = static_cast<KDirModel*>(sourceModel()); const KFileItem item = dirModel->itemForIndex(dirModel->index(sourceRow, KDirModel::Name, sourceParent)); - const QString regExpOrig = filterRegExp().pattern(); - const QStringList regExps = regExpOrig.split(' '); - foreach (const QString ®ExpStr, regExps) { - QRegExp regExp(regExpStr); - regExp.setPatternSyntax(QRegExp::Wildcard); - regExp.setCaseSensitivity(Qt::CaseInsensitive); + bool invertResult = false; + switch (m_filterMode) { + case NoFilter: + return true; + case FilterHideMatches: + invertResult = true; // fall through + case FilterShowMatches: { + // Mime type check + bool ret = m_mimeList.contains(item.determineMimeType()->name()); + if (!ret) { + return invertResult ? true : false; + } + // Pattern check + const QString regExpOrig = filterRegExp().pattern(); + const QStringList regExps = regExpOrig.split(' '); + foreach (const QString ®ExpStr, regExps) { + QRegExp regExp(regExpStr); + regExp.setPatternSyntax(QRegExp::Wildcard); + regExp.setCaseSensitivity(Qt::CaseInsensitive); - if (regExp.indexIn(item.name()) != -1) { - return true; + if (regExp.indexIn(item.name()) != -1) { + return invertResult ? false : true; + } + } + break; } } - return false; + return invertResult ? true : false; } --- applets/folderview/previewpluginsmodel.cpp +++ applets/folderview/previewpluginsmodel.cpp @@ -0,0 +1,111 @@ +/* + * Copyright © 2008 Fredrik Höglund <fredrik@kde.org> + * + * 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 <QStringList> +#include "previewpluginsmodel.h" + +PreviewPluginsModel::PreviewPluginsModel(QObject *parent) + : QAbstractListModel(parent) +{ + plugins = KServiceTypeTrader::self()->query("ThumbCreator"); + + // Sort the list alphabetially + QMap<QString, KSharedPtr<KService> > map; + for (int i = 0; i < plugins.size(); i++) { + map.insert(plugins[i]->name().toLower(), plugins[i]); + } + plugins = map.values(); +} + +PreviewPluginsModel::~PreviewPluginsModel() +{ +} + +Qt::ItemFlags PreviewPluginsModel::flags(const QModelIndex &index) const +{ + Q_UNUSED(index) + + return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled; +} + +QVariant PreviewPluginsModel::data(const QModelIndex &index, int role) const +{ + if (index.row() < 0 || index.row() >= plugins.size()) { + return QVariant(); + } + + switch (role) { + case Qt::DisplayRole: + return plugins.at(index.row())->name(); + + case Qt::CheckStateRole: + return checkedRows.contains(index.row()) ? Qt::Checked : Qt::Unchecked; + } + + return QVariant(); +} + +bool PreviewPluginsModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (role != Qt::CheckStateRole) { + return false; + } + + const Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt()); + if (state == Qt::Checked) { + checkedRows.append(index.row()); + } else { + checkedRows.removeAll(index.row()); + } + + emit dataChanged(index, index); + return true; +} + +int PreviewPluginsModel::indexOfPlugin(const QString &name) const +{ + for (int i = 0; i < plugins.size(); i++) { + if (plugins.at(i)->desktopEntryName() == name) { + return i; + } + } + return -1; +} + +void PreviewPluginsModel::setCheckedPlugins(const QStringList &list) +{ + foreach (const QString &name, list) { + const int row = indexOfPlugin(name); + if (row != -1) { + checkedRows.append(row); + emit dataChanged(index(row, 0), index(row, 0)); + } + } +} + +QStringList PreviewPluginsModel::checkedPlugins() const +{ + QStringList list; + foreach (int row, checkedRows) { + list.append(plugins.at(row)->desktopEntryName()); + } + return list; +} + + --- applets/folderview/folderview.cpp +++ applets/folderview/folderview.cpp @@ -1,5 +1,6 @@ /* * Copyright © 2008 Fredrik Höglund <fredrik@kde.org> + * Copyright © 2008 Rafael Fernández López <ereslibre@kde.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -18,7 +19,6 @@ */ #include "folderview.h" -#include "folderview.moc" #include <QApplication> #include <QClipboard> @@ -40,9 +40,12 @@ #include <KDirLister> #include <KDirModel> #include <KFileItemDelegate> +#include <kfileplacesmodel.h> #include <KGlobalSettings> #include <KMenu> #include <KStandardShortcut> +#include <KStringHandler> +#include <KWindowSystem> #include <kio/fileundomanager.h> #include <kio/paste.h> @@ -55,6 +58,8 @@ #include "proxymodel.h" #include "plasma/theme.h" +#include "plasma/corona.h" +#include "plasma/widgets/scrollbar.h" #include "plasma/paintutils.h" #ifdef Q_WS_X11 @@ -65,35 +70,145 @@ #include <limits.h> -// Wraps a QScrollBar in a QGraphicsProxyWidget -class ScrollBar : public QGraphicsProxyWidget +K_EXPORT_PLASMA_APPLET(folderview, FolderView) + +MimeModel::MimeModel(QObject *parent) + : QStringListModel(parent) { -public: - ScrollBar(Qt::Orientation orientation, QGraphicsWidget *parent); - void setRange(int min, int max) { static_cast<QScrollBar*>(widget())->setRange(min, max); } - void setSingleStep(int val) { static_cast<QScrollBar*>(widget())->setSingleStep(val); } - void setPageStep(int val) { static_cast<QScrollBar*>(widget())->setPageStep(val); } - void setValue(int val) { static_cast<QScrollBar*>(widget())->setValue(val); } - int value() const { return static_cast<QScrollBar*>(widget())->value(); } - int minimum() const { return static_cast<QScrollBar*>(widget())->minimum(); } - int maximum() const { return static_cast<QScrollBar*>(widget())->maximum(); } - QScrollBar *nativeWidget() const { return static_cast<QScrollBar*>(widget()); } -}; + m_mimetypes = KMimeType::allMimeTypes(); +} -ScrollBar::ScrollBar(Qt::Orientation orientation, QGraphicsWidget *parent) - : QGraphicsProxyWidget(parent) +QVariant MimeModel::data(const QModelIndex &index, int role) const { - QScrollBar *scrollbar = new QScrollBar(orientation); - scrollbar->setAttribute(Qt::WA_NoSystemBackground); - setWidget(scrollbar); + if (!index.isValid()) { + return QVariant(); + } + KMimeType *mime = static_cast<KMimeType*>(index.internalPointer()); + switch (role) { + case Qt::DisplayRole: { + if (!mime->comment().isEmpty()) { + QString description; + if (mime->patterns().count()) { + description = mime->patterns().join(", "); + } else { + description = mime->name(); + } + return QString("%1 (%2)").arg(mime->comment()).arg(description); + } else { + return mime->name(); + } + } + case Qt::DecorationRole: + return KIcon(mime->iconName()); + case Qt::CheckStateRole: + return m_state[mime]; + default: + return QStringListModel::data(index, role); + } } +Qt::ItemFlags MimeModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags itemFlags = QStringListModel::flags(index); + itemFlags &= ~Qt::ItemIsEditable; + if (!index.isValid()) { + return itemFlags; + } + return itemFlags | Qt::ItemIsUserCheckable; +} +QModelIndex MimeModel::index(int row, int column, const QModelIndex &parent) const +{ + if (parent.isValid() || row >= m_mimetypes.count()) { + return QModelIndex(); + } + return createIndex(row, column, (void*) m_mimetypes[row].data()); +} +int MimeModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) { + return 0; + } + return m_mimetypes.count(); +} + +bool MimeModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid()) { + return false; + } + + if (role == Qt::CheckStateRole) { + KMimeType *mime = static_cast<KMimeType*>(index.internalPointer()); + m_state[mime] = (Qt::CheckState) value.toInt(); + emit dataChanged(index, index); + return true; + } + + return QStringListModel::setData(index, value, role); +} + + + // --------------------------------------------------------------------------- +ProxyMimeModel::ProxyMimeModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ +} + +void ProxyMimeModel::setSourceModel(QAbstractItemModel *sourceModel) +{ + QSortFilterProxyModel::setSourceModel(sourceModel); + sort(0); +} + +void ProxyMimeModel::setFilter(const QString &filter) +{ + m_filter = filter; + invalidateFilter(); +} + +bool ProxyMimeModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + KMimeType *leftPtr = static_cast<KMimeType*>(left.internalPointer()); + KMimeType *rightPtr = static_cast<KMimeType*>(right.internalPointer()); + + return KStringHandler::naturalCompare(leftPtr->comment(), rightPtr->comment()) < 0; +} + +bool ProxyMimeModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + QModelIndex sourceIndex = sourceModel()->index(source_row, 0, source_parent); + KMimeType *mime = static_cast<KMimeType*>(sourceIndex.internalPointer()); + if (m_filter.isEmpty()) { + return true; + } + + bool fastRet = mime->comment().contains(m_filter, Qt::CaseInsensitive) || + ((!mime->patterns().count() || mime->comment().isEmpty()) && mime->name().contains(m_filter, Qt::CaseInsensitive)); + + if (fastRet) { + return true; + } + + foreach (const QString &pattern, mime->patterns()) { + if (pattern.contains(m_filter, Qt::CaseInsensitive)) { + return true; + } + } + + return false; +} + + + +// --------------------------------------------------------------------------- + + FolderView::FolderView(QObject *parent, const QVariantList &args) : Plasma::Containment(parent, args), m_titleHeight(0), @@ -102,10 +217,15 @@ m_newMenu(0), m_actionCollection(this), m_columns(0), - m_layoutValid(false), + m_rows(0), + m_validRows(0), m_layoutBroken(false), + m_needPostLayoutPass(false), + m_initialListing(true), + m_positionsLoaded(false), m_doubleClick(false), - m_dragInProgress(false) + m_dragInProgress(false), + m_animFrame(0) { setContainmentType(DesktopContainment); setAspectRatioMode(Plasma::IgnoreAspectRatio); @@ -121,7 +241,6 @@ m_model->setSourceModel(m_dirModel); m_model->setSortLocaleAware(true); m_model->setFilterCaseSensitivity(Qt::CaseInsensitive); - m_model->sort(0, Qt::AscendingOrder); connect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(rowsInserted(QModelIndex,int,int))); connect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(rowsRemoved(QModelIndex,int,int))); @@ -135,14 +254,12 @@ connect(m_delegate, SIGNAL(commitData(QWidget*)), SLOT(commitData(QWidget*))); m_selectionModel = new QItemSelectionModel(m_model, this); - m_scrollBar = new ScrollBar(Qt::Vertical, this); + m_scrollBar = new Plasma::ScrollBar(Qt::Vertical, this); m_scrollBar->hide(); connect(m_scrollBar->nativeWidget(), SIGNAL(valueChanged(int)), SLOT(scrollBarValueChanged(int))); - if ( args.count() ) { - m_url = KUrl(args.value(0).toString()); - } else { - m_url = KUrl(); + if (args.count() > 0) { + setUrl(KUrl(args.value(0).toString())); } // As we use some part of konqueror libkonq must be added to have translations @@ -152,6 +269,8 @@ void FolderView::init() { + Containment::init(); + // We handle the caching ourselves setCacheMode(NoCache); @@ -166,17 +285,44 @@ m_font = cg.readEntry("desktopFont", QFont("Sans Serif", 10)); cg = config(); + + m_customLabel = cg.readEntry("customLabel", ""); + m_customIconSize = cg.readEntry("customIconSize", 0); + m_drawShadows = cg.readEntry("drawShadows", true); + m_numTextLines = cg.readEntry("numTextLines", 2); + m_textColor = cg.readEntry("textColor", QColor(Qt::transparent)); + if (!m_url.isValid()) { - m_url = cg.readEntry("url", KUrl(QDir::homePath())); + setUrl(cg.readEntry("url", KUrl(isContainment() ? QString("desktop:/") : QDir::homePath()))); + } else { + KConfigGroup cg = config(); + cg.writeEntry("url", m_url); } + m_filterFiles = cg.readEntry("filterFiles", "*"); + m_filterType = cg.readEntry("filter", 0); + m_filterFilesMimeList = cg.readEntry("mimeFilter", QStringList()); + m_model->setFilterMode(ProxyModel::filterModeFromInt(m_filterType)); + m_model->setMimeTypeFilterList(m_filterFilesMimeList); + m_sortDirsFirst = cg.readEntry("sortDirsFirst", true); + m_sortColumn = cg.readEntry("sortColumn", int(KDirModel::Name)); + m_model->setSortDirectoriesFirst(m_sortDirsFirst); + m_model->sort(m_sortColumn != -1 ? m_sortColumn : KDirModel::Name, Qt::AscendingOrder); + KDirLister *lister = new KDirLister(this); + connect(lister, SIGNAL(completed()), SLOT(listingCompleted())); + connect(lister, SIGNAL(canceled()), SLOT(listingCanceled())); lister->openUrl(m_url); m_model->setFilterFixedString(m_filterFiles); m_dirModel->setDirLister(lister); + m_flow = isContainment() ? QListView::TopToBottom : QListView::LeftToRight; + m_flow = static_cast<QListView::Flow>(cg.readEntry("flow", static_cast<int>(m_flow))); + m_iconsLocked = cg.readEntry("iconsLocked", false); + m_alignToGrid = cg.readEntry("alignToGrid", false); + createActions(); connect(QApplication::clipboard(), SIGNAL(dataChanged()), SLOT(clipboardDataChanged())); @@ -187,59 +333,317 @@ delete m_newMenu; } +void FolderView::saveState(KConfigGroup &config) const +{ + Q_UNUSED(config) + + if (m_delayedSaveTimer.isActive()) { + //m_delayedSaveTimer.stop(); // Can't stop the timer since this is a const method + saveIconPositions(); + } +} + +QScrollBar *FolderView::verticalScrollBar() const +{ + return m_scrollBar->nativeWidget(); +} + +QAbstractItemModel *FolderView::model() const +{ + return m_model; +} + +QRect FolderView::visibleArea() const +{ + return mapToViewport(contentsRect()).toAlignedRect(); +} + +void FolderView::createAnimationFrames() +{ + m_animFrames = QPixmap(100, 100 * 20); + m_animFrames.fill(Qt::transparent); + + QPainterPath path; + path.addRect(-2, -40, 4, 16); + + QPainter p(&m_animFrames); + p.setRenderHint(QPainter::Antialiasing); + p.translate(50, 50); + + for (int i = 0; i < 20; i++) + { + p.translate(1, 1); + for (int j = 0; j < 20; j++) + { + p.fillPath(path, QColor(0, 0, 0, 128)); + p.rotate(360 / 20); + } + p.translate(-1, -1); + for (int j = 0; j < 20; j++) + { + const QColor color = (i == j) ? Qt::white : QColor(200, 200, 200); + p.fillPath(path, color); + p.rotate(360 / 20); + } + p.translate(0, 100); + } +} + void FolderView::createConfigurationInterface(KConfigDialog *parent) { - QWidget *widget = new QWidget; - ui.setupUi(widget); + QWidget *widgetFilter = new QWidget; + QWidget *widgetDisplay = new QWidget; + QWidget *widgetLocation = new QWidget; + uiFilter.setupUi(widgetFilter); + uiDisplay.setupUi(widgetDisplay); + uiLocation.setupUi(widgetLocation); + if (m_url == KUrl("desktop:/")) { - ui.showDesktopFolder->setChecked(true); - ui.selectLabel->setEnabled(false); - ui.lineEdit->setEnabled(false); + uiLocation.showDesktopFolder->setChecked(true); + uiLocation.selectLabel->setEnabled(false); + uiLocation.lineEdit->setEnabled(false); } else { - ui.showCustomFolder->setChecked(true); - ui.lineEdit->setUrl(m_url); + uiLocation.showCustomFolder->setChecked(true); + uiLocation.lineEdit->setUrl(m_url); } - ui.lineEdit->setMode(KFile::Directory); - ui.filterFiles->setText(m_filterFiles); + uiLocation.lineEdit->setMode(KFile::Directory); + uiFilter.filterFilesPattern->setText(m_filterFiles); - parent->addPage(widget, parent->windowTitle(), icon()); + MimeModel *mimeModel = new MimeModel(uiFilter.filterFilesList); + ProxyMimeModel *pMimeModel = new ProxyMimeModel(uiFilter.filterFilesList); + pMimeModel->setSourceModel(mimeModel); + uiFilter.filterFilesList->setModel(pMimeModel); + + // The label is not shown when the applet is acting as a containment, + // so don't confuse the user by making it editable. + if (isContainment()) { + uiDisplay.labelEdit->hide(); + } + + uiDisplay.labelEdit->setText(m_titleText); + + const QList<int> iconSizes = QList<int>() << 16 << 22 << 32 << 48 << 64 << 128; + uiDisplay.sizeSlider->setRange(0, iconSizes.size() - 1); + uiDisplay.sizeSlider->setValue(iconSizes.indexOf(iconSize().width())); + + uiDisplay.sortCombo->addItem(i18nc("Sort Icons", "Unsorted"), -1); + uiDisplay.sortCombo->addItem(m_actionCollection.action("sort_name")->text(), KDirModel::Name); + uiDisplay.sortCombo->addItem(m_actionCollection.action("sort_size")->text(), KDirModel::Size); + uiDisplay.sortCombo->addItem(m_actionCollection.action("sort_type")->text(), KDirModel::Type); + uiDisplay.sortCombo->addItem(m_actionCollection.action("sort_date")->text(), KDirModel::ModifiedTime); + + uiDisplay.flowCombo->addItem(i18n("Top to Bottom"), QListView::TopToBottom); + uiDisplay.flowCombo->addItem(i18n("Left to Right"), QListView::LeftToRight); + + uiDisplay.alignToGrid->setChecked(m_alignToGrid); + uiDisplay.lockInPlace->setChecked(m_iconsLocked); + uiDisplay.drawShadows->setChecked(m_drawShadows); + uiDisplay.numLinesEdit->setValue(m_numTextLines); + + if (m_textColor != Qt::transparent) { + uiDisplay.colorButton->setColor(m_textColor); + } else { + uiDisplay.colorButton->setColor(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor)); + } + + for (int i = 0; i < uiDisplay.sortCombo->maxCount(); i++) { + if (m_sortColumn == uiDisplay.sortCombo->itemData(i).toInt()) { + uiDisplay.sortCombo->setCurrentIndex(i); + break; + } + } + + for (int i = 0; i < uiDisplay.flowCombo->maxCount(); i++) { + if (m_flow == uiDisplay.flowCombo->itemData(i).toInt()) { + uiDisplay.flowCombo->setCurrentIndex(i); + break; + } + } + + // Hide the icon arrangement controls when we're not acting as a containment, + // since this option doesn't make much sense in the applet. + if (!isContainment()) { + uiDisplay.flowLabel->hide(); + uiDisplay.flowCombo->hide(); + } + + connect(uiFilter.searchMimetype, SIGNAL(textChanged(QString)), pMimeModel, SLOT(setFilter(QString))); + + parent->addPage(widgetLocation, i18n("Location"), "folder"); + parent->addPage(widgetDisplay, i18n("Display"), "preferences-desktop-display"); + parent->addPage(widgetFilter, i18n("Filter"), "view-filter"); + parent->setButtons(KDialog::Ok | KDialog::Cancel | KDialog::Apply); connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted())); connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted())); - connect(ui.showCustomFolder, SIGNAL(toggled(bool)), this, SLOT(customFolderToggled(bool))); + connect(uiLocation.showCustomFolder, SIGNAL(toggled(bool)), this, SLOT(customFolderToggled(bool))); + connect(uiFilter.filterType, SIGNAL(currentIndexChanged(int)), this, SLOT(filterChanged(int))); + connect(uiFilter.selectAll, SIGNAL(clicked(bool)), this, SLOT(selectUnselectAll())); + connect(uiFilter.deselectAll, SIGNAL(clicked(bool)), this, SLOT(selectUnselectAll())); + + KConfigGroup cg = config(); + int filter = cg.readEntry("filter", 0); + uiFilter.filterType->setCurrentIndex(filter); + filterChanged(filter); + + QStringList selectedItems = cg.readEntry("mimeFilter", QStringList()); + + if (selectedItems.count()) { + for (int i = 0; i < pMimeModel->rowCount(); i++) { + const QModelIndex index = pMimeModel->index(i, 0); + const KMimeType *mime = static_cast<KMimeType*>(pMimeModel->mapToSource(index).internalPointer()); + if (selectedItems.contains(mime->name())) { + selectedItems.removeAll(mime->name()); + uiFilter.filterFilesList->model()->setData(index, Qt::Checked, Qt::CheckStateRole); + } + } + } } void FolderView::configAccepted() { KUrl url; - if (ui.showDesktopFolder->isChecked()) + if (uiLocation.showDesktopFolder->isChecked()) { url = KUrl("desktop:/"); - else - url = ui.lineEdit->url(); + } else { + url = uiLocation.lineEdit->url(); + } - if (url.isEmpty() || (url.isLocalFile() && !QFile::exists(url.path()))) + if (url.isEmpty() || (url.isLocalFile() && !QFile::exists(url.path()))) { url = KUrl(QDir::homePath()); + } - if (m_url != url || m_filterFiles != ui.filterFiles->text()) { + // Now, we have to iterate over all items (not only the filtered ones). For that reason we have + // to ask the source model, not the proxy model. + QStringList selectedItems; + ProxyMimeModel *proxyModel = static_cast<ProxyMimeModel*>(uiFilter.filterFilesList->model()); + for (int i = 0; i < proxyModel->sourceModel()->rowCount(); i++) { + const QModelIndex index = proxyModel->sourceModel()->index(i, 0); + if (index.model()->data(index, Qt::CheckStateRole).toInt() == Qt::Checked) { + selectedItems << static_cast<KMimeType*>(index.internalPointer())->name(); + } + } + + int filterType = uiFilter.filterType->currentIndex(); + KConfigGroup cg = config(); + bool needRelayout = false; + bool needRepaint = false; + + if (m_drawShadows != uiDisplay.drawShadows->isChecked()) { + m_drawShadows = uiDisplay.drawShadows->isChecked(); + cg.writeEntry("drawShadows", m_drawShadows); + needRepaint = true; + } + + const QColor color = uiDisplay.colorButton->color(); + if ((m_textColor != Qt::transparent && color != m_textColor) || + (m_textColor == Qt::transparent && color != Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor))) + { + m_textColor = color; + cg.writeEntry("textColor", m_textColor); + needRepaint = true; + } + + if (m_numTextLines != uiDisplay.numLinesEdit->value()) { + m_numTextLines = uiDisplay.numLinesEdit->value(); + cg.writeEntry("numTextLines", m_numTextLines); + needRelayout = true; + } + + const QList<int> iconSizes = QList<int>() << 16 << 22 << 32 << 48 << 64 << 128; + int size = iconSizes.at(uiDisplay.sizeSlider->value()); + if ((m_customIconSize == 0 && size != KIconLoader::global()->currentSize(KIconLoader::Desktop)) || + (m_customIconSize != 0 && size != m_customIconSize)) + { + m_customIconSize = size; + cg.writeEntry("customIconSize", m_customIconSize); + needRelayout = true; + } + + int sortColumn = uiDisplay.sortCombo->itemData(uiDisplay.sortCombo->currentIndex()).toInt(); + if (m_sortColumn != sortColumn) { + if (sortColumn != -1) { + m_sortColumn = sortColumn; + m_model->invalidate(); + m_model->sort(m_sortColumn, Qt::AscendingOrder); + m_layoutBroken = false; + needRelayout = true; + cg.writeEntry("sortColumn", m_sortColumn); + } else { + m_layoutBroken = true; + // Note: updateSortActionsState() will set m_sortColumn and write the config entry. + } + updateSortActionsState(); + } + + int flow = uiDisplay.flowCombo->itemData(uiDisplay.flowCombo->currentIndex()).toInt(); + if (m_flow != flow) { + m_flow = static_cast<QListView::Flow>(flow); + cg.writeEntry("flow", flow); + needRelayout = true; + } + + if (m_alignToGrid != uiDisplay.alignToGrid->isChecked()) { + m_alignToGrid = uiDisplay.alignToGrid->isChecked(); + cg.writeEntry("alignToGrid", m_alignToGrid); + m_actionCollection.action("auto_align")->setChecked(m_alignToGrid); + if (m_alignToGrid && m_layoutBroken) { + alignIconsToGrid(); + } + } + + if (m_iconsLocked != uiDisplay.lockInPlace->isChecked()) { + m_iconsLocked = uiDisplay.lockInPlace->isChecked(); + cg.writeEntry("iconsLocked", m_iconsLocked); + m_actionCollection.action("lock_icons")->setChecked(m_iconsLocked); + } + + const QString label = uiDisplay.labelEdit->text(); + if ((m_customLabel.isEmpty() && label != m_titleText) || + (!m_customLabel.isEmpty() && label != m_customLabel)) + { + m_customLabel = label; + setUrl(url); + cg.writeEntry("customLabel", m_customLabel); + needRepaint = true; + } + + if (m_url != url || m_filterFiles != uiFilter.filterFilesPattern->text() || + m_filterFilesMimeList != selectedItems || m_filterType != filterType) { + m_initialListing = true; + m_validRows = 0; m_dirModel->dirLister()->openUrl(url); - m_model->setFilterFixedString(ui.filterFiles->text()); - m_url = url; - m_filterFiles = ui.filterFiles->text(); + m_model->setFilterFixedString(uiFilter.filterFilesPattern->text()); + m_filterFiles = uiFilter.filterFilesPattern->text(); + m_filterFilesMimeList = selectedItems; + m_filterType = filterType; + setUrl(url); - KConfigGroup cg = config(); cg.writeEntry("url", m_url); cg.writeEntry("filterFiles", m_filterFiles); + cg.writeEntry("filter", m_filterType); + cg.writeEntry("mimeFilter", m_filterFilesMimeList); - emit configNeedsSaving(); + m_model->setMimeTypeFilterList(m_filterFilesMimeList); + m_model->setFilterMode(ProxyModel::filterModeFromInt(m_filterType)); } + + if (needRelayout) { + m_validRows = 0; + m_delayedLayoutTimer.start(10, this); + } else if (needRepaint) { + markEverythingDirty(); + } + + emit configNeedsSaving(); } void FolderView::customFolderToggled(bool checked) { - ui.selectLabel->setEnabled(checked); - ui.lineEdit->setEnabled(checked); + uiLocation.selectLabel->setEnabled(checked); + uiLocation.lineEdit->setEnabled(checked); } void FolderView::fontSettingsChanged() @@ -249,8 +653,8 @@ if (m_font != font) { m_font = font; - m_layoutValid = false; - markEverythingDirty(); + m_validRows = 0; + m_delayedLayoutTimer.start(10, this); } } @@ -258,35 +662,73 @@ { if (group == KIconLoader::Desktop) { - m_layoutValid = false; - markEverythingDirty(); + m_validRows = 0; + m_delayedLayoutTimer.start(10, this); } } void FolderView::themeChanged() { // We'll mark the layout as invalid here just in case the content margins - // have changed - m_layoutValid = false; + // have changed. + m_validRows = 0; + layoutItems(); - // Update the scrollbar geometry - QRectF r = QRectF(contentsRect().right() - m_scrollBar->geometry().width(), contentsRect().top(), - m_scrollBar->geometry().width(), contentsRect().height()); - if (m_scrollBar->geometry() != r) { - m_scrollBar->setGeometry(r); - } - + updateScrollBarGeometry(); markEverythingDirty(); } void FolderView::rowsInserted(const QModelIndex &parent, int first, int last) { Q_UNUSED(parent) - Q_UNUSED(first) - Q_UNUSED(last) - m_layoutValid = false; - update(); + if (!m_positionsLoaded) { + loadIconPositions(); + m_positionsLoaded = true; + } + + if (!m_layoutBroken || m_initialListing) { + if (first < m_validRows) { + m_validRows = 0; + } + m_delayedLayoutTimer.start(10, this); + } else { + const QStyleOptionViewItemV4 option = viewOptions(); + const QRect cr = contentsRect().toRect(); + const QSize grid = gridSize(); + QPoint pos = QPoint(); + + m_items.insert(first, last - first + 1, ViewItem()); + + // If a single item was inserted and we have a saved position from a deleted file, + // reuse that position. + if (first == last && !m_lastDeletedPos.isNull()) { + const QModelIndex index = m_model->index(first, 0); + const QSize size = m_delegate->sizeHint(option, index).boundedTo(grid); + m_items[first].rect = QRect(m_lastDeletedPos.x() + (grid.width() - size.width()) / 2, + m_lastDeletedPos.y(), size.width(), size.height()); + m_items[first].layouted = true; + markAreaDirty(m_items[first].rect); + m_lastDeletedPos = QPoint(); + m_validRows = m_items.size(); + return; + } + + // Lay out the newly inserted files + for (int i = first; i <= last; i++) { + const QModelIndex index = m_model->index(i, 0); + const QSize size = m_delegate->sizeHint(option, index).boundedTo(grid); + pos = findNextEmptyPosition(pos, grid, cr); + m_items[i].rect = QRect(pos.x() + (grid.width() - size.width()) / 2, pos.y(), + size.width(), size.height()); + m_items[first].layouted = true; + markAreaDirty(m_items[i].rect); + } + + m_validRows = m_items.size(); + m_delayedSaveTimer.start(5000, this); + updateScrollBar(); + } } void FolderView::rowsRemoved(const QModelIndex &parent, int first, int last) @@ -294,35 +736,58 @@ Q_UNUSED(parent) if (!m_layoutBroken) { - m_layoutValid = false; - update(); + if (first < m_validRows) { + m_validRows = 0; + } + m_delayedLayoutTimer.start(10, this); } else { for (int i = first; i <= last; i++) { markAreaDirty(m_items[i].rect); } + // When a single item is removed, we'll save the position and use it for the next new item. + // The reason for this is that when a file is renamed, it will first be removed from the view + // and then reinserted. + if (first == last) { + const QSize size = gridSize(); + m_lastDeletedPos.rx() = m_items[first].rect.x() - (size.width() - m_items[first].rect.width()) / 2; + m_lastDeletedPos.ry() = m_items[first].rect.y(); + } m_items.remove(first, last - first + 1); + m_delayedSaveTimer.start(5000, this); + m_validRows = m_items.size(); } } void FolderView::modelReset() { - m_layoutValid = false; - update(); + m_validRows = 0; + layoutItems(); } void FolderView::layoutChanged() { - m_layoutValid = false; - update(); + m_validRows = 0; + layoutItems(); } void FolderView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { - Q_UNUSED(topLeft) - Q_UNUSED(bottomRight) + const QStyleOptionViewItemV4 option = viewOptions(); + const QSize grid = gridSize(); - m_layoutValid = false; - update(); + // Update the size of the items and center them in the grid cell + for (int i = topLeft.row(); i <= bottomRight.row(); i++) { + if (!m_items[i].layouted) { + continue; + } + const QModelIndex index = m_model->index(i, 0); + const QSize size = m_delegate->sizeHint(option, index).boundedTo(grid); + if (size != m_items[i].rect.size()) { + m_items[i].rect.translate((m_items[i].rect.width() - size.width()) / 2, 0); + } + markAreaDirty(QRect(m_items[i].rect.x() - (grid.width() - m_items[i].rect.width()) / 2, + m_items[i].rect.y(), grid.width(), grid.height())); + } } void FolderView::clipboardDataChanged() @@ -364,10 +829,19 @@ int spacing = 10; int margin = 10; - qreal available = width - 2 * margin + spacing; + qreal available = width - 2 * margin; return qFloor(available / (gridSize().width() + spacing)); } +int FolderView::rowsForHeight(qreal height) const +{ + int spacing = 10; + int margin = 10; + + qreal available = height - 2 * margin; + return qFloor(available / (gridSize().height() + spacing)); +} + QPointF FolderView::mapToViewport(const QPointF &point) const { return point + QPointF(0, m_scrollBar->value()); @@ -388,50 +862,284 @@ return rect.translated(0, -m_scrollBar->value()); } +QPoint inline FolderView::nextGridPosition(const QPoint &lastPos, const QSize &grid, const QRect &contentRect) const +{ + int spacing = 10; + int margin = 10; + + if (lastPos.isNull()) { + return QPoint(contentRect.left() + margin, contentRect.top() + m_titleHeight + margin); + } + + QPoint pos = lastPos; + + if (m_flow == QListView::LeftToRight) { + pos.rx() += grid.width() + spacing; + if ((pos.x() + grid.width() + 10) >= (contentRect.right() - m_scrollBar->geometry().width() - margin)) { + pos.ry() += grid.height() + spacing; + pos.rx() = contentRect.left() + margin; + } + } else { + pos.ry() += grid.height() + spacing; + if ((pos.y() + grid.height() + 10) >= (contentRect.bottom() - margin)) { + pos.rx() += grid.width() + spacing; + pos.ry() = contentRect.top() + m_titleHeight + margin; + } + } + + return pos; +} + +QPoint FolderView::findNextEmptyPosition(const QPoint &prevPos, const QSize &gridSize, const QRect &contentRect) const +{ + QPoint pos = prevPos; + bool done = false; + + while (!done) + { + done = true; + pos = nextGridPosition(pos, gridSize, contentRect); + const QRect r(pos, gridSize); + for (int i = 0; i < m_items.count(); i++) { + if (m_items.at(i).rect.intersects(r)) { + done = false; + break; + } + } + } + + return pos; +} + void FolderView::layoutItems() { QStyleOptionViewItemV4 option = viewOptions(); m_items.resize(m_model->rowCount()); - const QRectF rect = contentsRect(); - int spacing = 10; - int margin = 10; - int x = rect.x() + margin; - int y = rect.y() + margin + m_titleHeight; + const QRect visibleRect = mapToViewport(contentsRect()).toAlignedRect(); + const QRect rect = contentsRect().toRect(); + const QSize grid = gridSize(); + int maxWidth = rect.width() - m_scrollBar->geometry().width(); + int maxHeight = rect.height() - m_titleHeight; + m_columns = columnsForWidth(maxWidth); + m_rows = rowsForHeight(maxHeight); + bool needUpdate = false; - QSize grid = gridSize(); - int maxWidth = rect.width() - m_scrollBar->geometry().width() - margin; - int rowHeight = 0; - int maxColumns = columnsForWidth(maxWidth); - int column = 0; - m_delegate->setMaximumSize(grid); - for (int i = 0; i < m_items.size(); i++) { - const QModelIndex index = m_model->index(i, 0); - QSize size = m_delegate->sizeHint(option, index).boundedTo(grid); + // If we're starting with the first item + if (m_validRows == 0) { + m_needPostLayoutPass = false; + m_currentLayoutPos = QPoint(); + } - QPoint pos(x + (grid.width() - size.width()) / 2, y); - m_items[i].rect = QRect(pos, size); + if (!m_savedPositions.isEmpty()) { + m_layoutBroken = true; + // Restart the delayed cache clear timer if it's running and we haven't + // finished laying out the icons. + if (m_delayedCacheClearTimer.isActive() && m_validRows < m_items.size()) { + m_delayedCacheClearTimer.start(5000, this); + } + } else { + m_layoutBroken = false; + } - rowHeight = qMax(rowHeight, size.height()); - x += grid.width() + spacing; + // Do a 20 millisecond layout pass + QTime time; + time.start(); + do { + const int count = qMin(m_validRows + 5, m_items.size()); + if (!m_savedPositions.isEmpty()) { - if (++column >= maxColumns) { - y += rowHeight + spacing; - rowHeight = 0; - column = 0; - x = rect.x() + margin; + // Layout with saved icon positions + // ================================================================ + for (int i = m_validRows; i < count; i++) { + const QModelIndex index = m_model->index(i, 0); + const QSize size = m_delegate->sizeHint(option, index).boundedTo(grid); + KFileItem item = m_model->itemForIndex(index); + + const QPoint pos = m_savedPositions.value(item.name(), QPoint(-1, -1)); + if (pos != QPoint(-1, -1)) { + m_items[i].rect = QRect(pos + QPoint((grid.width() - size.width()) / 2, 0), size); + m_items[i].layouted = true; + if (m_items[i].rect.intersects(visibleRect)) { + needUpdate = true; + } + } else { + // We don't have a saved position for this file, so we'll record the + // size and lay it out in a second layout pass. + m_items[i].rect = QRect(QPoint(), size); + m_items[i].layouted = false; + m_needPostLayoutPass = true; + } + } + // If we've finished laying out all the icons + if (!m_initialListing && !m_needPostLayoutPass && count == m_items.size()) { + needUpdate |= doLayoutSanityCheck(); + } + } else { + + // Automatic layout + // ================================================================ + QPoint pos = m_currentLayoutPos; + for (int i = m_validRows; i < count; i++) { + const QModelIndex index = m_model->index(i, 0); + const QSize size = m_delegate->sizeHint(option, index).boundedTo(grid); + + pos = nextGridPosition(pos, grid, rect); + m_items[i].rect = QRect(pos.x() + (grid.width() - size.width()) / 2, pos.y(), + size.width(), size.height()); + m_items[i].layouted = true; + if (m_items[i].rect.intersects(visibleRect)) { + needUpdate = true; + } + } + m_currentLayoutPos = pos; } + m_validRows = count; + } while (m_validRows < m_items.size() && time.elapsed() < 30); + + + // Second layout pass for files that didn't have a saved position + // ==================================================================== + if (m_validRows == m_items.size() && m_needPostLayoutPass) { + QPoint pos = QPoint(); + for (int i = 0; i < m_items.size(); i++) { + if (m_items[i].layouted) { + continue; + } + pos = findNextEmptyPosition(pos, grid, rect); + m_items[i].rect.moveTo(pos.x() + (grid.width() - m_items[i].rect.width()) / 2, pos.y()); + if (m_items[i].rect.intersects(visibleRect)) { + needUpdate = true; + } + } + needUpdate |= doLayoutSanityCheck(); + m_needPostLayoutPass = false; + return; } + if (m_validRows < m_items.size() || m_needPostLayoutPass) { + m_delayedLayoutTimer.start(10, this); + } + + if (needUpdate) { + m_dirtyRegion = QRegion(visibleRect); + update(); + } + updateScrollBar(); - m_columns = maxColumns; - m_layoutValid = true; - m_layoutBroken = false; - m_dirtyRegion = QRegion(mapToViewport(rect).toAlignedRect()); } +void FolderView::alignIconsToGrid() +{ + int margin = 10; + int spacing = 10; + const QRect cr = contentsRect().toRect(); + const QSize size = gridSize() + QSize(spacing, spacing); + int topMargin = margin + cr.top() + m_titleHeight; + int leftMargin = margin + cr.left(); + int vOffset = topMargin + size.height() / 2; + int hOffset = leftMargin + size.width() / 2; + bool layoutChanged = false; + + for (int i = 0; i < m_items.size(); i++) { + const QPoint center = m_items[i].rect.center(); + const int col = qRound((center.x() - hOffset) / qreal(size.width())); + const int row = qRound((center.y() - vOffset) / qreal(size.height())); + + const QPoint pos(leftMargin + col * size.width() + (size.width() - m_items[i].rect.width() - spacing) / 2, + topMargin + row * size.height()); + + if (pos != m_items[i].rect.topLeft()) { + m_items[i].rect.moveTo(pos); + layoutChanged = true; + } + } + + if (layoutChanged) { + doLayoutSanityCheck(); + updateScrollBar(); + markEverythingDirty(); + m_layoutBroken = true; + updateSortActionsState(); + m_delayedSaveTimer.start(5000, this); + m_savedPositions.clear(); + } +} + +bool FolderView::doLayoutSanityCheck() +{ + // Make sure that the distance from the top of the viewport to the + // topmost item is 10 pixels. + int minY = INT_MAX; + for (int i = 0; i < m_validRows; i++) { + if (!m_items[i].layouted) { + continue; + } + minY = qMin(minY, m_items[i].rect.y()); + } + + int topMargin = contentsRect().top() + 10 + m_titleHeight; + if (minY != topMargin) { + int delta = topMargin - minY; + for (int i = 0; i < m_validRows; i++) { + if (!m_items[i].layouted) { + continue; + } + m_items[i].rect.translate(0, delta); + } + return true; + } + + return false; +} + +void FolderView::saveIconPositions() const +{ + if (m_layoutBroken && !m_initialListing && m_validRows == m_items.size()) { + int version = 1; + QStringList data; + data << QString::number(version); + data << QString::number(m_items.size()); + + const QSize size = gridSize(); + for (int i = 0; i < m_items.size(); i++) { + QModelIndex index = m_model->index(i, 0); + KFileItem item = m_model->itemForIndex(index); + int x = m_items[i].rect.x() - (size.width() - m_items[i].rect.width()) / 2; + data << item.name(); + data << QString::number(x); + data << QString::number(m_items[i].rect.y()); + } + + config().writeEntry("savedPositions", data); + } else { + config().deleteEntry("savedPositions"); + } +} + +void FolderView::loadIconPositions() +{ + QStringList data = config().readEntry("savedPositions", QStringList()); + if (data.isEmpty()) { + return; + } + + // Sanity checks + if (data.size() < 5 || data.at(0).toInt() != 1 || ((data.size() - 2) % 3) || + data.at(1).toInt() != ((data.size() - 2) / 3)) { + return; + } + + for (int i = 2; i < data.size(); i += 3) { + const QString &name = data.at(i); + int x = data.at(i + 1).toInt(); + int y = data.at(i + 2).toInt(); + m_savedPositions.insert(name, QPoint(x, y)); + } +} + void FolderView::updateScrollBar() { // Find the height of the viewport @@ -527,6 +1235,11 @@ void FolderView::updateTextShadows(const QColor &textColor) { + if (!m_drawShadows) { + m_delegate->setShadowColor(Qt::transparent); + return; + } + QColor shadowColor; // Use black shadows with bright text, and white shadows with dark text. @@ -552,8 +1265,7 @@ void FolderView::paintInterface(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRect &contentRect) { // Make sure the backbuffer pixmap has the same size as the content rect - if (m_pixmap.isNull() || m_pixmap.size() != contentRect.size()) - { + if (m_pixmap.isNull() || m_pixmap.size() != contentRect.size()) { if (!contentRect.isValid()) { return; } @@ -567,54 +1279,30 @@ return; } + const QColor themeTextColor = Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor); + const QColor textColor = (m_textColor.alpha() > 0 ? m_textColor : themeTextColor); + painter->setClipRect(clipRect); - QStyleOptionViewItemV4 opt = viewOptions(); - opt.palette.setColor(QPalette::All, QPalette::Text, Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor)); - updateTextShadows(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor)); - // Paint the folder title - QPen currentPen = painter->pen(); - m_titleHeight = painter->fontMetrics().height(); - - QString titleText; - if (m_url == KUrl("desktop:/")) { - titleText = i18n("Desktop"); //FIXME: 4.2 make it "Desktop Folder; - } else if (m_url.isLocalFile() && m_url.path().startsWith(KUrl("~").path())) { -#ifndef Q_WS_WIN - titleText = m_url.path().replace(KUrl("~").path(), i18n("Home")); -#else - titleText = m_url.path().replace(QDir::homePath(), i18n("Home")); -#endif - } else { - titleText = m_url.pathOrUrl(); + m_titleHeight = isContainment() ? 0 : painter->fontMetrics().height() + 4; + if (m_titleHeight > 0 && option->exposedRect.y() <= m_titleHeight) { + QString titleText = m_titleText; + titleText = painter->fontMetrics().elidedText(titleText, Qt::ElideMiddle, contentRect.width()); + QColor titleColor = textColor; + QPixmap titlePixmap = Plasma::PaintUtils::shadowText(titleText, + titleColor, + m_delegate->shadowColor(), + m_delegate->shadowOffset().toPoint()); + painter->drawPixmap(contentRect.topLeft(), titlePixmap); + //Draw underline + int width = contentRect.width() - (m_scrollBar->isVisible() ? m_scrollBar->geometry().width() + 4 : 0); + painter->fillRect(contentRect.left(), contentRect.top() + m_titleHeight - 2, width, 1, + QColor(0, 0, 0, 64)); + painter->fillRect(contentRect.left(), contentRect.top() + m_titleHeight - 1, width, 1, + QColor(255, 255, 255, 64)); } - titleText = painter->fontMetrics().elidedText(titleText, Qt::ElideMiddle, contentRect.width()); - QColor titleColor = Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor); - QPixmap titlePixmap = Plasma::PaintUtils::shadowText(titleText, - titleColor, - m_delegate->shadowColor(), - m_delegate->shadowOffset().toPoint()); - painter->drawPixmap(contentRect.topLeft(), titlePixmap); - //Draw underline - painter->setPen(Qt::NoPen); - QLinearGradient lineGrad(contentRect.topLeft(), contentRect.topRight()); - QColor lineColor(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor)); - lineColor.setAlphaF(0.8); - lineGrad.setColorAt(0.0, lineColor); - lineColor.setAlphaF(0.0); - lineGrad.setColorAt(1.0, lineColor); - QBrush lineBrush(lineGrad); - painter->setBrush(lineBrush); - painter->drawRect(contentRect.left(), contentRect.top() + m_titleHeight, contentRect.width(), 1); - - painter->setPen(currentPen); - - if (!m_layoutValid) { - layoutItems(); - } - if (m_viewScrolled) { m_dirtyRegion += scrollBackbufferContents(); m_viewScrolled = false; @@ -624,22 +1312,24 @@ // Update the dirty region in the backbuffer // ========================================= - if (!m_dirtyRegion.isEmpty()) - { + if (!m_dirtyRegion.isEmpty()) { + QStyleOptionViewItemV4 opt = viewOptions(); + opt.palette.setColor(QPalette::All, QPalette::Text, textColor); + updateTextShadows(textColor); + QPainter p(&m_pixmap); p.translate(-contentRect.topLeft() - QPoint(0, offset)); p.setClipRegion(m_dirtyRegion); // Clear the dirty region p.setCompositionMode(QPainter::CompositionMode_Source); - p.fillRect(mapToViewport(contentRect), Qt::transparent); + p.fillRect(mapToViewport(contentRect).toAlignedRect(), Qt::transparent); p.setCompositionMode(QPainter::CompositionMode_SourceOver); - for (int i = 0; i < m_items.size(); i++) - { + for (int i = 0; i < m_validRows; i++) { opt.rect = m_items[i].rect; - if (!m_dirtyRegion.intersects(opt.rect)) { + if (!m_items[i].layouted || !m_dirtyRegion.intersects(opt.rect)) { continue; } @@ -657,7 +1347,7 @@ updateTextShadows(palette().color(QPalette::HighlightedText)); opt.state |= QStyle::State_Selected; } else { - updateTextShadows(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor)); + updateTextShadows(textColor); } if (hasFocus() && index == m_selectionModel->currentIndex()) { @@ -739,60 +1429,75 @@ p.drawTiledPixmap(0, m_pixmap.height() - 16, m_pixmap.width(), 16, m_bottomFadeTile); } p.end(); + painter->drawPixmap(contentRect.topLeft(), pixmap); } else { painter->drawPixmap(contentRect.topLeft(), m_pixmap); } + + if (m_validRows < m_items.size() || m_initialListing) { + if (!m_animTimer.isActive()) { + m_animFrame = 0; + m_animTimer.start(150, this); + } + if (m_animFrames.isNull()) { + createAnimationFrames(); + } + const QSize size(m_animFrames.width(), m_animFrames.width()); + QPoint pos = contentRect.center() - QPoint(size.width() / 2, size.width() / 2); + painter->drawPixmap(pos, m_animFrames, QRect(QPoint(0, size.height() * m_animFrame), size)); + } else if (m_animTimer.isActive()) { + m_animTimer.stop(); + update(); + } } -QModelIndex FolderView::indexAt(const QPointF &point) +QModelIndex FolderView::indexAt(const QPointF &point) const { - if (!m_layoutValid) - layoutItems(); - if (!mapToViewport(contentsRect()).contains(point)) return QModelIndex(); - for (int i = 0; i < m_items.size(); i++) { - if (m_items[i].rect.contains(point.toPoint())) + for (int i = 0; i < m_validRows; i++) { + if (m_items[i].layouted && m_items[i].rect.contains(point.toPoint())) return m_model->index(i, 0); } return QModelIndex(); } -QRectF FolderView::visualRect(const QModelIndex &index) +QRect FolderView::visualRect(const QModelIndex &index) const { - if (!m_layoutValid) - layoutItems(); + if (!index.isValid() || index.row() < 0 || index.row() >= m_validRows || + !m_items[index.row()].layouted) { + return QRect(); + } - if (!index.isValid() || index.row() < 0 || index.row() > m_items.size()) - return QRectF(); - return m_items[index.row()].rect; } void FolderView::constraintsEvent(Plasma::Constraints constraints) { // We should probably only do this when acting as the desktop containment - //if (constraints & Plasma::FormFactorConstraint) - // setBackgroundHints(Applet::NoBackground); + if (constraints & Plasma::FormFactorConstraint) { + if (isContainment()) { + setBackgroundHints(Applet::NoBackground); + } else if (formFactor() == Plasma::Planar || formFactor() == Plasma::MediaCenter) { + setBackgroundHints(Applet::TranslucentBackground); + } + } - setBackgroundHints(Applet::TranslucentBackground); - if (constraints & Plasma::SizeConstraint) { - // Update the scrollbar geometry - QRectF r = QRectF(contentsRect().right() - m_scrollBar->geometry().width(), contentsRect().top(), - m_scrollBar->geometry().width(), contentsRect().height()); - m_scrollBar->setGeometry(r); - - int maxWidth = contentsRect().width() - m_scrollBar->geometry().width() - 10; - if (columnsForWidth(maxWidth) != m_columns) { + updateScrollBarGeometry(); + int maxWidth = contentsRect().width() - m_scrollBar->geometry().width(); + int maxHeight = contentsRect().height() - m_titleHeight; + if ((m_flow == QListView::LeftToRight && columnsForWidth(maxWidth) != m_columns) || + (m_flow == QListView::TopToBottom && rowsForHeight(maxHeight) != m_rows)) { // The scrollbar range will be updated after the re-layout - m_layoutValid = false; + m_validRows = 0; + m_delayedLayoutTimer.start(10, this); } else { updateScrollBar(); markEverythingDirty(); @@ -800,6 +1505,50 @@ } } +void FolderView::updateScrollBarGeometry() +{ + QRectF cr = contentsRect(); + + QRectF r = QRectF(cr.right() - m_scrollBar->geometry().width(), cr.top() + m_titleHeight, + m_scrollBar->geometry().width(), cr.height() - m_titleHeight); + if (m_scrollBar->geometry() != r) { + m_scrollBar->setGeometry(r); + } +} + +void FolderView::setUrl(const KUrl &url) +{ + m_url = url; + + if (!m_customLabel.isEmpty()) { + m_titleText = m_customLabel; + } else if (m_url == KUrl("desktop:/")) { + m_titleText = i18n("Desktop Folder"); + } else { + m_titleText = m_url.pathOrUrl(); + + KFilePlacesModel places; + const QModelIndex index = places.closestItem(url); + if (index.isValid()) { + m_titleText = m_titleText.right(m_titleText.length() - places.url(index).pathOrUrl().length()); + + if (!m_titleText.isEmpty()) { + if (m_titleText.at(0) == '/') { + m_titleText.remove(0, 1); + } + + if (layoutDirection() == Qt::RightToLeft) { + m_titleText.prepend(" < "); + } else { + m_titleText.prepend(" > "); + } + } + + m_titleText.prepend(places.text(index)); + } + } +} + void FolderView::createActions() { KIO::FileUndoManager *manager = KIO::FileUndoManager::self(); @@ -828,10 +1577,16 @@ paste->setEnabled(false); } - KAction *reload = new KAction(i18n("&Reload"), this); - reload->setShortcut(KStandardShortcut::reload()); + KAction *reload = new KAction(i18n("&Reload"), this); connect(reload, SIGNAL(triggered()), SLOT(refreshIcons())); + KAction *refresh = new KAction(isContainment() ? i18n("&Refresh Desktop") : i18n("&Refresh View"), this); + refresh->setShortcut(KStandardShortcut::reload()); + if (isContainment()) { + refresh->setIcon(KIcon("user-desktop")); + } + connect(refresh, SIGNAL(triggered()), SLOT(refreshIcons())); + KAction *rename = new KAction(KIcon("edit-rename"), i18n("&Rename"), this); rename->setShortcut(Qt::Key_F2); connect(rename, SIGNAL(triggered()), SLOT(renameSelectedIcon())); @@ -852,14 +1607,68 @@ m_actionCollection.addAction("paste", paste); m_actionCollection.addAction("pasteto", pasteTo); m_actionCollection.addAction("reload", reload); + m_actionCollection.addAction("refresh", refresh); m_actionCollection.addAction("rename", rename); m_actionCollection.addAction("trash", trash); m_actionCollection.addAction("del", del); - // Create the new menu if (KAuthorized::authorize("editable_desktop_icons")) { + KAction *alignToGrid = new KAction(i18n("Align to Grid"), this); + alignToGrid->setCheckable(true); + alignToGrid->setChecked(m_alignToGrid); + connect(alignToGrid, SIGNAL(toggled(bool)), SLOT(toggleAlignToGrid(bool))); + + KAction *lockIcons = new KAction(i18nc("Icons on the desktop", "Lock in Place"), this); + lockIcons->setCheckable(true); + lockIcons->setChecked(m_iconsLocked); + connect(lockIcons, SIGNAL(toggled(bool)), SLOT(toggleIconsLocked(bool))); + + m_sortingGroup = new QActionGroup(this); + connect(m_sortingGroup, SIGNAL(triggered(QAction*)), SLOT(sortingChanged(QAction*))); + QAction *sortByName = m_sortingGroup->addAction(i18nc("Sort icons", "By Name")); + QAction *sortBySize = m_sortingGroup->addAction(i18nc("Sort icons", "By Size")); + QAction *sortByType = m_sortingGroup->addAction(i18nc("Sort icons", "By Type")); + QAction *sortByDate = m_sortingGroup->addAction(i18nc("Sort icons", "By Date")); + sortByName->setCheckable(true); + sortBySize->setCheckable(true); + sortByType->setCheckable(true); + sortByDate->setCheckable(true); + + KAction *dirsFirst = new KAction(i18nc("Sort icons", "Directories First"), this); + dirsFirst->setCheckable(true); + dirsFirst->setChecked(m_sortDirsFirst); + connect(dirsFirst, SIGNAL(toggled(bool)), SLOT(toggleDirectoriesFirst(bool))); + + QMenu *sortMenu = new QMenu(i18n("Sort Icons")); + sortMenu->addAction(sortByName); + sortMenu->addAction(sortBySize); + sortMenu->addAction(sortByType); + sortMenu->addAction(sortByDate); + sortMenu->addSeparator(); + sortMenu->addAction(dirsFirst); + + QMenu *iconsMenu = new QMenu; + iconsMenu->addMenu(sortMenu); + iconsMenu->addSeparator(); + iconsMenu->addAction(alignToGrid); + iconsMenu->addAction(lockIcons); + + QAction *iconsMenuAction = new KAction(i18n("Icons"), this); + iconsMenuAction->setIcon(KIcon("preferences-desktop-icons")); + iconsMenuAction->setMenu(iconsMenu); + + // Create the new menu m_newMenu = new KNewMenu(&m_actionCollection, view(), "new_menu"); connect(m_newMenu->menu(), SIGNAL(aboutToShow()), this, SLOT(aboutToShowCreateNew())); + + m_actionCollection.addAction("lock_icons", lockIcons); + m_actionCollection.addAction("auto_align", alignToGrid); + m_actionCollection.addAction("icons_menu", iconsMenuAction); + m_actionCollection.addAction("sort_name", sortByName); + m_actionCollection.addAction("sort_size", sortBySize); + m_actionCollection.addAction("sort_type", sortByType); + m_actionCollection.addAction("sort_date", sortByDate); + updateSortActionsState(); } // Note: We have to create our own action collection, because the one Plasma::Applet @@ -892,15 +1701,84 @@ actions.append(m_actionCollection.action("undo")); actions.append(m_actionCollection.action("paste")); - + QAction *separator = new QAction(this); separator->setSeparator(true); actions.append(separator); + + if (QAction *iconsMenu = m_actionCollection.action("icons_menu")) { + actions.append(iconsMenu); + } + + actions.append(m_actionCollection.action("refresh")); + + separator = new QAction(this); + separator->setSeparator(true); + actions.append(separator); + + if (isContainment()) { + // allow to configure option when running as containment + QAction* configAction = new QAction(i18n("%1 Settings", name()), this); + configAction->setIcon(KIcon("configure")); + configAction->setShortcutContext(Qt::WidgetShortcut); //don't clash with other view + connect(configAction, SIGNAL(triggered(bool)), this, SLOT(showConfigurationInterfacePlasmoid())); + actions.append(configAction); + + QAction* desktopAction = new QAction(i18n("Desktop Settings"), this); + desktopAction->setIcon(KIcon("configure")); + desktopAction->setShortcutContext(Qt::WidgetShortcut); //don't clash with other views + connect(desktopAction, SIGNAL(triggered(bool)), this, SLOT(showConfigurationInterface())); + actions.append(desktopAction); + + separator = new QAction(this); + separator->setSeparator(true); + actions.append(separator); + } } return actions; } +void FolderView::showConfigurationInterfacePlasmoid() +{ + // don't show desktop settings when containment as Applet::showConfigurationInterface() + if (!hasConfigurationInterface()) { + return; + } + + if (immutability() != Plasma::Mutable && !KAuthorized::authorize("PlasmaAllowConfigureWhenLocked")) { + return; + } + + const QString dialogId = QString("%1settingscontainment%2").arg(id()).arg(name()); + KConfigDialog * dlg = KConfigDialog::exists(dialogId); + + if (dlg) { + KWindowSystem::setOnDesktop(dlg->winId(), KWindowSystem::currentDesktop()); + dlg->show(); + KWindowSystem::activateWindow(dlg->winId()); + return; + } + + const QString windowTitle = i18nc("@title:window", "%1 Settings", name()); + + KConfigSkeleton *nullManager = new KConfigSkeleton(0); + KConfigDialog *dialog = new KConfigDialog(0, dialogId, nullManager); + dialog->setFaceType(KPageDialog::Auto); + dialog->setWindowTitle(windowTitle); + dialog->setAttribute(Qt::WA_DeleteOnClose, true); + createConfigurationInterface(dialog); + //TODO: would be nice to not show dialog if there are no pages added? + connect(dialog, SIGNAL(finished()), nullManager, SLOT(deleteLater())); + //TODO: Apply button does not correctly work for now, so do not show it + dialog->showButton(KDialog::Apply, false); + connect(dialog, SIGNAL(applyClicked()), this, SLOT(configChanged())); + connect(dialog, SIGNAL(okClicked()), this, SLOT(configChanged())); + dialog->show(); + + emit releaseVisualFocus(); +} + void FolderView::aboutToShowCreateNew() { if (m_newMenu) { @@ -954,7 +1832,7 @@ void FolderView::refreshIcons() { - // TODO Implement me! + m_dirModel->dirLister()->updateDirectory(m_url); } void FolderView::renameSelectedIcon() @@ -964,20 +1842,21 @@ return; // Don't allow renaming of files the aren't visible in the view - const QRectF rect = visualRect(index); + const QRect rect = visualRect(index); if (!mapToViewport(contentsRect()).contains(rect)) { return; } QStyleOptionViewItemV4 option = viewOptions(); - option.rect = mapToScene(mapFromViewport(rect)).boundingRect().toRect(); + option.rect = rect; - // ### Note that we don't embed the editor in the applet as a - // QGraphicsProxyWidget here, because calling setFocus() on the - // editor or the proxy doesn't work properly when we do. - QWidget *editor = m_delegate->createEditor(view(), option, index); + QWidget *editor = m_delegate->createEditor(0, option, index); + editor->setAttribute(Qt::WA_NoSystemBackground); editor->installEventFilter(m_delegate); + QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(this); + proxy->setWidget(editor); + m_delegate->updateEditorGeometry(editor, option, index); m_delegate->setEditorData(editor, index); @@ -987,6 +1866,106 @@ m_editorIndex = index; } +void FolderView::toggleIconsLocked(bool locked) +{ + m_iconsLocked = locked; + config().writeEntry("iconsLocked", locked); + emit configNeedsSaving(); +} + +void FolderView::toggleAlignToGrid(bool align) +{ + m_alignToGrid = align; + + if (align) { + alignIconsToGrid(); + } + + config().writeEntry("alignToGrid", align); + emit configNeedsSaving(); +} + +void FolderView::toggleDirectoriesFirst(bool enable) +{ + m_sortDirsFirst = enable; + + m_model->setSortDirectoriesFirst(m_sortDirsFirst); + if (m_sortColumn != -1) { + m_savedPositions.clear(); + m_model->invalidate(); + } + + config().writeEntry("sortDirsFirst", m_sortDirsFirst); + emit configNeedsSaving(); +} + +void FolderView::sortingChanged(QAction *action) +{ + int column = KDirModel::Name; + + if (action == m_actionCollection.action("sort_name")) { + column = KDirModel::Name; + } else if (action == m_actionCollection.action("sort_size")) { + column = KDirModel::Size; + } else if (action == m_actionCollection.action("sort_type")) { + column = KDirModel::Type; + } else if (action == m_actionCollection.action("sort_date")) { + column = KDirModel::ModifiedTime; + } + + if (column != m_sortColumn) { + m_savedPositions.clear(); + m_model->invalidate(); + m_model->sort(column, Qt::AscendingOrder); + m_delayedSaveTimer.start(5000, this); + m_sortColumn = column; + m_layoutBroken = false; + config().writeEntry("sortColumn", m_sortColumn); + emit configNeedsSaving(); + } +} + +void FolderView::updateSortActionsState() +{ + if (m_sortColumn != -1 && m_layoutBroken) { + foreach (QAction *action, m_sortingGroup->actions()) { + action->setChecked(false); + } + m_sortColumn = -1; + config().writeEntry("sortColumn", m_sortColumn); + emit configNeedsSaving(); + return; + } + + switch (m_sortColumn) + { + case KDirModel::Name: + m_actionCollection.action("sort_name")->setChecked(true); + break; + case KDirModel::Size: + m_actionCollection.action("sort_size")->setChecked(true); + break; + case KDirModel::Type: + m_actionCollection.action("sort_type")->setChecked(true); + break; + case KDirModel::ModifiedTime: + m_actionCollection.action("sort_date")->setChecked(true); + break; + } +} + +void FolderView::listingCompleted() +{ + m_delayedCacheClearTimer.start(5000, this); + m_initialListing = false; +} + +void FolderView::listingCanceled() +{ + m_delayedCacheClearTimer.start(5000, this); + m_initialListing = false; +} + void FolderView::commitData(QWidget *editor) { m_delegate->setModelData(editor, m_model, m_editorIndex); @@ -996,16 +1975,34 @@ { Q_UNUSED(hint) + editor->removeEventFilter(m_delegate); if (editor->hasFocus()) { setFocus(); } editor->hide(); - editor->removeEventFilter(m_delegate); editor->deleteLater(); markEverythingDirty(); } +void FolderView::filterChanged(int index) +{ + uiFilter.filterFilesPattern->setEnabled(index != 0); + uiFilter.searchMimetype->setEnabled(index != 0); + uiFilter.filterFilesList->setEnabled(index != 0); + uiFilter.selectAll->setEnabled(index != 0); + uiFilter.deselectAll->setEnabled(index != 0); +} + +void FolderView::selectUnselectAll() +{ + Qt::CheckState state = sender() == uiFilter.selectAll ? Qt::Checked : Qt::Unchecked; + for (int i = 0; i < uiFilter.filterFilesList->model()->rowCount(); i++) { + const QModelIndex index = uiFilter.filterFilesList->model()->index(i, 0); + uiFilter.filterFilesList->model()->setData(index, state, Qt::CheckStateRole); + } +} + void FolderView::moveToTrash(Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) { Q_UNUSED(buttons) @@ -1051,6 +2048,7 @@ QList<QAction*> editActions; editActions.append(m_actionCollection.action("rename")); + KConfigGroup configGroup(KGlobal::config(), "KDE"); bool showDeleteCommand = configGroup.readEntry("ShowDeleteCommand", false); if (!hasRemoteFiles) { @@ -1123,7 +2121,8 @@ void FolderView::mousePressEvent(QGraphicsSceneMouseEvent *event) { - if (!contentsRect().contains(event->pos())) { + if (!contentsRect().contains(event->pos()) || + event->pos().y() < contentsRect().top() + m_titleHeight) { Plasma::Applet::mousePressEvent(event); return; } @@ -1156,6 +2155,7 @@ { if (event->modifiers() & Qt::ControlModifier) { m_selectionModel->select(index, QItemSelectionModel::Toggle); + m_selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate); markAreaDirty(visualRect(index)); } else if (!m_selectionModel->isSelected(index)) { m_selectionModel->select(index, QItemSelectionModel::ClearAndSelect); @@ -1166,14 +2166,19 @@ m_buttonDownPos = pos; event->accept(); return; + } else if (event->modifiers() & Qt::ControlModifier) { + // make current selection persistent + m_selectionModel->select(m_selectionModel->selection(), QItemSelectionModel::Select); } // If empty space was pressed m_pressedIndex = QModelIndex(); m_buttonDownPos = pos; if (m_selectionModel->hasSelection()) { - m_selectionModel->clearSelection(); - markEverythingDirty(); + if (!(event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier))) { + m_selectionModel->clearSelection(); + markEverythingDirty(); + } } event->accept(); } @@ -1192,10 +2197,11 @@ const QPointF pos = mapToViewport(event->pos()); const QModelIndex index = indexAt(pos); - if (index.isValid() && index == m_pressedIndex) { + if (index.isValid() && index == m_pressedIndex && !(event->modifiers() & Qt::ControlModifier)) { if (!m_doubleClick && KGlobalSettings::singleClick()) { const KFileItem item = m_model->itemForIndex(index); item.run(); + emit releaseVisualFocus(); m_selectionModel->clearSelection(); markEverythingDirty(); } @@ -1203,8 +2209,7 @@ // mousePressEvent() if the item is already selected when it's pressed, // so we need to do that here. if (m_selectionModel->currentIndex() != index || - m_selectionModel->selectedIndexes().count() > 1) - { + m_selectionModel->selectedIndexes().count() > 1) { m_selectionModel->select(index, QItemSelectionModel::ClearAndSelect); m_selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate); markEverythingDirty(); @@ -1245,6 +2250,7 @@ // Activate the item const KFileItem item = m_model->itemForIndex(index); item.run(); + emit releaseVisualFocus(); m_selectionModel->clearSelection(); markEverythingDirty(); @@ -1299,7 +2305,7 @@ selection.select(m_model->index(start, 0), m_model->index(end, 0)); } - m_selectionModel->select(selection, QItemSelectionModel::ClearAndSelect); + m_selectionModel->select(selection, QItemSelectionModel::ToggleCurrent); // Update the current index if (m_hoveredIndex.isValid()) { @@ -1326,7 +2332,9 @@ void FolderView::dragEnterEvent(QGraphicsSceneDragDropEvent *event) { - event->setAccepted(event->mimeData()->hasUrls()); + const QString appletMimeType = static_cast<Plasma::Corona*>(scene())->appletMimeType(); + event->setAccepted(event->mimeData()->hasUrls() || (isContainment() && + immutability() == Plasma::Mutable && event->mimeData()->hasFormat(appletMimeType))); } void FolderView::dragMoveEvent(QGraphicsSceneDragDropEvent *event) @@ -1336,10 +2344,13 @@ return; } + const QString appletMimeType = static_cast<Plasma::Corona*>(scene())->appletMimeType(); QRectF dirtyRect = visualRect(m_hoveredIndex); m_hoveredIndex = QModelIndex(); - if (index.isValid() && (m_model->flags(index) & Qt::ItemIsDropEnabled)) { + if (index.isValid() && (m_model->flags(index) & Qt::ItemIsDropEnabled) && + !event->mimeData()->hasFormat(appletMimeType)) + { dirtyRect |= visualRect(index); bool onOurself = false; @@ -1362,6 +2373,13 @@ void FolderView::dropEvent(QGraphicsSceneDragDropEvent *event) { + // If the dropped item is an applet, let Containment handle it + const QString appletMimeType = static_cast<Plasma::Corona*>(scene())->appletMimeType(); + if (isContainment() && event->mimeData()->hasFormat(appletMimeType)) { + Containment::dropEvent(event); + return; + } + // Check if the drop event originated from this applet. // Normally we'd do this by checking if the source widget matches the target widget // in the drag and drop operation, but since two QGraphicsItems can be part of the @@ -1386,11 +2404,24 @@ // If we get to this point, the drag was started from within the applet, // so instead of moving/copying/linking the dropped URL's to the folder, // we'll move the items in the view. - const QPoint delta = (mapToViewport(event->pos()) - m_buttonDownPos).toPoint(); - if (delta.isNull()) { + QPoint delta = (mapToViewport(event->pos()) - m_buttonDownPos).toPoint(); + if (delta.isNull() || m_iconsLocked) { return; } + // If this option is set, we'll assume the dragged icons were aligned + // to the grid before the drag started, and just adjust the delta we use + // to move all of them. + if (m_alignToGrid) { + const QSize size = gridSize() + QSize(10, 10); + if ((qAbs(delta.x()) < size.width() / 2) && (qAbs(delta.y()) < size.height() / 2)) { + return; + } + + delta.rx() = qRound(delta.x() / qreal(size.width())) * size.width(); + delta.ry() = qRound(delta.y() / qreal(size.height())) * size.height(); + } + foreach (const QUrl &url, event->mimeData()->urls()) { const QModelIndex index = m_model->indexForUrl(url); if (index.isValid()) { @@ -1398,25 +2429,14 @@ } } - // Make sure that the distance from the top of the viewport to the - // topmost item is 10 pixels. - int minY = INT_MAX; - for (int i = 0; i < m_items.size(); i++) { - minY = qMin(minY, m_items[i].rect.y()); - } - - int topMargin = contentsRect().top() + 10 + m_titleHeight; - if (minY != topMargin) { - int delta = topMargin - minY; - for (int i = 0; i < m_items.size(); i++) { - m_items[i].rect.translate(0, delta); - } - } - + // Make sure no icons have negative coordinates etc. + doLayoutSanityCheck(); updateScrollBar(); markEverythingDirty(); m_layoutBroken = true; + updateSortActionsState(); + m_delayedSaveTimer.start(5000, this); } // pos is the position where the mouse was clicked in the applet. @@ -1424,12 +2444,12 @@ void FolderView::startDrag(const QPointF &pos, QWidget *widget) { QModelIndexList indexes = m_selectionModel->selectedIndexes(); - QRectF boundingRect; + QRect boundingRect; foreach (const QModelIndex &index, indexes) { boundingRect |= visualRect(index); } - QPixmap pixmap(boundingRect.toAlignedRect().size()); + QPixmap pixmap(boundingRect.size()); pixmap.fill(Qt::transparent); QStyleOptionViewItemV4 option = viewOptions(); @@ -1440,7 +2460,7 @@ QPainter p(&pixmap); foreach (const QModelIndex &index, indexes) { - option.rect = visualRect(index).translated(-boundingRect.topLeft()).toAlignedRect(); + option.rect = visualRect(index).translated(-boundingRect.topLeft()); if (index == m_hoveredIndex) option.state |= QStyle::State_MouseOver; else @@ -1454,6 +2474,9 @@ // before calling QDrag::exec(), since it's a blocking call. markAreaDirty(boundingRect); + // Unset the hovered index so dropEvent won't think the icons are being + // dropped on a dragged folder. + m_hoveredIndex = QModelIndex(); m_dragInProgress = true; QDrag *drag = new QDrag(widget); @@ -1470,15 +2493,17 @@ QSize FolderView::iconSize() const { - const int size = KIconLoader::global()->currentSize(KIconLoader::Desktop); + const int size = (m_customIconSize != 0) ? m_customIconSize : KIconLoader::global()->currentSize(KIconLoader::Desktop); return QSize(size, size); } QSize FolderView::gridSize() const { + const QFontMetrics fm(m_font); + const int textHeight = fm.lineSpacing() * m_numTextLines; QSize size = iconSize(); - size.rwidth() *= 2; - size.rheight() *= 2; + size.rheight() = size.height() + textHeight + 16; + size.rwidth() = qMax(size.width() * 2, fm.averageCharWidth() * 10); return size; } @@ -1494,11 +2519,38 @@ option.decorationSize = iconSize(); option.displayAlignment = Qt::AlignHCenter; option.textElideMode = Qt::ElideRight; - option.features = QStyleOptionViewItemV2::WrapText; option.locale = QLocale::system(); option.widget = 0; option.viewItemPosition = QStyleOptionViewItemV4::OnlyOne; + if (m_numTextLines > 1) { + option.features = QStyleOptionViewItemV2::WrapText; + } + return option; } +void FolderView::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == m_delayedSaveTimer.timerId()) { + m_delayedSaveTimer.stop(); + saveIconPositions(); + emit configNeedsSaving(); + } else if (event->timerId() == m_delayedCacheClearTimer.timerId()) { + m_delayedCacheClearTimer.stop(); + m_savedPositions.clear(); + } else if (event->timerId() == m_delayedLayoutTimer.timerId()) { + m_delayedLayoutTimer.stop(); + layoutItems(); + } else if (event->timerId() == m_animTimer.timerId()) { + if (++m_animFrame >= 20) { + m_animFrame = 0; + } + const QSizeF size(m_animFrames.width(), m_animFrames.width()); + update(QRectF(contentsRect().center() - QPointF(size.width() / 2, size.width() / 2), size)); + } + + Containment::timerEvent(event); +} + +#include "folderview.moc" --- applets/folderview/folderviewadapter.cpp +++ applets/folderview/folderviewadapter.cpp @@ -0,0 +1,58 @@ +/* + * Copyright © 2008 Fredrik Höglund <fredrik@kde.org> + * + * 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 "folderviewadapter.h" + +FolderViewAdapter::FolderViewAdapter(FolderView *view) + : KAbstractViewAdapter(view), m_view(view) +{ +} + +QAbstractItemModel *FolderViewAdapter::model() const +{ + return m_view->model(); +} + +QSize FolderViewAdapter::iconSize() const +{ + return m_view->iconSize(); +} + +QPalette FolderViewAdapter::palette() const +{ + return m_view->palette(); +} + +QRect FolderViewAdapter::visibleArea() const +{ + return m_view->visibleArea(); +} + +QRect FolderViewAdapter::visualRect(const QModelIndex &index) const +{ + return m_view->visualRect(index); +} + +void FolderViewAdapter::connect(Signal signal, QObject *receiver, const char *slot) +{ + if (signal == ScrollBarValueChanged) { + QObject::connect(m_view->verticalScrollBar(), SIGNAL(valueChanged(int)), receiver, slot); + } +} + --- applets/folderview/proxymodel.h +++ applets/folderview/proxymodel.h @@ -21,6 +21,7 @@ #define PROXYMODEL_H #include <QSortFilterProxyModel> +#include <QStringList> class KFileItem; class KUrl; @@ -28,15 +29,37 @@ class ProxyModel : public QSortFilterProxyModel { public: + enum FilterMode { + NoFilter = 0, + FilterShowMatches, + FilterHideMatches + }; + ProxyModel(QObject *parent = 0); ~ProxyModel(); + void setFilterMode(FilterMode filterMode); + FilterMode filterMode() const; + + void setMimeTypeFilterList(const QStringList &mimeList); + const QStringList &mimeTypeFilterList() const; + + void setSortDirectoriesFirst(bool enable); + bool sortDirectoriesFirst() const; + QModelIndex indexForUrl(const KUrl &url) const; KFileItem itemForIndex(const QModelIndex &index) const; bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + static FilterMode filterModeFromInt(int filterMode); + protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + +private: + FilterMode m_filterMode; + QStringList m_mimeList; + bool m_sortDirsFirst; }; #endif --- applets/folderview/previewpluginsmodel.h +++ applets/folderview/previewpluginsmodel.h @@ -0,0 +1,46 @@ +/* + * Copyright © 2008 Fredrik Höglund <fredrik@kde.org> + * + * 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 <QAbstractListModel> +#include <KServiceTypeTrader> +#include <KService> +#include <QList> + +class QStringList; + +class PreviewPluginsModel : public QAbstractListModel +{ +public: + PreviewPluginsModel(QObject *parent = 0); + virtual ~PreviewPluginsModel(); + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + int rowCount(const QModelIndex &parent = QModelIndex()) const { Q_UNUSED(parent) return plugins.size(); } + void setCheckedPlugins(const QStringList &list); + QStringList checkedPlugins() const; + +private: + int indexOfPlugin(const QString &name) const; + +private: + KService::List plugins; + QList<int> checkedRows; +}; + --- applets/folderview/folderview.h +++ applets/folderview/folderview.h @@ -1,5 +1,6 @@ /* * Copyright © 2008 Fredrik Höglund <fredrik@kde.org> + * Copyright © 2008 Rafael Fernández López <ereslibre@kde.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,24 +22,36 @@ #define FOLDERVIEW_H #include <QPersistentModelIndex> +#include <QSortFilterProxyModel> +#include <QListView> #include <QStyleOption> #include <QPointer> +#include <QBasicTimer> #include <KActionCollection> +#include <KMimeType> #include <plasma/containment.h> -#include "ui_folderviewConfig.h" +#include "ui_folderviewFilterConfig.h" +#include "ui_folderviewDisplayConfig.h" +#include "ui_folderviewLocationConfig.h" class KDirModel; class KFileItemDelegate; class KNewMenu; class QItemSelectionModel; class ProxyModel; -class ScrollBar; +namespace Plasma +{ + class ScrollBar; +} + struct ViewItem { + ViewItem() : rect(QRect()), layouted(false) {} QRect rect; + bool layouted; }; class FolderView : public Plasma::Containment @@ -50,8 +63,17 @@ ~FolderView(); void init(); + void saveState(KConfigGroup &config) const; + void createAnimationFrames(); void paintInterface(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRect &contentsRect); void setPath(const QString&); + QRect visualRect(const QModelIndex &index) const; + QModelIndex indexAt(const QPointF &point) const; + QSize iconSize() const; + QSize gridSize() const; + QScrollBar *verticalScrollBar() const; + QAbstractItemModel *model() const; + QRect visibleArea() const; protected: void createConfigurationInterface(KConfigDialog *parent); @@ -87,27 +109,46 @@ void moveToTrash(Qt::MouseButtons, Qt::KeyboardModifiers); void deleteSelectedIcons(); void undoTextChanged(const QString &text); + void toggleIconsLocked(bool locked); + void toggleAlignToGrid(bool align); + void toggleDirectoriesFirst(bool enable); + void sortingChanged(QAction *action); + void listingCompleted(); + void listingCanceled(); + void commitData(QWidget *editor); void closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint); + void filterChanged(int index); + void selectUnselectAll(); + + void showConfigurationInterfacePlasmoid(); + private: + void setUrl(const KUrl &url); void createActions(); + void updateSortActionsState(); KUrl::List selectedUrls() const; void showContextMenu(QWidget *widget, const QPoint &pos, const QModelIndexList &indexes); int columnsForWidth(qreal width) const; + int rowsForHeight(qreal height) const; + QPoint nextGridPosition(const QPoint &prevPos, const QSize &gridSize, const QRect &contentRect) const; + QPoint findNextEmptyPosition(const QPoint &prevPos, const QSize &gridSize, const QRect &contentRect) const; void layoutItems(); + void alignIconsToGrid(); + bool doLayoutSanityCheck(); + void saveIconPositions() const; + void loadIconPositions(); void updateScrollBar(); + void updateScrollBarGeometry(); QRect scrollBackbufferContents(); void markAreaDirty(const QRect &rect); void markAreaDirty(const QRectF &rect) { markAreaDirty(rect.toAlignedRect()); } void markEverythingDirty(); void updateTextShadows(const QColor &textColor); - QModelIndex indexAt(const QPointF &point); - QRectF visualRect(const QModelIndex &index); - QSize iconSize() const; - QSize gridSize() const; QStyleOptionViewItemV4 viewOptions() const; + void timerEvent(QTimerEvent *event); void startDrag(const QPointF &pos, QWidget *widget); void constraintsEvent(Plasma::Constraints constraints); void focusInEvent(QFocusEvent *event); @@ -128,24 +169,35 @@ KFileItemDelegate *m_delegate; KDirModel *m_dirModel; ProxyModel *m_model; - ScrollBar *m_scrollBar; + Plasma::ScrollBar *m_scrollBar; QPixmap m_pixmap; QPixmap m_topFadeTile; QPixmap m_bottomFadeTile; QRegion m_dirtyRegion; QItemSelectionModel *m_selectionModel; KUrl m_url; + QString m_titleText; int m_titleHeight; int m_lastScrollValue; bool m_viewScrolled; + int m_filterType; QString m_filterFiles; + QStringList m_filterFilesMimeList; QFont m_font; + QColor m_textColor; QPointer<KNewMenu> m_newMenu; KActionCollection m_actionCollection; + QActionGroup *m_sortingGroup; QVector<ViewItem> m_items; + QHash<QString, QPoint> m_savedPositions; int m_columns; - bool m_layoutValid; + int m_rows; + int m_validRows; + int m_sortColumn; bool m_layoutBroken; + bool m_needPostLayoutPass; + bool m_initialListing; + bool m_positionsLoaded; QPersistentModelIndex m_hoveredIndex; QPersistentModelIndex m_pressedIndex; QPersistentModelIndex m_editorIndex; @@ -153,11 +205,75 @@ QRectF m_viewportRect; QPointF m_buttonDownPos; QTime m_pressTime; - Ui::folderviewConfig ui; + Ui::folderviewFilterConfig uiFilter; + Ui::folderviewDisplayConfig uiDisplay; + Ui::folderviewLocationConfig uiLocation; bool m_doubleClick; bool m_dragInProgress; + bool m_iconsLocked; + bool m_alignToGrid; + bool m_sortDirsFirst; + bool m_drawShadows; + QString m_customLabel; + int m_customIconSize; + int m_numTextLines; + QListView::Flow m_flow; + QPoint m_lastDeletedPos; + QPoint m_currentLayoutPos; + int m_animFrame; + QPixmap m_animFrames; + QBasicTimer m_delayedSaveTimer; + QBasicTimer m_delayedCacheClearTimer; + QBasicTimer m_delayedLayoutTimer; + QBasicTimer m_animTimer; }; -K_EXPORT_PLASMA_APPLET(folderview, FolderView) + +// --------------------------------------------------------------------------- + + + +class MimeModel : public QStringListModel +{ +public: + MimeModel(QObject *parent = 0); + + virtual QVariant data(const QModelIndex &index, int role) const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + +private: + KMimeType::List m_mimetypes; + QMap<KMimeType*, Qt::CheckState> m_state; +}; + + + +// --------------------------------------------------------------------------- + + + +class ProxyMimeModel : public QSortFilterProxyModel +{ +Q_OBJECT + +public: + ProxyMimeModel(QObject *parent = 0); + + virtual void setSourceModel(QAbstractItemModel *sourceModel); + +public slots: + void setFilter(const QString &filter); + +protected: + virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; + +private: + QString m_filter; +}; + #endif --- applets/folderview/plasma-applet-folderview.desktop +++ applets/folderview/plasma-applet-folderview.desktop @@ -1,8 +1,7 @@ [Desktop Entry] Name=Folder View -Name[ar]=عرض المجلد +Name[be@latin]=Prahlad kataloha Name[bg]=Директория -Name[bn]=ফোল্ডার ভিউ Name[bn_IN]=ফোল্ডার প্রদর্শন Name[ca]=Vista de carpeta Name[cs]=Pohled na složku @@ -26,9 +25,11 @@ Name[ja]=フォルダビュー Name[kk]=Қапшық көрінісі Name[km]=ទិដ្ឋភាពជាថត +Name[kn]=ಕಡತಕೋಶ ನೋಟ Name[ko]=폴더 보기 +Name[ku]=Bergehê Peldankê Name[lt]=Aplanko rodymas -Name[lv]=Mapes skats +Name[lv]=Mapju skats Name[mk]=Преглед на папки Name[ml]=കൂട കാഴ്ച Name[mr]=संचयीका दृश्य @@ -46,6 +47,7 @@ Name[sr@latin]=prikaz fascikle Name[sv]=Katalogvy Name[ta]=அடைவு காட்சி +Name[te]=ఫొల్డర్ వీక్షణం Name[tg]=Намоиши феҳрист Name[th]=มุมมองโฟลเดอร์ Name[tr]=Dizin Görünümü @@ -56,6 +58,23 @@ Name[x-test]=xxFolder Viewxx Name[zh_CN]=文件夹视图 Name[zh_TW]=資料夾檢視 +Comment=Display the content of folders (Desktop as default) +Comment[be@latin]=Pakazvaj źmieściva kataloha (zmoŭčana: stała) +Comment[el]=Εμφάνιση των περιεχομένων φακέλων (προκαθορισμένα ο Desktop) +Comment[gl]=Mostra o contido dos cartafoles (Por omisión, o escritorio) +Comment[gu]=ફોલ્ડરોની વિગતો દર્શાવો (ડેસ્કટોપ મૂળભૂત તરીકે) +Comment[kk]=Қапшықтағыны көрсету (Әдетті Үстел) +Comment[km]=បង្ហាញមាតិការបស់ថត (ផ្ទៃតុជាលំនាំដើម) +Comment[kn]=ಕಡತಕೋಶಗಳ ಒಳಪಿಡಿಯನ್ನು ತೋರಿಸು (ಪೂರ್ವನಿಯೋಜಿತವಾಗಿ ಗಣಕತೆರೆಯದ್ದು) +Comment[ku]=Naveroka peldankan nîşan bide (Sermasê standard e) +Comment[nds]=Den Inholt vun Ornern wiesen (standardwies de Schriefdisch) +Comment[pt]=Mostrar o conteúdo das pastas ('Desktop' por omissão) +Comment[pt_BR]=Mostrar o conteúdo das pastas ('Desktop' por omissão) +Comment[sv]=Visa innehåll i kataloger (Desktop är förval) +Comment[tr]=Dizinlerin içeriğini göster (Öntanımlı olarak Masaüstü) +Comment[uk]=Показує вміст тек (типово, стільниці) +Comment[x-test]=xxDisplay the content of folders (Desktop as default)xx +Comment[zh_TW]=顯示資料夾內容(預設顯示桌面資料夾) Type=Service Icon=folder X-KDE-ServiceTypes=Plasma/Applet,Plasma/Containment --- applets/folderview/CMakeLists.txt +++ applets/folderview/CMakeLists.txt @@ -1,12 +1,26 @@ project(plasma-folderview) +# Find the required Libaries +find_package(KDE4 REQUIRED) +include(KDE4Defaults) +find_package(Plasma REQUIRED) + +add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS}) +include_directories( + ${CMAKE_SOURCE_DIR} + ${CMAKE_BINARY_DIR} + ${KDE4_INCLUDES} + ) + set(folderview_SRCS folderview.cpp - proxymodel.cpp) + proxymodel.cpp + folderviewadapter.cpp + previewpluginsmodel.cpp) -kde4_add_ui_files(folderview_SRCS folderviewConfig.ui) +kde4_add_ui_files(folderview_SRCS folderviewFilterConfig.ui folderviewDisplayConfig.ui folderviewLocationConfig.ui) kde4_add_plugin(plasma_applet_folderview ${folderview_SRCS}) -target_link_libraries(plasma_applet_folderview ${PLASMA_LIBS} konq ${KDE4_KIO_LIBS} ${X11_LIBRARIES}) +target_link_libraries(plasma_applet_folderview ${PLASMA_LIBS} konq ${KDE4_KIO_LIBS} ${KDE4_KFILE_LIBS} ${X11_LIBRARIES}) install(TARGETS plasma_applet_folderview DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES plasma-applet-folderview.desktop DESTINATION ${SERVICES_INSTALL_DIR}) --- applets/folderview/folderviewadapter.h +++ applets/folderview/folderviewadapter.h @@ -0,0 +1,44 @@ +/* + * Copyright © 2008 Fredrik Höglund <fredrik@kde.org> + * + * 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 FOLDERVIEWADAPTER_H +#define FOLDERVIEWADAPTER_H + +#include "kabstractviewadapter_p.h" +#include "folderview.h" + +#include <QScrollBar> + +class FolderViewAdapter : public KAbstractViewAdapter +{ +public: + FolderViewAdapter(FolderView *view); + QAbstractItemModel *model() const; + QSize iconSize() const; + QPalette palette() const; + QRect visibleArea() const; + QRect visualRect(const QModelIndex &index) const; + void connect(Signal signal, QObject *receiver, const char *slot); + +private: + FolderView *m_view; +}; + +#endif + --- applets/folderview/folderviewDisplayConfig.ui +++ applets/folderview/folderviewDisplayConfig.ui @@ -0,0 +1,381 @@ +<ui version="4.0" > + <class>folderviewDisplayConfig</class> + <widget class="QWidget" name="folderviewDisplayConfig" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>522</width> + <height>682</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3" > + <item> + <layout class="QHBoxLayout" name="horizontalLayout_5" > + <item> + <widget class="QLabel" name="lblCustomLabel" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Fixed" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Label:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy" > + <cstring>labelEdit</cstring> + </property> + </widget> + </item> + <item> + <widget class="KLineEdit" name="labelEdit" > + <property name="whatsThis" > + <string>This edit field contains the label which is shown at the top of the applet. + +By default it contains the name of the place being shown in the applet, but if you prefer to have a custom label you can enter it here.</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2" > + <property name="title" > + <string>Icon Settings</string> + </property> + <property name="flat" > + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout" > + <item> + <widget class="QLabel" name="label_4" > + <property name="text" > + <string>Icon size:</string> + </property> + <property name="buddy" > + <cstring>sizeSlider</cstring> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_7" > + <item> + <spacer name="horizontalSpacer_8" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType" > + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>25</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_5" > + <property name="text" > + <string>Small</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QSlider" name="sizeSlider" > + <property name="whatsThis" > + <string>Use this slider to increase or decrease the size of the icons in the view.</string> + </property> + <property name="maximum" > + <number>6</number> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="tickPosition" > + <enum>QSlider::TicksBelow</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_6" > + <property name="text" > + <string>Large</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_7" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_3" > + <property name="title" > + <string>Text Settings</string> + </property> + <property name="flat" > + <bool>true</bool> + </property> + <layout class="QGridLayout" name="gridLayout_3" > + <item row="0" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Text color:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy" > + <cstring>colorButton</cstring> + </property> + </widget> + </item> + <item row="0" column="1" > + <layout class="QHBoxLayout" name="horizontalLayout_3" > + <item> + <widget class="KColorButton" name="colorButton" > + <property name="whatsThis" > + <string>Click this button to choose the color which is used for the text labels in the view.</string> + </property> + <property name="text" > + <string/> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Number of lines:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy" > + <cstring>numLinesEdit</cstring> + </property> + </widget> + </item> + <item row="1" column="1" > + <layout class="QHBoxLayout" name="horizontalLayout_4" > + <item> + <widget class="QSpinBox" name="numLinesEdit" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis" > + <string>Use this control to choose how many lines of text will be shown below the icons.</string> + </property> + <property name="minimum" > + <number>1</number> + </property> + <property name="maximum" > + <number>10</number> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_6" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="2" column="0" colspan="2" > + <widget class="QCheckBox" name="drawShadows" > + <property name="whatsThis" > + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Bitstream Vera Sans'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Check this option if you want the text labels to cast a shadow on the background.</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Shadows help make the text easier to read by making it stand out more from the background.</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">Note that with dark text colors, this option will cause the text to glow with a bright halo, instead of casting a shadow.</span></p></body></html></string> + </property> + <property name="text" > + <string>Show text shadows</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox" > + <property name="title" > + <string>Icon Arrangement</string> + </property> + <property name="flat" > + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2" > + <item> + <layout class="QHBoxLayout" name="horizontalLayout" > + <item> + <layout class="QGridLayout" name="gridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="flowLabel" > + <property name="text" > + <string>Arrange icons:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy" > + <cstring>flowCombo</cstring> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QComboBox" name="flowCombo" > + <property name="whatsThis" > + <string>Use this control to choose if you want the icons to be arranged top to bottom starting on the left side of the view, or arranged left to right starting at the top of the view.</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Sort icons:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="buddy" > + <cstring>sortCombo</cstring> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QComboBox" name="sortCombo" > + <property name="whatsThis" > + <string>Use this control to choose the criteria by which the icons will be sorted in the view.</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer name="horizontalSpacer" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>248</width> + <height>18</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QCheckBox" name="alignToGrid" > + <property name="whatsThis" > + <string>Check this option if you want the icons to be arranged in a grid. + +When this option is checked, icons will automatically snap to the nearest grid cell when you move them around in the view.</string> + </property> + <property name="text" > + <string>Align icons in a grid</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="lockInPlace" > + <property name="whatsThis" > + <string>Check this option if you do not want the icons to be moveable in the view. + +This option is useful if you want to avoid accidentally moving the icons while interacting with them.</string> + </property> + <property name="text" > + <string>Lock icons in place</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer_2" > + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>44</width> + <height>216</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>KLineEdit</class> + <extends>QLineEdit</extends> + <header>klineedit.h</header> + </customwidget> + <customwidget> + <class>KColorButton</class> + <extends>QPushButton</extends> + <header>kcolorbutton.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> --- applets/folderview/folderviewFilterConfig.ui +++ applets/folderview/folderviewFilterConfig.ui @@ -0,0 +1,225 @@ +<ui version="4.0" > + <class>folderviewFilterConfig</class> + <widget class="QWidget" name="folderviewFilterConfig" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>509</width> + <height>375</height> + </rect> + </property> + <property name="sizePolicy" > + <sizepolicy vsizetype="Expanding" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize" > + <size> + <width>500</width> + <height>0</height> + </size> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2" > + <item> + <widget class="QComboBox" name="filterType" > + <item> + <property name="text" > + <string>Show All Files</string> + </property> + </item> + <item> + <property name="text" > + <string>Show Files Matching</string> + </property> + </item> + <item> + <property name="text" > + <string>Hide Files Matching</string> + </property> + </item> + </widget> + </item> + <item> + <widget class="QFrame" name="fileFilters" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>10</verstretch> + </sizepolicy> + </property> + <property name="frameShape" > + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Plain</enum> + </property> + <property name="lineWidth" > + <number>0</number> + </property> + <layout class="QGridLayout" name="gridLayout_3" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>0</number> + </property> + <item row="0" column="1" > + <layout class="QGridLayout" name="mimeTypeLayout" > + <property name="horizontalSpacing" > + <number>0</number> + </property> + <property name="verticalSpacing" > + <number>4</number> + </property> + <property name="margin" > + <number>0</number> + </property> + <item row="5" column="0" > + <layout class="QHBoxLayout" name="horizontalLayout" > + <property name="spacing" > + <number>0</number> + </property> + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QListView" name="filterFilesList" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Expanding" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="selectionMode" > + <enum>QAbstractItemView::NoSelection</enum> + </property> + <property name="flow" > + <enum>QListView::TopToBottom</enum> + </property> + <property name="uniformItemSizes" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout" > + <property name="spacing" > + <number>4</number> + </property> + <property name="leftMargin" > + <number>4</number> + </property> + <property name="topMargin" > + <number>0</number> + </property> + <property name="rightMargin" > + <number>0</number> + </property> + <property name="bottomMargin" > + <number>0</number> + </property> + <item> + <widget class="QPushButton" name="selectAll" > + <property name="text" > + <string>Select All</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="deselectAll" > + <property name="text" > + <string>Deselect All</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer" > + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </item> + <item row="3" column="0" > + <widget class="KLineEdit" name="searchMimetype" > + <property name="clickMessage" > + <string>Search file type</string> + </property> + <property name="showClearButton" stdset="0" > + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="fileTypesLabel" > + <property name="text" > + <string>File types:</string> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="fileNameLabel" > + <property name="text" > + <string>File name pattern:</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="KLineEdit" name="filterFilesPattern" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Expanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" > + <string>Space-separated list of extensions, e.g. *.txt * .od*</string> + </property> + <property name="whatsThis" > + <string>Space-separated list of extensions, e.g. *.txt * .od* to display only office- and text-files</string> + </property> + <property name="alignment" > + <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set> + </property> + <property name="urlDropsEnabled" > + <bool>false</bool> + </property> + <property name="clickMessage" > + <string>Pattern filter</string> + </property> + <property name="showClearButton" stdset="0" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>KLineEdit</class> + <extends>QLineEdit</extends> + <header>klineedit.h</header> + </customwidget> + </customwidgets> + <includes> + <include location="global" >KDE/KUrlRequester</include> + </includes> + <resources/> + <connections/> +</ui>
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor