Commit a5c4d499 authored by Julien Borderie's avatar Julien Borderie Committed by Albert Astals Cid
Browse files

[KMenuEdit] Ability to sort elements

BUGS: 108419
REVIEW: 107409
FIXED-IN: 4.11.0
parent c552cd06
......@@ -23,6 +23,7 @@
#include <KAction>
#include <KActionCollection>
#include <KActionMenu>
#include <KConfig>
#include <KDebug>
#include <KGlobal>
......@@ -85,6 +86,26 @@ void KMenuEdit::setupActions()
action->setIcon(KIcon("menu_new_sep"));
action->setText(i18n("New S&eparator"));
action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_I));
// "sort all" menu
KActionMenu* sortAllMenu = new KActionMenu(KIcon("view-sort-ascending"), i18n("&Sort All By ..."), this);
sortAllMenu->setDelayed(false);
actionCollection()->addAction(SORT_ALL_ACTION_NAME, sortAllMenu);
action = actionCollection()->addAction(SORT_ALL_BY_NAME_ACTION_NAME);
action->setText(i18n("&Name"));
sortAllMenu->addAction(action);
action = actionCollection()->addAction(SORT_ALL_BY_DESCRIPTION_ACTION_NAME);
action->setText(i18n("&Description"));
sortAllMenu->addAction(action);
// "sort selection" menu
KActionMenu* sortMenu = new KActionMenu(KIcon("view-sort-ascending"), i18n("&Sort By ..."), this);
sortMenu->setDelayed(false);
actionCollection()->addAction(SORT_ACTION_NAME, sortMenu);
action = actionCollection()->addAction(SORT_BY_NAME_ACTION_NAME);
action->setText(i18n("&Name"));
sortMenu->addAction(action);
action = actionCollection()->addAction(SORT_BY_DESCRIPTION_ACTION_NAME);
action->setText(i18n("&Description"));
sortMenu->addAction(action);
actionCollection()->addAction(KStandardAction::Save, this, SLOT(slotSave()));
actionCollection()->addAction(KStandardAction::Quit, this, SLOT(close()));
......
......@@ -20,6 +20,9 @@
<Separator/>
<Action name="delete"/>
<Separator/>
<Action name="sort"/>
<Action name="sort_all"/>
<Separator/>
<Action name="restore_system_menu"/>
</Menu>
......@@ -37,6 +40,9 @@
<Action name="edit_paste"/>
<Separator/>
<Action name="delete"/>
<Separator/>
<Action name="sort"/>
<Action name="sort_all"/>
</ToolBar>
</kpartgui>
......@@ -30,7 +30,7 @@
#endif
static const char description[] = I18N_NOOP("KDE menu editor");
static const char version[] = "0.8";
static const char version[] = "0.9";
static KMenuEdit *menuEdit = 0;
......
......@@ -33,6 +33,7 @@
#include <QMenu>
#include <QApplication>
#include <QtDBus/QtDBus>
#include <QSignalMapper>
#include <KAction>
#include <KActionCollection>
......@@ -115,6 +116,55 @@ TreeItem::~TreeItem()
{
}
/**
* @brief Return the description.
* @return Description, or an empty string if none.
*/
QString TreeItem::description() const
{
QString description;
if (isEntry()) {
description = entryInfo()->description;
}
return description;
}
/**
* @brief Compare two items using their names.
* @param item1 First item.
* @param item2 Second item.
* @return Integer less than, equal to, or greater than zero if item1 is less than, equal to, or greater than item2.
*/
bool TreeItem::itemNameLessThan(QTreeWidgetItem *item1, QTreeWidgetItem *item2)
{
TreeItem *treeItem1 = static_cast<TreeItem*>(item1);
TreeItem *treeItem2 = static_cast<TreeItem*>(item2);
return treeItem1->name().toLower() < treeItem2->name().toLower();
}
/**
* @brief Compare two items using their descriptions. If both are empty, sort them by name.
* @param item1 First item.
* @param item2 Second item.
* @return Integer less than, equal to, or greater than zero if item1 is less than, equal to, or greater than item2.
*/
bool TreeItem::itemDescriptionLessThan(QTreeWidgetItem *item1, QTreeWidgetItem *item2)
{
// extract descriptions in lower case
TreeItem *treeItem1 = static_cast<TreeItem*>(item1);
TreeItem *treeItem2 = static_cast<TreeItem*>(item2);
const QString description1 = treeItem1->description().toLower();
const QString description2 = treeItem2->description().toLower();
// if description is missing for both items, sort them using their names
if (description1.isEmpty() && description2.isEmpty()) {
return itemNameLessThan(item1, item2);
}
else {
return description1 < description2;
}
}
void TreeItem::setName(const QString &name)
{
if (m_name == name) {
......@@ -215,6 +265,22 @@ TreeView::TreeView( KActionCollection *ac, QWidget *parent, const char *name )
connect(m_ac->action("newsubmenu"), SIGNAL(activated()), SLOT(newsubmenu()));
connect(m_ac->action("newsep"), SIGNAL(activated()), SLOT(newsep()));
// map sorting actions
m_sortSignalMapper = new QSignalMapper(this);
QAction *action = m_ac->action(SORT_BY_NAME_ACTION_NAME);
connect(action, SIGNAL(activated()), m_sortSignalMapper, SLOT(map()));
m_sortSignalMapper->setMapping(action, SortByName);
action = m_ac->action(SORT_BY_DESCRIPTION_ACTION_NAME);
connect(action, SIGNAL(activated()), m_sortSignalMapper, SLOT(map()));
m_sortSignalMapper->setMapping(action, SortByDescription);
action = m_ac->action(SORT_ALL_BY_NAME_ACTION_NAME);
connect(action, SIGNAL(activated()), m_sortSignalMapper, SLOT(map()));
m_sortSignalMapper->setMapping(action, SortAllByName);
action = m_ac->action(SORT_ALL_BY_DESCRIPTION_ACTION_NAME);
connect(action, SIGNAL(activated()), m_sortSignalMapper, SLOT(map()));
m_sortSignalMapper->setMapping(action, SortAllByDescription);
connect(m_sortSignalMapper, SIGNAL(mapped(const int)), this, SLOT(sort(const int)));
m_menuFile = new MenuFile(KStandardDirs::locateLocal("xdgconf-menu", "applications-kmenuedit.menu"));
m_rootFolder = new MenuFolderInfo;
m_separator = new MenuSeparatorInfo;
......@@ -263,6 +329,12 @@ void TreeView::setViewMode(bool showHidden)
m_rmb->addAction( m_ac->action("newsubmenu") );
m_rmb->addAction( m_ac->action("newsep") );
m_rmb->addSeparator();
action = m_ac->action(SORT_ACTION_NAME);
m_rmb->addAction(action);
action->setEnabled(false);
m_showHidden = showHidden;
readMenuFolderInfo();
fill();
......@@ -582,12 +654,15 @@ void TreeView::itemSelected(QTreeWidgetItem *item)
dselected = _item->isHiddenInMenu();
}
// change actions activation
m_ac->action("edit_cut")->setEnabled(selected);
m_ac->action("edit_copy")->setEnabled(selected);
if (m_ac->action("delete")) {
m_ac->action("delete")->setEnabled(selected && !dselected);
}
m_ac->action(SORT_ACTION_NAME)->setEnabled(selected && _item->isDirectory() && (_item->childCount() > 0));
m_ac->action(SORT_ALL_ACTION_NAME)->setEnabled(true);
if (!item) {
emit disableAction();
......@@ -1406,6 +1481,106 @@ void TreeView::paste()
setLayoutDirty(parentItem);
}
/**
* This slot is called from the signal mapper to sort children contained in an item.
* This item is determinated according to the chosen sort type.
*
* @brief Determine which item is to sort, and do it.
* @param sortCmd Sort type.
*/
void TreeView::sort(const int sortCmd)
{
// determine the chosen sort type and the selected item
SortType sortType = (SortType) sortCmd;
TreeItem *itemToSort;
if (sortType == SortByName || sortType == SortByDescription) {
itemToSort = static_cast<TreeItem*>(selectedItem());
} else if (sortType == SortAllByName) {
sortType = SortByName;
itemToSort = static_cast<TreeItem*>(invisibleRootItem());
} else if (sortType == SortAllByDescription) {
sortType = SortByDescription;
itemToSort = static_cast<TreeItem*>(invisibleRootItem());
}
// proceed to the sorting
sortItem(itemToSort, sortType);
}
/**
* Sort children of the given item, according to the sort type.
* The sorting is done on children groups, splited by separator items.
*
* @brief Sort item children.
* @param item Item to sort.
* @param sortType Sort type.
*/
void TreeView::sortItem(TreeItem *item, const SortType& sortType)
{
// sort the selected item only if contains children
if ( (!item->isDirectory()) || (item->childCount() == 0) ) {
return;
}
// remove contained children
QList<QTreeWidgetItem*> children = item->takeChildren();
// sort children groups, splited by separator items
QList<QTreeWidgetItem*>::iterator startIt = children.begin();
QList<QTreeWidgetItem*>::iterator currentIt = children.begin();
while (currentIt != children.end()) {
TreeItem *child = static_cast<TreeItem*>(*currentIt);
// if it's a separator, sort previous items and continue on following items
if (child->isSeparator() && startIt != currentIt) {
sortItemChildren(startIt, currentIt, sortType);
startIt = currentIt + 1;
}
++currentIt;
}
sortItemChildren(startIt, currentIt, sortType);
// insert sorted children in the tree
item->addChildren(children);
foreach (QTreeWidgetItem *child, children) {
// recreate item widget for separators
TreeItem *treeItem = static_cast<TreeItem*>(child);
if (treeItem->isSeparator()) {
setItemWidget(treeItem, 0, new SeparatorWidget);
}
// try to sort sub-children
sortItem(static_cast<TreeItem*>(child), sortType);
}
// flag current item as dirty
TreeItem *itemToFlagAsDirty = item;
// if tree root item, set the entire layout as dirty
if (item == invisibleRootItem()) {
itemToFlagAsDirty = 0;
}
setLayoutDirty(itemToFlagAsDirty);
}
/**
* Sort a children range defined with two list iterators, according to the sort type.
*
* @brief Sort a children range.
* @param begin First child iterator.
* @param end Last child iterator (exclusive, pointed child won't be affected).
* @param sortType Sort type.
*/
void TreeView::sortItemChildren(const QList<QTreeWidgetItem*>::iterator& begin, const QList<QTreeWidgetItem*>::iterator& end, const SortType& sortType)
{
// sort by name
if (sortType == SortByName) {
qSort(begin, end, TreeItem::itemNameLessThan);
}
// sort by description
else if (sortType == SortByDescription) {
qSort(begin, end, TreeItem::itemDescriptionLessThan);
}
}
void TreeView::del()
{
TreeItem *item = (TreeItem*)selectedItem();
......
......@@ -31,6 +31,7 @@
class QMenu;
class QDropEvent;
class QSignalMapper;
class KActionCollection;
class KDesktopFile;
......@@ -40,12 +41,21 @@ class MenuEntryInfo;
class MenuSeparatorInfo;
class KShortcut;
static const QString SORT_ACTION_NAME = "sort";
static const QString SORT_BY_NAME_ACTION_NAME = "sort_by_name";
static const QString SORT_BY_DESCRIPTION_ACTION_NAME = "sort_by_description";
static const QString SORT_ALL_ACTION_NAME = "sort_all";
static const QString SORT_ALL_BY_NAME_ACTION_NAME = "sort_all_by_name";
static const QString SORT_ALL_BY_DESCRIPTION_ACTION_NAME = "sort_all_by_description";
class TreeItem : public QTreeWidgetItem
{
public:
TreeItem(QTreeWidgetItem *parent, QTreeWidgetItem *after, const QString &menuId, bool __init = false);
TreeItem(QTreeWidget *parent, QTreeWidgetItem *after, const QString &menuId, bool __init = false);
~TreeItem();
static bool itemNameLessThan(QTreeWidgetItem *item1, QTreeWidgetItem *item2);
static bool itemDescriptionLessThan(QTreeWidgetItem *item1, QTreeWidgetItem *item2);
QString menuId() const { return m_menuId; }
......@@ -55,14 +65,17 @@ public:
MenuFolderInfo *folderInfo() { return m_folderInfo; }
void setMenuFolderInfo(MenuFolderInfo *folderInfo) { m_folderInfo = folderInfo; }
MenuEntryInfo *entryInfo() { return m_entryInfo; }
MenuEntryInfo *entryInfo() const { return m_entryInfo; }
void setMenuEntryInfo(MenuEntryInfo *entryInfo) { m_entryInfo = entryInfo; }
QString name() const { return m_name; }
void setName(const QString &name);
QString description() const;
bool isDirectory() const { return m_folderInfo; }
bool isEntry() const { return m_entryInfo; }
bool isSeparator() const { return !isDirectory() && !isEntry(); }
bool isHiddenInMenu() const { return m_hidden; }
void setHiddenInMenu(bool b);
......@@ -131,8 +144,16 @@ protected Q_SLOTS:
void copy();
void paste();
void del();
void sort(const int sortCmd);
protected:
enum SortType {
SortByName = 0,
SortByDescription,
SortAllByName,
SortAllByDescription
};
void contextMenuEvent(QContextMenuEvent *event);
void dropEvent(QDropEvent *event);
void startDrag(Qt::DropActions supportedActions);
......@@ -145,6 +166,8 @@ protected:
void fill();
void fillBranch(MenuFolderInfo *folderInfo, TreeItem *parent);
QString findName(KDesktopFile *df, bool deleted);
void sortItem(TreeItem *item, const SortType& sortType);
void sortItemChildren(const QList<QTreeWidgetItem*>::iterator& begin, const QList<QTreeWidgetItem*>::iterator& end, const SortType& sortType);
void closeAllItems(QTreeWidgetItem *item);
TreeItem *expandPath(TreeItem *item, const QString &path);
......@@ -183,6 +206,7 @@ private:
bool m_detailedMenuEntries;
bool m_detailedEntriesNamesFirst;
QStringList m_dropMimeTypes;
QSignalMapper *m_sortSignalMapper;
};
class MenuItemMimeData : public QMimeData
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment