UI for the Notebookbar Customization tab

This patch generates the UI for the notebookbar customization tab.
Patch is working and one can show/hide the UIItem by clicking on the
checkbox.

Change-Id: I4923c57e6443474f178a72922646619f07b5ffc1
Reviewed-on: https://gerrit.libreoffice.org/73261
Tested-by: Jenkins
Reviewed-by: Szymon Kłos <szymon.klos@collabora.com>
Tested-by: Szymon Kłos <szymon.klos@collabora.com>
diff --git a/cui/Library_cui.mk b/cui/Library_cui.mk
index 4cf0015..b025205 100644
--- a/cui/Library_cui.mk
+++ b/cui/Library_cui.mk
@@ -97,6 +97,7 @@ $(eval $(call gb_Library_add_exception_objects,cui,\
    cui/source/customize/SvxConfigPageHelper \
    cui/source/customize/SvxMenuConfigPage \
    cui/source/customize/SvxToolbarConfigPage \
    cui/source/customize/SvxNotebookbarConfigPage \
    cui/source/customize/CustomNotebookbarGenerator \
    cui/source/dialogs/about \
    cui/source/dialogs/colorpicker \
diff --git a/cui/source/customize/CustomNotebookbarGenerator.cxx b/cui/source/customize/CustomNotebookbarGenerator.cxx
index 925f43d..40fbe90 100644
--- a/cui/source/customize/CustomNotebookbarGenerator.cxx
+++ b/cui/source/customize/CustomNotebookbarGenerator.cxx
@@ -114,7 +114,7 @@ OUString CustomNotebookbarGenerator::getCustomizedUIPath()
    return sCustomizedUIPath;
}

static OUString getOriginalUIPath()
OUString CustomNotebookbarGenerator::getOriginalUIPath()
{
    OUStringBuffer aOriginalUIPathBuffer = VclBuilderContainer::getUIRootDir();
    OUString sAppName, sNotebookbarUIFileName;
diff --git a/cui/source/customize/SvxNotebookbarConfigPage.cxx b/cui/source/customize/SvxNotebookbarConfigPage.cxx
new file mode 100644
index 0000000..d2ef348
--- /dev/null
+++ b/cui/source/customize/SvxNotebookbarConfigPage.cxx
@@ -0,0 +1,446 @@
/* -*- 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 <cassert>
#include <stdlib.h>
#include <time.h>
#include <typeinfo>

#include <vcl/commandinfoprovider.hxx>
#include <vcl/event.hxx>
#include <vcl/help.hxx>
#include <vcl/weld.hxx>
#include <vcl/decoview.hxx>
#include <vcl/virdev.hxx>
#include <vcl/settings.hxx>
#include <vcl/svapp.hxx>

#include <sfx2/app.hxx>
#include <sfx2/sfxdlg.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/viewsh.hxx>
#include <sfx2/msg.hxx>
#include <sfx2/msgpool.hxx>
#include <sfx2/minfitem.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/request.hxx>
#include <sfx2/filedlghelper.hxx>
#include <sfx2/sfxsids.hrc>
#include <svl/stritem.hxx>
#include <svtools/miscopt.hxx>
#include <tools/diagnose_ex.h>

#include <algorithm>
#include <helpids.h>
#include <strings.hrc>

#include <acccfg.hxx>
#include <cfg.hxx>
#include <SvxNotebookbarConfigPage.hxx>
#include <SvxConfigPageHelper.hxx>
#include <dialmgr.hxx>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <osl/file.hxx>
#include <CustomNotebookbarGenerator.hxx>
#include <sfx2/notebookbar/SfxNotebookBar.hxx>
#include <unotools/configmgr.hxx>

#define sTopLevelListBoxID "sTopLevelListBoxID"

static OUString charToString(const char* cString)
{
    OUStringBuffer aString;
    for (unsigned long i = 0; i < strlen(cString); i++)
    {
        aString.append(cString[i]);
    }
    return aString.makeStringAndClear();
}

static OUString getFileName(const OUString& aFileName)
{
    if (aFileName == "notebookbar.ui")
        return "Tabbed";
    else if (aFileName == "notebookbar_compact.ui")
        return "TabbedCompact";
    else if (aFileName == "notebookbar_groupedbar_full.ui")
        return "Groupedbar";
    else if (aFileName == "notebookbar_groupedbar_compact.ui")
        return "GroupedbarCompact";
    else
        return "None";
}

SvxNotebookbarConfigPage::SvxNotebookbarConfigPage(TabPageParent pParent, const SfxItemSet& rSet)
    : SvxConfigPage(pParent, rSet)
{
    m_xDescriptionFieldLb->set_visible(false);
    m_xSearchEdit->set_visible(false);
    m_xDescriptionField->set_visible(false);
    m_xMoveUpButton->set_visible(false);
    m_xMoveDownButton->set_visible(false);
    m_xAddCommandButton->set_visible(false);
    m_xRemoveCommandButton->set_visible(false);
    m_xLeftFunctionLabel->set_visible(false);
    m_xSearchLabel->set_visible(false);
    m_xCategoryLabel->set_visible(false);
    m_xCategoryListBox->set_visible(false);
    m_xInsertBtn->set_visible(false);
    m_xModifyBtn->set_visible(false);
    m_xResetBtn->set_visible(false);
    m_xCustomizeLabel->set_visible(false);

    weld::TreeView& rCommandCategoryBox = m_xFunctions->get_widget();
    rCommandCategoryBox.hide();

    m_xContentsListBox.reset(
        new SvxNotebookbarEntriesListBox(m_xBuilder->weld_tree_view("toolcontents"), this));
    std::vector<int> aWidths;
    weld::TreeView& rTreeView = m_xContentsListBox->get_widget();
    Size aSize(m_xFunctions->get_size_request());
    rTreeView.set_size_request(aSize.Width(), aSize.Height());

    int nExpectedSize = 16;

    int nStandardImageColWidth = rTreeView.get_checkbox_column_width();
    int nMargin = nStandardImageColWidth - nExpectedSize;
    if (nMargin < 16)
        nMargin = 16;

    if (SvxConfigPageHelper::GetImageType() & css::ui::ImageType::SIZE_LARGE)
        nExpectedSize = 24;
    else if (SvxConfigPageHelper::GetImageType() & css::ui::ImageType::SIZE_32)
        nExpectedSize = 32;

    int nImageColWidth = nExpectedSize + nMargin;

    aWidths.push_back(nStandardImageColWidth);
    aWidths.push_back(nImageColWidth);
    rTreeView.set_column_fixed_widths(aWidths);

    rTreeView.set_hexpand(true);
    rTreeView.set_vexpand(true);
    rTreeView.set_help_id(HID_SVX_CONFIG_NOTEBOOKBAR_CONTENTS);
    rTreeView.show();
}

SvxNotebookbarConfigPage::~SvxNotebookbarConfigPage() { disposeOnce(); }

void SvxNotebookbarConfigPage::DeleteSelectedTopLevel() {}

void SvxNotebookbarConfigPage::DeleteSelectedContent() {}

void SvxNotebookbarConfigPage::Init()
{
    m_xTopLevelListBox->clear();
    m_xContentsListBox->clear();
    m_xSaveInListBox->clear();
    CustomNotebookbarGenerator::createCustomizedUIFile();
    OUString sAppName;
    OUString sFileName;
    CustomNotebookbarGenerator::getFileNameAndAppName(sAppName, sFileName);
    OUString sNotebookbarInterface = getFileName(sFileName);

    OUString sScopeName
        = utl::ConfigManager::getProductName() + " " + sAppName + " -  " + sNotebookbarInterface;
    OUString sSaveInListBoxID = notebookbarTabScope;

    m_xSaveInListBox->append(sSaveInListBoxID, sScopeName);
    m_xSaveInListBox->set_active_id(sSaveInListBoxID);

    m_xTopLevelListBox->append(sTopLevelListBoxID, "All Commands");
    m_xTopLevelListBox->set_active_id(sTopLevelListBoxID);
    SelectElement();
}

SaveInData* SvxNotebookbarConfigPage::CreateSaveInData(
    const css::uno::Reference<css::ui::XUIConfigurationManager>& xCfgMgr,
    const css::uno::Reference<css::ui::XUIConfigurationManager>& xParentCfgMgr,
    const OUString& aModuleId, bool bDocConfig)
{
    return static_cast<SaveInData*>(
        new ToolbarSaveInData(xCfgMgr, xParentCfgMgr, aModuleId, bDocConfig));
}

void SvxNotebookbarConfigPage::UpdateButtonStates() {}

short SvxNotebookbarConfigPage::QueryReset()
{
    OUString msg = CuiResId(RID_SVXSTR_CONFIRM_TOOLBAR_RESET);

    OUString saveInName = m_xSaveInListBox->get_active_text();

    OUString label = SvxConfigPageHelper::replaceSaveInName(msg, saveInName);

    std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(
        GetDialogFrameWeld(), VclMessageType::Question, VclButtonsType::YesNo, label));
    int nValue = xQueryBox->run();
    if (nValue == RET_YES)
    {
        OUString sOriginalUIPath = CustomNotebookbarGenerator::getOriginalUIPath();
        OUString sCustomizedUIPath = CustomNotebookbarGenerator::getCustomizedUIPath();
        osl::File::copy(sOriginalUIPath, sCustomizedUIPath);
        OUString sAppName;
        OUString sFileName;
        CustomNotebookbarGenerator::getFileNameAndAppName(sAppName, sFileName);
        OUString sNotebookbarInterface = getFileName(sFileName);
        Sequence<OUString> sSequenceEntries;
        CustomNotebookbarGenerator::setCustomizedUIItem(sSequenceEntries, sNotebookbarInterface);
    }
    return nValue;
}

void SvxConfigPage::InsertEntryIntoNotebookbarTabUI(OUString& sUIItemID, OUString& sUIItemCommand,
                                                    int nPos, int nStartCol, int nSpace)
{
    auto xImage = GetSaveInData()->GetImage(sUIItemCommand);
    if (xImage.is())
        m_xContentsListBox->set_image(nPos, xImage, nStartCol);
    OUStringBuffer sDataInTree;
    for (int nIdx = 0; nIdx < nSpace; nIdx++)
        sDataInTree.append("    ");
    sDataInTree.append("-> ");
    sDataInTree.append(sUIItemID);
    m_xContentsListBox->set_text(nPos, sDataInTree.makeStringAndClear(), nStartCol + 1);
}

void SvxNotebookbarConfigPage::getNodeValue(xmlNode* pNodePtr, NotebookbarEntries& aNodeEntries)
{
    pNodePtr = pNodePtr->xmlChildrenNode;
    while (pNodePtr)
    {
        if (!(xmlStrcmp(pNodePtr->name, reinterpret_cast<const xmlChar*>("property"))))
        {
            xmlChar* UriValue = xmlGetProp(pNodePtr, reinterpret_cast<const xmlChar*>("name"));
            if (!(xmlStrcmp(UriValue, reinterpret_cast<const xmlChar*>("visible"))))
            {
                xmlChar* aValue = xmlNodeGetContent(pNodePtr);
                const char* cVisibleValue = reinterpret_cast<const char*>(aValue);
                OUString sVisibleValue = charToString(cVisibleValue);
                aNodeEntries.sVisibleValue = sVisibleValue;
                xmlFree(aValue);
            }
            if (!(xmlStrcmp(UriValue, reinterpret_cast<const xmlChar*>("action_name"))))
            {
                xmlChar* aValue = xmlNodeGetContent(pNodePtr);
                const char* cActionName = reinterpret_cast<const char*>(aValue);
                OUString sActionName = charToString(cActionName);
                aNodeEntries.sActionName = sActionName;
                xmlFree(aValue);
            }
            xmlFree(UriValue);
        }
        pNodePtr = pNodePtr->next;
    }
}

void SvxNotebookbarConfigPage::searchNodeandAttribute(std::vector<NotebookbarEntries>& aEntries,
                                                      xmlNode* pNodePtr, int nPos)
{
    pNodePtr = pNodePtr->xmlChildrenNode;
    while (pNodePtr)
    {
        if (pNodePtr->type == XML_ELEMENT_NODE)
        {
            const char* cNodeName = reinterpret_cast<const char*>(pNodePtr->name);
            OUString sNodeName = charToString(cNodeName);
            if (sNodeName == "object")
            {
                xmlChar* UriValue = xmlGetProp(pNodePtr, reinterpret_cast<const xmlChar*>("id"));
                const char* cUIItemID = reinterpret_cast<const char*>(UriValue);
                OUString sUIItemID = charToString(cUIItemID);
                xmlFree(UriValue);

                UriValue = xmlGetProp(pNodePtr, reinterpret_cast<const xmlChar*>("class"));
                const char* cClassId = reinterpret_cast<const char*>(UriValue);
                OUString sClassId = charToString(cClassId);
                xmlFree(UriValue);

                NotebookbarEntries nodeEntries;

                if (sClassId == "GtkMenuItem" || sClassId == "GtkToolButton")
                {
                    nodeEntries.sUIItemID = sUIItemID;
                    nodeEntries.nPos = nPos;
                    getNodeValue(pNodePtr, nodeEntries);
                    aEntries.push_back(nodeEntries);
                }
                else
                {
                    nodeEntries.sUIItemID = sUIItemID;
                    nodeEntries.nPos = nPos;
                    nodeEntries.sVisibleValue = "Null";
                    nodeEntries.sActionName = "Null";
                    aEntries.push_back(nodeEntries);
                }
            }
            searchNodeandAttribute(aEntries, pNodePtr, nPos + 1);
        }
        pNodePtr = pNodePtr->next;
    }
}

void SvxNotebookbarConfigPage::FillFunctionsList(std::vector<NotebookbarEntries>& aEntries)
{
    OUString sUIFilePath = CustomNotebookbarGenerator::getCustomizedUIPath();
    sal_uInt32 nflag = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write;
    osl::File aFile(sUIFilePath);
    if (aFile.open(nflag) == osl::FileBase::E_None)
    {
        xmlDocPtr pDoc;
        xmlNodePtr pNodePtr;
        char* cUIFileUIPath = CustomNotebookbarGenerator::convertToCharPointer(sUIFilePath);
        pDoc = xmlParseFile(cUIFileUIPath);
        pNodePtr = xmlDocGetRootElement(pDoc);
        int aRightPos = 0;
        searchNodeandAttribute(aEntries, pNodePtr, aRightPos);
        if (pDoc != nullptr)
        {
            xmlFreeDoc(pDoc);
        }
        delete[] cUIFileUIPath;
    }
}

void SvxNotebookbarConfigPage::SelectElement()
{
    m_xContentsListBox->clear();
    std::vector<NotebookbarEntries> aEntries;
    FillFunctionsList(aEntries);

    sal_Int64 nId = 0;
    for (unsigned long nIdx = 0; nIdx < aEntries.size(); nIdx++)
    {
        OUString sId(OUString::number(nId));
        m_xContentsListBox->insert(nIdx, sId);
        if (aEntries[nIdx].sActionName != "Null")
        {
            if (aEntries[nIdx].sVisibleValue == "True")
            {
                m_xContentsListBox->set_toggle(nIdx, TRISTATE_TRUE, 0);
            }
            else
            {
                m_xContentsListBox->set_toggle(nIdx, TRISTATE_FALSE, 0);
            }
        }
        InsertEntryIntoNotebookbarTabUI(aEntries[nIdx].sUIItemID, aEntries[nIdx].sActionName, nIdx,
                                        1, aEntries[nIdx].nPos);
        ++nId;
    }
    aEntries.clear();
}

SvxNotebookbarEntriesListBox::SvxNotebookbarEntriesListBox(std::unique_ptr<weld::TreeView> xParent,
                                                           SvxConfigPage* pPg)
    : SvxMenuEntriesListBox(std::move(xParent), pPg)
{
    m_xControl->connect_toggled(LINK(this, SvxNotebookbarEntriesListBox, CheckButtonHdl));
    m_xControl->connect_key_press(Link<const KeyEvent&, bool>());
    m_xControl->connect_key_press(LINK(this, SvxNotebookbarEntriesListBox, KeyInputHdl));
}

SvxNotebookbarEntriesListBox::~SvxNotebookbarEntriesListBox() {}

static OUString getUIItemID(OUString sString)
{
    sal_Int32 rPos = 1;
    sString = sString.getToken(rPos, '>', rPos);
    OUStringBuffer sUIItemID;
    for (int nIdx = 1; nIdx < sString.getLength(); nIdx++)
    {
        sUIItemID.append(sString[nIdx]);
    }
    return sUIItemID.makeStringAndClear();
}

static void EditRegistryFile(OUString& sUIItemID, OUString& sSetEntry)
{
    int nFlag = 0;
    OUString sAppName;
    OUString sFileName;
    CustomNotebookbarGenerator::getFileNameAndAppName(sAppName, sFileName);
    OUString sNotebookbarInterface = getFileName(sFileName);
    Sequence<OUString> aOldEntries
        = CustomNotebookbarGenerator::getCustomizedUIItem(sNotebookbarInterface);
    Sequence<OUString> aNewEntries(aOldEntries.getLength() + 1);
    for (int nIdx = 0; nIdx < aOldEntries.getLength(); nIdx++)
    {
        sal_Int32 rPos = 0;
        OUString sFirstValue = aOldEntries[nIdx].getToken(rPos, ',', rPos);
        if (sFirstValue == sUIItemID)
        {
            aOldEntries[nIdx] = sSetEntry;
            nFlag = 1;
            break;
        }
        aNewEntries[nIdx] = aOldEntries[nIdx];
    }

    if (nFlag == 0)
    {
        aNewEntries[aOldEntries.getLength()] = sSetEntry;
        CustomNotebookbarGenerator::setCustomizedUIItem(aNewEntries, sNotebookbarInterface);
    }
    else
    {
        CustomNotebookbarGenerator::setCustomizedUIItem(aOldEntries, sNotebookbarInterface);
    }
}

void SvxNotebookbarEntriesListBox::ChangedVisibility(int nRow)
{
    OUString sUIItemID = m_xControl->get_selected_text();
    OUString sVisible;
    sUIItemID = getUIItemID(sUIItemID);
    if (m_xControl->get_toggle(nRow, 0) == TRISTATE_TRUE)
        sVisible = "True";
    else
        sVisible = "False";
    OUString sSetEntries = sUIItemID + ",visible," + sVisible;
    Sequence<OUString> sSeqOfEntries(1);
    sSeqOfEntries[0] = sSetEntries;
    EditRegistryFile(sUIItemID, sSetEntries);
    CustomNotebookbarGenerator::modifyCustomizedUIFile(sSeqOfEntries);
}

IMPL_LINK(SvxNotebookbarEntriesListBox, CheckButtonHdl, const row_col&, rRowCol, void)
{
    ChangedVisibility(rRowCol.first);
}

IMPL_LINK(SvxNotebookbarEntriesListBox, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
{
    if (rKeyEvent.GetKeyCode() == KEY_SPACE)
    {
        int nRow = m_xControl->get_selected_index();
        m_xControl->set_toggle(
            nRow, m_xControl->get_toggle(nRow, 0) == TRISTATE_TRUE ? TRISTATE_FALSE : TRISTATE_TRUE,
            0);
        ChangedVisibility(nRow);
        return true;
    }
    return SvxMenuEntriesListBox::KeyInputHdl(rKeyEvent);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cui/source/customize/cfg.cxx b/cui/source/customize/cfg.cxx
index d250a89..d89aaa9 100644
--- a/cui/source/customize/cfg.cxx
+++ b/cui/source/customize/cfg.cxx
@@ -61,6 +61,7 @@
#include <cfg.hxx>
#include <SvxMenuConfigPage.hxx>
#include <SvxToolbarConfigPage.hxx>
#include <SvxNotebookbarConfigPage.hxx>
#include <SvxConfigPageHelper.hxx>
#include "eventdlg.hxx"
#include <dialmgr.hxx>
@@ -191,6 +192,12 @@ static VclPtr<SfxTabPage> CreateKeyboardConfigPage( TabPageParent pParent, const
       return VclPtr<SfxAcceleratorConfigPage>::Create(pParent, *rSet);
}

static VclPtr<SfxTabPage> CreateSvxNotebookbarConfigPage(TabPageParent pParent,
                                                         const SfxItemSet* rSet)
{
    return VclPtr<SvxNotebookbarConfigPage>::Create(pParent, *rSet);
}

static VclPtr<SfxTabPage> CreateSvxToolbarConfigPage( TabPageParent pParent, const SfxItemSet* rSet )
{
    return VclPtr<SvxToolbarConfigPage>::Create(pParent, *rSet);
@@ -215,6 +222,7 @@ SvxConfigDialog::SvxConfigDialog(weld::Window * pParent, const SfxItemSet* pInSe

    AddTabPage("menus", CreateSvxMenuConfigPage, nullptr);
    AddTabPage("toolbars", CreateSvxToolbarConfigPage, nullptr);
    AddTabPage("notebookbar", CreateSvxNotebookbarConfigPage, nullptr);
    AddTabPage("contextmenus", CreateSvxContextMenuConfigPage, nullptr);
    AddTabPage("keyboard", CreateKeyboardConfigPage, nullptr);
    AddTabPage("events", CreateSvxEventConfigPage, nullptr);
@@ -233,18 +241,28 @@ SvxConfigDialog::SvxConfigDialog(weld::Window * pParent, const SfxItemSet* pInSe
    }
}

void SvxConfigDialog::SetFrame(const css::uno::Reference< css::frame::XFrame >& xFrame)
void SvxConfigDialog::SetFrame(const css::uno::Reference<css::frame::XFrame>& xFrame)
{
    m_xFrame = xFrame;
    uno::Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext(),
                                                    uno::UNO_SET_THROW);

    if (!SvxConfigPageHelper::showKeyConfigTabPage( xFrame ))
    OUString aModuleId = SvxConfigPage::GetFrameWithDefaultAndIdentify(m_xFrame);
    uno::Reference<css::frame::XModuleManager2> xModuleManager(
        css::frame::ModuleManager::create(xContext));
    OUString aModuleName = SvxConfigPageHelper::GetUIModuleName(aModuleId, xModuleManager);
    if (aModuleName != "Writer" && aModuleName != "Calc" && aModuleName != "Impress"
        && aModuleName != "Draw")
        RemoveTabPage("notebookbar");

    if (!SvxConfigPageHelper::showKeyConfigTabPage(xFrame))
        RemoveTabPage("keyboard");
}

void SvxConfigDialog::PageCreated(const OString &rId, SfxTabPage& rPage)
{
    if (rId == "menus" || rId == "keyboard" ||
        rId == "toolbars" || rId == "contextmenus")
    if (rId == "menus" || rId == "keyboard" || rId == "notebookbar"
        || rId == "toolbars" || rId == "contextmenus")
    {
        rPage.SetFrame(m_xFrame);
    }
@@ -973,11 +991,16 @@ SvxConfigPage::SvxConfigPage(TabPageParent pParent, const SfxItemSet& rSet)
    , m_aUpdateDataTimer("UpdateDataTimer")
    , bInitialised(false)
    , pCurrentSaveInData(nullptr)
    , m_xSearchEdit(m_xBuilder->weld_entry("searchEntry"))
    , m_xCommandCategoryListBox(new CommandCategoryListBox(m_xBuilder->weld_combo_box("commandcategorylist")))
    , m_xFunctions(new CuiConfigFunctionListBox(m_xBuilder->weld_tree_view("functions")))
    , m_xCategoryLabel(m_xBuilder->weld_label("categorylabel"))
    , m_xCategoryListBox(m_xBuilder->weld_combo_box("commandcategorylist"))
    , m_xDescriptionFieldLb(m_xBuilder->weld_label("descriptionlabel"))
    , m_xDescriptionField(m_xBuilder->weld_text_view("desc"))
    , m_xLeftFunctionLabel(m_xBuilder->weld_label("leftfunctionlabel"))
    , m_xSearchEdit(m_xBuilder->weld_entry("searchEntry"))
    , m_xSearchLabel(m_xBuilder->weld_label("searchlabel"))
    , m_xCustomizeLabel(m_xBuilder->weld_label("customizelabel"))
    , m_xTopLevelListBox(m_xBuilder->weld_combo_box("toplevellist"))
    , m_xMoveUpButton(m_xBuilder->weld_button("up"))
    , m_xMoveDownButton(m_xBuilder->weld_button("down"))
@@ -1312,8 +1335,8 @@ bool SvxConfigPage::FillItemSet( SfxItemSet* )
    {
        SaveInData* pData =
            reinterpret_cast<SaveInData*>(m_xSaveInListBox->get_id(i).toInt64());

        result = pData->Apply();
        if(m_xSaveInListBox->get_id(i) != notebookbarTabScope)
            result = pData->Apply();
    }
    return result;
}
diff --git a/cui/source/inc/CustomNotebookbarGenerator.hxx b/cui/source/inc/CustomNotebookbarGenerator.hxx
index 89276a1..21424d0 100644
--- a/cui/source/inc/CustomNotebookbarGenerator.hxx
+++ b/cui/source/inc/CustomNotebookbarGenerator.hxx
@@ -29,6 +29,7 @@ class CustomNotebookbarGenerator
public:
    CustomNotebookbarGenerator();
    static OUString getCustomizedUIPath();
    static OUString getOriginalUIPath();
    static char* convertToCharPointer(const OUString& sString);
    static Sequence<OUString> getCustomizedUIItem(OUString sNotebookbarConfigType);
    static void getFileNameAndAppName(OUString& sAppName, OUString& sNotebookbarUIFileName);
diff --git a/cui/source/inc/SvxNotebookbarConfigPage.hxx b/cui/source/inc/SvxNotebookbarConfigPage.hxx
new file mode 100644
index 0000000..97ae96b
--- /dev/null
+++ b/cui/source/inc/SvxNotebookbarConfigPage.hxx
@@ -0,0 +1,85 @@
/* -*- 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 .
 */
#ifndef INCLUDED_CUI_SOURCE_INC_SVXNOTEBOOKBARCONFIGPAGE_HXX
#define INCLUDED_CUI_SOURCE_INC_SVXNOTEBOOKBARCONFIGPAGE_HXX

#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <vcl/weld.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/container/XIndexContainer.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/ui/XUIConfigurationListener.hpp>
#include <com/sun/star/ui/XUIConfigurationManager.hpp>
#include <com/sun/star/ui/XImageManager.hpp>
#include <com/sun/star/frame/XFrame.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/lang/XSingleComponentFactory.hpp>

#include <sfx2/tabdlg.hxx>
#include <memory>
#include <vector>

//#include "cfgutil.hxx"
#include "cfg.hxx" //for SvxConfigPage and SaveInData

class SvxNotebookbarConfigPage : public SvxConfigPage
{
private:
    void UpdateButtonStates() override;
    short QueryReset() override;
    void Init() override;
    void DeleteSelectedContent() override;
    void DeleteSelectedTopLevel() override;
    virtual void SelectElement() override;

public:
    struct NotebookbarEntries
    {
        OUString sUIItemID;
        OUString sActionName;
        OUString sVisibleValue;
        int nPos;
    };
    SvxNotebookbarConfigPage(TabPageParent pParent, const SfxItemSet& rItemSet);
    virtual ~SvxNotebookbarConfigPage() override;
    SaveInData* CreateSaveInData(const css::uno::Reference<css::ui::XUIConfigurationManager>&,
                                 const css::uno::Reference<css::ui::XUIConfigurationManager>&,
                                 const OUString& aModuleId, bool docConfig) override;
    static void FillFunctionsList(std::vector<NotebookbarEntries>& aEntries);
    static void searchNodeandAttribute(std::vector<NotebookbarEntries>& aEntries, xmlNode* pNodePtr,
                                       int nPos);
    static void getNodeValue(xmlNode* pNodePtr, NotebookbarEntries& aNodeEntries);
};

class SvxNotebookbarEntriesListBox final : public SvxMenuEntriesListBox
{
    void ChangedVisibility(int nRow);
    typedef std::pair<int, int> row_col;
    DECL_LINK(CheckButtonHdl, const row_col&, void);
    DECL_LINK(KeyInputHdl, const KeyEvent&, bool);

public:
    SvxNotebookbarEntriesListBox(std::unique_ptr<weld::TreeView> xControl, SvxConfigPage* pPg);
    virtual ~SvxNotebookbarEntriesListBox() override;
};

#endif // INCLUDED_CUI_SOURCE_INC_SVXNOTEBOOKBARCONFIGPAGE_HXX

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/cui/source/inc/cfg.hxx b/cui/source/inc/cfg.hxx
index eba2c91..2a88e26 100644
--- a/cui/source/inc/cfg.hxx
+++ b/cui/source/inc/cfg.hxx
@@ -45,6 +45,8 @@
#include "cfgutil.hxx"
#include "CommandCategoryListBox.hxx"

#define notebookbarTabScope "notebookbarTabScope"

static const char ITEM_DESCRIPTOR_COMMANDURL[]  = "CommandURL";
static const char ITEM_DESCRIPTOR_CONTAINER[]   = "ItemDescriptorContainer";
static const char ITEM_DESCRIPTOR_LABEL[]       = "Label";
@@ -380,15 +382,21 @@ protected:

    // Left side of the dialog where command categories and the available
    // commands in them are displayed as a searchable list
    std::unique_ptr<weld::Entry>               m_xSearchEdit;
    std::unique_ptr<CommandCategoryListBox>    m_xCommandCategoryListBox;
    std::unique_ptr<CuiConfigFunctionListBox>  m_xFunctions;

    std::unique_ptr<weld::Label>               m_xCategoryLabel;
    std::unique_ptr<weld::ComboBox>            m_xCategoryListBox;
    std::unique_ptr<weld::Label>               m_xDescriptionFieldLb;
    std::unique_ptr<weld::TextView>            m_xDescriptionField;
    std::unique_ptr<weld::Label>               m_xLeftFunctionLabel;
    std::unique_ptr<weld::Entry>               m_xSearchEdit;
    std::unique_ptr<weld::Label>               m_xSearchLabel;


    // Right side of the dialog where the contents of the selected
    // menu or toolbar are displayed
    std::unique_ptr<weld::Label>               m_xCustomizeLabel;
    std::unique_ptr<weld::ComboBox>            m_xTopLevelListBox;
    // Used to add and remove toolbars/menus
    std::unique_ptr<weld::MenuButton>          m_xGearBtn;
@@ -440,6 +448,8 @@ protected:

    void                InsertEntryIntoUI(SvxConfigEntry* pNewEntryData,
                                          int nPos, int nStartCol);
    void InsertEntryIntoNotebookbarTabUI(OUString& sUIItemId, OUString& sUIItemCommand, int nPos,
                                         int nStartCol, int nSpace);

    SvxEntries*     FindParentForChild( SvxEntries* pParentEntries,
                                        SvxConfigEntry* pChildData );
diff --git a/cui/source/inc/helpids.h b/cui/source/inc/helpids.h
index 318abb3..5f65df4 100644
--- a/cui/source/inc/helpids.h
+++ b/cui/source/inc/helpids.h
@@ -28,6 +28,7 @@
#define HID_OFADLG_TREELISTBOX "CUI_HID_OFADLG_TREELISTBOX"
#define HID_SVX_CONFIG_TOOLBAR "CUI_HID_SVX_CONFIG_TOOLBAR"
#define HID_SVX_CONFIG_TOOLBAR_CONTENTS "CUI_HID_SVX_CONFIG_TOOLBAR_CONTENTS"
#define HID_SVX_CONFIG_NOTEBOOKBAR_CONTENTS "CUI_HID_SVX_CONFIG_NOTEBOOKBAR_CONTENTS"
#define HID_HANGULDLG_SUGGESTIONS_GRID "CUI_HID_HANGULDLG_SUGGESTIONS_GRID"
#define HID_HANGULDLG_SUGGESTIONS_LIST "CUI_HID_HANGULDLG_SUGGESTIONS_LIST"
#define HID_SVX_CONFIG_NAME_SUBMENU "CUI_HID_SVX_CONFIG_NAME_SUBMENU"
diff --git a/cui/uiconfig/ui/customizedialog.ui b/cui/uiconfig/ui/customizedialog.ui
index c37772a..6d11653 100644
--- a/cui/uiconfig/ui/customizedialog.ui
+++ b/cui/uiconfig/ui/customizedialog.ui
@@ -224,10 +224,10 @@
              </packing>
            </child>
            <child type="tab">
              <object class="GtkLabel" id="contextmenus">
              <object class="GtkLabel" id="notebookbar">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes" context="customizedialog|contextmenus">Context Menus</property>
                <property name="label" translatable="yes" context="customizedialog|notebookbar">Notebookbar</property>
              </object>
              <packing>
                <property name="position">2</property>
@@ -271,10 +271,10 @@
              </packing>
            </child>
            <child type="tab">
              <object class="GtkLabel" id="keyboard">
              <object class="GtkLabel" id="contextmenus">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes" context="customizedialog|keyboard">Keyboard</property>
                <property name="label" translatable="yes" context="customizedialog|contextmenus">Context Menus</property>
              </object>
              <packing>
                <property name="position">3</property>
@@ -318,13 +318,60 @@
              </packing>
            </child>
            <child type="tab">
              <object class="GtkLabel" id="keyboard">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes" context="customizedialog|keyboard">Keyboard</property>
              </object>
              <packing>
                <property name="position">4</property>
                <property name="tab_fill">False</property>
              </packing>
            </child>
            <child>
              <object class="GtkGrid">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <child>
                  <placeholder/>
                </child>
                <child>
                  <placeholder/>
                </child>
                <child>
                  <placeholder/>
                </child>
                <child>
                  <placeholder/>
                </child>
                <child>
                  <placeholder/>
                </child>
                <child>
                  <placeholder/>
                </child>
                <child>
                  <placeholder/>
                </child>
                <child>
                  <placeholder/>
                </child>
                <child>
                  <placeholder/>
                </child>
              </object>
              <packing>
                <property name="position">5</property>
              </packing>
            </child>
            <child type="tab">
              <object class="GtkLabel" id="events">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes" context="customizedialog|events">Events</property>
              </object>
              <packing>
                <property name="position">4</property>
                <property name="position">5</property>
                <property name="tab_fill">False</property>
              </packing>
            </child>
@@ -344,4 +391,4 @@
      <action-widget response="101">reset</action-widget>
    </action-widgets>
  </object>
</interface>
</interface>
\ No newline at end of file