1
0
Fork 0
libreoffice/cui/source/customize/SvxMenuConfigPage.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

594 lines
20 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <sal/config.h>
#include <sal/log.hxx>
#include <dlgname.hxx>
#include <vcl/weld.hxx>
#include <vcl/svapp.hxx>
#include <vcl/commandevent.hxx>
#include <strings.hrc>
#include <helpids.h>
#include <cfg.hxx>
#include <SvxMenuConfigPage.hxx>
#include <SvxConfigPageHelper.hxx>
#include <dialmgr.hxx>
#include <comphelper/processfactory.hxx>
SvxMenuConfigPage::SvxMenuConfigPage(weld::Container* pPage, weld::DialogController* pController,
const SfxItemSet& rSet, bool bIsMenuBar)
: SvxConfigPage(pPage, pController, rSet)
, m_bIsMenuBar(bIsMenuBar)
{
m_xGearBtn = m_xBuilder->weld_menu_button(u"menugearbtn"_ustr);
m_xGearBtn->show();
m_xContentsListBox.reset(
new SvxMenuEntriesListBox(m_xBuilder->weld_tree_view(u"menucontents"_ustr), this));
weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
m_xDropTargetHelper.reset(new SvxConfigPageFunctionDropTarget(*this, rTreeView));
rTreeView.connect_size_allocate(LINK(this, SvxMenuConfigPage, MenuEntriesSizeAllocHdl));
Size aSize(m_xFunctions->get_size_request());
rTreeView.set_size_request(aSize.Width(), aSize.Height());
MenuEntriesSizeAllocHdl(aSize);
rTreeView.set_hexpand(true);
rTreeView.set_vexpand(true);
rTreeView.show();
rTreeView.connect_changed(LINK(this, SvxMenuConfigPage, SelectMenuEntry));
rTreeView.connect_popup_menu(LINK(this, SvxMenuConfigPage, ContentContextMenuHdl));
m_xFunctions->get_widget().connect_popup_menu(
LINK(this, SvxMenuConfigPage, FunctionContextMenuHdl));
m_xGearBtn->connect_selected(LINK(this, SvxMenuConfigPage, GearHdl));
m_xCommandCategoryListBox->connect_changed(LINK(this, SvxMenuConfigPage, SelectCategory));
m_xMoveUpButton->connect_clicked(LINK(this, SvxConfigPage, MoveHdl));
m_xMoveDownButton->connect_clicked(LINK(this, SvxConfigPage, MoveHdl));
m_xAddCommandButton->connect_clicked(LINK(this, SvxMenuConfigPage, AddCommandHdl));
m_xRemoveCommandButton->connect_clicked(LINK(this, SvxMenuConfigPage, RemoveCommandHdl));
m_xInsertBtn->connect_selected(LINK(this, SvxMenuConfigPage, InsertHdl));
m_xModifyBtn->connect_selected(LINK(this, SvxMenuConfigPage, ModifyItemHdl));
m_xResetBtn->connect_clicked(LINK(this, SvxMenuConfigPage, ResetMenuHdl));
// These operations are not possible on menus/context menus yet
m_xModifyBtn->remove_item(u"changeIcon"_ustr);
m_xModifyBtn->remove_item(u"resetIcon"_ustr);
m_xModifyBtn->remove_item(u"restoreItem"_ustr);
if (!bIsMenuBar)
{
//TODO: Remove this when the gear button is implemented for context menus
m_xGearBtn->set_sensitive(false);
m_xGearBtn->hide();
}
else
{
// TODO: Remove this when it is possible to reset menubar menus individually
m_xResetBtn->set_sensitive(false);
}
}
void SvxMenuConfigPage::ListModified()
{
// regenerate with the current ordering within the list
SvxEntries* pEntries = GetTopLevelSelection()->GetEntries();
pEntries->clear();
for (int i = 0; i < m_xContentsListBox->n_children(); ++i)
pEntries->push_back(weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(i)));
GetSaveInData()->SetModified();
GetTopLevelSelection()->SetModified();
UpdateButtonStates();
}
IMPL_LINK(SvxMenuConfigPage, MenuEntriesSizeAllocHdl, const Size&, rSize, void)
{
weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
std::vector<int> aWidths;
int nStandardImageColWidth = rTreeView.get_checkbox_column_width();
int nMargin = 16;
aWidths.push_back(rSize.Width() - (nMargin + nStandardImageColWidth));
rTreeView.set_column_fixed_widths(aWidths);
}
SvxMenuConfigPage::~SvxMenuConfigPage()
{
for (int i = 0, nCount = m_xSaveInListBox->get_count(); i < nCount; ++i)
delete weld::fromId<SaveInData*>(m_xSaveInListBox->get_id(i));
m_xSaveInListBox->clear();
}
// Populates the Menu combo box
void SvxMenuConfigPage::Init()
{
// ensure that the UI is cleared before populating it
m_xTopLevelListBox->clear();
m_xContentsListBox->clear();
ReloadTopLevelListBox();
m_xTopLevelListBox->set_active(m_xTopLevelListBox->get_count() ? 0 : -1);
SelectElement();
m_xCommandCategoryListBox->Init(comphelper::getProcessComponentContext(), m_xFrame,
m_aModuleId);
m_xCommandCategoryListBox->categorySelected(m_xFunctions.get(), OUString(), GetSaveInData());
SelectFunctionHdl(m_xFunctions->get_widget());
}
IMPL_LINK_NOARG(SvxMenuConfigPage, SelectMenuEntry, weld::TreeView&, void) { UpdateButtonStates(); }
void SvxMenuConfigPage::UpdateButtonStates()
{
// Disable Up and Down buttons depending on current selection
int selection = m_xContentsListBox->get_selected_index();
bool bIsSeparator
= selection != -1
&& weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(selection))->IsSeparator();
bool bIsValidSelection = (m_xContentsListBox->n_children() != 0 && selection != -1);
m_xMoveUpButton->set_sensitive(bIsValidSelection && selection != 0);
m_xMoveDownButton->set_sensitive(bIsValidSelection
&& selection != m_xContentsListBox->n_children() - 1);
m_xRemoveCommandButton->set_sensitive(bIsValidSelection);
m_xModifyBtn->set_sensitive(bIsValidSelection && !bIsSeparator);
// If there is no top level selection (menu), then everything working on the right box
// which contains the functions of the selected menu/toolbar needs to be disabled
SvxConfigEntry* pMenuData = GetTopLevelSelection();
m_xInsertBtn->set_sensitive(pMenuData != nullptr);
SvxConfigEntry* selectedCmd = CreateCommandFromSelection(GetScriptURL());
m_xAddCommandButton->set_sensitive(
pMenuData != nullptr && !IsCommandInMenuList(selectedCmd, pMenuData->GetEntries()));
delete selectedCmd;
if (bIsValidSelection)
{
m_xRemoveCommandButton->set_sensitive(pMenuData != nullptr);
}
//Handle the gear button
if (pMenuData && m_bIsMenuBar)
{
// Add option (gear_add) will always be enabled
m_xGearBtn->set_item_sensitive(u"menu_gear_delete"_ustr, pMenuData->IsDeletable());
m_xGearBtn->set_item_sensitive(u"menu_gear_rename"_ustr, pMenuData->IsRenamable());
m_xGearBtn->set_item_sensitive(u"menu_gear_move"_ustr, pMenuData->IsMovable());
}
}
void SvxMenuConfigPage::DeleteSelectedTopLevel()
{
SvxConfigEntry* pMenuData = GetTopLevelSelection();
SvxEntries* pParentEntries = FindParentForChild(GetSaveInData()->GetEntries(), pMenuData);
SvxConfigPageHelper::RemoveEntry(pParentEntries, pMenuData);
delete pMenuData;
ReloadTopLevelListBox();
GetSaveInData()->SetModified();
}
void SvxMenuConfigPage::DeleteSelectedContent()
{
int nActEntry = m_xContentsListBox->get_selected_index();
if (nActEntry == -1)
return;
// get currently selected menu entry
SvxConfigEntry* pMenuEntry
= weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(nActEntry));
// get currently selected menu
SvxConfigEntry* pMenu = GetTopLevelSelection();
// remove menu entry from the list for this menu
SvxConfigPageHelper::RemoveEntry(pMenu->GetEntries(), pMenuEntry);
// remove menu entry from UI
m_xContentsListBox->remove(nActEntry);
// if this is a submenu entry, redraw the menus list box
if (pMenuEntry->IsPopup())
{
ReloadTopLevelListBox();
}
// delete data for menu entry
delete pMenuEntry;
GetSaveInData()->SetModified();
pMenu->SetModified();
}
short SvxMenuConfigPage::QueryReset()
{
OUString msg = CuiResId(RID_CUISTR_CONFIRM_MENU_RESET);
OUString saveInName = m_xSaveInListBox->get_active_text();
OUString label = SvxConfigPageHelper::replaceSaveInName(msg, saveInName);
std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(
GetFrameWeld(), VclMessageType::Question, VclButtonsType::YesNo, label));
return xQueryBox->run();
}
void SvxMenuConfigPage::SelectElement()
{
weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
SvxConfigEntry* pMenuData = GetTopLevelSelection();
if (!pMenuData)
rTreeView.clear();
else
{
SvxEntries* pEntries = pMenuData->GetEntries();
rTreeView.bulk_insert_for_each(
pEntries->size(), [this, &rTreeView, pEntries](weld::TreeIter& rIter, int nIdx) {
auto const& entry = (*pEntries)[nIdx];
OUString sId(weld::toId(entry));
rTreeView.set_id(rIter, sId);
InsertEntryIntoUI(entry, rTreeView, rIter, true);
});
}
UpdateButtonStates();
}
IMPL_LINK(SvxMenuConfigPage, GearHdl, const OUString&, rIdent, void)
{
if (rIdent == "menu_gear_add")
{
SvxMainMenuOrganizerDialog aDialog(GetFrameWeld(), GetSaveInData()->GetEntries(), nullptr,
true);
if (aDialog.run() == RET_OK)
{
GetSaveInData()->SetEntries(aDialog.ReleaseEntries());
ReloadTopLevelListBox(aDialog.GetSelectedEntry());
GetSaveInData()->SetModified();
}
}
else if (rIdent == "menu_gear_delete")
{
DeleteSelectedTopLevel();
}
else if (rIdent == "menu_gear_rename")
{
SvxConfigEntry* pMenuData = GetTopLevelSelection();
OUString sCurrentName(SvxConfigPageHelper::stripHotKey(pMenuData->GetName()));
OUString sDesc = CuiResId(RID_CUISTR_LABEL_NEW_NAME);
SvxNameDialog aNameDialog(GetFrameWeld(), sCurrentName, sDesc);
aNameDialog.set_help_id(HID_SVX_CONFIG_RENAME_MENU);
aNameDialog.set_title(CuiResId(RID_CUISTR_RENAME_MENU));
if (aNameDialog.run() == RET_OK)
{
OUString sNewName = aNameDialog.GetName();
if (sCurrentName == sNewName)
return;
pMenuData->SetName(sNewName);
ReloadTopLevelListBox();
GetSaveInData()->SetModified();
}
}
else if (rIdent == "menu_gear_move")
{
SvxConfigEntry* pMenuData = GetTopLevelSelection();
SvxMainMenuOrganizerDialog aDialog(GetFrameWeld(), GetSaveInData()->GetEntries(), pMenuData,
false);
if (aDialog.run() == RET_OK)
{
GetSaveInData()->SetEntries(aDialog.ReleaseEntries());
ReloadTopLevelListBox();
GetSaveInData()->SetModified();
}
}
else
{
//This block should never be reached
SAL_WARN("cui.customize", "Unknown gear menu option: " << rIdent);
return;
}
UpdateButtonStates();
}
IMPL_LINK_NOARG(SvxMenuConfigPage, SelectCategory, weld::ComboBox&, void)
{
OUString aSearchTerm(m_xSearchEdit->get_text());
m_xCommandCategoryListBox->categorySelected(m_xFunctions.get(), aSearchTerm, GetSaveInData());
SelectFunctionHdl(m_xFunctions->get_widget());
}
IMPL_LINK_NOARG(SvxMenuConfigPage, AddCommandHdl, weld::Button&, void)
{
int nPos = AddFunction(-1, /*bAllowDuplicates*/ false);
if (nPos == -1)
return;
weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
SvxConfigEntry* pEntry = weld::fromId<SvxConfigEntry*>(rTreeView.get_id(nPos));
InsertEntryIntoUI(pEntry, rTreeView, nPos, true);
}
IMPL_LINK_NOARG(SvxMenuConfigPage, RemoveCommandHdl, weld::Button&, void)
{
DeleteSelectedContent();
if (GetSaveInData()->IsModified())
{
UpdateButtonStates();
}
}
IMPL_LINK(SvxMenuConfigPage, InsertHdl, const OUString&, rIdent, void)
{
weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
if (rIdent == "insertseparator")
{
SvxConfigEntry* pNewEntryData = new SvxConfigEntry;
pNewEntryData->SetUserDefined();
int nPos = AppendEntry(pNewEntryData, -1);
InsertEntryIntoUI(pNewEntryData, rTreeView, nPos, true);
}
else if (rIdent == "insertsubmenu")
{
OUString aNewName;
OUString aDesc = CuiResId(RID_CUISTR_SUBMENU_NAME);
SvxNameDialog aNameDialog(GetFrameWeld(), aNewName, aDesc);
aNameDialog.set_help_id(HID_SVX_CONFIG_NAME_SUBMENU);
aNameDialog.set_title(CuiResId(RID_CUISTR_ADD_SUBMENU));
if (aNameDialog.run() == RET_OK)
{
aNewName = aNameDialog.GetName();
SvxConfigEntry* pNewEntryData
= new SvxConfigEntry(aNewName, aNewName, true, /*bParentData*/ false);
pNewEntryData->SetName(aNewName);
pNewEntryData->SetUserDefined();
int nPos = AppendEntry(pNewEntryData, -1);
InsertEntryIntoUI(pNewEntryData, rTreeView, nPos, true);
ReloadTopLevelListBox();
m_xContentsListBox->scroll_to_row(nPos);
m_xContentsListBox->select(nPos);
GetSaveInData()->SetModified();
}
}
else
{
//This block should never be reached
SAL_WARN("cui.customize", "Unknown insert option: " << rIdent);
return;
}
if (GetSaveInData()->IsModified())
{
UpdateButtonStates();
}
}
IMPL_LINK(SvxMenuConfigPage, ModifyItemHdl, const OUString&, rIdent, void)
{
if (rIdent == "renameItem")
{
int nActEntry = m_xContentsListBox->get_selected_index();
SvxConfigEntry* pEntry
= weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(nActEntry));
OUString aNewName(SvxConfigPageHelper::stripHotKey(pEntry->GetName()));
OUString aDesc = CuiResId(RID_CUISTR_LABEL_NEW_NAME);
SvxNameDialog aNameDialog(GetFrameWeld(), aNewName, aDesc);
aNameDialog.set_help_id(HID_SVX_CONFIG_RENAME_MENU_ITEM);
aNameDialog.set_title(CuiResId(RID_CUISTR_RENAME_MENU));
if (aNameDialog.run() == RET_OK)
{
aNewName = aNameDialog.GetName();
pEntry->SetName(aNewName);
m_xContentsListBox->set_text(nActEntry, aNewName, 0);
GetSaveInData()->SetModified();
GetTopLevelSelection()->SetModified();
}
}
else
{
//This block should never be reached
SAL_WARN("cui.customize", "Unknown insert option: " << rIdent);
return;
}
if (GetSaveInData()->IsModified())
{
UpdateButtonStates();
}
}
IMPL_LINK_NOARG(SvxMenuConfigPage, ResetMenuHdl, weld::Button&, void)
{
SvxConfigEntry* pMenuData = GetTopLevelSelection();
if (pMenuData == nullptr)
{
SAL_WARN("cui.customize",
"RHB top level selection is null. A menu must be selected to reset!");
return;
}
std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(
GetFrameWeld(), VclMessageType::Question, VclButtonsType::YesNo,
CuiResId(RID_CUISTR_CONFIRM_RESTORE_DEFAULT_MENU)));
// Resetting individual top-level menus is not possible at the moment.
// So we are resetting only if it is a context menu
if (m_bIsMenuBar || xQueryBox->run() != RET_YES)
return;
sal_Int32 nPos = m_xTopLevelListBox->get_active();
ContextMenuSaveInData* pSaveInData = static_cast<ContextMenuSaveInData*>(GetSaveInData());
pSaveInData->ResetContextMenu(pMenuData);
// ensure that the UI is cleared before populating it
m_xTopLevelListBox->clear();
m_xContentsListBox->clear();
ReloadTopLevelListBox();
// Reselect the reset menu
m_xTopLevelListBox->set_active(nPos);
SelectElement();
}
SaveInData* SvxMenuConfigPage::CreateSaveInData(
const css::uno::Reference<css::ui::XUIConfigurationManager>& xCfgMgr,
const css::uno::Reference<css::ui::XUIConfigurationManager>& xParentCfgMgr,
const OUString& aModuleId, bool bDocConfig)
{
if (!m_bIsMenuBar)
return static_cast<SaveInData*>(
new ContextMenuSaveInData(xCfgMgr, xParentCfgMgr, aModuleId, bDocConfig));
return static_cast<SaveInData*>(
new MenuSaveInData(xCfgMgr, xParentCfgMgr, aModuleId, bDocConfig));
}
IMPL_LINK(SvxMenuConfigPage, ContentContextMenuHdl, const CommandEvent&, rCEvt, bool)
{
if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
return false;
weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
// Select clicked entry
std::unique_ptr<weld::TreeIter> xIter(rTreeView.make_iterator());
if (!rTreeView.get_dest_row_at_pos(rCEvt.GetMousePosPixel(), xIter.get(), false))
return false;
rTreeView.select(*xIter);
SelectMenuEntry(rTreeView);
int nSelectIndex = m_xContentsListBox->get_selected_index();
bool bIsSeparator
= nSelectIndex != -1
&& weld::fromId<SvxConfigEntry*>(m_xContentsListBox->get_id(nSelectIndex))->IsSeparator();
bool bIsValidSelection = (m_xContentsListBox->n_children() != 0 && nSelectIndex != -1);
std::unique_ptr<weld::Builder> xBuilder(
Application::CreateBuilder(&rTreeView, u"cui/ui/entrycontextmenu.ui"_ustr));
auto xContextMenu = xBuilder->weld_menu(u"menu"_ustr);
xContextMenu->set_visible(u"add"_ustr, false);
xContextMenu->set_visible(u"remove"_ustr, bIsValidSelection);
xContextMenu->set_visible(u"rename"_ustr, bIsValidSelection && !bIsSeparator);
xContextMenu->set_visible(u"changeIcon"_ustr, false);
xContextMenu->set_visible(u"resetIcon"_ustr, false);
xContextMenu->set_visible(u"restoreDefault"_ustr, false);
OUString sCommand(xContextMenu->popup_at_rect(
&rTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1, 1))));
if (sCommand == "remove")
{
RemoveCommandHdl(*m_xRemoveCommandButton);
}
else if (sCommand == "rename")
{
ModifyItemHdl(u"renameItem"_ustr);
}
else if (!sCommand.isEmpty())
SAL_WARN("cui.customize", "Unknown context menu action: " << sCommand);
return true;
}
IMPL_LINK(SvxMenuConfigPage, FunctionContextMenuHdl, const CommandEvent&, rCEvt, bool)
{
if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
return false;
weld::TreeView& rTreeView = m_xFunctions->get_widget();
// Select clicked entry
std::unique_ptr<weld::TreeIter> xIter(rTreeView.make_iterator());
if (!rTreeView.get_dest_row_at_pos(rCEvt.GetMousePosPixel(), xIter.get(), false))
return false;
rTreeView.select(*xIter);
SelectFunctionHdl(rTreeView);
std::unique_ptr<weld::Builder> xBuilder(
Application::CreateBuilder(&rTreeView, u"cui/ui/entrycontextmenu.ui"_ustr));
auto xContextMenu = xBuilder->weld_menu(u"menu"_ustr);
xContextMenu->set_visible(u"add"_ustr, true);
xContextMenu->set_visible(u"remove"_ustr, false);
xContextMenu->set_visible(u"rename"_ustr, false);
xContextMenu->set_visible(u"changeIcon"_ustr, false);
xContextMenu->set_visible(u"resetIcon"_ustr, false);
xContextMenu->set_visible(u"restoreDefault"_ustr, false);
OUString sCommand(xContextMenu->popup_at_rect(
&rTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1, 1))));
if (sCommand == "add")
{
AddCommandHdl(*m_xAddCommandButton);
}
else if (!sCommand.isEmpty())
SAL_WARN("cui.customize", "Unknown context menu action: " << sCommand);
return true;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */