diff options
Diffstat (limited to 'kmenuedit')
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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 Binary files differnew file mode 100644 index 000000000..b121605f7 --- /dev/null +++ b/kmenuedit/hi16-app-kmenuedit.png diff --git a/kmenuedit/hi22-app-kmenuedit.png b/kmenuedit/hi22-app-kmenuedit.png Binary files differnew file mode 100644 index 000000000..65509221a --- /dev/null +++ b/kmenuedit/hi22-app-kmenuedit.png diff --git a/kmenuedit/hi32-app-kmenuedit.png b/kmenuedit/hi32-app-kmenuedit.png Binary files differnew file mode 100644 index 000000000..d1e532be1 --- /dev/null +++ b/kmenuedit/hi32-app-kmenuedit.png diff --git a/kmenuedit/hi48-app-kmenuedit.png b/kmenuedit/hi48-app-kmenuedit.png Binary files differnew file mode 100644 index 000000000..e6b483a7f --- /dev/null +++ b/kmenuedit/hi48-app-kmenuedit.png 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 <[email protected]> + * Copyright (C) 2001-2002 Raffaele Sandrini <[email protected]> + * Copyright (C) 2004 Waldo Bastian <[email protected]> + * + * 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"), "[email protected]"); + aboutData.addAuthor("Raffaele Sandrini", I18N_NOOP("Previous Maintainer"), "[email protected]"); + aboutData.addAuthor("Matthias Elter", I18N_NOOP("Original Author"), "[email protected]"); + + 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>&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>&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 <[email protected]> + * Lubos Lunak <[email protected]> + * + * 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 <[email protected]> + * Lubos Lunak <[email protected]> + * + * 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 <[email protected]> + * Copyright (C) 2001-2002 Raffaele Sandrini <[email protected]) + * + * 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 <[email protected]> + * + * 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>&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>&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 <[email protected]> + * Copyright (C) 2001-2002 Raffaele Sandrini <[email protected]) + * + * 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"), "[email protected]"); + aboutData.addAuthor("Raffaele Sandrini", I18N_NOOP("Previous Maintainer"), "[email protected]"); + aboutData.addAuthor("Matthias Elter", I18N_NOOP("Original Author"), "[email protected]"); + + 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 Binary files differnew file mode 100644 index 000000000..56613f41c --- /dev/null +++ b/kmenuedit/pixmaps/cr22-action-menu_new.png diff --git a/kmenuedit/pixmaps/cr22-action-menu_new_sep.png b/kmenuedit/pixmaps/cr22-action-menu_new_sep.png Binary files differnew file mode 100644 index 000000000..bff0ce6d0 --- /dev/null +++ b/kmenuedit/pixmaps/cr22-action-menu_new_sep.png diff --git a/kmenuedit/pixmaps/cr32-action-menu_new.png b/kmenuedit/pixmaps/cr32-action-menu_new.png Binary files differnew file mode 100644 index 000000000..d1e532be1 --- /dev/null +++ b/kmenuedit/pixmaps/cr32-action-menu_new.png diff --git a/kmenuedit/pixmaps/cr32-action-menu_new_sep.png b/kmenuedit/pixmaps/cr32-action-menu_new_sep.png Binary files differnew file mode 100644 index 000000000..ff02e1d2b --- /dev/null +++ b/kmenuedit/pixmaps/cr32-action-menu_new_sep.png diff --git a/kmenuedit/pixmaps/lo16-action-menu_new.png b/kmenuedit/pixmaps/lo16-action-menu_new.png Binary files differnew file mode 100644 index 000000000..b121605f7 --- /dev/null +++ b/kmenuedit/pixmaps/lo16-action-menu_new.png 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 <[email protected]> + * Copyright (C) 2001-2002 Raffaele Sandrini <[email protected]) + * Copyright (C) 2003 Waldo Bastian <[email protected]> + * + * 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 <[email protected]> + * Copyright (C) 2001-2002 Raffaele Sandrini <[email protected]> + * Copyright (C) 2003 Waldo Bastian <[email protected]> + * + * 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 |