kde-workspace/kmenuedit/treeview.cpp
Ivailo Monev d366561ceb generic: adjust to KService::newServicePath() change
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2022-09-29 23:28:56 +03:00

2002 lines
56 KiB
C++

/*
* Copyright (C) 2000 Matthias Elter <elter@kde.org>
* Copyright (C) 2001-2002 Raffaele Sandrini <sandrini@kde.org>
* Copyright (C) 2003 Waldo Bastian <bastian@kde.org>
* Copyright (C) 2008 Laurent Montel <montel@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "treeview.h"
#include <unistd.h>
#include <QDir>
#include <QHeaderView>
#include <QPainter>
#include <QRegExp>
#include <QPixmap>
#include <QtGui/qevent.h>
#include <QMenu>
#include <QApplication>
#include <QtDBus/QtDBus>
#include <QSignalMapper>
#include <KAction>
#include <KActionCollection>
#include <KBuildSycocaProgressDialog>
#include <KDebug>
#include <KDesktopFile>
#include <KGlobal>
#include <KIconLoader>
#include <KInputDialog>
#include <KLocale>
#include <KMessageBox>
#include <KService>
#include <KServiceGroup>
#include <KConfig>
#include <KConfigGroup>
#include <KStandardDirs>
#include <KIO/NetAccess>
#include "moc_treeview.cpp"
#include "menufile.h"
#include "menuinfo.h"
#define MOVE_FOLDER 'M'
#define COPY_FOLDER 'C'
#define MOVE_FILE 'm'
#define COPY_FILE 'c'
#define COPY_SEPARATOR 'S'
static const char *s_internalMimeType = "application/x-kmenuedit-internal";
class SeparatorWidget : public QWidget
{
public:
SeparatorWidget()
: QWidget(0)
{
}
protected:
void paintEvent(QPaintEvent * /*event*/)
{
QPainter p(this);
// Draw Separator
int h = (height() / 2) -1;
// if (isSelected()) {
// p->setPen( palette().color( QPalette::HighlightedText ) );
// } else {
// p->setPen( palette().color( QPalette::Text ) );
// }
p.drawLine(2, h, width() - 4, h);
}
};
TreeItem::TreeItem(QTreeWidgetItem *parent, QTreeWidgetItem *after, const QString& menuId, bool _m_init)
: QTreeWidgetItem(parent, after),
m_hidden(false),
m_init(_m_init),
m_layoutDirty(false),
m_menuId(menuId),
m_folderInfo(0),
m_entryInfo(0)
{
}
TreeItem::TreeItem(QTreeWidget *parent, QTreeWidgetItem *after, const QString& menuId, bool _m_init)
: QTreeWidgetItem(parent, after),
m_hidden(false),
m_init(_m_init),
m_layoutDirty(false),
m_menuId(menuId),
m_folderInfo(0),
m_entryInfo(0)
{
load();
}
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) {
return;
}
m_name = name;
update();
}
void TreeItem::setHiddenInMenu(bool b)
{
if (m_hidden == b) {
return;
}
m_hidden = b;
update();
}
void TreeItem::update()
{
QString s = m_name;
if (m_hidden) {
s += i18n(" [Hidden]");
}
setText(0, s);
}
void TreeItem::load()
{
if (m_folderInfo && !m_init) {
m_init = true;
TreeView *tv = static_cast<TreeView *>(treeWidget());
tv->fillBranch(m_folderInfo, this);
}
}
bool TreeItem::isLayoutDirty() const
{
if (m_layoutDirty) {
return true;
}
for (int i = 0; i < childCount(); ++i) {
TreeItem *item = dynamic_cast<TreeItem *>(child(i));
if (!item) {
continue;
}
if (item->isLayoutDirty()) {
return true;
}
}
return false;
}
static QPixmap appIcon(const QString &iconName)
{
QPixmap normal = KIconLoader::global()->loadIcon(iconName, KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(), 0L, true);
return normal;
}
TreeView::TreeView( KActionCollection *ac, QWidget *parent, const char *name )
: QTreeWidget(parent), m_ac(ac), m_popupMenu(0), m_clipboard(0),
m_clipboardFolderInfo(0), m_clipboardEntryInfo(0),
m_layoutDirty(false),
m_detailedMenuEntries(true), m_detailedEntriesNamesFirst(true)
{
m_dropMimeTypes << s_internalMimeType << KUrl::List::mimeDataTypes();
qRegisterMetaType<TreeItem *>("TreeItem");
setObjectName(name);
setAllColumnsShowFocus(true);
setRootIsDecorated(true);
setSortingEnabled(false);
setDragEnabled(true);
setAcceptDrops(true);
setMinimumWidth(240);
setHeaderLabels(QStringList() << QString(""));
header()->hide();
// listen for creation
connect(m_ac->action(NEW_ITEM_ACTION_NAME), SIGNAL(triggered()), SLOT(newitem()));
connect(m_ac->action(NEW_SUBMENU_ACTION_NAME), SIGNAL(triggered()), SLOT(newsubmenu()));
connect(m_ac->action(NEW_SEPARATOR_ACTION_NAME), SIGNAL(triggered()), SLOT(newsep()));
// listen for copy
connect(m_ac->action(CUT_ACTION_NAME), SIGNAL(triggered()), SLOT(cut()));
connect(m_ac->action(COPY_ACTION_NAME), SIGNAL(triggered()), SLOT(copy()));
connect(m_ac->action(PASTE_ACTION_NAME), SIGNAL(triggered()), SLOT(paste()));
// listen for deleting
connect(m_ac->action(DELETE_ACTION_NAME), SIGNAL(triggered()), SLOT(del()));
// listen for sorting
m_sortSignalMapper = new QSignalMapper(this);
QAction *action = m_ac->action(SORT_BY_NAME_ACTION_NAME);
connect(action, SIGNAL(triggered()), m_sortSignalMapper, SLOT(map()));
m_sortSignalMapper->setMapping(action, SortByName);
action = m_ac->action(SORT_BY_DESCRIPTION_ACTION_NAME);
connect(action, SIGNAL(triggered()), m_sortSignalMapper, SLOT(map()));
m_sortSignalMapper->setMapping(action, SortByDescription);
action = m_ac->action(SORT_ALL_BY_NAME_ACTION_NAME);
connect(action, SIGNAL(triggered()), m_sortSignalMapper, SLOT(map()));
m_sortSignalMapper->setMapping(action, SortAllByName);
action = m_ac->action(SORT_ALL_BY_DESCRIPTION_ACTION_NAME);
connect(action, SIGNAL(triggered()), m_sortSignalMapper, SLOT(map()));
m_sortSignalMapper->setMapping(action, SortAllByDescription);
connect(m_sortSignalMapper, SIGNAL(mapped(const int)), this, SLOT(sort(const int)));
// connect moving up/down actions
connect(m_ac->action(MOVE_UP_ACTION_NAME), SIGNAL(triggered()), SLOT(moveUpItem()));
connect(m_ac->action(MOVE_DOWN_ACTION_NAME), SIGNAL(triggered()), SLOT(moveDownItem()));
// listen for selection
connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
SLOT(itemSelected(QTreeWidgetItem*)));
m_menuFile = new MenuFile(KStandardDirs::locateLocal("xdgconf-menu", "applications-kmenuedit.menu"));
m_rootFolder = new MenuFolderInfo;
m_separator = new MenuSeparatorInfo;
}
TreeView::~TreeView()
{
cleanupClipboard();
delete m_rootFolder;
delete m_separator;
}
void TreeView::setViewMode(bool showHidden)
{
// setup popup menu
delete m_popupMenu;
m_popupMenu = new QMenu(this);
// creation
m_popupMenu->addAction(m_ac->action(NEW_ITEM_ACTION_NAME));
m_popupMenu->addAction(m_ac->action(NEW_SUBMENU_ACTION_NAME));
m_popupMenu->addAction(m_ac->action(NEW_SEPARATOR_ACTION_NAME));
m_popupMenu->addSeparator();
// copy
m_popupMenu->addAction(m_ac->action(CUT_ACTION_NAME));
m_popupMenu->addAction(m_ac->action(COPY_ACTION_NAME));
m_popupMenu->addAction(m_ac->action(PASTE_ACTION_NAME));
m_popupMenu->addSeparator();
// delete
m_popupMenu->addAction( m_ac->action(DELETE_ACTION_NAME));
m_popupMenu->addSeparator();
// move
m_popupMenu->addAction(m_ac->action(MOVE_UP_ACTION_NAME));
m_popupMenu->addAction(m_ac->action(MOVE_DOWN_ACTION_NAME));
m_popupMenu->addSeparator();
// sort
m_popupMenu->addAction(m_ac->action(SORT_ACTION_NAME));
m_showHidden = showHidden;
readMenuFolderInfo();
fill();
}
void TreeView::readMenuFolderInfo(MenuFolderInfo *folderInfo, KServiceGroup::Ptr folder, const QString &prefix)
{
if (!folderInfo)
{
folderInfo = m_rootFolder;
folder = KServiceGroup::root();
}
if (!folder || !folder->isValid())
return;
folderInfo->caption = folder->caption();
folderInfo->comment = folder->comment();
// Item names may contain ampersands. To avoid them being converted
// to accelerators, replace them with two ampersands.
folderInfo->hidden = folder->noDisplay();
folderInfo->directoryFile = folder->directoryEntryPath();
folderInfo->icon = folder->icon();
QString id = folder->relPath();
int i = id.lastIndexOf('/', -2);
id = id.mid(i+1);
folderInfo->id = id;
folderInfo->fullId = prefix + id;
foreach(const KSycocaEntry::Ptr &e, folder->entries(true, !m_showHidden, true, m_detailedMenuEntries && !m_detailedEntriesNamesFirst))
{
if (e->isType(KST_KServiceGroup))
{
KServiceGroup::Ptr g(KServiceGroup::Ptr::staticCast(e));
MenuFolderInfo *subFolderInfo = new MenuFolderInfo();
readMenuFolderInfo(subFolderInfo, g, folderInfo->fullId);
folderInfo->add(subFolderInfo, true);
}
else if (e->isType(KST_KService))
{
folderInfo->add(new MenuEntryInfo(KService::Ptr::staticCast(e)), true);
}
else if (e->isType(KST_KServiceSeparator))
{
folderInfo->add(m_separator, true);
}
}
}
void TreeView::fill()
{
QApplication::setOverrideCursor(Qt::WaitCursor);
clear();
fillBranch(m_rootFolder, 0);
QApplication::restoreOverrideCursor();
}
QString TreeView::findName(KDesktopFile *df, bool deleted)
{
QString name = df->readName();
if (deleted)
{
if (name == "empty")
name.clear();
if (name.isEmpty())
{
QString file = df->fileName();
QString res = df->resource();
bool isLocal = true;
const QStringList files = KGlobal::dirs()->findAllResources(res.toLatin1(), file);
for(QStringList::ConstIterator it = files.constBegin();
it != files.constEnd();
++it)
{
if (isLocal)
{
isLocal = false;
continue;
}
KDesktopFile df2(*it);
name = df2.readName();
if (!name.isEmpty() && (name != "empty"))
return name;
}
}
}
return name;
}
TreeItem *TreeView::createTreeItem(TreeItem *parent, QTreeWidgetItem *after, MenuFolderInfo *folderInfo, bool m_init)
{
TreeItem *item;
if (parent) {
item = new TreeItem(parent, after, QString(), m_init);
} else {
item = new TreeItem(this, after, QString(), m_init);
}
item->setMenuFolderInfo(folderInfo);
item->setName(folderInfo->caption);
item->setIcon(0, appIcon(folderInfo->icon));
item->setDirectoryPath(folderInfo->fullId);
item->setHiddenInMenu(folderInfo->hidden);
item->load();
return item;
}
TreeItem *TreeView::createTreeItem(TreeItem *parent, QTreeWidgetItem *after, MenuEntryInfo *entryInfo, bool m_init)
{
bool hidden = entryInfo->hidden;
TreeItem* item;
if (parent) {
item = new TreeItem(parent, after, entryInfo->menuId(),m_init);
} else {
item = new TreeItem(this, after, entryInfo->menuId(), m_init);
}
QString name;
if (m_detailedMenuEntries && entryInfo->description.length() != 0) {
if (m_detailedEntriesNamesFirst) {
name = entryInfo->caption + " (" + entryInfo->description + ')';
} else {
name = entryInfo->description + " (" + entryInfo->caption + ')';
}
} else {
name = entryInfo->caption;
}
//kDebug() << parent << after << name;
item->setMenuEntryInfo(entryInfo);
item->setName(name);
item->setIcon(0, appIcon(entryInfo->icon));
item->setHiddenInMenu(hidden);
item->load();
return item;
}
TreeItem *TreeView::createTreeItem(TreeItem *parent, QTreeWidgetItem *after, MenuSeparatorInfo *, bool init)
{
TreeItem* item;
if (parent) {
item = new TreeItem(parent, after, QString(), init);
} else {
item = new TreeItem(this, after, QString(), init);
}
setItemWidget(item, 0, new SeparatorWidget);
return item;
}
void TreeView::fillBranch(MenuFolderInfo *folderInfo, TreeItem *parent)
{
QString relPath = parent ? parent->directory() : QString();
TreeItem *after = 0;
foreach (MenuInfo *info, folderInfo->initialLayout)
{
MenuEntryInfo *entry = dynamic_cast<MenuEntryInfo*>(info);
if (entry)
{
after = createTreeItem(parent, after, entry);
continue;
}
MenuFolderInfo *subFolder = dynamic_cast<MenuFolderInfo*>(info);
if (subFolder)
{
after = createTreeItem(parent, after, subFolder);
continue;
}
MenuSeparatorInfo *separator = dynamic_cast<MenuSeparatorInfo*>(info);
if (separator)
{
after = createTreeItem(parent, after, separator);
continue;
}
}
}
void TreeView::closeAllItems(QTreeWidgetItem *item)
{
item->setExpanded(false);
for (int i = 0; i < item->childCount(); ++i) {
closeAllItems(item->child(i));
}
}
TreeItem *TreeView::expandPath(TreeItem *item, const QString &path)
{
int i = path.indexOf("/");
QString subMenu = path.left(i+1);
QString restMenu = path.mid(i+1);
for (int i = 0; i < item->childCount(); ++i) {
TreeItem *childItem = dynamic_cast<TreeItem *>(item->child(i));
if (!childItem) {
continue;
}
MenuFolderInfo *folderInfo = childItem->folderInfo();
if (folderInfo && (folderInfo->id == subMenu)) {
childItem->setExpanded(true);
if (!restMenu.isEmpty()) {
return expandPath(childItem, restMenu);
} else {
return childItem;
}
}
}
return 0;
}
void TreeView::selectMenu(const QString &menu)
{
for (int i = 0; i < topLevelItemCount(); ++i) {
closeAllItems(topLevelItem(i));
}
if (menu.length() <= 1)
{
setCurrentItem(topLevelItem(0));
clearSelection();
return; // Root menu
}
QString restMenu = menu;
if ( menu.startsWith( '/' ) )
restMenu = menu.mid(1);
if (!restMenu.endsWith('/'))
restMenu += '/';
TreeItem *item = 0;
int i = restMenu.indexOf("/");
QString subMenu = restMenu.left(i+1);
restMenu = restMenu.mid(i+1);
for (int i = 0; i < topLevelItemCount(); ++i) {
item = dynamic_cast<TreeItem *>(topLevelItem(i));
if (!item) {
continue;
}
MenuFolderInfo *folderInfo = item->folderInfo();
if (folderInfo && (folderInfo->id == subMenu)) {
if (!restMenu.isEmpty()) {
item = expandPath(item, restMenu);
}
break;
}
}
if (item)
{
setCurrentItem(item);
scrollToItem(item);
}
}
void TreeView::selectMenuEntry(const QString &menuEntry)
{
TreeItem *item = static_cast<TreeItem *>(selectedItem());
if (!item) {
item = static_cast<TreeItem *>(currentItem());
}
if (!item) {
return;
}
QTreeWidgetItem *parent = item->parent();
if (parent) {
for (int i = 0; i < parent->childCount(); ++i) {
TreeItem *item = dynamic_cast<TreeItem *>(parent->child(i));
if (!item || item->isDirectory()) {
continue;
}
MenuEntryInfo *entry = item->entryInfo();
if (entry && entry->menuId() == menuEntry) {
setCurrentItem(item);
scrollToItem(item);
return;
}
}
} else {
// top level
for (int i = 0; i < topLevelItemCount(); ++i) {
TreeItem *item = dynamic_cast<TreeItem *>(topLevelItem(i));
if (!item || item->isDirectory()) {
continue;
}
MenuEntryInfo *entry = item->entryInfo();
if (entry && entry->menuId() == menuEntry) {
setCurrentItem(item);
scrollToItem(item);
return;
}
}
}
}
void TreeView::itemSelected(QTreeWidgetItem *item)
{
// ensure the item is visible as selected
setItemSelected(item, true);
TreeItem *_item = static_cast<TreeItem*>(item);
TreeItem *parentItem = 0;
bool selected = false;
bool dselected = false;
if (_item) {
selected = true;
dselected = _item->isHiddenInMenu();
parentItem = getParentItem(_item);
}
// change actions activation
m_ac->action(CUT_ACTION_NAME)->setEnabled(selected);
m_ac->action(COPY_ACTION_NAME)->setEnabled(selected);
m_ac->action(PASTE_ACTION_NAME)->setEnabled(m_clipboard != 0);
if (m_ac->action(DELETE_ACTION_NAME)) {
m_ac->action(DELETE_ACTION_NAME)->setEnabled(selected && !dselected);
}
m_ac->action(SORT_BY_NAME_ACTION_NAME)->setEnabled(selected && _item->isDirectory() && (_item->childCount() > 0));
m_ac->action(SORT_BY_DESCRIPTION_ACTION_NAME)->setEnabled(m_ac->action(SORT_BY_NAME_ACTION_NAME)->isEnabled());
m_ac->action(MOVE_UP_ACTION_NAME)->setEnabled(selected && (parentItem->indexOfChild(_item) > 0));
m_ac->action(MOVE_DOWN_ACTION_NAME)->setEnabled(selected && (parentItem->indexOfChild(_item) < parentItem->childCount() - 1));
if (!item) {
emit disableAction();
return;
}
if (_item->isDirectory()) {
emit entrySelected(_item->folderInfo());
} else {
emit entrySelected(_item->entryInfo());
}
}
void TreeView::currentDataChanged(MenuFolderInfo *folderInfo)
{
TreeItem *item = (TreeItem*)selectedItem();
if (item == 0 || folderInfo == 0) {
return;
}
item->setName(folderInfo->caption);
item->setIcon(0, appIcon(folderInfo->icon));
}
void TreeView::currentDataChanged(MenuEntryInfo *entryInfo)
{
TreeItem *item = (TreeItem*)selectedItem();
if (item == 0 || entryInfo == 0) {
return;
}
QString name;
if (m_detailedMenuEntries && entryInfo->description.length() != 0) {
if (m_detailedEntriesNamesFirst) {
name = entryInfo->caption + " (" + entryInfo->description + ')';
} else {
name = entryInfo->description + " (" + entryInfo->caption + ')';
}
} else {
name = entryInfo->caption;
}
item->setName(name);
item->setIcon(0, appIcon(entryInfo->icon));
}
QStringList TreeView::fileList(const QString& rPath)
{
QString relativePath = rPath;
// truncate "/.directory"
int pos = relativePath.lastIndexOf("/.directory");
if (pos > 0) relativePath.truncate(pos);
QStringList filelist;
// loop through all resource dirs and build a file list
const QStringList resdirlist = KGlobal::dirs()->resourceDirs("apps");
for (QStringList::ConstIterator it = resdirlist.constBegin(); it != resdirlist.constEnd(); ++it)
{
QDir dir((*it) + '/' + relativePath);
if(!dir.exists()) continue;
dir.setFilter(QDir::Files);
dir.setNameFilters(QStringList() << "*.desktop;*.kdelnk");
// build a list of files
const QStringList files = dir.entryList();
for (QStringList::ConstIterator it = files.constBegin(); it != files.constEnd(); ++it) {
// does not work?!
//if (filelist.contains(*it)) continue;
if (relativePath.isEmpty()) {
filelist.removeAll(*it); // hack
filelist.append(*it);
}
else {
filelist.removeAll(relativePath + '/' + *it); //hack
filelist.append(relativePath + '/' + *it);
}
}
}
return filelist;
}
QStringList TreeView::dirList(const QString& rPath)
{
QString relativePath = rPath;
// truncate "/.directory"
int pos = relativePath.lastIndexOf("/.directory");
if (pos > 0) relativePath.truncate(pos);
QStringList dirlist;
// loop through all resource dirs and build a subdir list
const QStringList resdirlist = KGlobal::dirs()->resourceDirs("apps");
for (QStringList::ConstIterator it = resdirlist.constBegin(); it != resdirlist.constEnd(); ++it)
{
QDir dir((*it) + '/' + relativePath);
if(!dir.exists()) continue;
dir.setFilter(QDir::Dirs);
// build a list of subdirs
const QStringList subdirs = dir.entryList();
for (QStringList::ConstIterator it = subdirs.constBegin(); it != subdirs.constEnd(); ++it) {
if ((*it) == "." || (*it) == "..") continue;
// does not work?!
// if (dirlist.contains(*it)) continue;
if (relativePath.isEmpty()) {
dirlist.removeAll(*it); //hack
dirlist.append(*it);
}
else {
dirlist.removeAll(relativePath + '/' + *it); //hack
dirlist.append(relativePath + '/' + *it);
}
}
}
return dirlist;
}
Qt::DropActions TreeView::supportedDropActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
QStringList TreeView::mimeTypes() const
{
return m_dropMimeTypes;
}
void TreeView::startDrag(Qt::DropActions supportedActions)
{
QList<QTreeWidgetItem *> items;
items << selectedItem();
QMimeData *data = mimeData(items);
if (!data) {
return;
}
QDrag *drag = new QDrag(this);
int iconSize = KIconLoader::global()->currentSize(KIconLoader::Small);
drag->setPixmap(selectedItem()->icon(0).pixmap(iconSize, iconSize));
drag->setMimeData(data);
drag->exec(supportedActions, Qt::MoveAction);
}
QMimeData *TreeView::mimeData(const QList<QTreeWidgetItem *> items) const
{
if (items.isEmpty()) {
return 0;
}
return new MenuItemMimeData(dynamic_cast<TreeItem *>(items.first()));
}
static QString createDesktopFile(const QString &file, QString *menuId, QStringList *excludeList)
{
QString base = file.mid(file.lastIndexOf('/')+1);
base = base.left(base.lastIndexOf('.'));
QRegExp r("(.*)(?=-\\d+)");
base = (r.indexIn(base) > -1) ? r.cap(1) : base;
QString result = KService::newServicePath(base, menuId, excludeList);
excludeList->append(*menuId);
// Todo for Undo-support: Undo menuId allocation:
return result;
}
static KDesktopFile *copyDesktopFile(MenuEntryInfo *entryInfo, QString *menuId, QStringList *excludeList)
{
QString result = createDesktopFile(entryInfo->file(), menuId, excludeList);
KDesktopFile *df = entryInfo->desktopFile()->copyTo(result);
df->desktopGroup().deleteEntry("Categories"); // Don't set any categories!
return df;
}
static QString createDirectoryFile(const QString &file, QStringList *excludeList)
{
QString base = file.mid(file.lastIndexOf('/')+1);
base = base.left(base.lastIndexOf('.'));
QString result;
int i = 1;
while(true)
{
if (i == 1)
result = base + ".directory";
else
result = base + QString("-%1.directory").arg(i);
if (!excludeList->contains(result))
{
if (KStandardDirs::locate("xdgdata-dirs", result).isEmpty())
break;
}
i++;
}
excludeList->append(result);
result = KStandardDirs::locateLocal("xdgdata-dirs", result);
return result;
}
bool TreeView::dropMimeData(QTreeWidgetItem *item, int index, const QMimeData *data, Qt::DropAction action)
{
// get destination folder
TreeItem *titem = item ? dynamic_cast<TreeItem*>(item) : 0;
if (item && !titem) {
return false;
}
TreeItem *parentItem = 0;
QTreeWidgetItem *after = titem;
// find the parent item and which item the dropped item should go after
if (titem) {
if (titem->isDirectory()) {
parentItem = titem;
after = titem->child(index);
if (!after) {
after = titem->child(titem->childCount() - 1);
}
} else {
parentItem = dynamic_cast<TreeItem *>(titem->parent());
if (titem->parent() && !parentItem) {
return false;
}
}
} else if (index > 0) {
after = topLevelItem(index);
if (!after) {
after = topLevelItem(topLevelItemCount() - 1);
}
}
QString folder = parentItem ? parentItem->directory() : "/";
MenuFolderInfo *parentFolderInfo = parentItem ? parentItem->folderInfo() : m_rootFolder;
kDebug() << "think we're dropping on" << (parentItem ? parentItem->text(0) : "Top Level") << index;
if (!data->hasFormat(s_internalMimeType)) {
// External drop
if (!KUrl::List::canDecode(data)) {
return false;
}
KUrl::List urls = KUrl::List::fromMimeData(data);;
if (urls.isEmpty() || !urls[0].isLocalFile()) {
return false;
}
//FIXME: this should really support multiple DnD
QString path = urls[0].path();
if (!path.endsWith(QLatin1String(".desktop"))) {
return false;
}
QString menuId;
QString result = createDesktopFile(path, &menuId, &m_newMenuIds);
KDesktopFile orig_df(path);
KDesktopFile *df = orig_df.copyTo(result);
df->desktopGroup().deleteEntry("Categories"); // Don't set any categories!
KService::Ptr s(new KService(df));
s->setMenuId(menuId);
MenuEntryInfo *entryInfo = new MenuEntryInfo(s, df);
QString oldCaption = entryInfo->caption;
QString newCaption = parentFolderInfo->uniqueItemCaption(oldCaption, oldCaption);
entryInfo->setCaption(newCaption);
// Add file to menu
// m_menuFile->addEntry(folder, menuId);
m_menuFile->pushAction(MenuFile::ADD_ENTRY, folder, menuId);
// create the TreeItem
if (parentItem) {
parentItem->setExpanded(true);
}
// update fileInfo data
parentFolderInfo->add(entryInfo);
TreeItem *newItem = createTreeItem(parentItem, after, entryInfo, true);
setCurrentItem(newItem);
setLayoutDirty(parentItem);
return true;
}
QVariant p(data->data(s_internalMimeType));
const MenuItemMimeData *itemData = dynamic_cast<const MenuItemMimeData *>(data);
if (!itemData) {
return false;
}
TreeItem *dragItem = itemData->item();
if (!dragItem || dragItem == after) {
return false; // Nothing to do
}
//kDebug() << "an internal drag of" << dragItem->text(0) << (parentItem ? parentItem->text(0) : "Top level");
if (dragItem->isDirectory()) {
MenuFolderInfo *folderInfo = dragItem->folderInfo();
if (action == Qt::CopyAction) {
// FIXME:
// * Create new .directory file
} else {
TreeItem *tmpItem = static_cast<TreeItem*>(parentItem);
while (tmpItem) {
if (tmpItem == dragItem) {
return false;
}
tmpItem = static_cast<TreeItem*>(tmpItem->parent());
}
// Remove MenuFolderInfo
TreeItem *oldParentItem = static_cast<TreeItem*>(dragItem->parent());
MenuFolderInfo *oldParentFolderInfo = oldParentItem ? oldParentItem->folderInfo() : m_rootFolder;
oldParentFolderInfo->take(folderInfo);
// Move menu
QString oldFolder = folderInfo->fullId;
QString folderName = folderInfo->id;
QString newFolder = m_menuFile->uniqueMenuName(folder, folderName, parentFolderInfo->existingMenuIds());
folderInfo->id = newFolder;
// Add file to menu
//m_menuFile->moveMenu(oldFolder, folder + newFolder);
kDebug() << "moving" << dragItem->text(0) << "to" << folder + newFolder;
m_menuFile->pushAction(MenuFile::MOVE_MENU, oldFolder, folder + newFolder);
// Make sure caption is unique
QString newCaption = parentFolderInfo->uniqueMenuCaption(folderInfo->caption);
if (newCaption != folderInfo->caption) {
folderInfo->setCaption(newCaption);
}
// create the TreeItem
if (parentItem) {
parentItem->setExpanded(true);
}
// update fileInfo data
folderInfo->updateFullId(parentFolderInfo->fullId);
folderInfo->setInUse(true);
parentFolderInfo->add(folderInfo);
if (parentItem != oldParentItem) {
if (oldParentItem) {
oldParentItem->takeChild(oldParentItem->indexOfChild(dragItem));
} else {
takeTopLevelItem(indexOfTopLevelItem(dragItem));
}
}
if (parentItem) {
parentItem->insertChild(after ? parentItem->indexOfChild(after) + 1 : parentItem->childCount(), dragItem);
} else {
insertTopLevelItem(after ? indexOfTopLevelItem(after) : topLevelItemCount(), dragItem);
}
dragItem->setName(folderInfo->caption);
dragItem->setDirectoryPath(folderInfo->fullId);
setCurrentItem(dragItem);
}
} else if (dragItem->isEntry()) {
MenuEntryInfo *entryInfo = dragItem->entryInfo();
QString menuId = entryInfo->menuId();
if (action == Qt::CopyAction) {
// Need to copy file and then add it
KDesktopFile *df = copyDesktopFile(entryInfo, &menuId, &m_newMenuIds); // Duplicate
//UNDO-ACTION: NEW_MENU_ID (menuId)
KService::Ptr s(new KService(df));
s->setMenuId(menuId);
entryInfo = new MenuEntryInfo(s, df);
QString oldCaption = entryInfo->caption;
QString newCaption = parentFolderInfo->uniqueItemCaption(oldCaption, oldCaption);
entryInfo->setCaption(newCaption);
} else {
del(dragItem, false);
QString oldCaption = entryInfo->caption;
QString newCaption = parentFolderInfo->uniqueItemCaption(oldCaption);
entryInfo->setCaption(newCaption);
entryInfo->setInUse(true);
}
// Add file to menu
// m_menuFile->addEntry(folder, menuId);
m_menuFile->pushAction(MenuFile::ADD_ENTRY, folder, menuId);
// create the TreeItem
if (parentItem) {
parentItem->setExpanded(true);
}
// update fileInfo data
parentFolderInfo->add(entryInfo);
TreeItem *newItem = createTreeItem(parentItem, after, entryInfo);
setCurrentItem(newItem);
} else {
// copying a separator
if (action != Qt::CopyAction) {
del(dragItem, false);
}
TreeItem *newItem = createTreeItem(parentItem, after, m_separator);
setCurrentItem(newItem);
}
kDebug() << "setting the layout to be dirty at" << parentItem;
setLayoutDirty(parentItem);
return true;
}
QTreeWidgetItem *TreeView::selectedItem()
{
QList<QTreeWidgetItem *> selection = selectedItems();
if (selection.isEmpty()) {
return 0;
}
return selection.first();
}
void TreeView::contextMenuEvent(QContextMenuEvent *event)
{
if (m_popupMenu && itemAt(event->pos())) {
m_popupMenu->exec(event->globalPos());
}
}
void TreeView::dropEvent(QDropEvent *event)
{
// this prevents QTreeWidget from interfering with our moves
QTreeView::dropEvent(event);
}
void TreeView::newsubmenu()
{
TreeItem *parentItem = 0;
TreeItem *item = (TreeItem*)selectedItem();
bool ok;
QString caption = KInputDialog::getText( i18n( "New Submenu" ),
i18n( "Submenu name:" ), QString(), &ok, this );
if (!ok) return;
QString file = caption;
file.replace('/', '-');
file = createDirectoryFile(file, &m_newDirectoryList); // Create
// get destination folder
QString folder;
if(!item)
{
parentItem = 0;
folder.clear();
}
else if(item->isDirectory())
{
parentItem = item;
item = 0;
folder = parentItem->directory();
}
else
{
parentItem = static_cast<TreeItem*>(item->parent());
folder = parentItem ? parentItem->directory() : QString();
}
MenuFolderInfo *parentFolderInfo = parentItem ? parentItem->folderInfo() : m_rootFolder;
MenuFolderInfo *folderInfo = new MenuFolderInfo();
folderInfo->caption = parentFolderInfo->uniqueMenuCaption(caption);
folderInfo->id = m_menuFile->uniqueMenuName(folder, caption, parentFolderInfo->existingMenuIds());
folderInfo->directoryFile = file;
folderInfo->icon = "package";
folderInfo->hidden = false;
folderInfo->setDirty();
KDesktopFile *df = new KDesktopFile(file);
KConfigGroup desktopGroup = df->desktopGroup();
desktopGroup.writeEntry("Name", folderInfo->caption);
desktopGroup.writeEntry("Icon", folderInfo->icon);
df->sync();
delete df;
// Add file to menu
// m_menuFile->addMenu(folder + folderInfo->id, file);
m_menuFile->pushAction(MenuFile::ADD_MENU, folder + folderInfo->id, file);
folderInfo->fullId = parentFolderInfo->fullId + folderInfo->id;
// create the TreeItem
if (parentItem)
parentItem->setExpanded(true);
// update fileInfo data
parentFolderInfo->add(folderInfo);
TreeItem *newItem = createTreeItem(parentItem, item, folderInfo, true);
setCurrentItem(newItem);
setLayoutDirty(parentItem);
}
void TreeView::newitem()
{
TreeItem *parentItem = 0;
TreeItem *item = (TreeItem*)selectedItem();
bool ok;
QString caption = KInputDialog::getText( i18n( "New Item" ),
i18n( "Item name:" ), QString(), &ok, this );
if (!ok) return;
QString menuId;
QString file = caption;
file.replace('/', '-');
file = createDesktopFile(file, &menuId, &m_newMenuIds); // Create
KDesktopFile *df = new KDesktopFile(file);
KConfigGroup desktopGroup = df->desktopGroup();
desktopGroup.writeEntry("Name", caption);
desktopGroup.writeEntry("Type", "Application");
// get destination folder
QString folder;
if(!item)
{
parentItem = 0;
folder.clear();
}
else if(item->isDirectory())
{
parentItem = item;
item = 0;
folder = parentItem->directory();
}
else
{
parentItem = static_cast<TreeItem*>(item->parent());
folder = parentItem ? parentItem->directory() : QString();
}
MenuFolderInfo *parentFolderInfo = parentItem ? parentItem->folderInfo() : m_rootFolder;
// Add file to menu
// m_menuFile->addEntry(folder, menuId);
m_menuFile->pushAction(MenuFile::ADD_ENTRY, folder, menuId);
KService::Ptr s(new KService(df));
s->setMenuId(menuId);
MenuEntryInfo *entryInfo = new MenuEntryInfo(s, df);
// create the TreeItem
if(parentItem)
parentItem->setExpanded(true);
// update fileInfo data
parentFolderInfo->add(entryInfo);
TreeItem *newItem = createTreeItem(parentItem, item, entryInfo, true);
setCurrentItem(newItem);
setLayoutDirty(parentItem);
}
void TreeView::newsep()
{
TreeItem *parentItem = 0;
TreeItem *item = (TreeItem*)selectedItem();
if(!item)
{
parentItem = 0;
}
else if(item->isDirectory())
{
parentItem = item;
item = 0;
}
else
{
parentItem = static_cast<TreeItem*>(item->parent());
}
// create the TreeItem
if(parentItem)
parentItem->setExpanded(true);
TreeItem *newItem = createTreeItem(parentItem, item, m_separator, true);
setCurrentItem(newItem);
setLayoutDirty(parentItem);
}
void TreeView::cut()
{
copy( true );
// Select new current item
// TODO: is this completely redundant?
setCurrentItem(currentItem());
}
void TreeView::copy()
{
copy( false );
}
void TreeView::copy( bool cutting )
{
TreeItem *item = (TreeItem*)selectedItem();
// nil selected? -> nil to copy
if (item == 0) return;
if (cutting)
setLayoutDirty((TreeItem*)item->parent());
// clean up old stuff
cleanupClipboard();
// is item a folder or a file?
if(item->isDirectory())
{
QString folder = item->directory();
if (cutting)
{
// Place in clipboard
m_clipboard = MOVE_FOLDER;
m_clipboardFolderInfo = item->folderInfo();
del(item, false);
}
else
{
// Place in clipboard
m_clipboard = COPY_FOLDER;
m_clipboardFolderInfo = item->folderInfo();
}
}
else if (item->isEntry())
{
if (cutting)
{
// Place in clipboard
m_clipboard = MOVE_FILE;
m_clipboardEntryInfo = item->entryInfo();
del(item, false);
}
else
{
// Place in clipboard
m_clipboard = COPY_FILE;
m_clipboardEntryInfo = item->entryInfo();
}
}
else
{
// Place in clipboard
m_clipboard = COPY_SEPARATOR;
if (cutting)
del(item, false);
}
m_ac->action(PASTE_ACTION_NAME)->setEnabled(true);
}
void TreeView::paste()
{
TreeItem *parentItem = 0;
TreeItem *item = (TreeItem*)selectedItem();
// nil selected? -> nil to paste to
if (item == 0) return;
// is there content in the clipboard?
if (!m_clipboard) return;
// get destination folder
QString folder;
if(item->isDirectory())
{
parentItem = item;
item = 0;
folder = parentItem->directory();
}
else
{
parentItem = static_cast<TreeItem*>(item->parent());
folder = parentItem ? parentItem->directory() : QString();
}
MenuFolderInfo *parentFolderInfo = parentItem ? parentItem->folderInfo() : m_rootFolder;
int command = m_clipboard;
if ((command == COPY_FOLDER) || (command == MOVE_FOLDER))
{
MenuFolderInfo *folderInfo = m_clipboardFolderInfo;
if (command == COPY_FOLDER)
{
// Ugh.. this is hard :)
// * Create new .directory file
// Add
}
else if (command == MOVE_FOLDER)
{
// Move menu
QString oldFolder = folderInfo->fullId;
QString folderName = folderInfo->id;
QString newFolder = m_menuFile->uniqueMenuName(folder, folderName, parentFolderInfo->existingMenuIds());
folderInfo->id = newFolder;
// Add file to menu
// m_menuFile->moveMenu(oldFolder, folder + newFolder);
m_menuFile->pushAction(MenuFile::MOVE_MENU, oldFolder, folder + newFolder);
// Make sure caption is unique
QString newCaption = parentFolderInfo->uniqueMenuCaption(folderInfo->caption);
if (newCaption != folderInfo->caption)
{
folderInfo->setCaption(newCaption);
}
// create the TreeItem
if(parentItem)
parentItem->setExpanded(true);
// update fileInfo data
folderInfo->fullId = parentFolderInfo->fullId + folderInfo->id;
folderInfo->setInUse(true);
parentFolderInfo->add(folderInfo);
TreeItem *newItem = createTreeItem(parentItem, item, folderInfo);
setCurrentItem(newItem);
}
m_clipboard = COPY_FOLDER; // Next one copies.
}
else if ((command == COPY_FILE) || (command == MOVE_FILE))
{
MenuEntryInfo *entryInfo = m_clipboardEntryInfo;
QString menuId;
if (command == COPY_FILE)
{
// Need to copy file and then add it
KDesktopFile *df = copyDesktopFile(entryInfo, &menuId, &m_newMenuIds); // Duplicate
KService::Ptr s(new KService(df));
s->setMenuId(menuId);
entryInfo = new MenuEntryInfo(s, df);
QString oldCaption = entryInfo->caption;
QString newCaption = parentFolderInfo->uniqueItemCaption(oldCaption, oldCaption);
entryInfo->setCaption(newCaption);
}
else if (command == MOVE_FILE)
{
menuId = entryInfo->menuId();
m_clipboard = COPY_FILE; // Next one copies.
QString oldCaption = entryInfo->caption;
QString newCaption = parentFolderInfo->uniqueItemCaption(oldCaption);
entryInfo->setCaption(newCaption);
entryInfo->setInUse(true);
}
// Add file to menu
// m_menuFile->addEntry(folder, menuId);
m_menuFile->pushAction(MenuFile::ADD_ENTRY, folder, menuId);
// create the TreeItem
if(parentItem)
parentItem->setExpanded(true);
// update fileInfo data
parentFolderInfo->add(entryInfo);
TreeItem *newItem = createTreeItem(parentItem, item, entryInfo, true);
setCurrentItem(newItem);
}
else
{
// create separator
if(parentItem)
parentItem->setExpanded(true);
TreeItem *newItem = createTreeItem(parentItem, item, m_separator, true);
setCurrentItem(newItem);
}
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 == SortAllByDescription) {
sortType = SortByDescription;
itemToSort = static_cast<TreeItem*>(invisibleRootItem());
} else /* if (sortType == SortAllByName) */ {
sortType = SortByName;
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);
}
}
/**
* @brief Move up the selected item.
*/
void TreeView::moveUpItem() {
moveUpOrDownItem(true);
}
/**
* @brief Move down the selected item.
*/
void TreeView::moveDownItem() {
moveUpOrDownItem(false);
}
/**
* Move the selected item on desired direction (up or down).
*
* @brief Move up/down the selected item.
* @param isMovingUpAction True to move up, false to move down.
*/
void TreeView::moveUpOrDownItem(bool isMovingUpAction)
{
// get the selected item and its parent
TreeItem *sourceItem = static_cast<TreeItem*>(selectedItem());
if (!sourceItem)
return;
TreeItem *parentItem = getParentItem(sourceItem);
// get selected item index
int sourceItemIndex = parentItem->indexOfChild(sourceItem);
// find the second item to swap
TreeItem *destItem = 0;
int destIndex;
if (isMovingUpAction) {
destIndex = sourceItemIndex - 1;
destItem = static_cast<TreeItem*>(parentItem->child(destIndex));
}
else {
destIndex = sourceItemIndex + 1;
destItem = static_cast<TreeItem*>(parentItem->child(destIndex));
}
// swap items
parentItem->removeChild(sourceItem);
parentItem->insertChild(destIndex, sourceItem);
// recreate item widget for separators
if (sourceItem->isSeparator()) {
setItemWidget(sourceItem, 0, new SeparatorWidget);
}
if (destItem->isSeparator()) {
setItemWidget(destItem, 0, new SeparatorWidget);
}
// set the focus on the source item
setCurrentItem(sourceItem);
// flag parent item as dirty (if the parent is the root item, set the entire layout as dirty)
if (parentItem == invisibleRootItem()) {
parentItem = 0;
}
setLayoutDirty(parentItem);
}
/**
* For a given item, return its parent. For top items, return the invisible root item.
*
* @brief Get the parent item.
* @param item Item.
* @return Parent item.
*/
TreeItem* TreeView::getParentItem(QTreeWidgetItem *item) const
{
QTreeWidgetItem *parentItem = item->parent();
if (!parentItem) {
parentItem = invisibleRootItem();
}
return static_cast<TreeItem*>(parentItem);
}
void TreeView::del()
{
TreeItem *item = (TreeItem*)selectedItem();
// nil selected? -> nil to delete
if (item == 0) return;
del(item, true);
// Select new current item
// TODO: is this completely redundant?
setCurrentItem(currentItem());
}
void TreeView::del(TreeItem *item, bool deleteInfo)
{
TreeItem *parentItem = static_cast<TreeItem*>(item->parent());
// is file a .directory or a .desktop file
if(item->isDirectory())
{
if ( KMessageBox::warningYesNo(this, i18n("All submenus of '%1' will be removed. Do you want to continue?", item->name() ) ) == KMessageBox::No )
return;
MenuFolderInfo *folderInfo = item->folderInfo();
// Remove MenuFolderInfo
MenuFolderInfo *parentFolderInfo = parentItem ? parentItem->folderInfo() : m_rootFolder;
parentFolderInfo->take(folderInfo);
folderInfo->setInUse(false);
if (m_clipboard == COPY_FOLDER && (m_clipboardFolderInfo == folderInfo))
{
// Copy + Del == Cut
m_clipboard = MOVE_FOLDER; // Clipboard now owns folderInfo
}
else
{
if (folderInfo->takeRecursive(m_clipboardFolderInfo))
m_clipboard = MOVE_FOLDER; // Clipboard now owns m_clipboardFolderInfo
if (deleteInfo)
delete folderInfo; // Delete folderInfo
}
// Remove from menu
// m_menuFile->removeMenu(item->directory());
m_menuFile->pushAction(MenuFile::REMOVE_MENU, item->directory(), QString());
// Remove tree item
delete item;
}
else if (item->isEntry())
{
MenuEntryInfo *entryInfo = item->entryInfo();
QString menuId = entryInfo->menuId();
// Remove MenuFolderInfo
MenuFolderInfo *parentFolderInfo = parentItem ? parentItem->folderInfo() : m_rootFolder;
parentFolderInfo->take(entryInfo);
entryInfo->setInUse(false);
if (m_clipboard == COPY_FILE && (m_clipboardEntryInfo == entryInfo))
{
// Copy + Del == Cut
m_clipboard = MOVE_FILE; // Clipboard now owns entryInfo
}
else
{
if (deleteInfo)
delete entryInfo; // Delete entryInfo
}
// Remove from menu
QString folder = parentItem ? parentItem->directory() : QString();
// m_menuFile->removeEntry(folder, menuId);
m_menuFile->pushAction(MenuFile::REMOVE_ENTRY, folder, menuId);
// Remove tree item
delete item;
}
else
{
// Remove separator
delete item;
}
setLayoutDirty(parentItem);
}
void TreeView::cleanupClipboard() {
if (m_clipboard == MOVE_FOLDER)
delete m_clipboardFolderInfo;
m_clipboardFolderInfo = 0;
if (m_clipboard == MOVE_FILE)
delete m_clipboardEntryInfo;
m_clipboardEntryInfo = 0;
m_clipboard = 0;
}
static QStringList extractLayout(QTreeWidget *tree, QTreeWidgetItem *parent)
{
QStringList layout;
if (!parent && !tree) {
return layout;
}
bool firstFolder = true;
bool firstEntry = true;
int max = parent ? parent->childCount() : tree->topLevelItemCount();
for (int i = 0; i < max; ++i) {
TreeItem *item = dynamic_cast<TreeItem *>(parent ? parent->child(i) : tree->topLevelItem(i));
if (!item) {
continue;
}
if (item->isDirectory()) {
if (firstFolder) {
firstFolder = false;
layout << ":M"; // Add new folders here...
}
layout << (item->folderInfo()->id);
} else if (item->isEntry()) {
if (firstEntry) {
firstEntry = false;
layout << ":F"; // Add new entries here...
}
layout << (item->entryInfo()->menuId());
} else {
layout << ":S";
}
}
return layout;
}
void TreeItem::saveLayout(MenuFile *menuFile)
{
if (m_layoutDirty) {
QStringList layout = extractLayout(0, this);
menuFile->setLayout(folderInfo()->fullId, layout);
m_layoutDirty = false;
}
for (int i = 0; i < childCount(); ++i) {
TreeItem *item = dynamic_cast<TreeItem *>(child(i));
if (item) {
item->saveLayout(menuFile);
}
}
}
void TreeView::saveLayout()
{
if (m_layoutDirty) {
QStringList layout = extractLayout(this, 0);
m_menuFile->setLayout(m_rootFolder->fullId, layout);
m_layoutDirty = false;
}
for (int i = 0; i < topLevelItemCount(); ++i) {
TreeItem *item = dynamic_cast<TreeItem *>(topLevelItem(i));
if (item) {
item->saveLayout(m_menuFile);
}
}
}
bool TreeView::save()
{
saveLayout();
m_rootFolder->save(m_menuFile);
bool success = m_menuFile->performAllActions();
m_newMenuIds.clear();
m_newDirectoryList.clear();
if (success)
{
KBuildSycocaProgressDialog::rebuildKSycoca(this);
}
else
{
KMessageBox::sorry(this, "<qt>"+i18n("Menu changes could not be saved because of the following problem:")+"<br><br>"+
m_menuFile->error()+"</qt>");
}
sendReloadMenu();
return success;
}
void TreeView::setLayoutDirty(TreeItem *parentItem)
{
if (parentItem)
parentItem->setLayoutDirty();
else
m_layoutDirty = true;
}
bool TreeView::isLayoutDirty()
{
for (int i = 0; i < topLevelItemCount(); ++i) {
TreeItem *item = dynamic_cast<TreeItem *>(topLevelItem(i));
if (!item) {
continue;
}
if (item->isLayoutDirty()) {
return true;
}
}
return false;
}
bool TreeView::dirty()
{
return m_layoutDirty || m_rootFolder->hasDirt() || m_menuFile->dirty() || isLayoutDirty();
}
void TreeView::findServiceShortcut(const KShortcut&cut, KService::Ptr &service)
{
service = m_rootFolder->findServiceShortcut(cut);
}
void TreeView::restoreMenuSystem()
{
if ( KMessageBox::warningYesNo( this, i18n( "Do you want to restore the system menu? Warning: This will remove all custom menus." ) )==KMessageBox::No )
return;
QString kmenueditfile = KStandardDirs::locateLocal("xdgconf-menu", "applications-kmenuedit.menu");
if ( QFile::exists( kmenueditfile ) )
{
if ( !QFile::remove( kmenueditfile ) )
qWarning()<<"Could not delete "<<kmenueditfile;
}
QString xdgdir = KGlobal::dirs()->KStandardDirs::localxdgdatadir();
if ( !KIO::NetAccess::del( QString(xdgdir + "/applications") , this) )
qWarning()<<"Could not delete dir :"<<( xdgdir+"/applications" );
if ( !KIO::NetAccess::del( QString(xdgdir +"/desktop-directories") , this) )
qWarning()<<"Could not delete dir :"<<( xdgdir + "/desktop-directories");
KBuildSycocaProgressDialog::rebuildKSycoca(this);
clear();
cleanupClipboard();
delete m_rootFolder;
delete m_separator;
m_layoutDirty = false;
m_newMenuIds.clear();
m_newDirectoryList.clear();
m_menuFile->restoreMenuSystem(kmenueditfile);
m_rootFolder = new MenuFolderInfo;
m_separator = new MenuSeparatorInfo;
readMenuFolderInfo();
fill();
sendReloadMenu();
emit disableAction();
emit entrySelected(( MenuEntryInfo* ) 0 );
}
void TreeView::updateTreeView(bool showHidden)
{
m_showHidden = showHidden;
clear();
cleanupClipboard();
delete m_rootFolder;
delete m_separator;
m_layoutDirty = false;
m_newMenuIds.clear();
m_newDirectoryList.clear();
m_rootFolder = new MenuFolderInfo;
m_separator = new MenuSeparatorInfo;
readMenuFolderInfo();
fill();
sendReloadMenu();
emit disableAction();
emit entrySelected(( MenuEntryInfo* ) 0 );
}
void TreeView::sendReloadMenu()
{
QDBusMessage message = QDBusMessage::createSignal("/kickoff", "org.kde.plasma", "reloadMenu");
QDBusConnection::sessionBus().send(message);
}
MenuItemMimeData::MenuItemMimeData(TreeItem *item)
: QMimeData(),
m_item(item)
{
}
TreeItem *MenuItemMimeData::item() const
{
return m_item;
}
QStringList MenuItemMimeData::formats() const
{
QStringList formats;
if (!m_item) {
return formats;
}
formats << s_internalMimeType;
return formats;
}
bool MenuItemMimeData::hasFormat(const QString &mimeType) const
{
return m_item && mimeType == s_internalMimeType;
}
QVariant MenuItemMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const
{
Q_UNUSED(type);
if (m_item && mimeType == s_internalMimeType) {
return qVariantFromValue<TreeItem*>(m_item);
}
return QVariant();
}