#include <QApplication>
#include <QMimeData>
#include <QTimer>
+#include <QWidget>
+
+#include <algorithm>
+#include <vector>
// #define KFILEITEMMODEL_DEBUG
m_dirLister = new KFileItemModelDirLister(this);
m_dirLister->setAutoUpdate(true);
m_dirLister->setDelayedMimeTypes(true);
- m_dirLister->setMainWindow(qApp->activeWindow());
+
+ const QWidget* parentWidget = qobject_cast<QWidget*>(parent);
+ if (parentWidget) {
+ m_dirLister->setMainWindow(parentWidget->window());
+ }
connect(m_dirLister, SIGNAL(started(KUrl)), this, SIGNAL(directoryLoadingStarted()));
connect(m_dirLister, SIGNAL(canceled()), this, SLOT(slotCanceled()));
void KFileItemModel::refreshDirectory(const KUrl& url)
{
+ // Refresh all expanded directories first (Bug 295300)
+ foreach (const KUrl& expandedUrl, m_expandedDirs) {
+ m_dirLister->openUrl(expandedUrl, KDirLister::Reload);
+ }
+
m_dirLister->openUrl(url, KDirLister::Reload);
}
itemsToRemove.append(m_itemData.at(index)->item);
++index;
}
+
+ QSet<KUrl> urlsToRemove;
+ urlsToRemove.reserve(itemsToRemove.count() + 1);
+ urlsToRemove.insert(url);
+ foreach (const KFileItem& item, itemsToRemove) {
+ KUrl url = item.url();
+ url.adjustPath(KUrl::RemoveTrailingSlash);
+ urlsToRemove.insert(url);
+ }
+
+ QSet<KFileItem>::iterator it = m_filteredItems.begin();
+ while (it != m_filteredItems.end()) {
+ const KUrl url = it->url();
+ KUrl parentUrl = url.upUrl();
+ parentUrl.adjustPath(KUrl::RemoveTrailingSlash);
+
+ if (urlsToRemove.contains(parentUrl)) {
+ it = m_filteredItems.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
removeItems(itemsToRemove);
}
{
if (m_filter.pattern() != nameFilter) {
dispatchPendingItemsToInsert();
-
m_filter.setPattern(nameFilter);
+ applyFilters();
+ }
+}
+
+QString KFileItemModel::nameFilter() const
+{
+ return m_filter.pattern();
+}
+
+void KFileItemModel::setMimeTypeFilters(const QStringList& filters)
+{
+ if (m_filter.mimeTypes() != filters) {
+ dispatchPendingItemsToInsert();
+ m_filter.setMimeTypes(filters);
+ applyFilters();
+ }
+}
+
+QStringList KFileItemModel::mimeTypeFilters() const
+{
+ return m_filter.mimeTypes();
+}
- // Check which shown items from m_itemData must get
- // hidden and hence moved to m_filteredItems.
- KFileItemList newFilteredItems;
- foreach (ItemData* itemData, m_itemData) {
+void KFileItemModel::applyFilters()
+{
+ // Check which shown items from m_itemData must get
+ // hidden and hence moved to m_filteredItems.
+ KFileItemList newFilteredItems;
+
+ foreach (ItemData* itemData, m_itemData) {
+ // Only filter non-expanded items as child items may never
+ // exist without a parent item
+ if (!itemData->values.value("isExpanded").toBool()) {
if (!m_filter.matches(itemData->item)) {
- // Only filter non-expanded items as child items may never
- // exist without a parent item
- if (!itemData->values.value("isExpanded").toBool()) {
- newFilteredItems.append(itemData->item);
- m_filteredItems.insert(itemData->item);
- }
+ newFilteredItems.append(itemData->item);
+ m_filteredItems.insert(itemData->item);
}
}
+ }
- removeItems(newFilteredItems);
+ removeItems(newFilteredItems);
- // Check which hidden items from m_filteredItems should
- // get visible again and hence removed from m_filteredItems.
- KFileItemList newVisibleItems;
+ // Check which hidden items from m_filteredItems should
+ // get visible again and hence removed from m_filteredItems.
+ KFileItemList newVisibleItems;
- QMutableSetIterator<KFileItem> it(m_filteredItems);
- while (it.hasNext()) {
- const KFileItem item = it.next();
- if (m_filter.matches(item)) {
- newVisibleItems.append(item);
- it.remove();
- }
+ QMutableSetIterator<KFileItem> it(m_filteredItems);
+ while (it.hasNext()) {
+ const KFileItem item = it.next();
+ if (m_filter.matches(item)) {
+ newVisibleItems.append(item);
+ it.remove();
}
-
- insertItems(newVisibleItems);
}
-}
-QString KFileItemModel::nameFilter() const
-{
- return m_filter.pattern();
+ insertItems(newVisibleItems);
}
QList<KFileItemModel::RoleInfo> KFileItemModel::rolesInformation()
RoleInfo info;
info.role = map[i].role;
info.translation = i18nc(map[i].roleTranslationContext, map[i].roleTranslation);
- info.group = i18nc(map[i].groupTranslationContext, map[i].groupTranslation);
+ if (map[i].groupTranslation) {
+ info.group = i18nc(map[i].groupTranslationContext, map[i].groupTranslation);
+ } else {
+ // For top level roles, groupTranslation is 0. We must make sure that
+ // info.group is an empty string then because the code that generates
+ // menus tries to put the actions into sub menus otherwise.
+ info.group = QString();
+ }
info.requiresNepomuk = map[i].requiresNepomuk;
info.requiresIndexer = map[i].requiresIndexer;
rolesInfo.append(info);
{
m_maximumUpdateIntervalTimer->stop();
dispatchPendingItemsToInsert();
+
+ emit directoryLoadingCanceled();
}
void KFileItemModel::slotNewItems(const KFileItemList& items)
}
}
- if (m_filter.pattern().isEmpty()) {
+ if (!m_filter.hasSetFilters()) {
m_pendingItemsToInsert.append(items);
} else {
- // The name-filter is active. Hide filtered items
+ // The name or type filter is active. Hide filtered items
// before inserting them into the model and remember
// the filtered items in m_filteredItems.
KFileItemList filteredItems;
foreach (const KFileItem& item, itemsToRemove) {
m_filteredItems.remove(item);
}
+
+ if (m_requestRole[ExpandedParentsCountRole] && m_expandedParentsCountRoot >= 0) {
+ // Remove all filtered children of deleted items. First, we put the
+ // deleted URLs into a set to provide fast lookup while iterating
+ // over m_filteredItems and prevent quadratic complexity if there
+ // are N removed items and N filtered items.
+ QSet<KUrl> urlsToRemove;
+ urlsToRemove.reserve(itemsToRemove.count());
+ foreach (const KFileItem& item, itemsToRemove) {
+ KUrl url = item.url();
+ url.adjustPath(KUrl::RemoveTrailingSlash);
+ urlsToRemove.insert(url);
+ }
+
+ QSet<KFileItem>::iterator it = m_filteredItems.begin();
+ while (it != m_filteredItems.end()) {
+ const KUrl url = it->url();
+ KUrl parentUrl = url.upUrl();
+ parentUrl.adjustPath(KUrl::RemoveTrailingSlash);
+
+ if (urlsToRemove.contains(parentUrl)) {
+ it = m_filteredItems.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
}
removeItems(itemsToRemove);
if (index > maxIndex) {
index = maxIndex;
}
- while ((pathA.at(index) != QLatin1Char('/') || pathB.at(index) != QLatin1Char('/')) && index > 0) {
+ while (index > 0 && (pathA.at(index) != QLatin1Char('/') || pathB.at(index) != QLatin1Char('/'))) {
--index;
}
return !m_dirLister->url().isLocalFile();
}
+static bool localeAwareLessThan(const QChar& c1, const QChar& c2)
+{
+ return QString::localeAwareCompare(c1, c2) < 0;
+}
+
QList<QPair<int, QVariant> > KFileItemModel::nameRoleGroups() const
{
Q_ASSERT(!m_itemData.isEmpty());
QString groupValue;
QChar firstChar;
- bool isLetter = false;
for (int i = 0; i <= maxIndex; ++i) {
if (isChildItem(i)) {
continue;
if (firstChar != newFirstChar) {
QString newGroupValue;
- if (newFirstChar >= QLatin1Char('A') && newFirstChar <= QLatin1Char('Z')) {
- // Apply group 'A' - 'Z'
- newGroupValue = newFirstChar;
- isLetter = true;
+ if (newFirstChar.isLetter()) {
+ // Try to find a matching group in the range 'A' to 'Z'.
+ static std::vector<QChar> lettersAtoZ;
+ if (lettersAtoZ.empty()) {
+ for (char c = 'A'; c <= 'Z'; ++c) {
+ lettersAtoZ.push_back(QLatin1Char(c));
+ }
+ }
+
+ std::vector<QChar>::iterator it = std::lower_bound(lettersAtoZ.begin(), lettersAtoZ.end(), newFirstChar, localeAwareLessThan);
+ if (it != lettersAtoZ.end()) {
+ if (localeAwareLessThan(newFirstChar, *it) && it != lettersAtoZ.begin()) {
+ // newFirstChar belongs to the group preceding *it.
+ // Example: for an umlaut 'A' in the German locale, *it would be 'B' now.
+ --it;
+ }
+ newGroupValue = *it;
+ } else {
+ newGroupValue = newFirstChar;
+ }
} else if (newFirstChar >= QLatin1Char('0') && newFirstChar <= QLatin1Char('9')) {
// Apply group '0 - 9' for any name that starts with a digit
newGroupValue = i18nc("@title:group Groups that start with a digit", "0 - 9");
- isLetter = false;
} else {
- if (isLetter) {
- // If the current group is 'A' - 'Z' check whether a locale character
- // fits into the existing group.
- // TODO: This does not work in the case if e.g. the group 'O' starts with
- // an umlaut 'O' -> provide unit-test to document this known issue
- const QChar prevChar(firstChar.unicode() - ushort(1));
- const QChar nextChar(firstChar.unicode() + ushort(1));
- const QString currChar(newFirstChar);
- const bool partOfCurrentGroup = currChar.localeAwareCompare(prevChar) > 0 &&
- currChar.localeAwareCompare(nextChar) < 0;
- if (partOfCurrentGroup) {
- continue;
- }
- }
newGroupValue = i18nc("@title:group", "Others");
- isLetter = false;
}
if (newGroupValue != groupValue) {