summaryrefslogtreecommitdiffstats
path: root/kmenuedit
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit4aed2c8219774f5d797760606b8489a92ddc5163 (patch)
tree3f8c130f7d269626bf6a9447407ef6c35954426a /kmenuedit
downloadtdebase-4aed2c8219774f5d797760606b8489a92ddc5163.tar.gz
tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kmenuedit')
-rw-r--r--kmenuedit/Makefile.am52
-rw-r--r--kmenuedit/basictab.cpp500
-rw-r--r--kmenuedit/basictab.h87
-rw-r--r--kmenuedit/hi16-app-kmenuedit.pngbin0 -> 679 bytes
-rw-r--r--kmenuedit/hi22-app-kmenuedit.pngbin0 -> 1485 bytes
-rw-r--r--kmenuedit/hi32-app-kmenuedit.pngbin0 -> 2481 bytes
-rw-r--r--kmenuedit/hi48-app-kmenuedit.pngbin0 -> 1963 bytes
-rw-r--r--kmenuedit/kcontrol_main.cpp58
-rw-r--r--kmenuedit/kcontroleditui.rc40
-rw-r--r--kmenuedit/khotkeys.cpp148
-rw-r--r--kmenuedit/khotkeys.h44
-rw-r--r--kmenuedit/kmenuedit.cpp186
-rw-r--r--kmenuedit/kmenuedit.desktop92
-rw-r--r--kmenuedit/kmenuedit.h62
-rw-r--r--kmenuedit/kmenueditui.rc44
-rw-r--r--kmenuedit/main.cpp88
-rw-r--r--kmenuedit/menufile.cpp552
-rw-r--r--kmenuedit/menufile.h114
-rw-r--r--kmenuedit/menuinfo.cpp502
-rw-r--r--kmenuedit/menuinfo.h192
-rw-r--r--kmenuedit/pixmaps/Makefile.am5
-rw-r--r--kmenuedit/pixmaps/cr22-action-menu_new.pngbin0 -> 1247 bytes
-rw-r--r--kmenuedit/pixmaps/cr22-action-menu_new_sep.pngbin0 -> 162 bytes
-rw-r--r--kmenuedit/pixmaps/cr32-action-menu_new.pngbin0 -> 2481 bytes
-rw-r--r--kmenuedit/pixmaps/cr32-action-menu_new_sep.pngbin0 -> 596 bytes
-rw-r--r--kmenuedit/pixmaps/lo16-action-menu_new.pngbin0 -> 679 bytes
-rw-r--r--kmenuedit/treeview.cpp1581
-rw-r--r--kmenuedit/treeview.h179
-rw-r--r--kmenuedit/uninstall.desktop2
29 files changed, 4528 insertions, 0 deletions
diff --git a/kmenuedit/Makefile.am b/kmenuedit/Makefile.am
new file mode 100644
index 000000000..7e4b2c66c
--- /dev/null
+++ b/kmenuedit/Makefile.am
@@ -0,0 +1,52 @@
+INCLUDES = $(all_includes)
+
+bin_PROGRAMS =
+lib_LTLIBRARIES =
+kdeinit_LTLIBRARIES = kmenuedit.la kcontroledit.la
+
+noinst_LTLIBRARIES = libkmenueditcommon.la
+
+CLEANFILES = dummy.cpp
+
+libkmenueditcommon_la_SOURCES = basictab.cpp treeview.cpp kmenuedit.cpp \
+ khotkeys.cpp menufile.cpp menuinfo.cpp
+
+libkmenueditcommon_la_LIBADD = $(LIB_KDEUI) $(LIB_KIO)
+libkmenueditcommon_la_LDFLAGS = $(all_libraries) -module -avoid-version
+
+kmenuedit_la_SOURCES = main.cpp
+kmenuedit_la_LIBADD = libkmenueditcommon.la
+kmenuedit_la_LDFLAGS = $(all_libraries) -module -avoid-version
+
+kcontroledit_la_SOURCES = kcontrol_main.cpp
+kcontroledit_la_LIBADD = libkmenueditcommon.la
+kcontroledit_la_LDFLAGS = $(all_libraries) -module -avoid-version
+
+noinst_HEADERS = kmenuedit.h treeview.h basictab.h khotkeys.h \
+ menufile.h menuinfo.h
+
+METASOURCES = AUTO
+
+xdg_apps_DATA = kmenuedit.desktop
+
+install-data-local: uninstall.desktop
+ $(mkinstalldirs) $(DESTDIR)$(kde_appsdir)/System
+ $(INSTALL_DATA) $(srcdir)/uninstall.desktop $(DESTDIR)$(kde_appsdir)/System/kmenuedit.desktop
+
+KDE_ICON = kmenuedit
+
+EXTRA_DIST = $(xdg_apps_DATA)
+
+rcdir = $(kde_datadir)/kmenuedit
+rc_DATA = kmenueditui.rc
+
+rc2dir = $(kde_datadir)/kcontroledit
+rc2_DATA = kcontroleditui.rc
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/kmenuedit.pot
+
+dummy.cpp:
+ echo > dummy.cpp
+
+SUBDIRS = pixmaps
diff --git a/kmenuedit/basictab.cpp b/kmenuedit/basictab.cpp
new file mode 100644
index 000000000..f6971a1f2
--- /dev/null
+++ b/kmenuedit/basictab.cpp
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2000 Matthias Elter <elter@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 <qcheckbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qfileinfo.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qwhatsthis.h>
+
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kglobal.h>
+#include <kdialog.h>
+#include <kkeybutton.h>
+#include <klineedit.h>
+#include <kmessagebox.h>
+#include <kicondialog.h>
+#include <kdesktopfile.h>
+#include <kurlrequester.h>
+#include <kfiledialog.h>
+#include <kcombobox.h>
+#include <kkeydialog.h>
+#include <kprocess.h>
+#include "khotkeys.h"
+
+#include "menuinfo.h"
+
+#include "basictab.h"
+#include "basictab.moc"
+
+BasicTab::BasicTab( QWidget *parent, const char *name )
+ : QWidget(parent, name)
+{
+ _menuFolderInfo = 0;
+ _menuEntryInfo = 0;
+
+ QGridLayout *layout = new QGridLayout(this, 6, 2,
+ KDialog::marginHint(),
+ KDialog::spacingHint());
+
+ // general group
+ QGroupBox *general_group = new QGroupBox(this);
+ QGridLayout *grid = new QGridLayout(general_group, 5, 2,
+ KDialog::marginHint(),
+ KDialog::spacingHint());
+
+ general_group->setAcceptDrops(false);
+
+ // setup line inputs
+ _nameEdit = new KLineEdit(general_group);
+ _nameEdit->setAcceptDrops(false);
+ _descriptionEdit = new KLineEdit(general_group);
+ _descriptionEdit->setAcceptDrops(false);
+ _commentEdit = new KLineEdit(general_group);
+ _commentEdit->setAcceptDrops(false);
+ _execEdit = new KURLRequester(general_group);
+ _execEdit->lineEdit()->setAcceptDrops(false);
+ QWhatsThis::add(_execEdit,i18n(
+ "Following the command, you can have several place holders which will be replaced "
+ "with the actual values when the actual program is run:\n"
+ "%f - a single file name\n"
+ "%F - a list of files; use for applications that can open several local files at once\n"
+ "%u - a single URL\n"
+ "%U - a list of URLs\n"
+ "%d - the folder of the file to open\n"
+ "%D - a list of folders\n"
+ "%i - the icon\n"
+ "%m - the mini-icon\n"
+ "%c - the caption"));
+
+ _launchCB = new QCheckBox(i18n("Enable &launch feedback"), general_group);
+ _systrayCB = new QCheckBox(i18n("&Place in system tray"), general_group);
+
+ // setup labels
+ _nameLabel = new QLabel(_nameEdit, i18n("&Name:"), general_group);
+ _descriptionLabel = new QLabel(_descriptionEdit, i18n("&Description:"), general_group);
+ _commentLabel = new QLabel(_commentEdit, i18n("&Comment:"), general_group);
+ _execLabel = new QLabel(_execEdit, i18n("Co&mmand:"), general_group);
+ grid->addWidget(_nameLabel, 0, 0);
+ grid->addWidget(_descriptionLabel, 1, 0);
+ grid->addWidget(_commentLabel, 2, 0);
+ grid->addWidget(_execLabel, 3, 0);
+
+ // connect line inputs
+ connect(_nameEdit, SIGNAL(textChanged(const QString&)),
+ SLOT(slotChanged()));
+ connect(_descriptionEdit, SIGNAL(textChanged(const QString&)),
+ SLOT(slotChanged()));
+ connect(_commentEdit, SIGNAL(textChanged(const QString&)),
+ SLOT(slotChanged()));
+ connect(_execEdit, SIGNAL(textChanged(const QString&)),
+ SLOT(slotChanged()));
+ connect(_execEdit, SIGNAL(urlSelected(const QString&)),
+ SLOT(slotExecSelected()));
+ connect(_launchCB, SIGNAL(clicked()), SLOT(launchcb_clicked()));
+ connect(_systrayCB, SIGNAL(clicked()), SLOT(systraycb_clicked()));
+
+ // add line inputs to the grid
+ grid->addMultiCellWidget(_nameEdit, 0, 0, 1, 1);
+ grid->addMultiCellWidget(_descriptionEdit, 1, 1, 1, 1);
+ grid->addMultiCellWidget(_commentEdit, 2, 2, 1, 2);
+ grid->addMultiCellWidget(_execEdit, 3, 3, 1, 2);
+ grid->addMultiCellWidget(_launchCB, 4, 4, 0, 2);
+ grid->addMultiCellWidget(_systrayCB, 5, 5, 0, 2);
+
+ // setup icon button
+ _iconButton = new KIconButton(general_group);
+ _iconButton->setFixedSize(56,56);
+ _iconButton->setIconSize(48);
+ connect(_iconButton, SIGNAL(iconChanged(QString)), SLOT(slotChanged()));
+ grid->addMultiCellWidget(_iconButton, 0, 1, 2, 2);
+
+ // add the general group to the main layout
+ layout->addMultiCellWidget(general_group, 0, 0, 0, 1);
+
+ // path group
+ _path_group = new QGroupBox(this);
+ QVBoxLayout *vbox = new QVBoxLayout(_path_group, KDialog::marginHint(),
+ KDialog::spacingHint());
+
+ QHBox *hbox = new QHBox(_path_group);
+ hbox->setSpacing(KDialog::spacingHint());
+
+ _pathLabel = new QLabel(i18n("&Work path:"), hbox);
+
+ _pathEdit = new KURLRequester(hbox);
+ _pathEdit->setMode(KFile::Directory | KFile::LocalOnly);
+ _pathEdit->lineEdit()->setAcceptDrops(false);
+
+ _pathLabel->setBuddy(_pathEdit);
+
+ connect(_pathEdit, SIGNAL(textChanged(const QString&)),
+ SLOT(slotChanged()));
+ vbox->addWidget(hbox);
+ layout->addMultiCellWidget(_path_group, 1, 1, 0, 1);
+
+ // terminal group
+ _term_group = new QGroupBox(this);
+ vbox = new QVBoxLayout(_term_group, KDialog::marginHint(),
+ KDialog::spacingHint());
+
+ _terminalCB = new QCheckBox(i18n("Run in term&inal"), _term_group);
+ connect(_terminalCB, SIGNAL(clicked()), SLOT(termcb_clicked()));
+ vbox->addWidget(_terminalCB);
+
+ hbox = new QHBox(_term_group);
+ hbox->setSpacing(KDialog::spacingHint());
+ _termOptLabel = new QLabel(i18n("Terminal &options:"), hbox);
+ _termOptEdit = new KLineEdit(hbox);
+ _termOptEdit->setAcceptDrops(false);
+ _termOptLabel->setBuddy(_termOptEdit);
+
+ connect(_termOptEdit, SIGNAL(textChanged(const QString&)),
+ SLOT(slotChanged()));
+ vbox->addWidget(hbox);
+ layout->addMultiCellWidget(_term_group, 2, 2, 0, 1);
+
+ _termOptEdit->setEnabled(false);
+
+ // uid group
+ _uid_group = new QGroupBox(this);
+ vbox = new QVBoxLayout(_uid_group, KDialog::marginHint(),
+ KDialog::spacingHint());
+
+ _uidCB = new QCheckBox(i18n("&Run as a different user"), _uid_group);
+ connect(_uidCB, SIGNAL(clicked()), SLOT(uidcb_clicked()));
+ vbox->addWidget(_uidCB);
+
+ hbox = new QHBox(_uid_group);
+ hbox->setSpacing(KDialog::spacingHint());
+ _uidLabel = new QLabel(i18n("&Username:"), hbox);
+ _uidEdit = new KLineEdit(hbox);
+ _uidEdit->setAcceptDrops(false);
+ _uidLabel->setBuddy(_uidEdit);
+
+ connect(_uidEdit, SIGNAL(textChanged(const QString&)),
+ SLOT(slotChanged()));
+ vbox->addWidget(hbox);
+ layout->addMultiCellWidget(_uid_group, 3, 3, 0, 1);
+
+ _uidEdit->setEnabled(false);
+
+ layout->setRowStretch(0, 2);
+
+ // key binding group
+ general_group_keybind = new QGroupBox(this);
+ layout->addMultiCellWidget( general_group_keybind, 4, 4, 0, 1 );
+ // dummy widget in order to make it look a bit better
+ layout->addWidget( new QWidget(this), 5, 0 );
+ layout->setRowStretch( 5, 4 );
+ QGridLayout *grid_keybind = new QGridLayout(general_group_keybind, 3, 1,
+ KDialog::marginHint(),
+ KDialog::spacingHint());
+
+ //_keyEdit = new KLineEdit(general_group_keybind);
+ //_keyEdit->setReadOnly( true );
+ //_keyEdit->setText( "" );
+ //QPushButton* _keyButton = new QPushButton( i18n( "Change" ),
+ // general_group_keybind );
+ //connect( _keyButton, SIGNAL( clicked()), this, SLOT( keyButtonPressed()));
+ _keyEdit = new KKeyButton(general_group_keybind);
+ grid_keybind->addWidget(new QLabel(_keyEdit, i18n("Current shortcut &key:"), general_group_keybind), 0, 0);
+ connect( _keyEdit, SIGNAL(capturedShortcut(const KShortcut&)),
+ this, SLOT(slotCapturedShortcut(const KShortcut&)));
+ grid_keybind->addWidget(_keyEdit, 0, 1);
+ //grid_keybind->addWidget(_keyButton, 0, 2 );
+
+ if (!KHotKeys::present())
+ general_group_keybind->hide();
+
+ slotDisableAction();
+}
+
+void BasicTab::slotDisableAction()
+{
+ //disable all group at the begining.
+ //because there is not file selected.
+ _nameEdit->setEnabled(false);
+ _descriptionEdit->setEnabled(false);
+ _commentEdit->setEnabled(false);
+ _execEdit->setEnabled(false);
+ _launchCB->setEnabled(false);
+ _systrayCB->setEnabled(false);
+ _nameLabel->setEnabled(false);
+ _descriptionLabel->setEnabled(false);
+ _commentLabel->setEnabled(false);
+ _execLabel->setEnabled(false);
+ _path_group->setEnabled(false);
+ _term_group->setEnabled(false);
+ _uid_group->setEnabled(false);
+ _iconButton->setEnabled(false);
+ // key binding part
+ general_group_keybind->setEnabled( false );
+}
+
+void BasicTab::enableWidgets(bool isDF, bool isDeleted)
+{
+ // set only basic attributes if it is not a .desktop file
+ _nameEdit->setEnabled(!isDeleted);
+ _descriptionEdit->setEnabled(!isDeleted);
+ _commentEdit->setEnabled(!isDeleted);
+ _iconButton->setEnabled(!isDeleted);
+ _execEdit->setEnabled(isDF && !isDeleted);
+ _launchCB->setEnabled(isDF && !isDeleted);
+ _systrayCB->setEnabled(isDF && !isDeleted);
+ _nameLabel->setEnabled(!isDeleted);
+ _descriptionLabel->setEnabled(!isDeleted);
+ _commentLabel->setEnabled(!isDeleted);
+ _execLabel->setEnabled(isDF && !isDeleted);
+
+ _path_group->setEnabled(isDF && !isDeleted);
+ _term_group->setEnabled(isDF && !isDeleted);
+ _uid_group->setEnabled(isDF && !isDeleted);
+ general_group_keybind->setEnabled( isDF && !isDeleted );
+
+ _termOptEdit->setEnabled(isDF && !isDeleted && _terminalCB->isChecked());
+ _termOptLabel->setEnabled(isDF && !isDeleted && _terminalCB->isChecked());
+
+ _uidEdit->setEnabled(isDF && !isDeleted && _uidCB->isChecked());
+ _uidLabel->setEnabled(isDF && !isDeleted && _uidCB->isChecked());
+}
+
+void BasicTab::setFolderInfo(MenuFolderInfo *folderInfo)
+{
+ blockSignals(true);
+ _menuFolderInfo = folderInfo;
+ _menuEntryInfo = 0;
+
+ _nameEdit->setText(folderInfo->caption);
+ _descriptionEdit->setText(folderInfo->genericname);
+ _descriptionEdit->setCursorPosition(0);
+ _commentEdit->setText(folderInfo->comment);
+ _commentEdit->setCursorPosition(0);
+ _iconButton->setIcon(folderInfo->icon);
+
+ // clean all disabled fields and return
+ _execEdit->lineEdit()->setText("");
+ _pathEdit->lineEdit()->setText("");
+ _termOptEdit->setText("");
+ _uidEdit->setText("");
+ _launchCB->setChecked(false);
+ _systrayCB->setChecked(false);
+ _terminalCB->setChecked(false);
+ _uidCB->setChecked(false);
+ _keyEdit->setShortcut(0, false);
+
+ enableWidgets(false, folderInfo->hidden);
+ blockSignals(false);
+}
+
+void BasicTab::setEntryInfo(MenuEntryInfo *entryInfo)
+{
+ blockSignals(true);
+ _menuFolderInfo = 0;
+ _menuEntryInfo = entryInfo;
+
+ if (!entryInfo)
+ {
+ _nameEdit->setText(QString::null);
+ _descriptionEdit->setText(QString::null);
+ _commentEdit->setText(QString::null);
+ _iconButton->setIcon(QString::null);
+
+ // key binding part
+ _keyEdit->setShortcut( KShortcut(), false );
+ _execEdit->lineEdit()->setText(QString::null);
+ _systrayCB->setChecked(false);
+
+ _pathEdit->lineEdit()->setText(QString::null);
+ _termOptEdit->setText(QString::null);
+ _uidEdit->setText(QString::null);
+
+ _launchCB->setChecked(false);
+ _terminalCB->setChecked(false);
+ _uidCB->setChecked(false);
+ enableWidgets(true, true);
+ blockSignals(false);
+ return;
+ }
+
+ KDesktopFile *df = entryInfo->desktopFile();
+
+ _nameEdit->setText(df->readName());
+ _descriptionEdit->setText(df->readGenericName());
+ _descriptionEdit->setCursorPosition(0);
+ _commentEdit->setText(df->readComment());
+ _commentEdit->setCursorPosition(0);
+ _iconButton->setIcon(df->readIcon());
+
+ // key binding part
+ if( KHotKeys::present())
+ {
+ _keyEdit->setShortcut( entryInfo->shortcut(), false );
+ }
+
+ QString temp = df->readPathEntry("Exec");
+ if (temp.left(12) == "ksystraycmd ")
+ {
+ _execEdit->lineEdit()->setText(temp.right(temp.length()-12));
+ _systrayCB->setChecked(true);
+ }
+ else
+ {
+ _execEdit->lineEdit()->setText(temp);
+ _systrayCB->setChecked(false);
+ }
+
+ _pathEdit->lineEdit()->setText(df->readPath());
+ _termOptEdit->setText(df->readEntry("TerminalOptions"));
+ _uidEdit->setText(df->readEntry("X-KDE-Username"));
+
+ if( df->hasKey( "StartupNotify" ))
+ _launchCB->setChecked(df->readBoolEntry("StartupNotify", true));
+ else // backwards comp.
+ _launchCB->setChecked(df->readBoolEntry("X-KDE-StartupNotify", true));
+
+ if(df->readNumEntry("Terminal", 0) == 1)
+ _terminalCB->setChecked(true);
+ else
+ _terminalCB->setChecked(false);
+
+ _uidCB->setChecked(df->readBoolEntry("X-KDE-SubstituteUID", false));
+
+ enableWidgets(true, entryInfo->hidden);
+ blockSignals(false);
+}
+
+void BasicTab::apply()
+{
+ if (_menuEntryInfo)
+ {
+ _menuEntryInfo->setDirty();
+ _menuEntryInfo->setCaption(_nameEdit->text());
+ _menuEntryInfo->setDescription(_descriptionEdit->text());
+ _menuEntryInfo->setIcon(_iconButton->icon());
+
+ KDesktopFile *df = _menuEntryInfo->desktopFile();
+ df->writeEntry("Comment", _commentEdit->text());
+ if (_systrayCB->isChecked())
+ df->writePathEntry("Exec", _execEdit->lineEdit()->text().prepend("ksystraycmd "));
+ else
+ df->writePathEntry("Exec", _execEdit->lineEdit()->text());
+
+ df->writePathEntry("Path", _pathEdit->lineEdit()->text());
+
+ if (_terminalCB->isChecked())
+ df->writeEntry("Terminal", 1);
+ else
+ df->writeEntry("Terminal", 0);
+
+ df->writeEntry("TerminalOptions", _termOptEdit->text());
+ df->writeEntry("X-KDE-SubstituteUID", _uidCB->isChecked());
+ df->writeEntry("X-KDE-Username", _uidEdit->text());
+ df->writeEntry("StartupNotify", _launchCB->isChecked());
+ }
+ else
+ {
+ _menuFolderInfo->setCaption(_nameEdit->text());
+ _menuFolderInfo->setGenericName(_descriptionEdit->text());
+ _menuFolderInfo->setComment(_commentEdit->text());
+ _menuFolderInfo->setIcon(_iconButton->icon());
+ }
+}
+
+void BasicTab::slotChanged()
+{
+ if (signalsBlocked())
+ return;
+ apply();
+ if (_menuEntryInfo)
+ emit changed( _menuEntryInfo );
+ else
+ emit changed( _menuFolderInfo );
+}
+
+void BasicTab::launchcb_clicked()
+{
+ slotChanged();
+}
+
+void BasicTab::systraycb_clicked()
+{
+ slotChanged();
+}
+
+void BasicTab::termcb_clicked()
+{
+ _termOptEdit->setEnabled(_terminalCB->isChecked());
+ _termOptLabel->setEnabled(_terminalCB->isChecked());
+ slotChanged();
+}
+
+void BasicTab::uidcb_clicked()
+{
+ _uidEdit->setEnabled(_uidCB->isChecked());
+ _uidLabel->setEnabled(_uidCB->isChecked());
+ slotChanged();
+}
+
+void BasicTab::slotExecSelected()
+{
+ QString path = _execEdit->lineEdit()->text();
+ if (!path.startsWith("'"))
+ _execEdit->lineEdit()->setText(KProcess::quote(path));
+}
+
+void BasicTab::slotCapturedShortcut(const KShortcut& cut)
+{
+ if (signalsBlocked())
+ return;
+
+ if( KKeyChooser::checkGlobalShortcutsConflict( cut, true, topLevelWidget())
+ || KKeyChooser::checkStandardShortcutsConflict( cut, true, topLevelWidget()))
+ return;
+
+ if ( KHotKeys::present() )
+ {
+ if (!_menuEntryInfo->isShortcutAvailable( cut ) )
+ {
+ KService::Ptr service;
+ emit findServiceShortcut(cut, service);
+ if (!service)
+ service = KHotKeys::findMenuEntry(cut.toString());
+ if (service)
+ {
+ KMessageBox::sorry(this, i18n("<qt>The key <b>%1</b> can not be used here because it is already used to activate <b>%2</b>.").arg(cut.toString(), service->name()));
+ return;
+ }
+ else
+ {
+ KMessageBox::sorry(this, i18n("<qt>The key <b>%1</b> can not be used here because it is already in use.").arg(cut.toString()));
+ return;
+ }
+ }
+ _menuEntryInfo->setShortcut( cut );
+ }
+ _keyEdit->setShortcut(cut, false);
+ if (_menuEntryInfo)
+ emit changed( _menuEntryInfo );
+}
+
diff --git a/kmenuedit/basictab.h b/kmenuedit/basictab.h
new file mode 100644
index 000000000..6f6bb63a9
--- /dev/null
+++ b/kmenuedit/basictab.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2000 Matthias Elter <elter@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.
+ *
+ */
+
+#ifndef __basictab_h__
+#define __basictab_h__
+
+#include <qwidget.h>
+#include <qstring.h>
+
+#include <klineedit.h>
+
+class KKeyButton;
+class KLineEdit;
+class KIconButton;
+class QCheckBox;
+class QGroupBox;
+class QLabel;
+class KURLRequester;
+class KComboBox;
+class KService;
+
+class MenuFolderInfo;
+class MenuEntryInfo;
+
+class BasicTab : public QWidget
+{
+ Q_OBJECT
+
+public:
+ BasicTab( QWidget *parent=0, const char *name=0 );
+
+ void apply();
+signals:
+ void changed( MenuFolderInfo * );
+ void changed( MenuEntryInfo * );
+ void findServiceShortcut(const KShortcut&, KService::Ptr &);
+
+public slots:
+ void setFolderInfo(MenuFolderInfo *folderInfo);
+ void setEntryInfo(MenuEntryInfo *entryInfo);
+ void slotDisableAction();
+protected slots:
+ void slotChanged();
+ void launchcb_clicked();
+ void systraycb_clicked();
+ void termcb_clicked();
+ void uidcb_clicked();
+ void slotCapturedShortcut(const KShortcut&);
+ void slotExecSelected();
+
+protected:
+ void enableWidgets(bool isDF, bool isDeleted);
+
+protected:
+ KLineEdit *_nameEdit, *_commentEdit;
+ KLineEdit *_descriptionEdit;
+ KKeyButton *_keyEdit;
+ KURLRequester *_execEdit, *_pathEdit;
+ KLineEdit *_termOptEdit, *_uidEdit;
+ QCheckBox *_terminalCB, *_uidCB, *_launchCB, *_systrayCB;
+ KIconButton *_iconButton;
+ QGroupBox *_path_group, *_term_group, *_uid_group, *general_group_keybind;
+ QLabel *_termOptLabel, *_uidLabel, *_pathLabel, *_nameLabel, *_commentLabel, *_execLabel;
+ QLabel *_descriptionLabel;
+
+ MenuFolderInfo *_menuFolderInfo;
+ MenuEntryInfo *_menuEntryInfo;
+ bool _isDeleted;
+};
+
+#endif
diff --git a/kmenuedit/hi16-app-kmenuedit.png b/kmenuedit/hi16-app-kmenuedit.png
new file mode 100644
index 000000000..b121605f7
--- /dev/null
+++ b/kmenuedit/hi16-app-kmenuedit.png
Binary files differ
diff --git a/kmenuedit/hi22-app-kmenuedit.png b/kmenuedit/hi22-app-kmenuedit.png
new file mode 100644
index 000000000..65509221a
--- /dev/null
+++ b/kmenuedit/hi22-app-kmenuedit.png
Binary files differ
diff --git a/kmenuedit/hi32-app-kmenuedit.png b/kmenuedit/hi32-app-kmenuedit.png
new file mode 100644
index 000000000..d1e532be1
--- /dev/null
+++ b/kmenuedit/hi32-app-kmenuedit.png
Binary files differ
diff --git a/kmenuedit/hi48-app-kmenuedit.png b/kmenuedit/hi48-app-kmenuedit.png
new file mode 100644
index 000000000..e6b483a7f
--- /dev/null
+++ b/kmenuedit/hi48-app-kmenuedit.png
Binary files differ
diff --git a/kmenuedit/kcontrol_main.cpp b/kmenuedit/kcontrol_main.cpp
new file mode 100644
index 000000000..5e74951c6
--- /dev/null
+++ b/kmenuedit/kcontrol_main.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2000 Matthias Elter <elter@kde.org>
+ * Copyright (C) 2001-2002 Raffaele Sandrini <sandrini@kde.org>
+ * Copyright (C) 2004 Waldo Bastian <bastian@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 <unistd.h>
+
+#include <kuniqueapplication.h>
+#include <klocale.h>
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+#include <kstandarddirs.h>
+
+#include "kmenuedit.h"
+
+static const char description[] = I18N_NOOP("KDE control center editor");
+static const char version[] = "1.0";
+
+extern "C" int KDE_EXPORT kdemain( int argc, char **argv )
+{
+ KLocale::setMainCatalogue("kmenuedit");
+ KAboutData aboutData("kcontroledit", I18N_NOOP("KDE Control Center Editor"),
+ version, description, KAboutData::License_GPL,
+ "(C) 2000-2004, Waldo Bastian, Raffaele Sandrini, Matthias Elter");
+ aboutData.addAuthor("Waldo Bastian", I18N_NOOP("Maintainer"), "bastian@kde.org");
+ aboutData.addAuthor("Raffaele Sandrini", I18N_NOOP("Previous Maintainer"), "sandrini@kde.org");
+ aboutData.addAuthor("Matthias Elter", I18N_NOOP("Original Author"), "elter@kde.org");
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KUniqueApplication::addCmdLineOptions();
+
+ if (!KUniqueApplication::start())
+ return 1;
+
+ KUniqueApplication app;
+
+ KMenuEdit *menuEdit = new KMenuEdit(true);
+ menuEdit->show();
+
+ app.setMainWidget(menuEdit);
+ return app.exec();
+}
diff --git a/kmenuedit/kcontroleditui.rc b/kmenuedit/kcontroleditui.rc
new file mode 100644
index 000000000..18bc04fcb
--- /dev/null
+++ b/kmenuedit/kcontroleditui.rc
@@ -0,0 +1,40 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="kcontroledit" version="4">
+
+<MenuBar>
+
+<Menu name="file" noMerge="1"><text>&amp;File</text>
+ <Action name="newitem"/>
+ <Action name="newsubmenu" />
+ <Separator/>
+ <Action name="file_save"/>
+ <Separator/>
+ <Action name="file_quit"/>
+</Menu>
+
+<Menu name="edit" noMerge="1"><text>&amp;Edit</text>
+ <Action name="edit_cut"/>
+ <Action name="edit_copy"/>
+ <Action name="edit_paste"/>
+ <Separator/>
+ <Action name="delete"/>
+</Menu>
+
+<Menu name="settings">
+ <Action name="show_removed"/>
+ <Action name="show_hidden"/>
+</Menu>
+</MenuBar>
+
+<ToolBar name="mainToolBar" noMerge="1" fullWidth="true"><text>Main Toolbar</text>
+ <Action name="newitem"/>
+ <Action name="newsubmenu"/>
+ <Separator/>
+ <Action name="edit_cut"/>
+ <Action name="edit_copy"/>
+ <Action name="edit_paste"/>
+ <Separator/>
+ <Action name="delete"/>
+</ToolBar>
+
+</kpartgui>
diff --git a/kmenuedit/khotkeys.cpp b/kmenuedit/khotkeys.cpp
new file mode 100644
index 000000000..65909ab75
--- /dev/null
+++ b/kmenuedit/khotkeys.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2000 Matthias Elter <elter@kde.org>
+ * Lubos Lunak <l.lunak@email.cz>
+ *
+ * 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 <klibloader.h>
+#include "khotkeys.h"
+
+extern "C"
+{
+ static void (*khotkeys_init_2)( void );
+ static void (*khotkeys_cleanup_2)( void );
+ static QString (*khotkeys_get_menu_entry_shortcut_2)( const QString& entry_P );
+ static QString (*khotkeys_change_menu_entry_shortcut_2)( const QString& entry_P,
+ const QString& shortcut_P );
+ static bool (*khotkeys_menu_entry_moved_2)( const QString& new_P, const QString& old_P );
+ static void (*khotkeys_menu_entry_deleted_2)( const QString& entry_P );
+ static QStringList (*khotkeys_get_all_shortcuts_2)( );
+ static KService::Ptr (*khotkeys_find_menu_entry_2)( const QString& shortcut_P );
+}
+
+static bool khotkeys_present = false;
+static bool khotkeys_inited = false;
+
+bool KHotKeys::init()
+{
+ khotkeys_inited = true;
+ KLibrary* lib = KLibLoader::self()->library( "kcm_khotkeys.la" );
+ if( lib == NULL ) return false;
+
+ khotkeys_init_2 = ( void (*)(void)) ( lib->symbol( "khotkeys_init" ));
+ khotkeys_cleanup_2 = ( void (*)(void)) ( lib->symbol( "khotkeys_cleanup" ));
+ khotkeys_get_menu_entry_shortcut_2 =
+ ( QString (*)( const QString& ))
+ ( lib->symbol( "khotkeys_get_menu_entry_shortcut" ));
+ khotkeys_change_menu_entry_shortcut_2 =
+ ( QString (*)( const QString&, const QString& ))
+ ( lib->symbol( "khotkeys_change_menu_entry_shortcut" ));
+ khotkeys_menu_entry_moved_2 =
+ ( bool (*)( const QString&, const QString& ))
+ ( lib->symbol( "khotkeys_menu_entry_moved" ));
+ khotkeys_menu_entry_deleted_2 =
+ ( void (*)( const QString& ))
+ ( lib->symbol( "khotkeys_menu_entry_deleted" ));
+ khotkeys_get_all_shortcuts_2 =
+ ( QStringList (*)( ))
+ ( lib->symbol( "khotkeys_get_all_shortcuts" ));
+ khotkeys_find_menu_entry_2 =
+ ( KService::Ptr (*)( const QString& ))
+ ( lib->symbol( "khotkeys_find_menu_entry" ));
+
+ if( khotkeys_init_2
+ && khotkeys_cleanup_2
+ && khotkeys_get_menu_entry_shortcut_2
+ && khotkeys_change_menu_entry_shortcut_2
+ && khotkeys_menu_entry_moved_2
+ && khotkeys_menu_entry_deleted_2 )
+ {
+ khotkeys_init_2();
+ khotkeys_present = true;
+ return true;
+ }
+ return false;
+}
+
+void KHotKeys::cleanup()
+{
+ if( khotkeys_inited && khotkeys_present )
+ khotkeys_cleanup_2();
+ khotkeys_inited = false;
+}
+
+bool KHotKeys::present()
+{
+ if( !khotkeys_inited )
+ init();
+ return khotkeys_present;
+}
+
+QString KHotKeys::getMenuEntryShortcut( const QString& entry_P )
+{
+ if( !khotkeys_inited )
+ init();
+ if( !khotkeys_present )
+ return "";
+ return khotkeys_get_menu_entry_shortcut_2( entry_P );
+}
+
+QString KHotKeys::changeMenuEntryShortcut( const QString& entry_P,
+ const QString shortcut_P )
+ {
+ if( !khotkeys_inited )
+ init();
+ if( !khotkeys_present )
+ return "";
+ return khotkeys_change_menu_entry_shortcut_2( entry_P, shortcut_P );
+ }
+
+bool KHotKeys::menuEntryMoved( const QString& new_P, const QString& old_P )
+{
+ if( !khotkeys_inited )
+ init();
+ if( !khotkeys_present )
+ return "";
+ return khotkeys_menu_entry_moved_2( new_P, old_P );
+}
+
+void KHotKeys::menuEntryDeleted( const QString& entry_P )
+{
+ if( !khotkeys_inited )
+ init();
+ if( !khotkeys_present )
+ return;
+ khotkeys_menu_entry_deleted_2( entry_P );
+}
+
+QStringList KHotKeys::allShortCuts( )
+{
+ if( !khotkeys_inited )
+ init();
+ if (!khotkeys_get_all_shortcuts_2)
+ return QStringList();
+ return khotkeys_get_all_shortcuts_2();
+}
+
+KService::Ptr KHotKeys::findMenuEntry( const QString &shortcut_P )
+{
+ if( !khotkeys_inited )
+ init();
+ if (!khotkeys_find_menu_entry_2)
+ return 0;
+ return khotkeys_find_menu_entry_2(shortcut_P);
+}
diff --git a/kmenuedit/khotkeys.h b/kmenuedit/khotkeys.h
new file mode 100644
index 000000000..15aaeeb62
--- /dev/null
+++ b/kmenuedit/khotkeys.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2000 Matthias Elter <elter@kde.org>
+ * Lubos Lunak <l.lunak@email.cz>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __khotkeys_public_h__
+#define __khotkeys_public_h__
+
+#include <qstring.h>
+#include <kservice.h>
+
+// see kdebase/khotkeys/kcontrol for info on these
+
+class KHotKeys
+{
+public:
+ static bool init();
+ static void cleanup();
+ static bool present();
+ static QString getMenuEntryShortcut( const QString& entry_P );
+ static QString changeMenuEntryShortcut( const QString& entry_P,
+ const QString shortcut_P );
+ static bool menuEntryMoved( const QString& new_P, const QString& old_P );
+ static void menuEntryDeleted( const QString& entry_P );
+ static QStringList allShortCuts( );
+ static KService::Ptr findMenuEntry( const QString &shortcut_P );
+};
+
+#endif
diff --git a/kmenuedit/kmenuedit.cpp b/kmenuedit/kmenuedit.cpp
new file mode 100644
index 000000000..ba8d6fc08
--- /dev/null
+++ b/kmenuedit/kmenuedit.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2000 Matthias Elter <elter@kde.org>
+ * Copyright (C) 2001-2002 Raffaele Sandrini <sandrini@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 <qsplitter.h>
+
+#include <kaction.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kedittoolbar.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kservice.h>
+#include <kstdaction.h>
+#include <kstdaccel.h>
+
+#include "treeview.h"
+#include "basictab.h"
+#include "kmenuedit.h"
+#include "kmenuedit.moc"
+
+KMenuEdit::KMenuEdit (bool controlCenter, QWidget *, const char *name)
+ : KMainWindow (0, name), m_tree(0), m_basicTab(0), m_splitter(0), m_controlCenter(controlCenter)
+{
+#if 0
+ m_showHidden = config->readBoolEntry("ShowHidden");
+#else
+ m_showHidden = false;
+#endif
+
+ // setup GUI
+ setupActions();
+ slotChangeView();
+}
+
+KMenuEdit::~KMenuEdit()
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("General");
+ config->writeEntry("SplitterSizes", m_splitter->sizes());
+
+ config->sync();
+}
+
+void KMenuEdit::setupActions()
+{
+ (void)new KAction(i18n("&New Submenu..."), "menu_new", 0, actionCollection(), "newsubmenu");
+ (void)new KAction(i18n("New &Item..."), "filenew", KStdAccel::openNew(), actionCollection(), "newitem");
+ if (!m_controlCenter)
+ (void)new KAction(i18n("New S&eparator"), "menu_new_sep", 0, actionCollection(), "newsep");
+
+ m_actionDelete = 0;
+
+ KStdAction::save(this, SLOT( slotSave() ), actionCollection());
+ KStdAction::quit(this, SLOT( close() ), actionCollection());
+ KStdAction::cut(0, 0, actionCollection());
+ KStdAction::copy(0, 0, actionCollection());
+ KStdAction::paste(0, 0, actionCollection());
+}
+
+void KMenuEdit::setupView()
+{
+ m_splitter = new QSplitter(Horizontal, this);
+ m_tree = new TreeView(m_controlCenter, actionCollection(), m_splitter);
+ m_basicTab = new BasicTab(m_splitter);
+
+ connect(m_tree, SIGNAL(entrySelected(MenuFolderInfo *)),
+ m_basicTab, SLOT(setFolderInfo(MenuFolderInfo *)));
+ connect(m_tree, SIGNAL(entrySelected(MenuEntryInfo *)),
+ m_basicTab, SLOT(setEntryInfo(MenuEntryInfo *)));
+ connect(m_tree, SIGNAL(disableAction()),
+ m_basicTab, SLOT(slotDisableAction() ) );
+
+ connect(m_basicTab, SIGNAL(changed(MenuFolderInfo *)),
+ m_tree, SLOT(currentChanged(MenuFolderInfo *)));
+
+ connect(m_basicTab, SIGNAL(changed(MenuEntryInfo *)),
+ m_tree, SLOT(currentChanged(MenuEntryInfo *)));
+
+ connect(m_basicTab, SIGNAL(findServiceShortcut(const KShortcut&, KService::Ptr &)),
+ m_tree, SLOT(findServiceShortcut(const KShortcut&, KService::Ptr &)));
+
+ // restore splitter sizes
+ KConfig* config = KGlobal::config();
+ QValueList<int> sizes = config->readIntListEntry("SplitterSizes");
+
+ if (sizes.isEmpty())
+ sizes << 1 << 3;
+ m_splitter->setSizes(sizes);
+ m_tree->setFocus();
+
+ setCentralWidget(m_splitter);
+}
+
+void KMenuEdit::slotChangeView()
+{
+#if 0
+ m_showHidden = m_actionShowHidden->isChecked();
+#else
+ m_showHidden = false;
+#endif
+
+ // disabling the updates prevents unnecessary redraws
+ setUpdatesEnabled( false );
+ guiFactory()->removeClient( this );
+
+ delete m_actionDelete;
+
+ m_actionDelete = new KAction(i18n("&Delete"), "editdelete", Key_Delete, actionCollection(), "delete");
+
+ if (!m_splitter)
+ setupView();
+ if (m_controlCenter)
+ setupGUI(KMainWindow::ToolBar|Keys|Save|Create, "kcontroleditui.rc");
+ else
+ setupGUI(KMainWindow::ToolBar|Keys|Save|Create, "kmenueditui.rc");
+
+ m_tree->setViewMode(m_showHidden);
+}
+
+void KMenuEdit::slotSave()
+{
+ m_tree->save();
+}
+
+bool KMenuEdit::queryClose()
+{
+ if (!m_tree->dirty()) return true;
+
+
+ int result;
+ if (m_controlCenter)
+ {
+ result = KMessageBox::warningYesNoCancel(this,
+ i18n("You have made changes to the Control Center.\n"
+ "Do you want to save the changes or discard them?"),
+ i18n("Save Control Center Changes?"),
+ KStdGuiItem::save(), KStdGuiItem::discard() );
+ }
+ else
+ {
+ result = KMessageBox::warningYesNoCancel(this,
+ i18n("You have made changes to the menu.\n"
+ "Do you want to save the changes or discard them?"),
+ i18n("Save Menu Changes?"),
+ KStdGuiItem::save(), KStdGuiItem::discard() );
+ }
+
+ switch(result)
+ {
+ case KMessageBox::Yes:
+ return m_tree->save();
+
+ case KMessageBox::No:
+ return true;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+void KMenuEdit::slotConfigureToolbars()
+{
+ KEditToolbar dlg( factory() );
+
+ dlg.exec();
+}
diff --git a/kmenuedit/kmenuedit.desktop b/kmenuedit/kmenuedit.desktop
new file mode 100644
index 000000000..1a59059ce
--- /dev/null
+++ b/kmenuedit/kmenuedit.desktop
@@ -0,0 +1,92 @@
+[Desktop Entry]
+Exec=kmenuedit
+Icon=kmenuedit
+DocPath=kmenuedit/index.html
+Type=Application
+X-KDE-StartupNotify=true
+OnlyShowIn=KDE;
+
+Name=Menu Editor
+Name[af]=Kieslys Redigeerder
+Name[ar]=محرر القوائم
+Name[az]=Menyu Editoru
+Name[be]=Рэдактар меню
+Name[bg]=Редактор на системното меню
+Name[bn]=মেনু সম্পাদক
+Name[br]=Aozer lañserioù
+Name[bs]=Editor menija
+Name[ca]=Editor del menú
+Name[cs]=Editor nabídek
+Name[csb]=Editora menu
+Name[cy]=Golygydd Dewislen
+Name[da]=Menueditor
+Name[de]=Menü-Editor
+Name[el]=Επεξεργαστής μενού
+Name[eo]=Menuredaktilo
+Name[es]=Editor de menús
+Name[et]=Menüü redaktor
+Name[eu]=Menu editorea
+Name[fa]=ویرایشگر گزینگان
+Name[fi]=Valikon asetukset
+Name[fr]=KMenuEdit
+Name[fy]=Menubewurker
+Name[ga]=Eagarthóir Roghchláir
+Name[gl]=Editor do Menu
+Name[he]=עורך התפריטים
+Name[hi]=मेन्यू संपादक
+Name[hr]=Uređivač izbornika
+Name[hu]=Menüszerkesztő
+Name[id]=Editor Menu
+Name[is]=Sýsla með valmyndir
+Name[it]=Editor dei menu
+Name[ja]=メニューエディタ
+Name[ka]=მენიუს რედაქტორი
+Name[kk]=Мәзір редакторы
+Name[km]=កម្មវិធី​និពន្ធ​ម៉ឺនុយ
+Name[ko]=메뉴 편집기
+Name[lo]=ເຄື່ອງມືແກ້ໄຂເມນູ
+Name[lt]=Meniu redaktorius
+Name[lv]=Izvēlnes Redaktors
+Name[mk]=Уредувач на мени
+Name[mn]=Цэс-Боловсруулагч
+Name[ms]=Editor Menu
+Name[mt]=Editur tal-menu
+Name[nb]=Menyredigering
+Name[nds]=Menüeditor
+Name[ne]=मेनु सम्पादक
+Name[nl]=Menubewerker
+Name[nn]=Menyredigering
+Name[nso]=Mofetosi wa Menu
+Name[oc]=Editor de menu
+Name[pa]=ਮੇਨੂ ਸੰਪਾਦਕ
+Name[pl]=Edytor menu
+Name[pt]=Editor de Menus
+Name[pt_BR]=Editor de Menus
+Name[ro]=Editor de meniuri
+Name[ru]=Редактор меню
+Name[rw]=Muhinduzi Ibikubiyemo
+Name[se]=Fállodoaimmaheaddji
+Name[sk]=Editor menu
+Name[sl]=Urejevalnik menijev
+Name[sr]=Уређивач менија
+Name[sr@Latn]=Uređivač menija
+Name[sv]=Menyeditor
+Name[ta]=பட்டியல் திருத்துபவர்
+Name[te]=పట్టి ఎడిటర్
+Name[tg]=Муҳаррири меню
+Name[th]=ตัวแก้ไขเมนู
+Name[tr]=Menü Düzenleyicisi
+Name[tt]=Saylaq Tözätü
+Name[uk]=Редактор меню
+Name[uz]=Menyu tahrirchi
+Name[uz@cyrillic]=Меню таҳрирчи
+Name[ven]=Musengulusi wa Menu
+Name[vi]=Biên soạn Thực đơn
+Name[wa]=Aspougneu di menus
+Name[xh]=Umhleli we Menu
+Name[zh_CN]=菜单编辑器
+Name[zh_TW]=選單編輯器
+Name[zu]=Umlungisi wemenu
+
+X-DCOP-ServiceType=Unique
+Categories=Qt;KDE;Settings;
diff --git a/kmenuedit/kmenuedit.h b/kmenuedit/kmenuedit.h
new file mode 100644
index 000000000..32e860e04
--- /dev/null
+++ b/kmenuedit/kmenuedit.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2000 Matthias Elter <elter@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.
+ *
+ */
+
+#ifndef __kmenuedit_h__
+#define __kmenuedit_h__
+
+#include <kmainwindow.h>
+#include <treeview.h>
+
+class BasicTab;
+class QSplitter;
+class KAction;
+class KToggleAction;
+
+class KMenuEdit : public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ KMenuEdit( bool controlCenter, QWidget *parent=0, const char *name=0 );
+ ~KMenuEdit();
+
+ void selectMenu(const QString &menu) { m_tree->selectMenu(menu); }
+ void selectMenuEntry(const QString &menuEntry) { m_tree->selectMenuEntry(menuEntry); }
+
+protected:
+ void setupView();
+ void setupActions();
+ bool queryClose();
+
+protected slots:
+ void slotSave();
+ void slotChangeView();
+ void slotConfigureToolbars();
+protected:
+ TreeView *m_tree;
+ BasicTab *m_basicTab;
+ QSplitter *m_splitter;
+
+ KAction *m_actionDelete;
+ KToggleAction *m_actionShowHidden;
+ bool m_showHidden;
+ bool m_controlCenter;
+};
+
+#endif
diff --git a/kmenuedit/kmenueditui.rc b/kmenuedit/kmenueditui.rc
new file mode 100644
index 000000000..66330e740
--- /dev/null
+++ b/kmenuedit/kmenueditui.rc
@@ -0,0 +1,44 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="kmenuedit" version="6">
+
+<MenuBar>
+
+<Menu name="file" noMerge="1"><text>&amp;File</text>
+ <Action name="newitem"/>
+ <Action name="newsubmenu" />
+ <Action name="newsep" />
+ <Separator/>
+ <Action name="file_save"/>
+ <Separator/>
+ <Action name="file_quit"/>
+</Menu>
+
+<Menu name="edit" noMerge="1"><text>&amp;Edit</text>
+ <Action name="edit_cut"/>
+ <Action name="edit_copy"/>
+ <Action name="edit_paste"/>
+ <Separator/>
+ <Action name="delete"/>
+</Menu>
+
+<Menu name="settings">
+ <Action name="show_removed"/>
+ <Action name="show_hidden"/>
+</Menu>
+</MenuBar>
+
+<ToolBar name="mainToolBar" noMerge="1" fullWidth="true"><text>Main Toolbar</text>
+ <Action name="file_save"/>
+ <Separator/>
+ <Action name="newitem"/>
+ <Action name="newsubmenu"/>
+ <Action name="newsep" />
+ <Separator/>
+ <Action name="edit_cut"/>
+ <Action name="edit_copy"/>
+ <Action name="edit_paste"/>
+ <Separator/>
+ <Action name="delete"/>
+</ToolBar>
+
+</kpartgui>
diff --git a/kmenuedit/main.cpp b/kmenuedit/main.cpp
new file mode 100644
index 000000000..abb4f5968
--- /dev/null
+++ b/kmenuedit/main.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2000 Matthias Elter <elter@kde.org>
+ * Copyright (C) 2001-2002 Raffaele Sandrini <sandrini@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 <unistd.h>
+
+#include <kuniqueapplication.h>
+#include <klocale.h>
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+
+#include "kmenuedit.h"
+#include "khotkeys.h"
+
+static const char description[] = I18N_NOOP("KDE menu editor");
+static const char version[] = "0.7";
+
+static const KCmdLineOptions options[] =
+{
+ { "+[menu]", I18N_NOOP("Sub menu to pre-select"), 0 },
+ { "+[menu-id]", I18N_NOOP("Menu entry to pre-select"), 0 },
+ KCmdLineLastOption
+};
+
+static KMenuEdit *menuEdit = 0;
+
+class KMenuApplication : public KUniqueApplication
+{
+public:
+ KMenuApplication() { }
+ virtual ~KMenuApplication() { KHotKeys::cleanup(); }
+
+ virtual int newInstance()
+ {
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ if (args->count() > 0)
+ {
+ menuEdit->selectMenu(QString::fromLocal8Bit(args->arg(0)));
+ if (args->count() > 1)
+ {
+ menuEdit->selectMenuEntry(QString::fromLocal8Bit(args->arg(1)));
+ }
+ }
+ return KUniqueApplication::newInstance();
+ }
+};
+
+
+extern "C" int KDE_EXPORT kdemain( int argc, char **argv )
+{
+ KAboutData aboutData("kmenuedit", I18N_NOOP("KDE Menu Editor"),
+ version, description, KAboutData::License_GPL,
+ "(C) 2000-2003, Waldo Bastian, Raffaele Sandrini, Matthias Elter");
+ aboutData.addAuthor("Waldo Bastian", I18N_NOOP("Maintainer"), "bastian@kde.org");
+ aboutData.addAuthor("Raffaele Sandrini", I18N_NOOP("Previous Maintainer"), "sandrini@kde.org");
+ aboutData.addAuthor("Matthias Elter", I18N_NOOP("Original Author"), "elter@kde.org");
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KUniqueApplication::addCmdLineOptions();
+ KCmdLineArgs::addCmdLineOptions( options );
+
+ if (!KUniqueApplication::start())
+ return 1;
+
+ KMenuApplication app;
+
+ menuEdit = new KMenuEdit(false);
+ menuEdit->show();
+
+ app.setMainWidget(menuEdit);
+ return app.exec();
+}
diff --git a/kmenuedit/menufile.cpp b/kmenuedit/menufile.cpp
new file mode 100644
index 000000000..7fe2be954
--- /dev/null
+++ b/kmenuedit/menufile.cpp
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C) 2003 Waldo Bastian <bastian@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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 <qfile.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+
+#include "menufile.h"
+
+
+#define MF_MENU "Menu"
+#define MF_PUBLIC_ID "-//freedesktop//DTD Menu 1.0//EN"
+#define MF_SYSTEM_ID "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd"
+#define MF_NAME "Name"
+#define MF_INCLUDE "Include"
+#define MF_EXCLUDE "Exclude"
+#define MF_FILENAME "Filename"
+#define MF_DELETED "Deleted"
+#define MF_NOTDELETED "NotDeleted"
+#define MF_MOVE "Move"
+#define MF_OLD "Old"
+#define MF_NEW "New"
+#define MF_DIRECTORY "Directory"
+#define MF_LAYOUT "Layout"
+#define MF_MENUNAME "Menuname"
+#define MF_SEPARATOR "Separator"
+#define MF_MERGE "Merge"
+
+MenuFile::MenuFile(const QString &file)
+ : m_fileName(file), m_bDirty(false)
+{
+ load();
+}
+
+MenuFile::~MenuFile()
+{
+}
+
+bool MenuFile::load()
+{
+ if (m_fileName.isEmpty())
+ return false;
+
+ QFile file( m_fileName );
+ if (!file.open( IO_ReadOnly ))
+ {
+ kdWarning() << "Could not read " << m_fileName << endl;
+ create();
+ return false;
+ }
+
+ QString errorMsg;
+ int errorRow;
+ int errorCol;
+ if ( !m_doc.setContent( &file, &errorMsg, &errorRow, &errorCol ) ) {
+ kdWarning() << "Parse error in " << m_fileName << ", line " << errorRow << ", col " << errorCol << ": " << errorMsg << endl;
+ file.close();
+ create();
+ return false;
+ }
+ file.close();
+
+ return true;
+}
+
+void MenuFile::create()
+{
+ QDomImplementation impl;
+ QDomDocumentType docType = impl.createDocumentType( MF_MENU, MF_PUBLIC_ID, MF_SYSTEM_ID );
+ m_doc = impl.createDocument(QString::null, MF_MENU, docType);
+}
+
+bool MenuFile::save()
+{
+ QFile file( m_fileName );
+
+ if (!file.open( IO_WriteOnly ))
+ {
+ kdWarning() << "Could not write " << m_fileName << endl;
+ m_error = i18n("Could not write to %1").arg(m_fileName);
+ return false;
+ }
+ QTextStream stream( &file );
+ stream.setEncoding(QTextStream::UnicodeUTF8);
+
+ stream << m_doc.toString();
+
+ file.close();
+
+ if (file.status() != IO_Ok)
+ {
+ kdWarning() << "Could not close " << m_fileName << endl;
+ m_error = i18n("Could not write to %1").arg(m_fileName);
+ return false;
+ }
+
+ m_bDirty = false;
+
+ return true;
+}
+
+QDomElement MenuFile::findMenu(QDomElement elem, const QString &menuName, bool create)
+{
+ QString menuNodeName;
+ QString subMenuName;
+ int i = menuName.find('/');
+ if (i >= 0)
+ {
+ menuNodeName = menuName.left(i);
+ subMenuName = menuName.mid(i+1);
+ }
+ else
+ {
+ menuNodeName = menuName;
+ }
+ if (i == 0)
+ return findMenu(elem, subMenuName, create);
+
+ if (menuNodeName.isEmpty())
+ return elem;
+
+ QDomNode n = elem.firstChild();
+ while( !n.isNull() )
+ {
+ QDomElement e = n.toElement(); // try to convert the node to an element.
+ if (e.tagName() == MF_MENU)
+ {
+ QString name;
+
+ QDomNode n2 = e.firstChild();
+ while ( !n2.isNull() )
+ {
+ QDomElement e2 = n2.toElement();
+ if (!e2.isNull() && e2.tagName() == MF_NAME)
+ {
+ name = e2.text();
+ break;
+ }
+ n2 = n2.nextSibling();
+ }
+
+ if (name == menuNodeName)
+ {
+ if (subMenuName.isEmpty())
+ return e;
+ else
+ return findMenu(e, subMenuName, create);
+ }
+ }
+ n = n.nextSibling();
+ }
+
+ if (!create)
+ return QDomElement();
+
+ // Create new node.
+ QDomElement newElem = m_doc.createElement(MF_MENU);
+ QDomElement newNameElem = m_doc.createElement(MF_NAME);
+ newNameElem.appendChild(m_doc.createTextNode(menuNodeName));
+ newElem.appendChild(newNameElem);
+ elem.appendChild(newElem);
+
+ if (subMenuName.isEmpty())
+ return newElem;
+ else
+ return findMenu(newElem, subMenuName, create);
+}
+
+static QString entryToDirId(const QString &path)
+{
+ // See also KDesktopFile::locateLocal
+ QString local;
+ if (path.startsWith("/"))
+ {
+ // XDG Desktop menu items come with absolute paths, we need to
+ // extract their relative path and then build a local path.
+ local = KGlobal::dirs()->relativeLocation("xdgdata-dirs", path);
+ }
+
+ if (local.isEmpty() || local.startsWith("/"))
+ {
+ // What now? Use filename only and hope for the best.
+ local = path.mid(path.findRev('/')+1);
+ }
+ return local;
+}
+
+static void purgeIncludesExcludes(QDomElement elem, const QString &appId, QDomElement &excludeNode, QDomElement &includeNode)
+{
+ // Remove any previous includes/excludes of appId
+ QDomNode n = elem.firstChild();
+ while( !n.isNull() )
+ {
+ QDomElement e = n.toElement(); // try to convert the node to an element.
+ bool bIncludeNode = (e.tagName() == MF_INCLUDE);
+ bool bExcludeNode = (e.tagName() == MF_EXCLUDE);
+ if (bIncludeNode)
+ includeNode = e;
+ if (bExcludeNode)
+ excludeNode = e;
+ if (bIncludeNode || bExcludeNode)
+ {
+ QDomNode n2 = e.firstChild();
+ while ( !n2.isNull() )
+ {
+ QDomNode next = n2.nextSibling();
+ QDomElement e2 = n2.toElement();
+ if (!e2.isNull() && e2.tagName() == MF_FILENAME)
+ {
+ if (e2.text() == appId)
+ {
+ e.removeChild(e2);
+ break;
+ }
+ }
+ n2 = next;
+ }
+ }
+ n = n.nextSibling();
+ }
+}
+
+static void purgeDeleted(QDomElement elem)
+{
+ // Remove any previous includes/excludes of appId
+ QDomNode n = elem.firstChild();
+ while( !n.isNull() )
+ {
+ QDomNode next = n.nextSibling();
+ QDomElement e = n.toElement(); // try to convert the node to an element.
+ if ((e.tagName() == MF_DELETED) ||
+ (e.tagName() == MF_NOTDELETED))
+ {
+ elem.removeChild(e);
+ }
+ n = next;
+ }
+}
+
+static void purgeLayout(QDomElement elem)
+{
+ // Remove any previous includes/excludes of appId
+ QDomNode n = elem.firstChild();
+ while( !n.isNull() )
+ {
+ QDomNode next = n.nextSibling();
+ QDomElement e = n.toElement(); // try to convert the node to an element.
+ if (e.tagName() == MF_LAYOUT)
+ {
+ elem.removeChild(e);
+ }
+ n = next;
+ }
+}
+
+void MenuFile::addEntry(const QString &menuName, const QString &menuId)
+{
+ m_bDirty = true;
+
+ m_removedEntries.remove(menuId);
+
+ QDomElement elem = findMenu(m_doc.documentElement(), menuName, true);
+
+ QDomElement excludeNode;
+ QDomElement includeNode;
+
+ purgeIncludesExcludes(elem, menuId, excludeNode, includeNode);
+
+ if (includeNode.isNull())
+ {
+ includeNode = m_doc.createElement(MF_INCLUDE);
+ elem.appendChild(includeNode);
+ }
+
+ QDomElement fileNode = m_doc.createElement(MF_FILENAME);
+ fileNode.appendChild(m_doc.createTextNode(menuId));
+ includeNode.appendChild(fileNode);
+}
+
+void MenuFile::setLayout(const QString &menuName, const QStringList &layout)
+{
+ m_bDirty = true;
+
+ QDomElement elem = findMenu(m_doc.documentElement(), menuName, true);
+
+ purgeLayout(elem);
+
+ QDomElement layoutNode = m_doc.createElement(MF_LAYOUT);
+ elem.appendChild(layoutNode);
+
+ for(QStringList::ConstIterator it = layout.begin();
+ it != layout.end(); ++it)
+ {
+ QString li = *it;
+ if (li == ":S")
+ {
+ layoutNode.appendChild(m_doc.createElement(MF_SEPARATOR));
+ }
+ else if (li == ":M")
+ {
+ QDomElement mergeNode = m_doc.createElement(MF_MERGE);
+ mergeNode.setAttribute("type", "menus");
+ layoutNode.appendChild(mergeNode);
+ }
+ else if (li == ":F")
+ {
+ QDomElement mergeNode = m_doc.createElement(MF_MERGE);
+ mergeNode.setAttribute("type", "files");
+ layoutNode.appendChild(mergeNode);
+ }
+ else if (li == ":A")
+ {
+ QDomElement mergeNode = m_doc.createElement(MF_MERGE);
+ mergeNode.setAttribute("type", "all");
+ layoutNode.appendChild(mergeNode);
+ }
+ else if (li.endsWith("/"))
+ {
+ li.truncate(li.length()-1);
+ QDomElement menuNode = m_doc.createElement(MF_MENUNAME);
+ menuNode.appendChild(m_doc.createTextNode(li));
+ layoutNode.appendChild(menuNode);
+ }
+ else
+ {
+ QDomElement fileNode = m_doc.createElement(MF_FILENAME);
+ fileNode.appendChild(m_doc.createTextNode(li));
+ layoutNode.appendChild(fileNode);
+ }
+ }
+}
+
+
+void MenuFile::removeEntry(const QString &menuName, const QString &menuId)
+{
+ m_bDirty = true;
+
+ m_removedEntries.append(menuId);
+
+ QDomElement elem = findMenu(m_doc.documentElement(), menuName, true);
+
+ QDomElement excludeNode;
+ QDomElement includeNode;
+
+ purgeIncludesExcludes(elem, menuId, excludeNode, includeNode);
+
+ if (excludeNode.isNull())
+ {
+ excludeNode = m_doc.createElement(MF_EXCLUDE);
+ elem.appendChild(excludeNode);
+ }
+
+ QDomElement fileNode = m_doc.createElement(MF_FILENAME);
+ fileNode.appendChild(m_doc.createTextNode(menuId));
+ excludeNode.appendChild(fileNode);
+}
+
+void MenuFile::addMenu(const QString &menuName, const QString &menuFile)
+{
+ m_bDirty = true;
+ QDomElement elem = findMenu(m_doc.documentElement(), menuName, true);
+
+ QDomElement dirElem = m_doc.createElement(MF_DIRECTORY);
+ dirElem.appendChild(m_doc.createTextNode(entryToDirId(menuFile)));
+ elem.appendChild(dirElem);
+}
+
+void MenuFile::moveMenu(const QString &oldMenu, const QString &newMenu)
+{
+ m_bDirty = true;
+
+ // Undelete the new menu
+ QDomElement elem = findMenu(m_doc.documentElement(), newMenu, true);
+ purgeDeleted(elem);
+ elem.appendChild(m_doc.createElement(MF_NOTDELETED));
+
+// TODO: GET RID OF COMMON PART, IT BREAKS STUFF
+ // Find common part
+ QStringList oldMenuParts = QStringList::split('/', oldMenu);
+ QStringList newMenuParts = QStringList::split('/', newMenu);
+ QString commonMenuName;
+ uint max = QMIN(oldMenuParts.count(), newMenuParts.count());
+ uint i = 0;
+ for(; i < max; i++)
+ {
+ if (oldMenuParts[i] != newMenuParts[i])
+ break;
+ commonMenuName += '/' + oldMenuParts[i];
+ }
+ QString oldMenuName;
+ for(uint j = i; j < oldMenuParts.count(); j++)
+ {
+ if (i != j)
+ oldMenuName += '/';
+ oldMenuName += oldMenuParts[j];
+ }
+ QString newMenuName;
+ for(uint j = i; j < newMenuParts.count(); j++)
+ {
+ if (i != j)
+ newMenuName += '/';
+ newMenuName += newMenuParts[j];
+ }
+
+ if (oldMenuName == newMenuName) return; // Can happen
+
+ elem = findMenu(m_doc.documentElement(), commonMenuName, true);
+
+ // Add instructions for moving
+ QDomElement moveNode = m_doc.createElement(MF_MOVE);
+ QDomElement node = m_doc.createElement(MF_OLD);
+ node.appendChild(m_doc.createTextNode(oldMenuName));
+ moveNode.appendChild(node);
+ node = m_doc.createElement(MF_NEW);
+ node.appendChild(m_doc.createTextNode(newMenuName));
+ moveNode.appendChild(node);
+ elem.appendChild(moveNode);
+}
+
+void MenuFile::removeMenu(const QString &menuName)
+{
+ m_bDirty = true;
+
+ QDomElement elem = findMenu(m_doc.documentElement(), menuName, true);
+
+ purgeDeleted(elem);
+ elem.appendChild(m_doc.createElement(MF_DELETED));
+}
+
+ /**
+ * Returns a unique menu-name for a new menu under @p menuName
+ * inspired by @p newMenu
+ */
+QString MenuFile::uniqueMenuName(const QString &menuName, const QString &newMenu, const QStringList & excludeList)
+{
+ QDomElement elem = findMenu(m_doc.documentElement(), menuName, false);
+
+ QString result = newMenu;
+ if (result.endsWith("/"))
+ result.truncate(result.length()-1);
+
+ QRegExp r("(.*)(?=-\\d+)");
+ result = (r.search(result) > -1) ? r.cap(1) : result;
+
+ int trunc = result.length(); // Position of trailing '/'
+
+ result.append("/");
+
+ for(int n = 1; ++n; )
+ {
+ if (findMenu(elem, result, false).isNull() && !excludeList.contains(result))
+ return result;
+
+ result.truncate(trunc);
+ result.append(QString("-%1/").arg(n));
+ }
+ return QString::null; // Never reached
+}
+
+void MenuFile::performAction(const ActionAtom *atom)
+{
+ switch(atom->action)
+ {
+ case ADD_ENTRY:
+ addEntry(atom->arg1, atom->arg2);
+ return;
+ case REMOVE_ENTRY:
+ removeEntry(atom->arg1, atom->arg2);
+ return;
+ case ADD_MENU:
+ addMenu(atom->arg1, atom->arg2);
+ return;
+ case REMOVE_MENU:
+ removeMenu(atom->arg1);
+ return;
+ case MOVE_MENU:
+ moveMenu(atom->arg1, atom->arg2);
+ return;
+ }
+}
+
+MenuFile::ActionAtom *MenuFile::pushAction(MenuFile::ActionType action, const QString &arg1, const QString &arg2)
+{
+ ActionAtom *atom = new ActionAtom;
+ atom->action = action;
+ atom->arg1 = arg1;
+ atom->arg2 = arg2;
+ m_actionList.append(atom);
+ return atom;
+}
+
+void MenuFile::popAction(ActionAtom *atom)
+{
+ if (m_actionList.getLast() != atom)
+ {
+ qWarning("MenuFile::popAction Error, action not last in list.");
+ return;
+ }
+ m_actionList.removeLast();
+ delete atom;
+}
+
+bool MenuFile::performAllActions()
+{
+ for(ActionAtom *atom; (atom = m_actionList.getFirst()); m_actionList.removeFirst())
+ {
+ performAction(atom);
+ delete atom;
+ }
+
+ // Entries that have been removed from the menu are added to .hidden
+ // so that they don't re-appear in Lost & Found
+ QStringList removed = m_removedEntries;
+ m_removedEntries.clear();
+ for(QStringList::ConstIterator it = removed.begin();
+ it != removed.end(); ++it)
+ {
+ addEntry("/.hidden/", *it);
+ }
+
+ m_removedEntries.clear();
+
+ if (!m_bDirty)
+ return true;
+
+ return save();
+}
+
+bool MenuFile::dirty()
+{
+ return (m_actionList.count() != 0) || m_bDirty;
+}
diff --git a/kmenuedit/menufile.h b/kmenuedit/menufile.h
new file mode 100644
index 000000000..61ca3a9f0
--- /dev/null
+++ b/kmenuedit/menufile.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2003 Waldo Bastian <bastian@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __menufile_h__
+#define __menufile_h__
+
+#include <qdom.h>
+#include <qstring.h>
+
+class MenuFile
+{
+public:
+ MenuFile(const QString &file);
+ ~MenuFile();
+
+ bool load();
+ bool save();
+ void create();
+ QString error() { return m_error; } // Returns the last error message
+
+ enum ActionType {
+ ADD_ENTRY = 0,
+ REMOVE_ENTRY,
+ ADD_MENU,
+ REMOVE_MENU,
+ MOVE_MENU
+ };
+
+ struct ActionAtom
+ {
+ ActionType action;
+ QString arg1;
+ QString arg2;
+ };
+
+ /**
+ * Create action atom and push it on the stack
+ */
+ ActionAtom *pushAction(ActionType action, const QString &arg1, const QString &arg2);
+
+ /**
+ * Pop @p atom from the stack.
+ * @p atom must be last item on the stack
+ */
+ void popAction(ActionAtom *atom);
+
+ /**
+ * Perform the specified action
+ */
+ void performAction(const ActionAtom *);
+
+ /**
+ * Perform all actions currently on the stack, remove them from the stack and
+ * save result
+ * @return whether save was successful
+ */
+ bool performAllActions();
+
+ /**
+ * Returns whether the stack contains any actions
+ */
+ bool dirty();
+
+ void addEntry(const QString &menuName, const QString &menuId);
+ void removeEntry(const QString &menuName, const QString &menuId);
+
+ void addMenu(const QString &menuName, const QString &menuFile);
+ void moveMenu(const QString &oldMenu, const QString &newMenu);
+ void removeMenu(const QString &menuName);
+
+ void setLayout(const QString &menuName, const QStringList &layout);
+
+ /**
+ * Returns a unique menu-name for a new menu under @p menuName
+ * inspired by @p newMenu and not part of @p excludeList
+ */
+ QString uniqueMenuName(const QString &menuName, const QString &newMenu, const QStringList &excludeList);
+
+protected:
+ /**
+ * Finds menu @p menuName in @p elem.
+ * If @p create is true, the menu is created if it doesn't exist yet.
+ * @return The menu dom-node of @p menuName
+ */
+ QDomElement findMenu(QDomElement elem, const QString &menuName, bool create);
+
+private:
+ QString m_error;
+ QString m_fileName;
+
+ QDomDocument m_doc;
+ bool m_bDirty;
+
+ QPtrList<ActionAtom> m_actionList;
+ QStringList m_removedEntries;
+};
+
+
+#endif
diff --git a/kmenuedit/menuinfo.cpp b/kmenuedit/menuinfo.cpp
new file mode 100644
index 000000000..1fa4cb436
--- /dev/null
+++ b/kmenuedit/menuinfo.cpp
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2003 Waldo Bastian <bastian@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 "menuinfo.h"
+#include "menufile.h"
+
+#include <qregexp.h>
+
+#include <kdesktopfile.h>
+#include <khotkeys.h>
+#include <kstandarddirs.h>
+
+//
+// MenuFolderInfo
+//
+
+static QStringList *s_allShortcuts = 0;
+static QStringList *s_newShortcuts = 0;
+static QStringList *s_freeShortcuts = 0;
+static QStringList *s_deletedApps = 0;
+
+// Add separator
+void MenuFolderInfo::add(MenuSeparatorInfo *info, bool initial)
+{
+ if (initial)
+ initialLayout.append(info);
+}
+
+// Add sub menu
+void MenuFolderInfo::add(MenuFolderInfo *info, bool initial)
+{
+ subFolders.append(info);
+ if (initial)
+ initialLayout.append(info);
+}
+
+// Remove sub menu (without deleting it)
+void MenuFolderInfo::take(MenuFolderInfo *info)
+{
+ subFolders.take(subFolders.findRef(info));
+}
+
+// Remove sub menu (without deleting it)
+bool MenuFolderInfo::takeRecursive(MenuFolderInfo *info)
+{
+ int i = subFolders.findRef(info);
+ if (i >= 0)
+ {
+ subFolders.take(i);
+ return true;
+ }
+
+ for(MenuFolderInfo *subFolderInfo = subFolders.first();
+ subFolderInfo; subFolderInfo = subFolders.next())
+ {
+ if (subFolderInfo->takeRecursive(info))
+ return true;
+ }
+ return false;
+}
+
+// Recursively update all fullIds
+void MenuFolderInfo::updateFullId(const QString &parentId)
+{
+ fullId = parentId + id;
+
+ for(MenuFolderInfo *subFolderInfo = subFolders.first();
+ subFolderInfo; subFolderInfo = subFolders.next())
+ {
+ subFolderInfo->updateFullId(fullId);
+ }
+}
+
+// Add entry
+void MenuFolderInfo::add(MenuEntryInfo *entry, bool initial)
+{
+ entries.append(entry);
+ if (initial)
+ initialLayout.append(entry);
+}
+
+// Remove entry
+void MenuFolderInfo::take(MenuEntryInfo *entry)
+{
+ entries.removeRef(entry);
+}
+
+
+// Return a unique sub-menu caption inspired by @p caption
+QString MenuFolderInfo::uniqueMenuCaption(const QString &caption)
+{
+ QRegExp r("(.*)(?=-\\d+)");
+ QString cap = (r.search(caption) > -1) ? r.cap(1) : caption;
+
+ QString result = caption;
+
+ for(int n = 1; ++n; )
+ {
+ bool ok = true;
+ for(MenuFolderInfo *subFolderInfo = subFolders.first();
+ subFolderInfo; subFolderInfo = subFolders.next())
+ {
+ if (subFolderInfo->caption == result)
+ {
+ ok = false;
+ break;
+ }
+ }
+ if (ok)
+ return result;
+
+ result = cap + QString("-%1").arg(n);
+ }
+ return QString::null; // Never reached
+}
+
+// Return a unique item caption inspired by @p caption
+QString MenuFolderInfo::uniqueItemCaption(const QString &caption, const QString &exclude)
+{
+ QRegExp r("(.*)(?=-\\d+)");
+ QString cap = (r.search(caption) > -1) ? r.cap(1) : caption;
+
+ QString result = caption;
+
+ for(int n = 1; ++n; )
+ {
+ bool ok = true;
+ if (result == exclude)
+ ok = false;
+ MenuEntryInfo *entryInfo;
+ for(QPtrListIterator<MenuEntryInfo> it(entries);
+ ok && (entryInfo = it.current()); ++it)
+ {
+ if (entryInfo->caption == result)
+ ok = false;
+ }
+ if (ok)
+ return result;
+
+ result = cap + QString("-%1").arg(n);
+ }
+ return QString::null; // Never reached
+}
+
+// Return a list of existing submenu ids
+QStringList MenuFolderInfo::existingMenuIds()
+{
+ QStringList result;
+ for(MenuFolderInfo *subFolderInfo = subFolders.first();
+ subFolderInfo; subFolderInfo = subFolders.next())
+ {
+ result.append(subFolderInfo->id);
+ }
+ return result;
+}
+
+void MenuFolderInfo::setDirty()
+{
+ dirty = true;
+}
+
+void MenuFolderInfo::save(MenuFile *menuFile)
+{
+ if (s_deletedApps)
+ {
+ // Remove hotkeys for applications that have been deleted
+ for(QStringList::ConstIterator it = s_deletedApps->begin();
+ it != s_deletedApps->end(); ++it)
+ {
+ KHotKeys::menuEntryDeleted(*it);
+ }
+ delete s_deletedApps;
+ s_deletedApps = 0;
+ }
+
+ if (dirty)
+ {
+ QString local = KDesktopFile::locateLocal(directoryFile);
+
+ KConfig *df = 0;
+ if (directoryFile != local)
+ {
+ KConfig orig(directoryFile, true, false, "apps");
+ df = orig.copyTo(local);
+ }
+ else
+ {
+ df = new KConfig(directoryFile, false, false, "apps");
+ }
+
+ df->setDesktopGroup();
+ df->writeEntry("Name", caption);
+ df->writeEntry("GenericName", genericname);
+ df->writeEntry("Comment", comment);
+ df->writeEntry("Icon", icon);
+ df->sync();
+ delete df;
+ dirty = false;
+ }
+
+ // Save sub-menus
+ for(MenuFolderInfo *subFolderInfo = subFolders.first();
+ subFolderInfo; subFolderInfo = subFolders.next())
+ {
+ subFolderInfo->save(menuFile);
+ }
+
+ // Save entries
+ MenuEntryInfo *entryInfo;
+ for(QPtrListIterator<MenuEntryInfo> it(entries);
+ (entryInfo = it.current()); ++it)
+ {
+ if (entryInfo->needInsertion())
+ menuFile->addEntry(fullId, entryInfo->menuId());
+ entryInfo->save();
+ }
+}
+
+bool MenuFolderInfo::hasDirt()
+{
+ if (dirty) return true;
+
+ // Check sub-menus
+ for(MenuFolderInfo *subFolderInfo = subFolders.first();
+ subFolderInfo; subFolderInfo = subFolders.next())
+ {
+ if (subFolderInfo->hasDirt()) return true;
+ }
+
+ // Check entries
+ MenuEntryInfo *entryInfo;
+ for(QPtrListIterator<MenuEntryInfo> it(entries);
+ (entryInfo = it.current()); ++it)
+ {
+ if (entryInfo->dirty) return true;
+ if (entryInfo->shortcutDirty) return true;
+ }
+ return false;
+}
+
+KService::Ptr MenuFolderInfo::findServiceShortcut(const KShortcut&cut)
+{
+ KService::Ptr result;
+ // Check sub-menus
+ for(MenuFolderInfo *subFolderInfo = subFolders.first();
+ subFolderInfo; subFolderInfo = subFolders.next())
+ {
+ result = subFolderInfo->findServiceShortcut(cut);
+ if (result)
+ return result;
+ }
+
+ // Check entries
+ MenuEntryInfo *entryInfo;
+ for(QPtrListIterator<MenuEntryInfo> it(entries);
+ (entryInfo = it.current()); ++it)
+ {
+ if (entryInfo->shortCut == cut)
+ return entryInfo->service;
+ }
+ return 0;
+}
+
+void MenuFolderInfo::setInUse(bool inUse)
+{
+ // Propagate to sub-menus
+ for(MenuFolderInfo *subFolderInfo = subFolders.first();
+ subFolderInfo; subFolderInfo = subFolders.next())
+ {
+ subFolderInfo->setInUse(inUse);
+ }
+
+ // Propagate to entries
+ MenuEntryInfo *entryInfo;
+ for(QPtrListIterator<MenuEntryInfo> it(entries);
+ (entryInfo = it.current()); ++it)
+ {
+ entryInfo->setInUse(inUse);
+ }
+}
+
+//
+// MenuEntryInfo
+//
+
+MenuEntryInfo::~MenuEntryInfo()
+{
+ df->rollback(false);
+ delete df;
+}
+
+KDesktopFile *MenuEntryInfo::desktopFile()
+{
+ if (!df)
+ {
+ df = new KDesktopFile(service->desktopEntryPath());
+ }
+ return df;
+}
+
+void MenuEntryInfo::setDirty()
+{
+ if (dirty) return;
+
+ dirty = true;
+
+ QString local = locateLocal("xdgdata-apps", service->menuId());
+ if (local != service->desktopEntryPath())
+ {
+ KDesktopFile *oldDf = desktopFile();
+ df = oldDf->copyTo(local);
+ df->setDesktopGroup();
+ delete oldDf;
+ }
+}
+
+bool MenuEntryInfo::needInsertion()
+{
+ // If entry is dirty and previously stored under applnk, then we need to be added explicity
+ return dirty && !service->desktopEntryPath().startsWith("/");
+}
+
+void MenuEntryInfo::save()
+{
+ if (dirty)
+ {
+ df->sync();
+ dirty = false;
+ }
+
+ if (shortcutDirty)
+ {
+ if( KHotKeys::present())
+ {
+ KHotKeys::changeMenuEntryShortcut( service->storageId(), shortCut.toStringInternal() );
+ }
+ shortcutDirty = false;
+ }
+}
+
+void MenuEntryInfo::setCaption(const QString &_caption)
+{
+ if (caption == _caption)
+ return;
+ caption = _caption;
+ setDirty();
+ desktopFile()->writeEntry("Name", caption);
+}
+
+void MenuEntryInfo::setDescription(const QString &_description)
+{
+ if (description == _description)
+ return;
+ description = _description;
+ setDirty();
+ desktopFile()->writeEntry("GenericName", description);
+}
+
+void MenuEntryInfo::setIcon(const QString &_icon)
+{
+ if (icon == _icon)
+ return;
+
+ icon = _icon;
+ setDirty();
+ desktopFile()->writeEntry("Icon", icon);
+}
+
+KShortcut MenuEntryInfo::shortcut()
+{
+ if (!shortcutLoaded)
+ {
+ shortcutLoaded = true;
+ if( KHotKeys::present())
+ {
+ shortCut = KHotKeys::getMenuEntryShortcut( service->storageId() );
+ }
+ }
+ return shortCut;
+}
+
+static bool isEmpty(const KShortcut &shortCut)
+{
+ for(int i = shortCut.count(); i--;)
+ {
+ if (!shortCut.seq(i).isNull())
+ return false;
+ }
+ return true;
+}
+
+static void freeShortcut(const KShortcut &shortCut)
+{
+ if (!isEmpty(shortCut))
+ {
+ QString shortcutKey = shortCut.toString();
+ if (s_newShortcuts)
+ s_newShortcuts->remove(shortcutKey);
+
+ if (!s_freeShortcuts)
+ s_freeShortcuts = new QStringList;
+
+ s_freeShortcuts->append(shortcutKey);
+ }
+}
+
+static void allocateShortcut(const KShortcut &shortCut)
+{
+ if (!isEmpty(shortCut))
+ {
+ QString shortcutKey = shortCut.toString();
+ if (s_freeShortcuts)
+ s_freeShortcuts->remove(shortcutKey);
+
+ if (!s_newShortcuts)
+ s_newShortcuts = new QStringList;
+
+ s_newShortcuts->append(shortcutKey);
+ }
+}
+
+void MenuEntryInfo::setShortcut(const KShortcut &_shortcut)
+{
+ if (shortCut == _shortcut)
+ return;
+
+ freeShortcut(shortCut);
+ allocateShortcut(_shortcut);
+
+ shortCut = _shortcut;
+ if (isEmpty(shortCut))
+ shortCut = KShortcut(); // Normalize
+
+ shortcutLoaded = true;
+ shortcutDirty = true;
+}
+
+void MenuEntryInfo::setInUse(bool inUse)
+{
+ if (inUse)
+ {
+ KShortcut temp = shortcut();
+ shortCut = KShortcut();
+ if (isShortcutAvailable(temp))
+ shortCut = temp;
+ else
+ shortcutDirty = true;
+ allocateShortcut(shortCut);
+
+ if (s_deletedApps)
+ s_deletedApps->remove(service->storageId());
+ }
+ else
+ {
+ freeShortcut(shortcut());
+
+ // Add to list of deleted apps
+ if (!s_deletedApps)
+ s_deletedApps = new QStringList;
+
+ s_deletedApps->append(service->storageId());
+ }
+}
+
+bool MenuEntryInfo::isShortcutAvailable(const KShortcut &_shortcut)
+{
+ if (shortCut == _shortcut)
+ return true;
+
+ QString shortcutKey = _shortcut.toString();
+ bool available = true;
+ if (!s_allShortcuts)
+ {
+ s_allShortcuts = new QStringList(KHotKeys::allShortCuts());
+ }
+ available = !s_allShortcuts->contains(shortcutKey);
+ if (available && s_newShortcuts)
+ {
+ available = !s_newShortcuts->contains(shortcutKey);
+ }
+ if (!available && s_freeShortcuts)
+ {
+ available = s_freeShortcuts->contains(shortcutKey);
+ }
+ return available;
+}
diff --git a/kmenuedit/menuinfo.h b/kmenuedit/menuinfo.h
new file mode 100644
index 000000000..17a17e7ff
--- /dev/null
+++ b/kmenuedit/menuinfo.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2003 Waldo Bastian <bastian@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.
+ *
+ */
+
+#ifndef __menuinfo_h__
+#define __menuinfo_h__
+
+#include <qstring.h>
+
+#include <kshortcut.h>
+#include <kservice.h>
+
+class MenuFile;
+class MenuEntryInfo;
+
+class MenuInfo
+{
+public:
+ MenuInfo() {}
+ virtual ~MenuInfo() {}
+};
+
+class MenuSeparatorInfo : public MenuInfo
+{
+public:
+ MenuSeparatorInfo() {}
+};
+
+class MenuFolderInfo : public MenuInfo
+{
+public:
+ MenuFolderInfo() : dirty(false), hidden(false) { subFolders.setAutoDelete(true); }
+
+ // Add separator
+ void add(MenuSeparatorInfo *, bool initial=false);
+
+ // Add sub menu
+ void add(MenuFolderInfo *, bool initial=false);
+
+ // Remove sub menu (without deleting it)
+ void take(MenuFolderInfo *);
+
+ // Remove sub menu (without deleting it)
+ // @return true if found
+ bool takeRecursive(MenuFolderInfo *info);
+
+ // Add entry
+ void add(MenuEntryInfo *, bool initial = false);
+
+ // Remove entry (without deleteing it)
+ void take(MenuEntryInfo *);
+
+ // Return a unique sub-menu caption inspired by @p caption
+ QString uniqueMenuCaption(const QString &caption);
+
+ // Return a unique item caption inspired by @p caption but different
+ // from @p exclude
+ QString uniqueItemCaption(const QString &caption, const QString &exclude = QString::null);
+
+ // Update full id's for this item and all submenus
+ void updateFullId(const QString &parentId);
+
+ // Return a list of existing submenu ids
+ QStringList existingMenuIds();
+
+ void setCaption(const QString &_caption)
+ {
+ if (_caption == caption) return;
+ caption = _caption;
+ setDirty();
+ }
+
+ void setIcon(const QString &_icon)
+ {
+ if (_icon == icon) return;
+ icon = _icon;
+ setDirty();
+ }
+
+ void setGenericName(const QString &_description)
+ {
+ if (_description == genericname) return;
+ genericname = _description;
+ setDirty();
+ }
+
+ void setComment(const QString &_comment)
+ {
+ if (_comment == comment) return;
+ comment = _comment;
+ setDirty();
+ }
+
+ // Mark menu as dirty
+ void setDirty();
+
+ // Return whether this menu or any entry or submenu contained in it is dirty.
+ bool hasDirt();
+
+ // Return whether this menu should be explicitly added to its parent menu
+ bool needInsertion();
+
+ // Save menu and all its entries and submenus
+ void save(MenuFile *);
+
+ // Search service by shortcut
+ KService::Ptr findServiceShortcut(const KShortcut&);
+
+ // Set whether the entry is in active use (as opposed to in the clipboard/deleted)
+ void setInUse(bool inUse);
+
+public:
+ QString id; // Relative to parent
+ QString fullId; // Name in tree
+ QString caption; // Visible name
+ QString genericname; // Generic description
+ QString comment; // Comment
+ QString directoryFile; // File describing this folder.
+ QString icon; // Icon
+ QPtrList<MenuFolderInfo> subFolders; // Sub menus in this folder
+ QPtrList<MenuEntryInfo> entries; // Menu entries in this folder
+ QPtrList<MenuInfo> initialLayout; // Layout of menu entries according to sycoca
+ bool dirty;
+ bool hidden;
+};
+
+class MenuEntryInfo : public MenuInfo
+{
+public:
+ MenuEntryInfo(const KService::Ptr &_service, KDesktopFile *_df = 0)
+ : service(_service), df(_df),
+ shortcutLoaded(false), shortcutDirty(false), dirty(_df != 0), hidden(false)
+ {
+ caption = service->name();
+ description = service->genericName();
+ icon = service->icon();
+ }
+ ~MenuEntryInfo();
+
+ void setCaption(const QString &_caption);
+ void setDescription(const QString &_description);
+ void setIcon(const QString &_icon);
+
+ QString menuId() const { return service->menuId(); }
+
+ QString file() const { return service->desktopEntryPath(); }
+
+ KShortcut shortcut();
+ void setShortcut(const KShortcut &_shortcut);
+ bool isShortcutAvailable(const KShortcut &_shortcut);
+
+ void setDirty();
+
+ // Set whether the entry is in active use (as opposed to in the clipboard/deleted)
+ void setInUse(bool inUse);
+
+ // Return whether this menu should be explicitly added to its parent menu
+ bool needInsertion();
+
+ void save();
+
+ KDesktopFile *desktopFile();
+
+public:
+ QString caption;
+ QString description;
+ QString icon;
+ KService::Ptr service;
+ KDesktopFile *df;
+ KShortcut shortCut;
+ bool shortcutLoaded;
+ bool shortcutDirty;
+ bool dirty;
+ bool hidden;
+};
+
+#endif
diff --git a/kmenuedit/pixmaps/Makefile.am b/kmenuedit/pixmaps/Makefile.am
new file mode 100644
index 000000000..1e138f9db
--- /dev/null
+++ b/kmenuedit/pixmaps/Makefile.am
@@ -0,0 +1,5 @@
+kmenuediticondir = $(kde_datadir)/kmenuedit/icons
+kmenuediticon_ICON = AUTO
+
+kcontrolediticondir = $(kde_datadir)/kcontroledit/icons
+kcontrolediticon_ICON = AUTO
diff --git a/kmenuedit/pixmaps/cr22-action-menu_new.png b/kmenuedit/pixmaps/cr22-action-menu_new.png
new file mode 100644
index 000000000..56613f41c
--- /dev/null
+++ b/kmenuedit/pixmaps/cr22-action-menu_new.png
Binary files differ
diff --git a/kmenuedit/pixmaps/cr22-action-menu_new_sep.png b/kmenuedit/pixmaps/cr22-action-menu_new_sep.png
new file mode 100644
index 000000000..bff0ce6d0
--- /dev/null
+++ b/kmenuedit/pixmaps/cr22-action-menu_new_sep.png
Binary files differ
diff --git a/kmenuedit/pixmaps/cr32-action-menu_new.png b/kmenuedit/pixmaps/cr32-action-menu_new.png
new file mode 100644
index 000000000..d1e532be1
--- /dev/null
+++ b/kmenuedit/pixmaps/cr32-action-menu_new.png
Binary files differ
diff --git a/kmenuedit/pixmaps/cr32-action-menu_new_sep.png b/kmenuedit/pixmaps/cr32-action-menu_new_sep.png
new file mode 100644
index 000000000..ff02e1d2b
--- /dev/null
+++ b/kmenuedit/pixmaps/cr32-action-menu_new_sep.png
Binary files differ
diff --git a/kmenuedit/pixmaps/lo16-action-menu_new.png b/kmenuedit/pixmaps/lo16-action-menu_new.png
new file mode 100644
index 000000000..b121605f7
--- /dev/null
+++ b/kmenuedit/pixmaps/lo16-action-menu_new.png
Binary files differ
diff --git a/kmenuedit/treeview.cpp b/kmenuedit/treeview.cpp
new file mode 100644
index 000000000..760c052fe
--- /dev/null
+++ b/kmenuedit/treeview.cpp
@@ -0,0 +1,1581 @@
+/*
+ * 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>
+ *
+ * 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 <unistd.h>
+
+#include <qcstring.h>
+#include <qcursor.h>
+#include <qdatastream.h>
+#include <qdir.h>
+#include <qdragobject.h>
+#include <qfileinfo.h>
+#include <qheader.h>
+#include <qpainter.h>
+#include <qpopupmenu.h>
+#include <qregexp.h>
+#include <qstringlist.h>
+
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kinputdialog.h>
+#include <klocale.h>
+#include <ksimpleconfig.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <kdesktopfile.h>
+#include <kaction.h>
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <kservice.h>
+#include <kservicegroup.h>
+#include <kmultipledrag.h>
+#include <kurldrag.h>
+
+#include "treeview.h"
+#include "treeview.moc"
+#include "khotkeys.h"
+#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'
+
+TreeItem::TreeItem(QListViewItem *parent, QListViewItem *after, const QString& menuId, bool __init)
+ :QListViewItem(parent, after), _hidden(false), _init(__init), _layoutDirty(false), _menuId(menuId),
+ m_folderInfo(0), m_entryInfo(0) {}
+
+TreeItem::TreeItem(QListView *parent, QListViewItem *after, const QString& menuId, bool __init)
+ : QListViewItem(parent, after), _hidden(false), _init(__init), _layoutDirty(false), _menuId(menuId),
+ m_folderInfo(0), m_entryInfo(0) {}
+
+void TreeItem::setName(const QString &name)
+{
+ _name = name;
+ update();
+}
+
+void TreeItem::setHidden(bool b)
+{
+ if (_hidden == b) return;
+ _hidden = b;
+ update();
+}
+
+void TreeItem::update()
+{
+ QString s = _name;
+ if (_hidden)
+ s += i18n(" [Hidden]");
+ setText(0, s);
+}
+
+void TreeItem::setOpen(bool o)
+{
+ if (o)
+ load();
+
+ QListViewItem::setOpen(o);
+}
+
+void TreeItem::load()
+{
+ if (m_folderInfo && !_init)
+ {
+ _init = true;
+ TreeView *tv = static_cast<TreeView *>(listView());
+ tv->fillBranch(m_folderInfo, this);
+ }
+}
+
+void TreeItem::paintCell ( QPainter * p, const QColorGroup & cg, int column, int width, int align )
+{
+ QListViewItem::paintCell(p, cg, column, width, align);
+
+ if (!m_folderInfo && !m_entryInfo)
+ {
+ // Draw Separator
+ int h = (height() / 2) -1;
+ if (isSelected())
+ p->setPen( cg.highlightedText() );
+ else
+ p->setPen( cg.text() );
+ p->drawLine(0, h,
+ width, h);
+ }
+}
+
+void TreeItem::setup()
+{
+ QListViewItem::setup();
+ if (!m_folderInfo && !m_entryInfo)
+ setHeight(8);
+}
+
+static QPixmap appIcon(const QString &iconName)
+{
+ QPixmap normal = KGlobal::iconLoader()->loadIcon(iconName, KIcon::Small, 0, KIcon::DefaultState, 0L, true);
+ // make sure they are not larger than 20x20
+ if (normal.width() > 20 || normal.height() > 20)
+ {
+ QImage tmp = normal.convertToImage();
+ tmp = tmp.smoothScale(20, 20);
+ normal.convertFromImage(tmp);
+ }
+ return normal;
+}
+
+
+TreeView::TreeView( bool controlCenter, KActionCollection *ac, QWidget *parent, const char *name )
+ : KListView(parent, name), m_ac(ac), m_rmb(0), m_clipboard(0),
+ m_clipboardFolderInfo(0), m_clipboardEntryInfo(0),
+ m_controlCenter(controlCenter), m_layoutDirty(false)
+{
+ setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
+ setAllColumnsShowFocus(true);
+ setRootIsDecorated(true);
+ setSorting(-1);
+ setAcceptDrops(true);
+ setDropVisualizer(true);
+ setDragEnabled(true);
+ setMinimumWidth(240);
+
+ addColumn("");
+ header()->hide();
+
+ connect(this, SIGNAL(dropped(QDropEvent*, QListViewItem*, QListViewItem*)),
+ SLOT(slotDropped(QDropEvent*, QListViewItem*, QListViewItem*)));
+
+ connect(this, SIGNAL(clicked( QListViewItem* )),
+ SLOT(itemSelected( QListViewItem* )));
+
+ connect(this,SIGNAL(selectionChanged ( QListViewItem * )),
+ SLOT(itemSelected( QListViewItem* )));
+
+ connect(this, SIGNAL(rightButtonPressed(QListViewItem*, const QPoint&, int)),
+ SLOT(slotRMBPressed(QListViewItem*, const QPoint&)));
+
+ // connect actions
+ connect(m_ac->action("newitem"), SIGNAL(activated()), SLOT(newitem()));
+ connect(m_ac->action("newsubmenu"), SIGNAL(activated()), SLOT(newsubmenu()));
+ if (m_ac->action("newsep"))
+ connect(m_ac->action("newsep"), SIGNAL(activated()), SLOT(newsep()));
+
+ m_menuFile = new MenuFile( locateLocal("xdgconf-menu", "applications-kmenuedit.menu"));
+ m_rootFolder = new MenuFolderInfo;
+ m_separator = new MenuSeparatorInfo;
+ m_drag = 0;
+
+ // Read menu format configuration information
+ KSharedConfig::Ptr pConfig = KSharedConfig::openConfig("kickerrc");
+
+ pConfig->setGroup("menus");
+ m_detailedMenuEntries = pConfig->readBoolEntry("DetailedMenuEntries",true);
+ if (m_detailedMenuEntries)
+ {
+ m_detailedEntriesNamesFirst = pConfig->readBoolEntry("DetailedEntriesNamesFirst",false);
+ }
+}
+
+TreeView::~TreeView() {
+ cleanupClipboard();
+ delete m_rootFolder;
+ delete m_separator;
+}
+
+void TreeView::setViewMode(bool showHidden)
+{
+ delete m_rmb;
+
+ // setup rmb menu
+ m_rmb = new QPopupMenu(this);
+ KAction *action;
+
+ action = m_ac->action("edit_cut");
+ if(action) {
+ action->plug(m_rmb);
+ action->setEnabled(false);
+ connect(action, SIGNAL(activated()), SLOT(cut()));
+ }
+
+ action = m_ac->action("edit_copy");
+ if(action) {
+ action->plug(m_rmb);
+ action->setEnabled(false);
+ connect(action, SIGNAL(activated()), SLOT(copy()));
+ }
+
+ action = m_ac->action("edit_paste");
+ if(action) {
+ action->plug(m_rmb);
+ action->setEnabled(false);
+ connect(action, SIGNAL(activated()), SLOT(paste()));
+ }
+
+ m_rmb->insertSeparator();
+
+ action = m_ac->action("delete");
+ if(action) {
+ action->plug(m_rmb);
+ action->setEnabled(false);
+ connect(action, SIGNAL(activated()), SLOT(del()));
+ }
+
+ m_rmb->insertSeparator();
+
+ if(m_ac->action("newitem"))
+ m_ac->action("newitem")->plug(m_rmb);
+ if(m_ac->action("newsubmenu"))
+ m_ac->action("newsubmenu")->plug(m_rmb);
+ if(m_ac->action("newsep"))
+ m_ac->action("newsep")->plug(m_rmb);
+
+ m_showHidden = showHidden;
+ readMenuFolderInfo();
+ fill();
+}
+
+void TreeView::readMenuFolderInfo(MenuFolderInfo *folderInfo, KServiceGroup::Ptr folder, const QString &prefix)
+{
+ if (!folderInfo)
+ {
+ folderInfo = m_rootFolder;
+ if (m_controlCenter)
+ folder = KServiceGroup::baseGroup("settings");
+ else
+ 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.findRev('/', -2);
+ id = id.mid(i+1);
+ folderInfo->id = id;
+ folderInfo->fullId = prefix + id;
+
+ KServiceGroup::List list = folder->entries(true, !m_showHidden, true, m_detailedMenuEntries && !m_detailedEntriesNamesFirst);
+
+ for(KServiceGroup::List::ConstIterator it = list.begin();
+ it != list.end(); ++it)
+ {
+ KSycocaEntry * e = *it;
+
+ if (e->isType(KST_KServiceGroup))
+ {
+ KServiceGroup::Ptr g(static_cast<KServiceGroup *>(e));
+ MenuFolderInfo *subFolderInfo = new MenuFolderInfo();
+ readMenuFolderInfo(subFolderInfo, g, folderInfo->fullId);
+ folderInfo->add(subFolderInfo, true);
+ }
+ else if (e->isType(KST_KService))
+ {
+ folderInfo->add(new MenuEntryInfo(static_cast<KService *>(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 = QString::null;
+ if (name.isEmpty())
+ {
+ QString file = df->fileName();
+ QString res = df->resource();
+
+ bool isLocal = true;
+ QStringList files = KGlobal::dirs()->findAllResources(res.latin1(), file);
+ for(QStringList::ConstIterator it = files.begin();
+ it != files.end();
+ ++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, QListViewItem *after, MenuFolderInfo *folderInfo, bool _init)
+{
+ TreeItem *item;
+ if (parent == 0)
+ item = new TreeItem(this, after, QString::null, _init);
+ else
+ item = new TreeItem(parent, after, QString::null, _init);
+
+ item->setMenuFolderInfo(folderInfo);
+ item->setName(folderInfo->caption);
+ item->setPixmap(0, appIcon(folderInfo->icon));
+ item->setDirectoryPath(folderInfo->fullId);
+ item->setHidden(folderInfo->hidden);
+ item->setExpandable(true);
+ return item;
+}
+
+TreeItem *TreeView::createTreeItem(TreeItem *parent, QListViewItem *after, MenuEntryInfo *entryInfo, bool _init)
+{
+ bool hidden = entryInfo->hidden;
+
+ TreeItem* item;
+ if (parent == 0)
+ item = new TreeItem(this, after, entryInfo->menuId(), _init);
+ else
+ item = new TreeItem(parent, after, entryInfo->menuId(),_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;
+ }
+ item->setMenuEntryInfo(entryInfo);
+ item->setName(name);
+ item->setPixmap(0, appIcon(entryInfo->icon));
+
+ item->setHidden(hidden);
+ return item;
+}
+
+TreeItem *TreeView::createTreeItem(TreeItem *parent, QListViewItem *after, MenuSeparatorInfo *, bool _init)
+{
+ TreeItem* item;
+ if (parent == 0)
+ item = new TreeItem(this, after, QString::null, _init);
+ else
+ item = new TreeItem(parent, after, QString::null,_init);
+
+ return item;
+}
+
+void TreeView::fillBranch(MenuFolderInfo *folderInfo, TreeItem *parent)
+{
+ QString relPath = parent ? parent->directory() : QString::null;
+ QPtrListIterator<MenuInfo> it( folderInfo->initialLayout );
+ TreeItem *after = 0;
+ for (MenuInfo *info; (info = it.current()); ++it)
+ {
+ 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(QListViewItem *item)
+{
+ if (!item) return;
+ while(item)
+ {
+ item->setOpen(false);
+ closeAllItems(item->firstChild());
+ item = item->nextSibling();
+ }
+}
+
+void TreeView::selectMenu(const QString &menu)
+{
+ closeAllItems(firstChild());
+
+ if (menu.length() <= 1)
+ {
+ setCurrentItem(firstChild());
+ clearSelection();
+ return; // Root menu
+ }
+
+ QString restMenu = menu.mid(1);
+ if (!restMenu.endsWith("/"))
+ restMenu += "/";
+
+ TreeItem *item = 0;
+ do
+ {
+ int i = restMenu.find("/");
+ QString subMenu = restMenu.left(i+1);
+ restMenu = restMenu.mid(i+1);
+
+ item = (TreeItem*)(item ? item->firstChild() : firstChild());
+ while(item)
+ {
+ MenuFolderInfo *folderInfo = item->folderInfo();
+ if (folderInfo && (folderInfo->id == subMenu))
+ {
+ item->setOpen(true);
+ break;
+ }
+ item = (TreeItem*) item->nextSibling();
+ }
+ }
+ while( item && !restMenu.isEmpty());
+
+ if (item)
+ {
+ setCurrentItem(item);
+ ensureItemVisible(item);
+ }
+}
+
+void TreeView::selectMenuEntry(const QString &menuEntry)
+{
+ TreeItem *item = (TreeItem *) selectedItem();
+ if (!item)
+ {
+ item = (TreeItem *) currentItem();
+ while (item && item->isDirectory())
+ item = (TreeItem*) item->nextSibling();
+ }
+ else
+ item = (TreeItem *) item->firstChild();
+
+ while(item)
+ {
+ MenuEntryInfo *entry = item->entryInfo();
+ if (entry && (entry->menuId() == menuEntry))
+ {
+ setCurrentItem(item);
+ ensureItemVisible(item);
+ return;
+ }
+ item = (TreeItem*) item->nextSibling();
+ }
+}
+
+void TreeView::itemSelected(QListViewItem *item)
+{
+ TreeItem *_item = (TreeItem*)item;
+ bool selected = false;
+ bool dselected = false;
+ if (_item) {
+ selected = true;
+ dselected = _item->isHidden();
+ }
+
+ 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);
+
+ if(!item)
+ {
+ emit disableAction();
+ return;
+ }
+
+ if (_item->isDirectory())
+ emit entrySelected(_item->folderInfo());
+ else
+ emit entrySelected(_item->entryInfo());
+}
+
+void TreeView::currentChanged(MenuFolderInfo *folderInfo)
+{
+ TreeItem *item = (TreeItem*)selectedItem();
+ if (item == 0) return;
+ if (folderInfo == 0) return;
+
+ item->setName(folderInfo->caption);
+ item->setPixmap(0, appIcon(folderInfo->icon));
+}
+
+void TreeView::currentChanged(MenuEntryInfo *entryInfo)
+{
+ TreeItem *item = (TreeItem*)selectedItem();
+ if (item == 0) return;
+ if (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->setPixmap(0, appIcon(entryInfo->icon));
+}
+
+QStringList TreeView::fileList(const QString& rPath)
+{
+ QString relativePath = rPath;
+
+ // truncate "/.directory"
+ int pos = relativePath.findRev("/.directory");
+ if (pos > 0) relativePath.truncate(pos);
+
+ QStringList filelist;
+
+ // loop through all resource dirs and build a file list
+ QStringList resdirlist = KGlobal::dirs()->resourceDirs("apps");
+ for (QStringList::ConstIterator it = resdirlist.begin(); it != resdirlist.end(); ++it)
+ {
+ QDir dir((*it) + "/" + relativePath);
+ if(!dir.exists()) continue;
+
+ dir.setFilter(QDir::Files);
+ dir.setNameFilter("*.desktop;*.kdelnk");
+
+ // build a list of files
+ QStringList files = dir.entryList();
+ for (QStringList::ConstIterator it = files.begin(); it != files.end(); ++it) {
+ // does not work?!
+ //if (filelist.contains(*it)) continue;
+
+ if (relativePath.isEmpty()) {
+ filelist.remove(*it); // hack
+ filelist.append(*it);
+ }
+ else {
+ filelist.remove(relativePath + "/" + *it); //hack
+ filelist.append(relativePath + "/" + *it);
+ }
+ }
+ }
+ return filelist;
+}
+
+QStringList TreeView::dirList(const QString& rPath)
+{
+ QString relativePath = rPath;
+
+ // truncate "/.directory"
+ int pos = relativePath.findRev("/.directory");
+ if (pos > 0) relativePath.truncate(pos);
+
+ QStringList dirlist;
+
+ // loop through all resource dirs and build a subdir list
+ QStringList resdirlist = KGlobal::dirs()->resourceDirs("apps");
+ for (QStringList::ConstIterator it = resdirlist.begin(); it != resdirlist.end(); ++it)
+ {
+ QDir dir((*it) + "/" + relativePath);
+ if(!dir.exists()) continue;
+ dir.setFilter(QDir::Dirs);
+
+ // build a list of subdirs
+ QStringList subdirs = dir.entryList();
+ for (QStringList::ConstIterator it = subdirs.begin(); it != subdirs.end(); ++it) {
+ if ((*it) == "." || (*it) == "..") continue;
+ // does not work?!
+ // if (dirlist.contains(*it)) continue;
+
+ if (relativePath.isEmpty()) {
+ dirlist.remove(*it); //hack
+ dirlist.append(*it);
+ }
+ else {
+ dirlist.remove(relativePath + "/" + *it); //hack
+ dirlist.append(relativePath + "/" + *it);
+ }
+ }
+ }
+ return dirlist;
+}
+
+bool TreeView::acceptDrag(QDropEvent* e) const
+{
+ if (e->provides("application/x-kmenuedit-internal") &&
+ (e->source() == const_cast<TreeView *>(this)))
+ return true;
+ KURL::List urls;
+ if (KURLDrag::decode(e, urls) && (urls.count() == 1) &&
+ urls[0].isLocalFile() && urls[0].path().endsWith(".desktop"))
+ return true;
+ return false;
+}
+
+
+static QString createDesktopFile(const QString &file, QString *menuId, QStringList *excludeList)
+{
+ QString base = file.mid(file.findRev('/')+1);
+ base = base.left(base.findRev('.'));
+
+ QRegExp r("(.*)(?=-\\d+)");
+ base = (r.search(base) > -1) ? r.cap(1) : base;
+
+ QString result = KService::newServicePath(true, 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->deleteEntry("Categories"); // Don't set any categories!
+
+ return df;
+}
+
+static QString createDirectoryFile(const QString &file, QStringList *excludeList)
+{
+ QString base = file.mid(file.findRev('/')+1);
+ base = base.left(base.findRev('.'));
+
+ 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 (locate("xdgdata-dirs", result).isEmpty())
+ break;
+ }
+ i++;
+ }
+ excludeList->append(result);
+ result = locateLocal("xdgdata-dirs", result);
+ return result;
+}
+
+
+void TreeView::slotDropped (QDropEvent * e, QListViewItem *parent, QListViewItem*after)
+{
+ if(!e) return;
+
+ // get destination folder
+ TreeItem *parentItem = static_cast<TreeItem*>(parent);
+ QString folder = parentItem ? parentItem->directory() : QString::null;
+ MenuFolderInfo *parentFolderInfo = parentItem ? parentItem->folderInfo() : m_rootFolder;
+
+ if (e->source() != this)
+ {
+ // External drop
+ KURL::List urls;
+ if (!KURLDrag::decode(e, urls) || (urls.count() != 1) || !urls[0].isLocalFile())
+ return;
+ QString path = urls[0].path();
+ if (!path.endsWith(".desktop"))
+ return;
+
+ QString menuId;
+ QString result = createDesktopFile(path, &menuId, &m_newMenuIds);
+ KDesktopFile orig_df(path);
+ KDesktopFile *df = orig_df.copyTo(result);
+ df->deleteEntry("Categories"); // Don't set any categories!
+
+ KService *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->setOpen(true);
+
+ // update fileInfo data
+ parentFolderInfo->add(entryInfo);
+
+ TreeItem *newItem = createTreeItem(parentItem, after, entryInfo, true);
+
+ setSelected ( newItem, true);
+ itemSelected( newItem);
+
+ m_drag = 0;
+ setLayoutDirty(parentItem);
+ return;
+ }
+
+ // is there content in the clipboard?
+ if (!m_drag) return;
+
+ if (m_dragItem == after) return; // Nothing to do
+
+ int command = m_drag;
+ if (command == MOVE_FOLDER)
+ {
+ MenuFolderInfo *folderInfo = m_dragInfo;
+ if (e->action() == QDropEvent::Copy)
+ {
+ // Ugh.. this is hard :)
+ // * Create new .directory file
+ // Add
+ }
+ else
+ {
+ TreeItem *tmpItem = static_cast<TreeItem*>(parentItem);
+ while ( tmpItem )
+ {
+ if ( tmpItem == m_dragItem )
+ {
+ m_drag = 0;
+ return;
+ }
+ tmpItem = static_cast<TreeItem*>(tmpItem->parent() );
+ }
+
+ // Remove MenuFolderInfo
+ TreeItem *oldParentItem = static_cast<TreeItem*>(m_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);
+ 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->setOpen(true);
+
+ // update fileInfo data
+ folderInfo->updateFullId(parentFolderInfo->fullId);
+ folderInfo->setInUse(true);
+ parentFolderInfo->add(folderInfo);
+
+ if ((parentItem != oldParentItem) || !after)
+ {
+ if (oldParentItem)
+ oldParentItem->takeItem(m_dragItem);
+ else
+ takeItem(m_dragItem);
+ if (parentItem)
+ parentItem->insertItem(m_dragItem);
+ else
+ insertItem(m_dragItem);
+ }
+ m_dragItem->moveItem(after);
+ m_dragItem->setName(folderInfo->caption);
+ m_dragItem->setDirectoryPath(folderInfo->fullId);
+ setSelected(m_dragItem, true);
+ itemSelected(m_dragItem);
+ }
+ }
+ else if (command == MOVE_FILE)
+ {
+ MenuEntryInfo *entryInfo = m_dragItem->entryInfo();
+ QString menuId = entryInfo->menuId();
+
+ if (e->action() == QDropEvent::Copy)
+ {
+
+ // Need to copy file and then add it
+ KDesktopFile *df = copyDesktopFile(entryInfo, &menuId, &m_newMenuIds); // Duplicate
+//UNDO-ACTION: NEW_MENU_ID (menuId)
+
+ KService *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(m_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->setOpen(true);
+
+ // update fileInfo data
+ parentFolderInfo->add(entryInfo);
+
+ TreeItem *newItem = createTreeItem(parentItem, after, entryInfo, true);
+
+ setSelected ( newItem, true);
+ itemSelected( newItem);
+ }
+ else if (command == COPY_SEPARATOR)
+ {
+ if (e->action() != QDropEvent::Copy)
+ del(m_dragItem, false);
+
+ TreeItem *newItem = createTreeItem(parentItem, after, m_separator, true);
+
+ setSelected ( newItem, true);
+ itemSelected( newItem);
+ }
+ else
+ {
+ // Error
+ }
+ m_drag = 0;
+ setLayoutDirty(parentItem);
+}
+
+
+void TreeView::startDrag()
+{
+ QDragObject *drag = dragObject();
+
+ if (!drag)
+ return;
+
+ drag->dragMove();
+}
+
+QDragObject *TreeView::dragObject()
+{
+ m_dragPath = QString::null;
+ TreeItem *item = (TreeItem*)selectedItem();
+ if(item == 0) return 0;
+
+ KMultipleDrag *drag = new KMultipleDrag( this );
+
+ if (item->isDirectory())
+ {
+ m_drag = MOVE_FOLDER;
+ m_dragInfo = item->folderInfo();
+ m_dragItem = item;
+ }
+ else if (item->isEntry())
+ {
+ m_drag = MOVE_FILE;
+ m_dragInfo = 0;
+ m_dragItem = item;
+ QString menuId = item->menuId();
+ m_dragPath = item->entryInfo()->service->desktopEntryPath();
+ if (!m_dragPath.isEmpty())
+ m_dragPath = locate("apps", m_dragPath);
+ if (!m_dragPath.isEmpty())
+ {
+ KURL url;
+ url.setPath(m_dragPath);
+ drag->addDragObject( new KURLDrag(url, 0));
+ }
+ }
+ else
+ {
+ m_drag = COPY_SEPARATOR;
+ m_dragInfo = 0;
+ m_dragItem = item;
+ }
+
+ drag->addDragObject( new QStoredDrag("application/x-kmenuedit-internal", 0));
+ if ( item->pixmap(0) )
+ drag->setPixmap(*item->pixmap(0));
+ return drag;
+}
+
+void TreeView::slotRMBPressed(QListViewItem*, const QPoint& p)
+{
+ TreeItem *item = (TreeItem*)selectedItem();
+ if(item == 0) return;
+
+ if(m_rmb) m_rmb->exec(p);
+}
+
+void TreeView::newsubmenu()
+{
+ TreeItem *parentItem = 0;
+ TreeItem *item = (TreeItem*)selectedItem();
+
+ bool ok;
+ QString caption = KInputDialog::getText( i18n( "New Submenu" ),
+ i18n( "Submenu name:" ), QString::null, &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 = QString::null;
+ }
+ else if(item->isDirectory())
+ {
+ parentItem = item;
+ item = 0;
+ folder = parentItem->directory();
+ }
+ else
+ {
+ parentItem = static_cast<TreeItem*>(item->parent());
+ folder = parentItem ? parentItem->directory() : QString::null;
+ }
+
+ 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);
+ df->writeEntry("Name", folderInfo->caption);
+ df->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->setOpen(true);
+
+ // update fileInfo data
+ parentFolderInfo->add(folderInfo);
+
+ TreeItem *newItem = createTreeItem(parentItem, item, folderInfo, true);
+
+ setSelected ( newItem, true);
+ itemSelected( 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::null, &ok, this );
+
+ if (!ok) return;
+
+ QString menuId;
+ QString file = caption;
+ file.replace('/', '-');
+
+ file = createDesktopFile(file, &menuId, &m_newMenuIds); // Create
+
+ KDesktopFile *df = new KDesktopFile(file);
+ df->writeEntry("Name", caption);
+ df->writeEntry("Type", "Application");
+
+ // get destination folder
+ QString folder;
+
+ if(!item)
+ {
+ parentItem = 0;
+ folder = QString::null;
+ }
+ else if(item->isDirectory())
+ {
+ parentItem = item;
+ item = 0;
+ folder = parentItem->directory();
+ }
+ else
+ {
+ parentItem = static_cast<TreeItem*>(item->parent());
+ folder = parentItem ? parentItem->directory() : QString::null;
+ }
+
+ 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 *s = new KService(df);
+ s->setMenuId(menuId);
+
+ MenuEntryInfo *entryInfo = new MenuEntryInfo(s, df);
+
+ // create the TreeItem
+ if(parentItem)
+ parentItem->setOpen(true);
+
+ // update fileInfo data
+ parentFolderInfo->add(entryInfo);
+
+ TreeItem *newItem = createTreeItem(parentItem, item, entryInfo, true);
+
+ setSelected ( newItem, true);
+ itemSelected( 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->setOpen(true);
+
+ TreeItem *newItem = createTreeItem(parentItem, item, m_separator, true);
+
+ setSelected ( newItem, true);
+ itemSelected( newItem);
+
+ setLayoutDirty(parentItem);
+}
+
+void TreeView::cut()
+{
+ copy( true );
+
+ m_ac->action("edit_cut")->setEnabled(false);
+ m_ac->action("edit_copy")->setEnabled(false);
+ m_ac->action("delete")->setEnabled(false);
+
+ // Select new current item
+ setSelected( currentItem(), true );
+ // Switch the UI to show that item
+ itemSelected( selectedItem() );
+}
+
+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("edit_paste")->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::null;
+ }
+
+ 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->setOpen(true);
+
+ // update fileInfo data
+ folderInfo->fullId = parentFolderInfo->fullId + folderInfo->id;
+ folderInfo->setInUse(true);
+ parentFolderInfo->add(folderInfo);
+
+ TreeItem *newItem = createTreeItem(parentItem, item, folderInfo);
+
+ setSelected ( newItem, true);
+ itemSelected( 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 *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->setOpen(true);
+
+ // update fileInfo data
+ parentFolderInfo->add(entryInfo);
+
+ TreeItem *newItem = createTreeItem(parentItem, item, entryInfo, true);
+
+ setSelected ( newItem, true);
+ itemSelected( newItem);
+ }
+ else
+ {
+ // create separator
+ if(parentItem)
+ parentItem->setOpen(true);
+
+ TreeItem *newItem = createTreeItem(parentItem, item, m_separator, true);
+
+ setSelected ( newItem, true);
+ itemSelected( newItem);
+ }
+ setLayoutDirty(parentItem);
+}
+
+void TreeView::del()
+{
+ TreeItem *item = (TreeItem*)selectedItem();
+
+ // nil selected? -> nil to delete
+ if (item == 0) return;
+
+ del(item, true);
+
+ m_ac->action("edit_cut")->setEnabled(false);
+ m_ac->action("edit_copy")->setEnabled(false);
+ m_ac->action("delete")->setEnabled(false);
+ // Select new current item
+ setSelected( currentItem(), true );
+ // Switch the UI to show that item
+ itemSelected( selectedItem() );
+}
+
+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())
+ {
+ 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::null);
+
+ // 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::null;
+ // 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(TreeItem *item)
+{
+ bool firstFolder = true;
+ bool firstEntry = true;
+ QStringList layout;
+ for(;item; item = static_cast<TreeItem*>(item->nextSibling()))
+ {
+ 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;
+}
+
+QStringList TreeItem::layout()
+{
+ QStringList layout = extractLayout(static_cast<TreeItem*>(firstChild()));
+ _layoutDirty = false;
+ return layout;
+}
+
+void TreeView::saveLayout()
+{
+ if (m_layoutDirty)
+ {
+ QStringList layout = extractLayout(static_cast<TreeItem*>(firstChild()));
+ m_menuFile->setLayout(m_rootFolder->fullId, layout);
+ m_layoutDirty = false;
+ }
+
+ QPtrList<QListViewItem> lst;
+ QListViewItemIterator it( this );
+ while ( it.current() ) {
+ TreeItem *item = static_cast<TreeItem*>(it.current());
+ if ( item->isLayoutDirty() )
+ {
+ m_menuFile->setLayout(item->folderInfo()->fullId, item->layout());
+ }
+ ++it;
+ }
+}
+
+bool TreeView::save()
+{
+ saveLayout();
+ m_rootFolder->save(m_menuFile);
+
+ bool success = m_menuFile->performAllActions();
+
+ m_newMenuIds.clear();
+ m_newDirectoryList.clear();
+
+ if (success)
+ {
+ KService::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>");
+ }
+ return success;
+}
+
+void TreeView::setLayoutDirty(TreeItem *parentItem)
+{
+ if (parentItem)
+ parentItem->setLayoutDirty();
+ else
+ m_layoutDirty = true;
+}
+
+bool TreeView::isLayoutDirty()
+{
+ QPtrList<QListViewItem> lst;
+ QListViewItemIterator it( this );
+ while ( it.current() ) {
+ if ( static_cast<TreeItem*>(it.current())->isLayoutDirty() )
+ return true;
+ ++it;
+ }
+ 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);
+}
+
diff --git a/kmenuedit/treeview.h b/kmenuedit/treeview.h
new file mode 100644
index 000000000..13d415c13
--- /dev/null
+++ b/kmenuedit/treeview.h
@@ -0,0 +1,179 @@
+/*
+ * 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>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __treeview_h__
+#define __treeview_h__
+
+#include <qstring.h>
+#include <klistview.h>
+#include <kservice.h>
+#include <kservicegroup.h>
+
+class QPopupMenu;
+class KActionCollection;
+class KDesktopFile;
+class MenuFile;
+class MenuFolderInfo;
+class MenuEntryInfo;
+class MenuSeparatorInfo;
+class KShortcut;
+
+class TreeItem : public QListViewItem
+{
+public:
+ TreeItem(QListViewItem *parent, QListViewItem *after, const QString &menuIdn, bool __init = false);
+ TreeItem(QListView *parent, QListViewItem* after, const QString &menuId, bool __init = false);
+
+ QString menuId() const { return _menuId; }
+
+ QString directory() const { return _directoryPath; }
+ void setDirectoryPath(const QString& path) { _directoryPath = path; }
+
+ MenuFolderInfo *folderInfo() { return m_folderInfo; }
+ void setMenuFolderInfo(MenuFolderInfo *folderInfo) { m_folderInfo = folderInfo; }
+
+ MenuEntryInfo *entryInfo() { return m_entryInfo; }
+ void setMenuEntryInfo(MenuEntryInfo *entryInfo) { m_entryInfo = entryInfo; }
+
+ QString name() const { return _name; }
+ void setName(const QString &name);
+
+ bool isDirectory() const { return m_folderInfo; }
+ bool isEntry() const { return m_entryInfo; }
+
+ bool isHidden() const { return _hidden; }
+ void setHidden(bool b);
+
+ bool isLayoutDirty() { return _layoutDirty; }
+ void setLayoutDirty() { _layoutDirty = true; }
+ QStringList layout();
+
+ virtual void setOpen(bool o);
+ void load();
+
+ virtual void paintCell(QPainter * p, const QColorGroup & cg, int column, int width, int align);
+ virtual void setup();
+
+private:
+ void update();
+
+ bool _hidden : 1;
+ bool _init : 1;
+ bool _layoutDirty : 1;
+ QString _menuId;
+ QString _name;
+ QString _directoryPath;
+ MenuFolderInfo *m_folderInfo;
+ MenuEntryInfo *m_entryInfo;
+};
+
+class TreeView : public KListView
+{
+ friend class TreeItem;
+ Q_OBJECT
+public:
+ TreeView(bool controlCenter, KActionCollection *ac, QWidget *parent=0, const char *name=0);
+ ~TreeView();
+
+ void readMenuFolderInfo(MenuFolderInfo *folderInfo=0, KServiceGroup::Ptr folder=0, const QString &prefix=QString::null);
+ void setViewMode(bool showHidden);
+ bool save();
+
+ bool dirty();
+
+ void selectMenu(const QString &menu);
+ void selectMenuEntry(const QString &menuEntry);
+
+public slots:
+ void currentChanged(MenuFolderInfo *folderInfo);
+ void currentChanged(MenuEntryInfo *entryInfo);
+ void findServiceShortcut(const KShortcut&, KService::Ptr &);
+
+signals:
+ void entrySelected(MenuFolderInfo *folderInfo);
+ void entrySelected(MenuEntryInfo *entryInfo);
+ void disableAction();
+protected slots:
+ void itemSelected(QListViewItem *);
+ void slotDropped(QDropEvent *, QListViewItem *, QListViewItem *);
+ void slotRMBPressed(QListViewItem*, const QPoint&);
+
+ void newsubmenu();
+ void newitem();
+ void newsep();
+
+ void cut();
+ void copy();
+ void paste();
+ void del();
+
+protected:
+ TreeItem *createTreeItem(TreeItem *parent, QListViewItem *after, MenuFolderInfo *folderInfo, bool _init = false);
+ TreeItem *createTreeItem(TreeItem *parent, QListViewItem *after, MenuEntryInfo *entryInfo, bool _init = false);
+ TreeItem *createTreeItem(TreeItem *parent, QListViewItem *after, MenuSeparatorInfo *sepInfo, bool _init = false);
+
+ void del(TreeItem *, bool deleteInfo);
+ void fill();
+ void fillBranch(MenuFolderInfo *folderInfo, TreeItem *parent);
+ QString findName(KDesktopFile *df, bool deleted);
+
+ void closeAllItems(QListViewItem *item);
+
+ // moving = src will be removed later
+ void copy( bool moving );
+
+ void cleanupClipboard();
+
+ bool isLayoutDirty();
+ void setLayoutDirty(TreeItem *);
+ void saveLayout();
+
+ QStringList fileList(const QString& relativePath);
+ QStringList dirList(const QString& relativePath);
+
+ virtual bool acceptDrag(QDropEvent* event) const;
+ virtual QDragObject *dragObject();
+ virtual void startDrag();
+
+private:
+ KActionCollection *m_ac;
+ QPopupMenu *m_rmb;
+ int m_clipboard;
+ MenuFolderInfo *m_clipboardFolderInfo;
+ MenuEntryInfo *m_clipboardEntryInfo;
+ int m_drag;
+ MenuFolderInfo *m_dragInfo;
+ TreeItem *m_dragItem;
+ QString m_dragPath;
+ bool m_showHidden;
+ bool m_controlCenter;
+ MenuFile *m_menuFile;
+ MenuFolderInfo *m_rootFolder;
+ MenuSeparatorInfo *m_separator;
+ QStringList m_newMenuIds;
+ QStringList m_newDirectoryList;
+ bool m_detailedMenuEntries;
+ bool m_detailedEntriesNamesFirst;
+ bool m_layoutDirty;
+};
+
+
+#endif
diff --git a/kmenuedit/uninstall.desktop b/kmenuedit/uninstall.desktop
new file mode 100644
index 000000000..e1e3e1732
--- /dev/null
+++ b/kmenuedit/uninstall.desktop
@@ -0,0 +1,2 @@
+[Desktop Entry]
+Hidden=true