1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
23 #include <cui/dlgname.hxx>
24 #include <vcl/weld.hxx>
25 #include <vcl/svapp.hxx>
26 #include <vcl/commandevent.hxx>
28 #include <strings.hrc>
32 #include <SvxMenuConfigPage.hxx>
33 #include <SvxConfigPageHelper.hxx>
34 #include <dialmgr.hxx>
36 #include <comphelper/processfactory.hxx>
38 SvxMenuConfigPage::SvxMenuConfigPage(weld::Container
* pPage
, weld::DialogController
* pController
,
39 const SfxItemSet
& rSet
, bool bIsMenuBar
)
40 : SvxConfigPage(pPage
, pController
, rSet
)
41 , m_bIsMenuBar(bIsMenuBar
)
43 m_xGearBtn
= m_xBuilder
->weld_menu_button("menugearbtn");
45 m_xContentsListBox
.reset(
46 new SvxMenuEntriesListBox(m_xBuilder
->weld_tree_view("menucontents"), this));
47 weld::TreeView
& rTreeView
= m_xContentsListBox
->get_widget();
48 m_xDropTargetHelper
.reset(new SvxConfigPageFunctionDropTarget(*this, rTreeView
));
49 rTreeView
.connect_size_allocate(LINK(this, SvxMenuConfigPage
, MenuEntriesSizeAllocHdl
));
50 Size
aSize(m_xFunctions
->get_size_request());
51 rTreeView
.set_size_request(aSize
.Width(), aSize
.Height());
52 MenuEntriesSizeAllocHdl(aSize
);
53 rTreeView
.set_hexpand(true);
54 rTreeView
.set_vexpand(true);
57 rTreeView
.connect_changed(LINK(this, SvxMenuConfigPage
, SelectMenuEntry
));
58 rTreeView
.connect_popup_menu(LINK(this, SvxMenuConfigPage
, ContentContextMenuHdl
));
60 m_xFunctions
->get_widget().connect_popup_menu(
61 LINK(this, SvxMenuConfigPage
, FunctionContextMenuHdl
));
63 m_xGearBtn
->connect_selected(LINK(this, SvxMenuConfigPage
, GearHdl
));
65 m_xCommandCategoryListBox
->connect_changed(LINK(this, SvxMenuConfigPage
, SelectCategory
));
67 m_xMoveUpButton
->connect_clicked(LINK(this, SvxConfigPage
, MoveHdl
));
68 m_xMoveDownButton
->connect_clicked(LINK(this, SvxConfigPage
, MoveHdl
));
70 m_xAddCommandButton
->connect_clicked(LINK(this, SvxMenuConfigPage
, AddCommandHdl
));
71 m_xRemoveCommandButton
->connect_clicked(LINK(this, SvxMenuConfigPage
, RemoveCommandHdl
));
73 m_xInsertBtn
->connect_selected(LINK(this, SvxMenuConfigPage
, InsertHdl
));
74 m_xModifyBtn
->connect_selected(LINK(this, SvxMenuConfigPage
, ModifyItemHdl
));
75 m_xResetBtn
->connect_clicked(LINK(this, SvxMenuConfigPage
, ResetMenuHdl
));
77 // These operations are not possible on menus/context menus yet
78 m_xModifyBtn
->remove_item("changeIcon");
79 m_xModifyBtn
->remove_item("resetIcon");
80 m_xModifyBtn
->remove_item("restoreItem");
84 //TODO: Remove this when the gear button is implemented for context menus
85 m_xGearBtn
->set_sensitive(false);
90 // TODO: Remove this when it is possible to reset menubar menus individually
91 m_xResetBtn
->set_sensitive(false);
95 void SvxMenuConfigPage::ListModified()
97 // regenerate with the current ordering within the list
98 SvxEntries
* pEntries
= GetTopLevelSelection()->GetEntries();
101 for (int i
= 0; i
< m_xContentsListBox
->n_children(); ++i
)
102 pEntries
->push_back(weld::fromId
<SvxConfigEntry
*>(m_xContentsListBox
->get_id(i
)));
104 GetSaveInData()->SetModified();
105 GetTopLevelSelection()->SetModified();
106 UpdateButtonStates();
109 IMPL_LINK(SvxMenuConfigPage
, MenuEntriesSizeAllocHdl
, const Size
&, rSize
, void)
111 weld::TreeView
& rTreeView
= m_xContentsListBox
->get_widget();
112 std::vector
<int> aWidths
;
114 int nStandardImageColWidth
= rTreeView
.get_checkbox_column_width();
117 aWidths
.push_back(rSize
.Width() - (nMargin
+ nStandardImageColWidth
));
118 rTreeView
.set_column_fixed_widths(aWidths
);
121 SvxMenuConfigPage::~SvxMenuConfigPage()
123 for (int i
= 0, nCount
= m_xSaveInListBox
->get_count(); i
< nCount
; ++i
)
124 delete weld::fromId
<SaveInData
*>(m_xSaveInListBox
->get_id(i
));
125 m_xSaveInListBox
->clear();
128 // Populates the Menu combo box
129 void SvxMenuConfigPage::Init()
131 // ensure that the UI is cleared before populating it
132 m_xTopLevelListBox
->clear();
133 m_xContentsListBox
->clear();
135 ReloadTopLevelListBox();
137 m_xTopLevelListBox
->set_active(m_xTopLevelListBox
->get_count() ? 0 : -1);
140 m_xCommandCategoryListBox
->Init(comphelper::getProcessComponentContext(), m_xFrame
,
142 m_xCommandCategoryListBox
->categorySelected(m_xFunctions
.get(), OUString(), GetSaveInData());
143 SelectFunctionHdl(m_xFunctions
->get_widget());
146 IMPL_LINK_NOARG(SvxMenuConfigPage
, SelectMenuEntry
, weld::TreeView
&, void) { UpdateButtonStates(); }
148 void SvxMenuConfigPage::UpdateButtonStates()
150 // Disable Up and Down buttons depending on current selection
151 int selection
= m_xContentsListBox
->get_selected_index();
155 && weld::fromId
<SvxConfigEntry
*>(m_xContentsListBox
->get_id(selection
))->IsSeparator();
156 bool bIsValidSelection
= (m_xContentsListBox
->n_children() != 0 && selection
!= -1);
158 m_xMoveUpButton
->set_sensitive(bIsValidSelection
&& selection
!= 0);
159 m_xMoveDownButton
->set_sensitive(bIsValidSelection
160 && selection
!= m_xContentsListBox
->n_children() - 1);
162 m_xRemoveCommandButton
->set_sensitive(bIsValidSelection
);
164 m_xModifyBtn
->set_sensitive(bIsValidSelection
&& !bIsSeparator
);
166 // If there is no top level selection (menu), then everything working on the right box
167 // which contains the functions of the selected menu/toolbar needs to be disabled
168 SvxConfigEntry
* pMenuData
= GetTopLevelSelection();
170 m_xInsertBtn
->set_sensitive(pMenuData
!= nullptr);
172 SvxConfigEntry
* selectedCmd
= CreateCommandFromSelection(GetScriptURL());
174 m_xAddCommandButton
->set_sensitive(
175 pMenuData
!= nullptr && !IsCommandInMenuList(selectedCmd
, pMenuData
->GetEntries()));
179 if (bIsValidSelection
)
181 m_xRemoveCommandButton
->set_sensitive(pMenuData
!= nullptr);
184 //Handle the gear button
185 if (pMenuData
&& m_bIsMenuBar
)
187 // Add option (gear_add) will always be enabled
188 m_xGearBtn
->set_item_sensitive("menu_gear_delete", pMenuData
->IsDeletable());
189 m_xGearBtn
->set_item_sensitive("menu_gear_rename", pMenuData
->IsRenamable());
190 m_xGearBtn
->set_item_sensitive("menu_gear_move", pMenuData
->IsMovable());
194 void SvxMenuConfigPage::DeleteSelectedTopLevel()
196 SvxConfigEntry
* pMenuData
= GetTopLevelSelection();
198 SvxEntries
* pParentEntries
= FindParentForChild(GetSaveInData()->GetEntries(), pMenuData
);
200 SvxConfigPageHelper::RemoveEntry(pParentEntries
, pMenuData
);
203 ReloadTopLevelListBox();
205 GetSaveInData()->SetModified();
208 void SvxMenuConfigPage::DeleteSelectedContent()
210 int nActEntry
= m_xContentsListBox
->get_selected_index();
215 // get currently selected menu entry
216 SvxConfigEntry
* pMenuEntry
217 = weld::fromId
<SvxConfigEntry
*>(m_xContentsListBox
->get_id(nActEntry
));
219 // get currently selected menu
220 SvxConfigEntry
* pMenu
= GetTopLevelSelection();
222 // remove menu entry from the list for this menu
223 SvxConfigPageHelper::RemoveEntry(pMenu
->GetEntries(), pMenuEntry
);
225 // remove menu entry from UI
226 m_xContentsListBox
->remove(nActEntry
);
228 // if this is a submenu entry, redraw the menus list box
229 if (pMenuEntry
->IsPopup())
231 ReloadTopLevelListBox();
234 // delete data for menu entry
237 GetSaveInData()->SetModified();
238 pMenu
->SetModified();
241 short SvxMenuConfigPage::QueryReset()
243 OUString msg
= CuiResId(RID_CUISTR_CONFIRM_MENU_RESET
);
245 OUString saveInName
= m_xSaveInListBox
->get_active_text();
247 OUString label
= SvxConfigPageHelper::replaceSaveInName(msg
, saveInName
);
249 std::unique_ptr
<weld::MessageDialog
> xQueryBox(Application::CreateMessageDialog(
250 GetFrameWeld(), VclMessageType::Question
, VclButtonsType::YesNo
, label
));
251 return xQueryBox
->run();
254 void SvxMenuConfigPage::SelectElement()
256 weld::TreeView
& rTreeView
= m_xContentsListBox
->get_widget();
258 SvxConfigEntry
* pMenuData
= GetTopLevelSelection();
263 SvxEntries
* pEntries
= pMenuData
->GetEntries();
265 rTreeView
.bulk_insert_for_each(
266 pEntries
->size(), [this, &rTreeView
, pEntries
](weld::TreeIter
& rIter
, int nIdx
) {
267 auto const& entry
= (*pEntries
)[nIdx
];
268 OUString
sId(weld::toId(entry
));
269 rTreeView
.set_id(rIter
, sId
);
270 InsertEntryIntoUI(entry
, rTreeView
, rIter
, true);
274 UpdateButtonStates();
277 IMPL_LINK(SvxMenuConfigPage
, GearHdl
, const OUString
&, rIdent
, void)
279 if (rIdent
== "menu_gear_add")
281 SvxMainMenuOrganizerDialog
aDialog(GetFrameWeld(), GetSaveInData()->GetEntries(), nullptr,
284 if (aDialog
.run() == RET_OK
)
286 GetSaveInData()->SetEntries(aDialog
.ReleaseEntries());
287 ReloadTopLevelListBox(aDialog
.GetSelectedEntry());
288 GetSaveInData()->SetModified();
291 else if (rIdent
== "menu_gear_delete")
293 DeleteSelectedTopLevel();
295 else if (rIdent
== "menu_gear_rename")
297 SvxConfigEntry
* pMenuData
= GetTopLevelSelection();
299 OUString
sCurrentName(SvxConfigPageHelper::stripHotKey(pMenuData
->GetName()));
300 OUString sDesc
= CuiResId(RID_CUISTR_LABEL_NEW_NAME
);
302 SvxNameDialog
aNameDialog(GetFrameWeld(), sCurrentName
, sDesc
);
303 aNameDialog
.set_help_id(HID_SVX_CONFIG_RENAME_MENU
);
304 aNameDialog
.set_title(CuiResId(RID_CUISTR_RENAME_MENU
));
306 if (aNameDialog
.run() == RET_OK
)
308 OUString sNewName
= aNameDialog
.GetName();
310 if (sCurrentName
== sNewName
)
313 pMenuData
->SetName(sNewName
);
315 ReloadTopLevelListBox();
317 GetSaveInData()->SetModified();
320 else if (rIdent
== "menu_gear_move")
322 SvxConfigEntry
* pMenuData
= GetTopLevelSelection();
324 SvxMainMenuOrganizerDialog
aDialog(GetFrameWeld(), GetSaveInData()->GetEntries(), pMenuData
,
326 if (aDialog
.run() == RET_OK
)
328 GetSaveInData()->SetEntries(aDialog
.ReleaseEntries());
330 ReloadTopLevelListBox();
332 GetSaveInData()->SetModified();
337 //This block should never be reached
338 SAL_WARN("cui.customize", "Unknown gear menu option: " << rIdent
);
342 UpdateButtonStates();
345 IMPL_LINK_NOARG(SvxMenuConfigPage
, SelectCategory
, weld::ComboBox
&, void)
347 OUString
aSearchTerm(m_xSearchEdit
->get_text());
349 m_xCommandCategoryListBox
->categorySelected(m_xFunctions
.get(), aSearchTerm
, GetSaveInData());
351 SelectFunctionHdl(m_xFunctions
->get_widget());
354 IMPL_LINK_NOARG(SvxMenuConfigPage
, AddCommandHdl
, weld::Button
&, void)
356 int nPos
= AddFunction(-1, /*bAllowDuplicates*/ false);
359 weld::TreeView
& rTreeView
= m_xContentsListBox
->get_widget();
360 SvxConfigEntry
* pEntry
= weld::fromId
<SvxConfigEntry
*>(rTreeView
.get_id(nPos
));
361 InsertEntryIntoUI(pEntry
, rTreeView
, nPos
, true);
364 IMPL_LINK_NOARG(SvxMenuConfigPage
, RemoveCommandHdl
, weld::Button
&, void)
366 DeleteSelectedContent();
367 if (GetSaveInData()->IsModified())
369 UpdateButtonStates();
373 IMPL_LINK(SvxMenuConfigPage
, InsertHdl
, const OUString
&, rIdent
, void)
375 weld::TreeView
& rTreeView
= m_xContentsListBox
->get_widget();
376 if (rIdent
== "insertseparator")
378 SvxConfigEntry
* pNewEntryData
= new SvxConfigEntry
;
379 pNewEntryData
->SetUserDefined();
380 int nPos
= AppendEntry(pNewEntryData
, -1);
381 InsertEntryIntoUI(pNewEntryData
, rTreeView
, nPos
, true);
383 else if (rIdent
== "insertsubmenu")
386 OUString aDesc
= CuiResId(RID_CUISTR_SUBMENU_NAME
);
388 SvxNameDialog
aNameDialog(GetFrameWeld(), aNewName
, aDesc
);
389 aNameDialog
.set_help_id(HID_SVX_CONFIG_NAME_SUBMENU
);
390 aNameDialog
.set_title(CuiResId(RID_CUISTR_ADD_SUBMENU
));
392 if (aNameDialog
.run() == RET_OK
)
394 aNewName
= aNameDialog
.GetName();
396 SvxConfigEntry
* pNewEntryData
397 = new SvxConfigEntry(aNewName
, aNewName
, true, /*bParentData*/ false);
398 pNewEntryData
->SetName(aNewName
);
399 pNewEntryData
->SetUserDefined();
401 int nPos
= AppendEntry(pNewEntryData
, -1);
402 InsertEntryIntoUI(pNewEntryData
, rTreeView
, nPos
, true);
404 ReloadTopLevelListBox();
406 m_xContentsListBox
->scroll_to_row(nPos
);
407 m_xContentsListBox
->select(nPos
);
409 GetSaveInData()->SetModified();
414 //This block should never be reached
415 SAL_WARN("cui.customize", "Unknown insert option: " << rIdent
);
419 if (GetSaveInData()->IsModified())
421 UpdateButtonStates();
425 IMPL_LINK(SvxMenuConfigPage
, ModifyItemHdl
, const OUString
&, rIdent
, void)
427 if (rIdent
== "renameItem")
429 int nActEntry
= m_xContentsListBox
->get_selected_index();
430 SvxConfigEntry
* pEntry
431 = weld::fromId
<SvxConfigEntry
*>(m_xContentsListBox
->get_id(nActEntry
));
433 OUString
aNewName(SvxConfigPageHelper::stripHotKey(pEntry
->GetName()));
434 OUString aDesc
= CuiResId(RID_CUISTR_LABEL_NEW_NAME
);
436 SvxNameDialog
aNameDialog(GetFrameWeld(), aNewName
, aDesc
);
437 aNameDialog
.set_help_id(HID_SVX_CONFIG_RENAME_MENU_ITEM
);
438 aNameDialog
.set_title(CuiResId(RID_CUISTR_RENAME_MENU
));
440 if (aNameDialog
.run() == RET_OK
)
442 aNewName
= aNameDialog
.GetName();
444 pEntry
->SetName(aNewName
);
445 m_xContentsListBox
->set_text(nActEntry
, aNewName
, 0);
447 GetSaveInData()->SetModified();
448 GetTopLevelSelection()->SetModified();
453 //This block should never be reached
454 SAL_WARN("cui.customize", "Unknown insert option: " << rIdent
);
458 if (GetSaveInData()->IsModified())
460 UpdateButtonStates();
464 IMPL_LINK_NOARG(SvxMenuConfigPage
, ResetMenuHdl
, weld::Button
&, void)
466 SvxConfigEntry
* pMenuData
= GetTopLevelSelection();
468 if (pMenuData
== nullptr)
470 SAL_WARN("cui.customize",
471 "RHB top level selection is null. A menu must be selected to reset!");
475 std::unique_ptr
<weld::MessageDialog
> xQueryBox(Application::CreateMessageDialog(
476 GetFrameWeld(), VclMessageType::Question
, VclButtonsType::YesNo
,
477 CuiResId(RID_CUISTR_CONFIRM_RESTORE_DEFAULT_MENU
)));
479 // Resetting individual top-level menus is not possible at the moment.
480 // So we are resetting only if it is a context menu
481 if (m_bIsMenuBar
|| xQueryBox
->run() != RET_YES
)
484 sal_Int32 nPos
= m_xTopLevelListBox
->get_active();
485 ContextMenuSaveInData
* pSaveInData
= static_cast<ContextMenuSaveInData
*>(GetSaveInData());
487 pSaveInData
->ResetContextMenu(pMenuData
);
489 // ensure that the UI is cleared before populating it
490 m_xTopLevelListBox
->clear();
491 m_xContentsListBox
->clear();
493 ReloadTopLevelListBox();
495 // Reselect the reset menu
496 m_xTopLevelListBox
->set_active(nPos
);
500 SaveInData
* SvxMenuConfigPage::CreateSaveInData(
501 const css::uno::Reference
<css::ui::XUIConfigurationManager
>& xCfgMgr
,
502 const css::uno::Reference
<css::ui::XUIConfigurationManager
>& xParentCfgMgr
,
503 const OUString
& aModuleId
, bool bDocConfig
)
506 return static_cast<SaveInData
*>(
507 new ContextMenuSaveInData(xCfgMgr
, xParentCfgMgr
, aModuleId
, bDocConfig
));
509 return static_cast<SaveInData
*>(
510 new MenuSaveInData(xCfgMgr
, xParentCfgMgr
, aModuleId
, bDocConfig
));
513 IMPL_LINK(SvxMenuConfigPage
, ContentContextMenuHdl
, const CommandEvent
&, rCEvt
, bool)
515 if (rCEvt
.GetCommand() != CommandEventId::ContextMenu
)
518 weld::TreeView
& rTreeView
= m_xContentsListBox
->get_widget();
520 // Select clicked entry
521 std::unique_ptr
<weld::TreeIter
> xIter(rTreeView
.make_iterator());
522 if (!rTreeView
.get_dest_row_at_pos(rCEvt
.GetMousePosPixel(), xIter
.get(), false))
524 rTreeView
.select(*xIter
);
525 SelectMenuEntry(rTreeView
);
527 int nSelectIndex
= m_xContentsListBox
->get_selected_index();
531 && weld::fromId
<SvxConfigEntry
*>(m_xContentsListBox
->get_id(nSelectIndex
))->IsSeparator();
532 bool bIsValidSelection
= (m_xContentsListBox
->n_children() != 0 && nSelectIndex
!= -1);
534 std::unique_ptr
<weld::Builder
> xBuilder(
535 Application::CreateBuilder(&rTreeView
, "cui/ui/entrycontextmenu.ui"));
536 auto xContextMenu
= xBuilder
->weld_menu("menu");
537 xContextMenu
->set_visible("add", false);
538 xContextMenu
->set_visible("remove", bIsValidSelection
);
539 xContextMenu
->set_visible("rename", bIsValidSelection
&& !bIsSeparator
);
540 xContextMenu
->set_visible("changeIcon", false);
541 xContextMenu
->set_visible("resetIcon", false);
542 xContextMenu
->set_visible("restoreDefault", false);
543 OUString
sCommand(xContextMenu
->popup_at_rect(
544 &rTreeView
, tools::Rectangle(rCEvt
.GetMousePosPixel(), Size(1, 1))));
546 if (sCommand
== "remove")
548 RemoveCommandHdl(*m_xRemoveCommandButton
);
550 else if (sCommand
== "rename")
552 ModifyItemHdl("renameItem");
554 else if (!sCommand
.isEmpty())
555 SAL_WARN("cui.customize", "Unknown context menu action: " << sCommand
);
559 IMPL_LINK(SvxMenuConfigPage
, FunctionContextMenuHdl
, const CommandEvent
&, rCEvt
, bool)
561 if (rCEvt
.GetCommand() != CommandEventId::ContextMenu
)
564 weld::TreeView
& rTreeView
= m_xFunctions
->get_widget();
566 // Select clicked entry
567 std::unique_ptr
<weld::TreeIter
> xIter(rTreeView
.make_iterator());
568 if (!rTreeView
.get_dest_row_at_pos(rCEvt
.GetMousePosPixel(), xIter
.get(), false))
570 rTreeView
.select(*xIter
);
571 SelectFunctionHdl(rTreeView
);
573 std::unique_ptr
<weld::Builder
> xBuilder(
574 Application::CreateBuilder(&rTreeView
, "cui/ui/entrycontextmenu.ui"));
575 auto xContextMenu
= xBuilder
->weld_menu("menu");
576 xContextMenu
->set_visible("add", true);
577 xContextMenu
->set_visible("remove", false);
578 xContextMenu
->set_visible("rename", false);
579 xContextMenu
->set_visible("changeIcon", false);
580 xContextMenu
->set_visible("resetIcon", false);
581 xContextMenu
->set_visible("restoreDefault", false);
582 OUString
sCommand(xContextMenu
->popup_at_rect(
583 &rTreeView
, tools::Rectangle(rCEvt
.GetMousePosPixel(), Size(1, 1))));
585 if (sCommand
== "add")
587 AddCommandHdl(*m_xAddCommandButton
);
589 else if (!sCommand
.isEmpty())
590 SAL_WARN("cui.customize", "Unknown context menu action: " << sCommand
);
594 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */