}
addSeparator();
- // Insert 'Sort By' and 'View Mode'
+ // Insert 'Sort By', 'Group By' and 'View Mode'
if (ContextMenuSettings::showSortBy()) {
addAction(m_mainWindow->actionCollection()->action(QStringLiteral("sort")));
}
+ if (ContextMenuSettings::showGroupBy()) {
+ addAction(m_mainWindow->actionCollection()->action(QStringLiteral("group")));
+ }
if (ContextMenuSettings::showViewMode()) {
addAction(m_mainWindow->actionCollection()->action(QStringLiteral("view_mode")));
}
}
menu->addAction(ac->action(QStringLiteral("show_hidden_files")));
menu->addAction(ac->action(QStringLiteral("sort")));
+ menu->addAction(ac->action(QStringLiteral("group")));
menu->addAction(ac->action(QStringLiteral("additional_info")));
if (!GeneralSettings::showStatusBar() || !GeneralSettings::showZoomSlider()) {
menu->addAction(ac->action(QStringLiteral("zoom")));
placesDock->setLocked(lock);
placesDock->setObjectName(QStringLiteral("placesDock"));
placesDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
-
+
m_placesPanel = new PlacesPanel(placesDock);
m_placesPanel->setCustomContextMenuActions({lockLayoutAction});
placesDock->setWidget(m_placesPanel);
// #define KFILEITEMMODEL_DEBUG
KFileItemModel::KFileItemModel(QObject *parent)
- : KItemModelBase("text", parent)
+ : KItemModelBase("text", "text", parent)
, m_dirLister(nullptr)
, m_sortDirsFirst(true)
, m_sortHiddenLast(false)
QElapsedTimer timer;
timer.start();
#endif
- switch (typeForRole(sortRole())) {
+ switch (typeForRole(groupRole())) {
+ case NoRole:
+ m_groups.clear();
+ break;
case NameRole:
m_groups = nameRoleGroups();
break;
m_groups = ratingRoleGroups();
break;
default:
- m_groups = genericStringRoleGroups(sortRole());
+ m_groups = genericStringRoleGroups(groupRole());
break;
}
}
}
+KFileItemModel::RoleInfo KFileItemModel::roleInformation(const QByteArray &role)
+{
+ static QHash<QByteArray, RoleInfo> information;
+ if (information.isEmpty()) {
+ int count = 0;
+ const RoleInfoMap *map = rolesInfoMap(count);
+ for (int i = 0; i < count; ++i) {
+ RoleInfo info;
+ info.role = map[i].role;
+ info.translation = map[i].roleTranslation.toString();
+ if (!map[i].groupTranslation.isEmpty()) {
+ info.group = map[i].groupTranslation.toString();
+ } 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.requiresBaloo = map[i].requiresBaloo;
+ info.requiresIndexer = map[i].requiresIndexer;
+ if (!map[i].tooltipTranslation.isEmpty()) {
+ info.tooltip = map[i].tooltipTranslation.toString();
+ } else {
+ info.tooltip = QString();
+ }
+
+ information.insert(map[i].role, info);
+ }
+ }
+
+ return information.value(role);
+}
+
QList<KFileItemModel::RoleInfo> KFileItemModel::rolesInformation()
{
static QList<RoleInfo> rolesInfo;
const RoleInfoMap *map = rolesInfoMap(count);
for (int i = 0; i < count; ++i) {
if (map[i].roleType != NoRole) {
- RoleInfo info;
- info.role = map[i].role;
- info.translation = map[i].roleTranslation.toString();
- if (!map[i].groupTranslation.isEmpty()) {
- info.group = map[i].groupTranslation.toString();
- } 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.requiresBaloo = map[i].requiresBaloo;
- info.requiresIndexer = map[i].requiresIndexer;
- if (!map[i].tooltipTranslation.isEmpty()) {
- info.tooltip = map[i].tooltipTranslation.toString();
- } else {
- info.tooltip = QString();
- }
+ RoleInfo info = roleInformation(map[i].role);
rolesInfo.append(info);
}
}
}
}
-void KFileItemModel::onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous)
+void KFileItemModel::onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous, bool resortItems)
{
Q_UNUSED(current)
Q_UNUSED(previous)
- resortAllItems();
+
+ if (resortItems) {
+ resortAllItems();
+ }
+}
+
+void KFileItemModel::onGroupRoleChanged(const QByteArray ¤t, const QByteArray &previous, bool resortItems)
+{
+ Q_UNUSED(previous)
+ m_groupRole = typeForRole(current);
+
+ if (!m_requestRole[m_sortRole]) {
+ QSet<QByteArray> newRoles = m_roles;
+ newRoles << current;
+ setRoles(newRoles);
+ }
+
+ if (resortItems) {
+ resortAllItems();
+ }
+}
+
+void KFileItemModel::onGroupOrderChanged(Qt::SortOrder current, Qt::SortOrder previous, bool resortItems)
+{
+ Q_UNUSED(current)
+ Q_UNUSED(previous)
+
+ if (resortItems) {
+ resortAllItems();
+ }
}
void KFileItemModel::loadSortingSettings()
m_items.clear();
m_items.reserve(itemCount);
- // Resort the items
- sort(m_itemData.begin(), m_itemData.end());
+ const QList<QPair<int, QVariant>> oldGroups = m_groups;
+
+ if (groupedSorting() && m_groupRole) {
+ // Hacky way to implement grouped sorting without rewriting more code.
+ // 1. Sort all items by grouping criteria. "Folders first" priority to be ignored.
+ // 2. Generate groups, which will stay usable after in-group sorting.
+ // 3. Perform sorts by the original sorting criteria inside groups.
+
+ RoleType originalSortRole = m_sortRole;
+ Qt::SortOrder originalSortOrder = sortOrder();
+ bool originalSortDirsFirst = m_sortDirsFirst;
+ m_sortRole = m_groupRole;
+ setSortOrder(groupOrder(), false);
+ m_sortDirsFirst = false;
+
+ sort(m_itemData.begin(), m_itemData.end());
+ m_groups.clear();
+ groups();
+
+ m_sortRole = originalSortRole;
+ setSortOrder(originalSortOrder, false);
+ m_sortDirsFirst = originalSortDirsFirst;
+
+ int lastIndex = 0, newIndex = 0;
+ for (int i = 0; i < m_groups.count() - 1; ++i) {
+ qCritical() << m_groups[i];
+ fflush(stderr);
+ newIndex = m_groups[i + 1].first;
+ sort(m_itemData.begin() + lastIndex, m_itemData.begin() + newIndex);
+ lastIndex = newIndex;
+ }
+ } else {
+ if (!m_groups.isEmpty()) {
+ m_groups.clear();
+ }
+ sort(m_itemData.begin(), m_itemData.end());
+ }
+
for (int i = 0; i < itemCount; ++i) {
m_items.insert(m_itemData.at(i)->item.url(), i);
}
Q_EMIT itemsMoved(KItemRange(firstMovedIndex, movedItemsCount), movedToIndexes);
} else if (groupedSorting()) {
- // The groups might have changed even if the order of the items has not.
- const QList<QPair<int, QVariant>> oldGroups = m_groups;
- m_groups.clear();
- if (groups() != oldGroups) {
+ if (m_groups != oldGroups) {
Q_EMIT groupsChanged();
}
}
-#ifdef KFILEITEMMODEL_DEBUG
+#ifdef KFILEITEMMODEL_DEBUGf
qCDebug(DolphinDebug) << "[TIME] Resorting of" << itemCount << "items:" << timer.elapsed();
#endif
}
std::reverse(itemRanges.begin(), itemRanges.end());
}
+ // N
+ //resortAllItems();
// The indexes in m_items are not correct anymore. Therefore, we clear m_items.
// It will be re-populated with the updated indices if index(const QUrl&) is called.
m_items.clear();
static const RoleInfoMap rolesInfoMap[] = {
// clang-format off
// | role | roleType | role translation | group translation | requires Baloo | requires indexer
- { nullptr, NoRole, KLazyLocalizedString(), KLazyLocalizedString(), KLazyLocalizedString(), false, false },
+ { nullptr, NoRole, kli18nc("@label", "None"), KLazyLocalizedString(), KLazyLocalizedString(), false, false },
{ "text", NameRole, kli18nc("@label", "Name"), KLazyLocalizedString(), KLazyLocalizedString(), false, false },
{ "size", SizeRole, kli18nc("@label", "Size"), KLazyLocalizedString(), KLazyLocalizedString(), false, false },
{ "modificationtime", ModificationTimeRole, kli18nc("@label", "Modified"), KLazyLocalizedString(), kli18nc("@tooltip", "The date format can be selected in settings."), false, false },
bool requiresIndexer;
};
+ /**
+ * @return Provides static information for a role that is supported
+ * by KFileItemModel. Some roles can only be determined if
+ * Baloo is enabled and/or the Baloo indexing is enabled.
+ */
+ static RoleInfo roleInformation(const QByteArray &role);
+
/**
* @return Provides static information for all available roles that
* are supported by KFileItemModel. Some roles can only be
protected:
void onGroupedSortingChanged(bool current) override;
void onSortRoleChanged(const QByteArray ¤t, const QByteArray &previous, bool resortItems = true) override;
- void onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous) override;
+ void onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous, bool resortItems = true) override;
+ void onGroupRoleChanged(const QByteArray ¤t, const QByteArray &previous, bool resortItems = true) override;
+ void onGroupOrderChanged(Qt::SortOrder current, Qt::SortOrder previous, bool resortItems = true) override;
private Q_SLOTS:
/**
- * Resorts all items dependent on the set sortRole(), sortOrder()
- * and foldersFirst() settings.
+ * Resorts all items dependent on the set sortRole(), sortOrder(),
+ * groupRole(), groupOrder() and foldersFirst() settings.
*/
void resortAllItems();
bool m_sortHiddenLast;
RoleType m_sortRole;
+ RoleType m_groupRole;
int m_sortingProgressPercent; // Value of directorySortingProgress() signal
QSet<QByteArray> m_roles;
, m_groupedSorting(false)
, m_sortRole()
, m_sortOrder(Qt::AscendingOrder)
+ , m_groupRole()
+ , m_groupOrder(Qt::AscendingOrder)
{
}
-KItemModelBase::KItemModelBase(const QByteArray &sortRole, QObject *parent)
+KItemModelBase::KItemModelBase(const QByteArray &sortRole, const QByteArray &groupRole, QObject *parent)
: QObject(parent)
, m_groupedSorting(false)
, m_sortRole(sortRole)
, m_sortOrder(Qt::AscendingOrder)
+ , m_groupRole(groupRole)
+ , m_groupOrder(Qt::AscendingOrder)
{
}
return m_sortRole;
}
-void KItemModelBase::setSortOrder(Qt::SortOrder order)
+void KItemModelBase::setSortOrder(Qt::SortOrder order, bool resortItems)
{
if (order != m_sortOrder) {
const Qt::SortOrder previous = m_sortOrder;
m_sortOrder = order;
- onSortOrderChanged(order, previous);
+ onSortOrderChanged(order, previous, resortItems);
Q_EMIT sortOrderChanged(order, previous);
}
}
+void KItemModelBase::setGroupRole(const QByteArray &role, bool regroupItems)
+{
+ if (role != m_groupRole) {
+ const QByteArray previous = m_groupRole;
+ m_groupRole = role;
+ onGroupRoleChanged(role, previous, regroupItems);
+ Q_EMIT groupRoleChanged(role, previous);
+ }
+}
+
+QByteArray KItemModelBase::groupRole() const
+{
+ return m_groupRole;
+}
+
+void KItemModelBase::setGroupOrder(Qt::SortOrder order, bool resortItems)
+{
+ if (order != m_groupOrder) {
+ const Qt::SortOrder previous = m_groupOrder;
+ m_groupOrder = order;
+ onGroupOrderChanged(order, previous, resortItems);
+ Q_EMIT groupOrderChanged(order, previous);
+ }
+}
+
QString KItemModelBase::roleDescription(const QByteArray &role) const
{
return QString::fromLatin1(role);
Q_UNUSED(resortItems)
}
-void KItemModelBase::onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous)
+void KItemModelBase::onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous, bool resortItems)
+{
+ Q_UNUSED(current)
+ Q_UNUSED(previous)
+ Q_UNUSED(resortItems)
+}
+
+void KItemModelBase::onGroupRoleChanged(const QByteArray ¤t, const QByteArray &previous, bool resortItems)
+{
+ Q_UNUSED(current)
+ Q_UNUSED(previous)
+ Q_UNUSED(resortItems)
+}
+
+void KItemModelBase::onGroupOrderChanged(Qt::SortOrder current, Qt::SortOrder previous, bool resortItems)
{
Q_UNUSED(current)
Q_UNUSED(previous)
+ Q_UNUSED(resortItems)
}
QUrl KItemModelBase::url(int index) const
public:
explicit KItemModelBase(QObject *parent = nullptr);
- explicit KItemModelBase(const QByteArray &sortRole, QObject *parent = nullptr);
+ explicit KItemModelBase(const QByteArray &sortRole, const QByteArray &groupRole, QObject *parent = nullptr);
~KItemModelBase() override;
/** @return The number of items. */
* called so that model-implementations can react on the sort order change. Afterwards the
* signal sortOrderChanged() will be emitted.
*/
- void setSortOrder(Qt::SortOrder order);
+ void setSortOrder(Qt::SortOrder order, bool resortItems = true);
Qt::SortOrder sortOrder() const;
+ /**
+ * Sets the group-role to \a role. The method KItemModelBase::onGroupRoleChanged() will be
+ * called so that model-implementations can react on the group-role change. Afterwards the
+ * signal groupRoleChanged() will be emitted.
+ * The implementation should regroup only if \a regroupItems is true.
+ */
+ void setGroupRole(const QByteArray &role, bool regroupItems = true);
+ QByteArray groupRole() const;
+
+ /**
+ * Sets the group order to \a order. The method KItemModelBase::onGroupOrderChanged() will be
+ * called so that model-implementations can react on the group order change. Afterwards the
+ * signal groupOrderChanged() will be emitted.
+ */
+ void setGroupOrder(Qt::SortOrder order, bool resortItems = true);
+ Qt::SortOrder groupOrder() const;
+
/**
* @return Translated description for the \p role. The description is e.g. used
* for the header in KItemListView.
void groupedSortingChanged(bool current);
void sortRoleChanged(const QByteArray ¤t, const QByteArray &previous);
void sortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous);
+ void groupRoleChanged(const QByteArray ¤t, const QByteArray &previous);
+ void groupOrderChanged(Qt::SortOrder current, Qt::SortOrder previous);
protected:
/**
* itemsRemoved() signal for all items, reorder the items internally and to emit a
* itemsInserted() signal afterwards.
*/
- virtual void onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous);
+ virtual void onSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous, bool resortItems = true);
+
+ /**
+ * Is invoked if the sort role has been changed by KItemModelBase::setSortRole(). Allows
+ * to react on the changed sort role before the signal sortRoleChanged() will be emitted.
+ * The implementation must assure that the items are sorted by the role given by \a current.
+ * Usually the most efficient way is to emit a
+ * itemsRemoved() signal for all items, reorder the items internally and to emit a
+ * itemsInserted() signal afterwards.
+ * The implementation should resort only if \a regroupItems is true.
+ */
+ virtual void onGroupRoleChanged(const QByteArray ¤t, const QByteArray &previous, bool resortItems = true);
+
+ /**
+ * Is invoked if the sort order has been changed by KItemModelBase::setSortOrder(). Allows
+ * to react on the changed sort order before the signal sortOrderChanged() will be emitted.
+ * The implementation must assure that the items are sorted by the order given by \a current.
+ * Usually the most efficient way is to emit a
+ * itemsRemoved() signal for all items, reorder the items internally and to emit a
+ * itemsInserted() signal afterwards.
+ */
+ virtual void onGroupOrderChanged(Qt::SortOrder current, Qt::SortOrder previous, bool resortItems = true);
private:
bool m_groupedSorting;
QByteArray m_sortRole;
Qt::SortOrder m_sortOrder;
+ QByteArray m_groupRole;
+ Qt::SortOrder m_groupOrder;
};
inline Qt::SortOrder KItemModelBase::sortOrder() const
return m_sortOrder;
}
+inline Qt::SortOrder KItemModelBase::groupOrder() const
+{
+ return m_groupOrder;
+}
+
#endif
m_viewProps->setHiddenFilesShown(viewProps.hiddenFilesShown());
m_viewProps->setSortRole(viewProps.sortRole());
m_viewProps->setSortOrder(viewProps.sortOrder());
+ m_viewProps->setGroupRole(viewProps.groupRole());
+ m_viewProps->setGroupOrder(viewProps.groupOrder());
KIO::ListJob *listJob = KIO::listRecursive(dir, KIO::HideProgressInfo);
connect(listJob, &KIO::ListJob::entries, this, &ApplyViewPropsJob::slotEntries);
return ContextMenuSettings::showAddToPlaces();
} else if (id == "sort") {
return ContextMenuSettings::showSortBy();
+ } else if (id == "group") {
+ return ContextMenuSettings::showGroupBy();
} else if (id == "view_mode") {
return ContextMenuSettings::showViewMode();
} else if (id == "open_in_new_tab") {
ContextMenuSettings::setShowAddToPlaces(visible);
} else if (id == "sort") {
ContextMenuSettings::setShowSortBy(visible);
+ } else if (id == "group") {
+ ContextMenuSettings::setShowGroupBy(visible);
} else if (id == "view_mode") {
ContextMenuSettings::setShowViewMode(visible);
} else if (id == "open_in_new_tab") {
<label>Show 'Sort By' in context menu.</label>
<default>true</default>
</entry>
+ <entry name="ShowGroupBy" type="Bool">
+ <label>Show 'Group By' in context menu.</label>
+ <default>true</default>
+ </entry>
<entry name="ShowViewMode" type="Bool">
<label>Show 'View Mode' in context menu.</label>
<default>true</default>
<entry name="GroupedSorting" type="Bool" >
<label context="@label">Grouped Sorting</label>
<whatsthis context="@info:whatsthis">When this option is enabled, the sorted items are categorized into groups.</whatsthis>
- <default>false</default>
+ <default>true</default>
</entry>
<entry name="SortRole" type="String" >
<max code="true">Qt::DescendingOrder</max>
</entry>
+ <entry name="GroupRole" type="String" >
+ <label context="@label">Group files by</label>
+ <whatsthis context="@info:whatsthis">This option defines which attribute (text, size, date, etc.) grouping is performed on.</whatsthis>
+ <default></default>
+ </entry>
+
+ <entry name="GroupOrder" type="Int" >
+ <label context="@label">Order in which to group files</label>
+ <default code="true">Qt::AscendingOrder</default>
+ <min code="true">Qt::AscendingOrder</min>
+ <max code="true">Qt::DescendingOrder</max>
+ </entry>
+
<entry name="SortFoldersFirst" type="Bool" >
<label context="@label">Show folders first when sorting files and folders</label>
<default>true</default>
</entry>
</group>
</kcfg>
-
-
actions,
{QStringLiteral("add_to_places"),
QStringLiteral("sort"),
+ QStringLiteral("group"),
QStringLiteral("view_mode"),
QStringLiteral("open_in_new_tab"),
QStringLiteral("open_in_new_window"),
return m_model->sortOrder();
}
+void DolphinView::setGroupRole(const QByteArray &role)
+{
+ if (role != groupRole()) {
+ ViewProperties props(viewPropertiesUrl());
+ props.setGroupRole(role);
+
+ KItemModelBase *model = m_container->controller()->model();
+ model->setGroupRole(role);
+
+ Q_EMIT groupRoleChanged(role);
+ }
+}
+
+QByteArray DolphinView::groupRole() const
+{
+ const KItemModelBase *model = m_container->controller()->model();
+ return model->groupRole();
+}
+
+void DolphinView::setGroupOrder(Qt::SortOrder order)
+{
+ if (groupOrder() != order) {
+ ViewProperties props(viewPropertiesUrl());
+ props.setGroupOrder(order);
+
+ m_model->setGroupOrder(order);
+
+ Q_EMIT groupOrderChanged(order);
+ }
+}
+
+Qt::SortOrder DolphinView::groupOrder() const
+{
+ return m_model->groupOrder();
+}
+
void DolphinView::setSortFoldersFirst(bool foldersFirst)
{
if (sortFoldersFirst() != foldersFirst) {
Q_EMIT sortOrderChanged(sortOrder);
}
+ const QByteArray groupRole = props.groupRole();
+ if (groupRole != m_model->groupRole()) {
+ m_model->setGroupRole(groupRole);
+ Q_EMIT groupRoleChanged(groupRole);
+ }
+
+ const Qt::SortOrder groupOrder = props.groupOrder();
+ if (groupOrder != m_model->groupOrder()) {
+ m_model->setGroupOrder(groupOrder);
+ Q_EMIT groupOrderChanged(groupOrder);
+ }
+
const bool sortFoldersFirst = props.sortFoldersFirst();
if (sortFoldersFirst != m_model->sortDirectoriesFirst()) {
m_model->setSortDirectoriesFirst(sortFoldersFirst);
* - show hidden files
* - show previews
* - enable grouping
+ * - grouping order
+ * - grouping type
*/
class DOLPHIN_EXPORT DolphinView : public QWidget
{
void setSortOrder(Qt::SortOrder order);
Qt::SortOrder sortOrder() const;
+ /**
+ * Updates the view properties of the current URL to the
+ * grouping given by \a role.
+ */
+ void setGroupRole(const QByteArray &role);
+ QByteArray groupRole() const;
+
+ /**
+ * Updates the view properties of the current URL to the
+ * sort order given by \a order.
+ */
+ void setGroupOrder(Qt::SortOrder order);
+ Qt::SortOrder groupOrder() const;
+
/** Sets a separate sorting with folders first (true) or a mixed sorting of files and folders (false). */
void setSortFoldersFirst(bool foldersFirst);
bool sortFoldersFirst() const;
/** Is emitted if the sort order (ascending or descending) has been changed. */
void sortOrderChanged(Qt::SortOrder order);
+ /** Is emitted if the grouping by name, size or date has been changed. */
+ void groupRoleChanged(const QByteArray &role);
+
+ /** Is emitted if the group order (ascending or descending) has been changed. */
+ void groupOrderChanged(Qt::SortOrder order);
+
/**
* Is emitted if the sorting of files and folders (separate with folders
* first or mixed) has been changed.
, m_actionCollection(collection)
, m_currentView(nullptr)
, m_sortByActions()
+ , m_groupByActions()
, m_visibleRoles()
{
Q_ASSERT(m_actionCollection);
connect(view, &DolphinView::groupedSortingChanged, this, &DolphinViewActionHandler::slotGroupedSortingChanged);
connect(view, &DolphinView::hiddenFilesShownChanged, this, &DolphinViewActionHandler::slotHiddenFilesShownChanged);
connect(view, &DolphinView::sortRoleChanged, this, &DolphinViewActionHandler::slotSortRoleChanged);
+ connect(view, &DolphinView::groupRoleChanged, this, &DolphinViewActionHandler::slotGroupRoleChanged);
+ connect(view, &DolphinView::groupOrderChanged, this, &DolphinViewActionHandler::slotGroupOrderChanged);
connect(view, &DolphinView::zoomLevelChanged, this, &DolphinViewActionHandler::slotZoomLevelChanged);
connect(view, &DolphinView::writeStateChanged, this, &DolphinViewActionHandler::slotWriteStateChanged);
slotWriteStateChanged(view->isFolderWritable());
QActionGroup *group = new QActionGroup(sortByActionMenu);
group->setExclusive(true);
- KToggleAction *ascendingAction = m_actionCollection->add<KToggleAction>(QStringLiteral("ascending"));
- ascendingAction->setActionGroup(group);
- connect(ascendingAction, &QAction::triggered, this, [this] {
+ KToggleAction *sortAscendingAction = m_actionCollection->add<KToggleAction>(QStringLiteral("sort_ascending"));
+ sortAscendingAction->setActionGroup(group);
+ connect(sortAscendingAction, &QAction::triggered, this, [this] {
m_currentView->setSortOrder(Qt::AscendingOrder);
});
- KToggleAction *descendingAction = m_actionCollection->add<KToggleAction>(QStringLiteral("descending"));
- descendingAction->setActionGroup(group);
- connect(descendingAction, &QAction::triggered, this, [this] {
+ KToggleAction *sortDescendingAction = m_actionCollection->add<KToggleAction>(QStringLiteral("sort_descending"));
+ sortDescendingAction->setActionGroup(group);
+ connect(sortDescendingAction, &QAction::triggered, this, [this] {
m_currentView->setSortOrder(Qt::DescendingOrder);
});
- sortByActionMenu->addAction(ascendingAction);
- sortByActionMenu->addAction(descendingAction);
+ sortByActionMenu->addAction(sortAscendingAction);
+ sortByActionMenu->addAction(sortDescendingAction);
sortByActionMenu->addSeparator();
sortByActionMenu->addAction(sortFoldersFirst);
sortByActionMenu->addAction(sortHiddenLast);
+ // View -> Group By
+ QActionGroup *groupByActionGroup = createFileItemRolesActionGroup(QStringLiteral("group_by_"));
+
+ KActionMenu *groupByActionMenu = m_actionCollection->add<KActionMenu>(QStringLiteral("group"));
+ groupByActionMenu->setIcon(QIcon::fromTheme(QStringLiteral("view-group")));
+ groupByActionMenu->setText(i18nc("@action:inmenu View", "Group By"));
+ groupByActionMenu->setPopupMode(QToolButton::InstantPopup);
+
+ const auto groupByActionGroupActions = groupByActionGroup->actions();
+ for (QAction *action : groupByActionGroupActions) {
+ groupByActionMenu->addAction(action);
+ }
+
+ groupByActionMenu->addSeparator();
+
+ KToggleAction *groupAscendingAction = m_actionCollection->add<KToggleAction>(QStringLiteral("group_ascending"));
+ groupAscendingAction->setActionGroup(group);
+ connect(groupAscendingAction, &QAction::triggered, this, [this] {
+ m_currentView->setGroupOrder(Qt::AscendingOrder);
+ });
+
+ KToggleAction *groupDescendingAction = m_actionCollection->add<KToggleAction>(QStringLiteral("group_descending"));
+ groupDescendingAction->setActionGroup(group);
+ connect(groupDescendingAction, &QAction::triggered, this, [this] {
+ m_currentView->setGroupOrder(Qt::DescendingOrder);
+ });
+
+ groupByActionMenu->addAction(groupAscendingAction);
+ groupByActionMenu->addAction(groupDescendingAction);
+
// View -> Additional Information
QActionGroup *visibleRolesGroup = createFileItemRolesActionGroup(QStringLiteral("show_"));
QActionGroup *DolphinViewActionHandler::createFileItemRolesActionGroup(const QString &groupPrefix)
{
const bool isSortGroup = (groupPrefix == QLatin1String("sort_by_"));
- Q_ASSERT(isSortGroup || groupPrefix == QLatin1String("show_"));
+ const bool isGroupGroup = (groupPrefix == QLatin1String("group_by_"));
+ Q_ASSERT(isSortGroup || isGroupGroup || groupPrefix == QLatin1String("show_"));
QActionGroup *rolesActionGroup = new QActionGroup(m_actionCollection);
- rolesActionGroup->setExclusive(isSortGroup);
+ rolesActionGroup->setExclusive(isSortGroup || isGroupGroup);
if (isSortGroup) {
connect(rolesActionGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::slotSortTriggered);
+ } else if (isGroupGroup) {
+ connect(rolesActionGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::slotGroupTriggered);
} else {
connect(rolesActionGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::toggleVisibleRole);
}
indexingEnabled = config.fileIndexingEnabled();
#endif
- const QList<KFileItemModel::RoleInfo> rolesInfo = KFileItemModel::rolesInformation();
+ QList<KFileItemModel::RoleInfo> rolesInfo = KFileItemModel::rolesInformation();
+ // Unlike sorting, grouping is optional. If creating for group_by_, include a None role.
+ if (isGroupGroup)
+ rolesInfo.append(KFileItemModel::roleInformation(nullptr));
+
for (const KFileItemModel::RoleInfo &info : rolesInfo) {
- if (!isSortGroup && info.role == "text") {
+ if (!isSortGroup && !isGroupGroup && info.role == "text") {
// It should not be possible to hide the "text" role
continue;
}
groupMenu->setActionGroup(rolesActionGroup);
groupMenuGroup = new QActionGroup(groupMenu);
- groupMenuGroup->setExclusive(isSortGroup);
+ groupMenuGroup->setExclusive(isSortGroup || isGroupGroup);
if (isSortGroup) {
connect(groupMenuGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::slotSortTriggered);
+ } else if (isGroupGroup) {
+ connect(groupMenuGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::slotGroupTriggered);
} else {
connect(groupMenuGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::toggleVisibleRole);
}
if (isSortGroup) {
m_sortByActions.insert(info.role, action);
+ } else if (isGroupGroup) {
+ m_groupByActions.insert(info.role, action);
} else {
m_visibleRoles.insert(info.role, action);
}
slotVisibleRolesChanged(m_currentView->visibleRoles(), QList<QByteArray>());
slotGroupedSortingChanged(m_currentView->groupedSorting());
slotSortRoleChanged(m_currentView->sortRole());
+ slotGroupRoleChanged(m_currentView->groupRole());
+ slotGroupOrderChanged(m_currentView->groupOrder());
slotZoomLevelChanged(m_currentView->zoomLevel(), -1);
// Updates the "show_hidden_files" action state and icon
void DolphinViewActionHandler::slotSortOrderChanged(Qt::SortOrder order)
{
- QAction *descending = m_actionCollection->action(QStringLiteral("descending"));
- QAction *ascending = m_actionCollection->action(QStringLiteral("ascending"));
+ QAction *descending = m_actionCollection->action(QStringLiteral("sort_descending"));
+ QAction *ascending = m_actionCollection->action(QStringLiteral("sort_ascending"));
+ const bool sortDescending = (order == Qt::DescendingOrder);
+ descending->setChecked(sortDescending);
+ ascending->setChecked(!sortDescending);
+}
+
+void DolphinViewActionHandler::slotGroupOrderChanged(Qt::SortOrder order)
+{
+ QAction *descending = m_actionCollection->action(QStringLiteral("group_descending"));
+ QAction *ascending = m_actionCollection->action(QStringLiteral("group_ascending"));
const bool sortDescending = (order == Qt::DescendingOrder);
descending->setChecked(sortDescending);
ascending->setChecked(!sortDescending);
}
}
- QAction *descending = m_actionCollection->action(QStringLiteral("descending"));
- QAction *ascending = m_actionCollection->action(QStringLiteral("ascending"));
+ QAction *descending = m_actionCollection->action(QStringLiteral("sort_descending"));
+ QAction *ascending = m_actionCollection->action(QStringLiteral("sort_ascending"));
if (role == "text" || role == "type" || role == "extension" || role == "tags" || role == "comment") {
descending->setText(i18nc("Sort descending", "Z-A"));
slotSortOrderChanged(m_currentView->sortOrder());
}
+void DolphinViewActionHandler::slotGroupRoleChanged(const QByteArray &role)
+{
+ KToggleAction *action = m_groupByActions.value(role);
+ if (action) {
+ action->setChecked(true);
+
+ if (!action->icon().isNull()) {
+ QAction *groupByMenu = m_actionCollection->action(QStringLiteral("group"));
+ groupByMenu->setIcon(action->icon());
+ }
+ }
+
+ QAction *descending = m_actionCollection->action(QStringLiteral("group_descending"));
+ QAction *ascending = m_actionCollection->action(QStringLiteral("group_ascending"));
+
+ if (role == "text" || role == "type" || role == "extension" || role == "tags" || role == "comment") {
+ descending->setText(i18nc("Group descending", "Z-A"));
+ ascending->setText(i18nc("Group ascending", "A-Z"));
+ } else if (role == "size") {
+ descending->setText(i18nc("Group descending", "Largest First"));
+ ascending->setText(i18nc("Group ascending", "Smallest First"));
+ } else if (role == "modificationtime" || role == "creationtime" || role == "accesstime") {
+ descending->setText(i18nc("Group descending", "Newest First"));
+ ascending->setText(i18nc("Group ascending", "Oldest First"));
+ } else if (role == "rating") {
+ descending->setText(i18nc("Group descending", "Highest First"));
+ ascending->setText(i18nc("Group ascending", "Lowest First"));
+ } else {
+ descending->setText(i18nc("Group descending", "Descending"));
+ ascending->setText(i18nc("Group ascending", "Ascending"));
+ }
+
+ slotGroupOrderChanged(m_currentView->groupOrder());
+}
+
void DolphinViewActionHandler::slotZoomLevelChanged(int current, int previous)
{
Q_UNUSED(previous)
m_currentView->setSortRole(role);
}
+void DolphinViewActionHandler::slotGroupTriggered(QAction *action)
+{
+ // The radiobuttons of the "Group By"-menu are split between the main-menu
+ // and several sub-menus. Because of this they don't have a common
+ // action-group that assures an exclusive toggle-state between the main-menu
+ // actions and the sub-menu-actions. If an action gets checked, it must
+ // be assured that all other actions get unchecked, except the ascending/
+ // descending actions
+ for (QAction *groupAction : std::as_const(m_sortByActions)) {
+ KActionMenu *actionMenu = qobject_cast<KActionMenu *>(groupAction);
+ if (actionMenu) {
+ const auto actions = actionMenu->menu()->actions();
+ for (QAction *subAction : actions) {
+ subAction->setChecked(false);
+ }
+ } else if (groupAction->actionGroup()) {
+ groupAction->setChecked(false);
+ }
+ }
+ action->setChecked(true);
+
+ // Apply the activated sort-role to the view
+ const QByteArray role = action->data().toByteArray();
+ m_currentView->setGroupRole(role);
+}
+
void DolphinViewActionHandler::slotAdjustViewProperties()
{
Q_EMIT actionBeingHandled();
*/
void slotSortRoleChanged(const QByteArray &role);
+ /**
+ * Updates the state of the 'Group Ascending/Descending' action.
+ */
+ void slotGroupOrderChanged(Qt::SortOrder order);
+
+ /**
+ * Updates the state of the 'Group by' actions.
+ */
+ void slotGroupRoleChanged(const QByteArray &role);
+
/**
* Updates the state of the 'Zoom In' and 'Zoom Out' actions.
*/
*/
void slotVisibleRolesChanged(const QList<QByteArray> ¤t, const QList<QByteArray> &previous);
+ /**
+ * Changes the grouping of the current view.
+ */
+ void slotGroupTriggered(QAction *);
+
/**
* Switches between sorting by groups or not.
*/
DolphinView *m_currentView;
QHash<QByteArray, KToggleAction *> m_sortByActions;
+ QHash<QByteArray, KToggleAction *> m_groupByActions;
QHash<QByteArray, KToggleAction *> m_visibleRoles;
};
return static_cast<Qt::SortOrder>(m_node->sortOrder());
}
+void ViewProperties::setGroupRole(const QByteArray &role)
+{
+ if (m_node->groupRole() != role) {
+ m_node->setGroupRole(role);
+ update();
+ }
+}
+
+QByteArray ViewProperties::groupRole() const
+{
+ return m_node->groupRole().toLatin1();
+}
+
+void ViewProperties::setGroupOrder(Qt::SortOrder groupOrder)
+{
+ if (m_node->groupOrder() != groupOrder) {
+ m_node->setGroupOrder(groupOrder);
+ update();
+ }
+}
+
+Qt::SortOrder ViewProperties::groupOrder() const
+{
+ return static_cast<Qt::SortOrder>(m_node->groupOrder());
+}
+
void ViewProperties::setSortFoldersFirst(bool foldersFirst)
{
if (m_node->sortFoldersFirst() != foldersFirst) {
void setSortOrder(Qt::SortOrder sortOrder);
Qt::SortOrder sortOrder() const;
+ void setGroupRole(const QByteArray &role);
+ QByteArray groupRole() const;
+
+ void setGroupOrder(Qt::SortOrder groupOrder);
+ Qt::SortOrder groupOrder() const;
+
void setSortFoldersFirst(bool foldersFirst);
bool sortFoldersFirst() const;