1 /*
2  *   SPDX-FileCopyrightText: 2000 Matthias Elter <elter@kde.org>
3  *   SPDX-FileCopyrightText: 2001-2002 Raffaele Sandrini <sandrini@kde.org>
4  *
5  *   SPDX-License-Identifier: GPL-2.0-or-later
6  *
7  */
8 #include "kmenuedit.h"
9 
10 #include <QFrame>
11 #include <QSplitter>
12 #include <QVBoxLayout>
13 
14 #include <KActionCollection>
15 #include <KActionMenu>
16 #include <KLocalizedString>
17 #include <KMessageBox>
18 #include <KService>
19 #include <KStandardAction>
20 #include <KStandardShortcut>
21 #include <KTreeWidgetSearchLine>
22 #include <KXMLGUIFactory>
23 #include <QAction>
24 #include <QIcon>
25 #include <sonnet/configdialog.h>
26 
27 #include "basictab.h"
28 #include "configurationmanager.h"
29 #include "kmenueditadaptor.h"
30 #include "preferencesdlg.h"
31 #include "treeview.h"
32 
KMenuEdit()33 KMenuEdit::KMenuEdit()
34     : KXmlGuiWindow(nullptr)
35 {
36     // dbus
37     (void)new KmenueditAdaptor(this);
38     QDBusConnection::sessionBus().registerObject(QStringLiteral("/KMenuEdit"), this);
39 
40     m_showHidden = ConfigurationManager::getInstance()->hiddenEntriesVisible();
41 
42     // setup GUI
43     setupActions();
44     slotChangeView();
45 }
46 
~KMenuEdit()47 KMenuEdit::~KMenuEdit()
48 {
49     ConfigurationManager::getInstance()->setSplitterSizes(m_splitter->sizes());
50 }
51 
setupActions()52 void KMenuEdit::setupActions()
53 {
54     QAction *action = actionCollection()->addAction(NEW_SUBMENU_ACTION_NAME);
55     action->setIcon(QIcon::fromTheme(QStringLiteral("menu_new")));
56     action->setText(i18n("&New Submenu..."));
57     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_N));
58     action = actionCollection()->addAction(NEW_ITEM_ACTION_NAME);
59     action->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
60     action->setText(i18n("New &Item..."));
61     actionCollection()->setDefaultShortcuts(action, KStandardShortcut::openNew());
62     action = actionCollection()->addAction(NEW_SEPARATOR_ACTION_NAME);
63     action->setIcon(QIcon::fromTheme(QStringLiteral("menu_new_sep")));
64     action->setText(i18n("New S&eparator"));
65     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_I));
66 
67     // "sort selection" menu
68     KActionMenu *sortMenu = new KActionMenu(QIcon::fromTheme(QStringLiteral("view-sort-ascending")), i18n("&Sort"), this);
69     sortMenu->setPopupMode(QToolButton::InstantPopup);
70     actionCollection()->addAction(SORT_ACTION_NAME, sortMenu);
71     action = actionCollection()->addAction(SORT_BY_NAME_ACTION_NAME);
72     action->setText(i18n("&Sort selection by Name"));
73     sortMenu->addAction(action);
74     action = actionCollection()->addAction(SORT_BY_DESCRIPTION_ACTION_NAME);
75     action->setText(i18n("&Sort selection by Description"));
76     sortMenu->addAction(action);
77     sortMenu->addSeparator();
78     action = actionCollection()->addAction(SORT_ALL_BY_NAME_ACTION_NAME);
79     action->setText(i18n("&Sort all by Name"));
80     sortMenu->addAction(action);
81     action = actionCollection()->addAction(SORT_ALL_BY_DESCRIPTION_ACTION_NAME);
82     action->setText(i18n("&Sort all by Description"));
83     sortMenu->addAction(action);
84 
85     // move up/down
86     action = actionCollection()->addAction(MOVE_UP_ACTION_NAME);
87     action->setIcon(QIcon::fromTheme(QStringLiteral("go-up")));
88     action->setText(i18n("Move &Up"));
89     action = actionCollection()->addAction(MOVE_DOWN_ACTION_NAME);
90     action->setIcon(QIcon::fromTheme(QStringLiteral("go-down")));
91     action->setText(i18n("Move &Down"));
92 
93     actionCollection()->addAction(KStandardAction::Save, this, SLOT(slotSave()));
94     actionCollection()->addAction(KStandardAction::Quit, this, SLOT(close()));
95     actionCollection()->addAction(KStandardAction::Cut);
96     actionCollection()->addAction(KStandardAction::Copy);
97     actionCollection()->addAction(KStandardAction::Paste);
98 
99     action = new QAction(i18n("Restore to System Menu"), this);
100     actionCollection()->addAction(QStringLiteral("restore_system_menu"), action);
101     connect(action, &QAction::triggered, this, &KMenuEdit::slotRestoreMenu);
102 
103     KStandardAction::preferences(this, SLOT(slotConfigure()), actionCollection());
104 }
105 
slotConfigure()106 void KMenuEdit::slotConfigure()
107 {
108     PreferencesDialog dialog(this);
109     if (dialog.exec()) {
110         bool newShowHiddenValue = ConfigurationManager::getInstance()->hiddenEntriesVisible();
111         if (newShowHiddenValue != m_showHidden) {
112             m_showHidden = newShowHiddenValue;
113             m_tree->updateTreeView(m_showHidden);
114             m_basicTab->updateHiddenEntry(m_showHidden);
115         }
116     }
117 }
118 
setupView()119 void KMenuEdit::setupView()
120 {
121     // setup search and tree view
122     m_tree = new TreeView(actionCollection(), this);
123 
124     m_searchLine = new KTreeWidgetSearchLine(this, m_tree);
125     m_searchLine->setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
126     m_searchLine->setKeepParentsVisible(true);
127     m_searchLine->setPlaceholderText(i18n("Search..."));
128     m_searchLine->setToolTip(i18n("Search through the list of applications below"));
129 
130     QVBoxLayout *treeLayout = new QVBoxLayout;
131     treeLayout->addWidget(m_searchLine);
132     treeLayout->addWidget(m_tree);
133     treeLayout->setContentsMargins(0, 0, 0, 0); // no padding, fixes alignment issues
134     QFrame *treeFrame = new QFrame; // required to insert tree + search into splitter
135     treeFrame->setLayout(treeLayout);
136 
137     m_splitter = new QSplitter(this);
138     m_splitter->setOrientation(Qt::Horizontal);
139     m_splitter->addWidget(treeFrame);
140 
141     // setup info tab view
142     m_basicTab = new BasicTab;
143     m_splitter->addWidget(m_basicTab);
144 
145     // add padding to splitter
146     m_splitter->setContentsMargins(/*left=*/5, /*top=*/0, /*right=*/5, /*bottom=*/0);
147 
148     // clang-format off
149     connect(m_tree, SIGNAL(entrySelected(MenuFolderInfo*)), m_basicTab, SLOT(setFolderInfo(MenuFolderInfo*)));
150     connect(m_tree, SIGNAL(entrySelected(MenuEntryInfo*)), m_basicTab, SLOT(setEntryInfo(MenuEntryInfo*)));
151     connect(m_tree, &TreeView::disableAction, m_basicTab, &BasicTab::slotDisableAction);
152 
153     connect(m_basicTab, SIGNAL(changed(MenuFolderInfo*)), m_tree, SLOT(currentDataChanged(MenuFolderInfo*)));
154     connect(m_basicTab, SIGNAL(changed(MenuEntryInfo*)), m_tree, SLOT(currentDataChanged(MenuEntryInfo*)));
155     // clang-format on
156 
157     connect(m_basicTab, &BasicTab::findServiceShortcut, m_tree, &TreeView::findServiceShortcut);
158 
159     connect(m_searchLine, &KTreeWidgetSearchLine::searchUpdated, m_tree, &TreeView::searchUpdated);
160     // restore splitter sizes
161     QList<int> sizes = ConfigurationManager::getInstance()->getSplitterSizes();
162     if (sizes.isEmpty()) {
163         sizes << 1 << 3;
164     }
165     m_splitter->setSizes(sizes);
166     m_searchLine->setFocus();
167 
168     setCentralWidget(m_splitter);
169 }
170 
selectMenu(const QString & menu)171 void KMenuEdit::selectMenu(const QString &menu)
172 {
173     m_tree->selectMenu(menu);
174 }
175 
selectMenuEntry(const QString & menuEntry)176 void KMenuEdit::selectMenuEntry(const QString &menuEntry)
177 {
178     m_tree->selectMenuEntry(menuEntry);
179 }
180 
slotChangeView()181 void KMenuEdit::slotChangeView()
182 {
183     guiFactory()->removeClient(this);
184 
185     delete m_actionDelete;
186 
187     m_actionDelete = actionCollection()->addAction(DELETE_ACTION_NAME);
188     m_actionDelete->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
189     m_actionDelete->setText(i18n("&Delete"));
190     actionCollection()->setDefaultShortcut(m_actionDelete, QKeySequence(Qt::Key_Delete));
191 
192     if (!m_splitter) {
193         setupView();
194     }
195     setupGUI(KXmlGuiWindow::ToolBar | Keys | Save | Create, QStringLiteral("kmenueditui.rc"));
196 
197     m_tree->setViewMode(m_showHidden);
198     m_basicTab->updateHiddenEntry(m_showHidden);
199 }
200 
slotSave()201 void KMenuEdit::slotSave()
202 {
203     m_tree->save();
204 }
205 
queryClose()206 bool KMenuEdit::queryClose()
207 {
208     if (!m_tree->dirty()) {
209         return true;
210     }
211 
212     int result;
213     result = KMessageBox::warningYesNoCancel(this,
214                                              i18n("You have made changes to the menu.\n"
215                                                   "Do you want to save the changes or discard them?"),
216                                              i18n("Save Menu Changes?"),
217                                              KStandardGuiItem::save(),
218                                              KStandardGuiItem::discard());
219 
220     switch (result) {
221     case KMessageBox::Yes:
222         return m_tree->save();
223 
224     case KMessageBox::No:
225         return true;
226 
227     default:
228         break;
229     }
230     return false;
231 }
232 
slotRestoreMenu()233 void KMenuEdit::slotRestoreMenu()
234 {
235     m_tree->restoreMenuSystem();
236 }
237 
restoreSystemMenu()238 void KMenuEdit::restoreSystemMenu()
239 {
240     m_tree->restoreMenuSystem();
241 }
242