tdf#152078 Add a Zoom slider to the Basic IDE

This patch adds a ZoomSlider to thee Basic IDE. It has the following characteristics:
- The Zoom varies from 50% to 400% considering that 100% is the font size defined in Tools - Options - Fonts dialog.
- All open editor windows use the same Zoom factor
- The zoom level is saved and restored across sessions

Change-Id: I63df02d0dc828fca4360d61b8aa2c7af6610d4db
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143159
Tested-by: Jenkins
Tested-by: Heiko Tietze <heiko.tietze@documentfoundation.org>
Reviewed-by: Heiko Tietze <heiko.tietze@documentfoundation.org>
diff --git a/basctl/sdi/baside.sdi b/basctl/sdi/baside.sdi
index 192b6cd..c1fc9b4 100644
--- a/basctl/sdi/baside.sdi
+++ b/basctl/sdi/baside.sdi
@@ -331,6 +331,16 @@ shell basctl_Shell
        StateMethod = GetState;
    ]

    SID_ATTR_ZOOMSLIDER
    [
        ExecMethod      = ExecuteGlobal;
        StateMethod     = GetState;
    ]

    SID_BASICIDE_CURRENT_ZOOM
    [
        StateMethod     = GetState;
    ]

    // Only, when BasicWindow up

@@ -657,4 +667,3 @@ shell basctl_DocShell
{
    import BasicIDEDocument;
}

diff --git a/basctl/source/basicide/baside2.hxx b/basctl/source/basicide/baside2.hxx
index 38ca2ae..969f823 100644
--- a/basctl/source/basicide/baside2.hxx
+++ b/basctl/source/basicide/baside2.hxx
@@ -97,6 +97,7 @@ private:

    void            ImpDoHighlight( sal_uInt32 nLineOff );
    void            ImplSetFont();
    sal_uInt16      nCurrentZoomLevel;

    bool            bHighlighting;
    bool            bDoSyntaxHighlight;
@@ -154,6 +155,9 @@ public:
    void            ChangeFontColor( Color aColor );
    void            UpdateSyntaxHighlighting ();

    void            SetEditorZoomLevel(sal_uInt16 nNewZoomLevel);
    sal_uInt16      GetCurrentZoom() { return nCurrentZoomLevel; }

    bool            GetProcedureName(std::u16string_view rLine, OUString& rProcType, OUString& rProcName) const;

    FactoryFunction GetUITestFactory() const override;
diff --git a/basctl/source/basicide/baside2b.cxx b/basctl/source/basicide/baside2b.cxx
index 2f768ed..89edcec 100644
--- a/basctl/source/basicide/baside2b.cxx
+++ b/basctl/source/basicide/baside2b.cxx
@@ -40,6 +40,7 @@
#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
#include <com/sun/star/script/XLibraryContainer2.hpp>
#include <comphelper/string.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <o3tl/string_view.hxx>
#include <officecfg/Office/Common.hxx>
#include <sfx2/dispatch.hxx>
@@ -262,6 +263,10 @@ EditorWindow::EditorWindow (vcl::Window* pParent, ModulWindow* pModulWindow) :
        std::unique_lock g(mutex_);
        notifier_ = n;
    }

    // The zoom level applied to the editor window is the zoom slider value in the shell
    nCurrentZoomLevel = GetShell()->GetCurrentZoomSliderValue();

    const Sequence<OUString> aPropertyNames{"FontHeight", "FontName"};
    n->addPropertiesChangeListener(aPropertyNames, listener_);
}
@@ -1232,6 +1237,7 @@ void EditorWindow::UpdateSyntaxHighlighting ()

void EditorWindow::ImplSetFont()
{
    // Get default font name and height defined in the Options dialog
    OUString sFontName(officecfg::Office::Common::Font::SourceViewFont::FontName::get().value_or(OUString()));
    if (sFontName.isEmpty())
    {
@@ -1240,7 +1246,12 @@ void EditorWindow::ImplSetFont()
                                                        GetDefaultFontFlags::NONE, GetOutDev()));
        sFontName = aTmpFont.GetFamilyName();
    }
    Size aFontSize(0, officecfg::Office::Common::Font::SourceViewFont::FontHeight::get());
    sal_uInt16 nDefaultFontHeight = officecfg::Office::Common::Font::SourceViewFont::FontHeight::get();

    // Calculate font size considering zoom level
    sal_uInt16 nNewFontHeight = nDefaultFontHeight * (static_cast<float>(nCurrentZoomLevel) / 100);
    Size aFontSize(0, nNewFontHeight);

    vcl::Font aFont(sFontName, aFontSize);
    aFont.SetColor(rModulWindow.GetLayout().GetFontColor());
    SetPointFont(*GetOutDev(), aFont); // FIXME RenderContext
@@ -1248,6 +1259,7 @@ void EditorWindow::ImplSetFont()

    rModulWindow.GetBreakPointWindow().SetFont(aFont);
    rModulWindow.GetLineNumberWindow().SetFont(aFont);
    rModulWindow.Invalidate();

    if (pEditEngine)
    {
@@ -1255,6 +1267,25 @@ void EditorWindow::ImplSetFont()
        pEditEngine->SetFont(aFont);
        pEditEngine->SetModified(bModified);
    }

    // Update controls
    if (SfxBindings* pBindings = GetBindingsPtr())
    {
        pBindings->Invalidate( SID_BASICIDE_CURRENT_ZOOM );
        pBindings->Invalidate( SID_ATTR_ZOOMSLIDER );
    }
}

void EditorWindow::SetEditorZoomLevel(sal_uInt16 nNewZoomLevel)
{
    if (nCurrentZoomLevel == nNewZoomLevel)
        return;

    if (nNewZoomLevel < MIN_ZOOM_LEVEL || nNewZoomLevel > MAX_ZOOM_LEVEL)
        return;

    nCurrentZoomLevel = nNewZoomLevel;
    ImplSetFont();
}

void EditorWindow::DoSyntaxHighlight( sal_uInt32 nPara )
@@ -1976,10 +2007,9 @@ void ComplexEditorWindow::Resize()

    Size aBrkSz(nBrkWidth, aSz.Height());

    Size aLnSz(aLineNumberWindow->GetWidth(), aSz.Height());

    if (aLineNumberWindow->IsVisible())
    {
        Size aLnSz(aLineNumberWindow->GetWidth(), aSz.Height());
        aBrkWindow->SetPosSizePixel( Point( DWBORDER, DWBORDER ), aBrkSz );
        aLineNumberWindow->SetPosSizePixel(Point(DWBORDER + aBrkSz.Width() - 1, DWBORDER), aLnSz);
        Size aEWSz(aSz.Width() - nBrkWidth - aLineNumberWindow->GetWidth() - nSBWidth + 2, aSz.Height());
diff --git a/basctl/source/basicide/basides1.cxx b/basctl/source/basicide/basides1.cxx
index 3c5cbd0..3f4e9f7 100644
--- a/basctl/source/basicide/basides1.cxx
+++ b/basctl/source/basicide/basides1.cxx
@@ -56,6 +56,8 @@
#include <vcl/textview.hxx>
#include <vcl/svapp.hxx>
#include <vcl/weld.hxx>
#include <svx/zoomsliderctrl.hxx>
#include <svx/zoomslideritem.hxx>

constexpr sal_Int32 TAB_HEIGHT_MARGIN = 10;

@@ -763,6 +765,31 @@ void Shell::ExecuteGlobal( SfxRequest& rReq )
        }
        break;

        case SID_ATTR_ZOOMSLIDER:
        {
            const SfxItemSet *pArgs = rReq.GetArgs();
            const SfxPoolItem* pItem;

            if ( pArgs && pArgs->GetItemState(SID_ATTR_ZOOMSLIDER, true, &pItem ) == SfxItemState::SET )
            {
                nCurrentZoomSliderValue = static_cast<const SvxZoomSliderItem*>(pItem)->GetValue();
                // Apply zoom to all open windows
                for (auto const& window : aWindowTable)
                {
                    ModulWindow* pModuleWindow = dynamic_cast<ModulWindow*>(window.second.get());
                    if (pModuleWindow)
                    {
                        EditorWindow& pEditorWindow = pModuleWindow->GetEditorWindow();
                        pEditorWindow.SetEditorZoomLevel(nCurrentZoomSliderValue);
                    }
                }

                if (SfxBindings* pBindings = GetBindingsPtr())
                    pBindings->Invalidate( SID_BASICIDE_CURRENT_ZOOM );
            }
        }
        break;

        default:
            if (pLayout)
                pLayout->ExecuteGlobal(rReq);
@@ -1012,6 +1039,19 @@ void Shell::GetState(SfxItemSet &rSet)
                }
            }
            break;
            case SID_BASICIDE_CURRENT_ZOOM:
            {
                // The current zoom value is only visible in a module window
                ModulWindow* pModuleWindow = dynamic_cast<ModulWindow*>(pCurWin.get());
                if (pModuleWindow)
                {
                    OUString sZoom;
                    sZoom = OUString::number(nCurrentZoomSliderValue) + "%";
                    SfxStringItem aItem( SID_BASICIDE_CURRENT_ZOOM, sZoom );
                    rSet.Put( aItem );
                }
            }
            break;
            // are interpreted by the controller:
            case SID_ATTR_SIZE:
            case SID_ATTR_INSERT:
@@ -1110,6 +1150,20 @@ void Shell::GetState(SfxItemSet &rSet)
                    rSet.DisableItem(nWh);
            }
            break;

            case SID_ATTR_ZOOMSLIDER:
            {
                // The zoom slider is only visible in a module window
                ModulWindow* pModuleWindow = dynamic_cast<ModulWindow*>(pCurWin.get());
                if (pModuleWindow)
                {
                    SvxZoomSliderItem aZoomSliderItem(GetCurrentZoomSliderValue(), GetMinZoom(), GetMaxZoom());
                    aZoomSliderItem.AddSnappingPoint(100);
                    rSet.Put( aZoomSliderItem );
                }
            }
            break;

            default:
                if (pLayout)
                    pLayout->GetState(rSet, nWh);
diff --git a/basctl/source/basicide/basidesh.cxx b/basctl/source/basicide/basidesh.cxx
index 5fa37b3..bc1204c 100644
--- a/basctl/source/basicide/basidesh.cxx
+++ b/basctl/source/basicide/basidesh.cxx
@@ -21,6 +21,8 @@

#include <comphelper/diagnose_ex.hxx>
#include <basic/basmgr.hxx>
#include <svx/zoomsliderctrl.hxx>
#include <svx/zoomslideritem.hxx>
#include <svx/svxids.hrc>
#include <iderid.hxx>
#include <strings.hrc>
@@ -43,6 +45,7 @@
#include <sfx2/viewfrm.hxx>
#include <svl/srchitem.hxx>
#include <tools/debug.hxx>
#include <unotools/viewoptions.hxx>

#if defined(DISABLE_DYNLOADING) || ENABLE_MERGELIBS
/* Avoid clash with the ones from svx/source/form/typemap.cxx */
@@ -74,6 +77,8 @@

namespace basctl
{
constexpr OUStringLiteral BASIC_IDE_EDITOR_WINDOW = u"BasicIDEEditorWindow";
constexpr OUStringLiteral BASIC_IDE_CURRENT_ZOOM = u"CurrentZoom";

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star;
@@ -185,6 +190,7 @@ void Shell::Init()

    LibBoxControl::RegisterControl( SID_BASICIDE_LIBSELECTOR );
    LanguageBoxControl::RegisterControl( SID_BASICIDE_CURRENT_LANG );
    SvxZoomSliderControl::RegisterControl( SID_ATTR_ZOOMSLIDER );

    GetViewFrame()->GetWindow().SetBackground(
        GetViewFrame()->GetWindow().GetSettings().GetStyleSettings().GetWindowColor()
@@ -199,6 +205,7 @@ void Shell::Init()
    nCurKey = 100;
    InitScrollBars();
    InitTabBar();
    InitZoomLevel();

    SetCurLib( ScriptDocument::getApplicationScriptDocument(), "Standard", false, false );

@@ -253,6 +260,10 @@ Shell::~Shell()
    pDialogLayout.disposeAndClear();
    pModulLayout.disposeAndClear();
    pTabBar.disposeAndClear();

    // Remember current zoom level
    SvtViewOptions(EViewType::Window, BASIC_IDE_EDITOR_WINDOW).SetUserItem(
        BASIC_IDE_CURRENT_ZOOM, Any(nCurrentZoomSliderValue));
}

void Shell::onDocumentCreated( const ScriptDocument& /*_rDocument*/ )
@@ -355,6 +366,21 @@ void Shell::onDocumentModeChanged( const ScriptDocument& _rDocument )
    }
}

void Shell::InitZoomLevel()
{
    nCurrentZoomSliderValue = DEFAULT_ZOOM_LEVEL;
    SvtViewOptions aWinOpt(EViewType::Window, BASIC_IDE_EDITOR_WINDOW);
    if (aWinOpt.Exists())
    {
        try
        {
            aWinOpt.GetUserItem(BASIC_IDE_CURRENT_ZOOM) >>= nCurrentZoomSliderValue;
        }
        catch(const css::container::NoSuchElementException&)
        { TOOLS_WARN_EXCEPTION("basctl.basicide", "Zoom level not defined"); }
    }
}

void Shell::StoreAllWindowData( bool bPersistent )
{
    for (auto const& window : aWindowTable)
diff --git a/basctl/source/basicide/linenumberwindow.cxx b/basctl/source/basicide/linenumberwindow.cxx
index 7dcbff0..74ead49 100644
--- a/basctl/source/basicide/linenumberwindow.cxx
+++ b/basctl/source/basicide/linenumberwindow.cxx
@@ -48,8 +48,6 @@ void LineNumberWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Re
    if (!txtView)
        return;

    GetParent()->Resize();

    int windowHeight = rRenderContext.GetOutputSize().Height();
    int nLineHeight = rRenderContext.GetTextHeight();
    if (!nLineHeight)
@@ -82,6 +80,9 @@ void LineNumberWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Re
    rRenderContext.SetTextColor(m_FontColor);
    for (sal_uInt32 n = nStartLine; n <= nEndLine; ++n, y += nLineHeight)
        rRenderContext.DrawText(Point(0, y - m_nCurYOffset), OUString::number(n));

    // Resize the parent after calculating the new width and height values
    GetParent()->Resize();
}

void LineNumberWindow::DataChanged(DataChangedEvent const& rDCEvt)
diff --git a/basctl/source/inc/basidesh.hxx b/basctl/source/inc/basidesh.hxx
index 3df158e..89e22c1 100644
--- a/basctl/source/inc/basidesh.hxx
+++ b/basctl/source/inc/basidesh.hxx
@@ -40,6 +40,12 @@ class StarBASIC;

namespace basctl
{

// Used to control zoom level
constexpr sal_uInt16 MIN_ZOOM_LEVEL = 50;
constexpr sal_uInt16 DEFAULT_ZOOM_LEVEL = 100;
constexpr sal_uInt16 MAX_ZOOM_LEVEL = 400;

class Layout;
class ModulWindow;
class ModulWindowLayout;
@@ -68,6 +74,8 @@ private:
    OUString            m_aCurLibName;
    std::shared_ptr<LocalizationMgr> m_pCurLocalizationMgr;

    // Current value of the zoom slider
    sal_uInt16          nCurrentZoomSliderValue;
    VclPtr<ScrollAdaptor> aHScrollBar;
    VclPtr<ScrollAdaptor> aVScrollBar;
    VclPtr<TabBar>       pTabBar;           // basctl::TabBar
@@ -92,6 +100,7 @@ private:
    void                Init();
    void                InitTabBar();
    void                InitScrollBars();
    void                InitZoomLevel();
    void                CheckWindows();
    void                RemoveWindows( const ScriptDocument& rDocument, std::u16string_view rLibName );
    void                UpdateWindows();
@@ -163,6 +172,10 @@ public:

    SfxUndoManager*     GetUndoManager() override;

    sal_uInt16          GetCurrentZoomSliderValue() { return nCurrentZoomSliderValue; }
    static sal_uInt16   GetMinZoom() { return MIN_ZOOM_LEVEL; }
    static sal_uInt16   GetMaxZoom() { return MAX_ZOOM_LEVEL; }

    virtual css::uno::Reference< css::view::XRenderable > GetRenderable() override;

    // virtual sal_uInt16           Print( SfxProgress &rProgress, sal_Bool bIsAPI, PrintDialog *pPrintDialog = 0 );
diff --git a/basctl/uiconfig/basicide/statusbar/statusbar.xml b/basctl/uiconfig/basicide/statusbar/statusbar.xml
index 9e65271..a5bde3ad 100644
--- a/basctl/uiconfig/basicide/statusbar/statusbar.xml
+++ b/basctl/uiconfig/basicide/statusbar/statusbar.xml
@@ -24,4 +24,6 @@
 <statusbar:statusbaritem xlink:href=".uno:InsertMode" statusbar:align="center" statusbar:width="55"/>
 <statusbar:statusbaritem xlink:href=".uno:Signature" statusbar:align="center" statusbar:ownerdraw="true" statusbar:width="16"/>
 <statusbar:statusbaritem xlink:href=".uno:Size" statusbar:align="left" statusbar:autosize="true" statusbar:ownerdraw="true" statusbar:width="140"/>
 <statusbar:statusbaritem xlink:href=".uno:ZoomSlider" statusbar:align="center" statusbar:ownerdraw="true" statusbar:mandatory="true" statusbar:width="130"/>
 <statusbar:statusbaritem xlink:href=".uno:BasicIDEZoom" statusbar:align="center" statusbar:mandatory="true" statusbar:width="35"/>
</statusbar:statusbar>
diff --git a/include/sfx2/sfxsids.hrc b/include/sfx2/sfxsids.hrc
index bd0990d..4e9badc 100644
--- a/include/sfx2/sfxsids.hrc
+++ b/include/sfx2/sfxsids.hrc
@@ -662,6 +662,7 @@ class SvxZoomItem;
#define SID_BASICIDE_ARG_DOCUMENT_MODEL     TypedWhichId<SfxUnoAnyItem>( SID_BASICIDE_START + 51 )
#define SID_BASICIDE_MANAGE_LANG            ( SID_BASICIDE_START + 52 )
#define SID_BASICIDE_CURRENT_LANG           ( SID_BASICIDE_START + 53 )
#define SID_BASICIDE_CURRENT_ZOOM           ( SID_BASICIDE_START + 54 )
#define SID_OPTIONS_TREEDIALOG              ( SID_BASICIDE_START + 862)

// SlotIds for Apps --------------------------------------------------------
diff --git a/sfx2/sdi/sfx.sdi b/sfx2/sdi/sfx.sdi
index d98e8e0..c56b80a 100644
--- a/sfx2/sdi/sfx.sdi
+++ b/sfx2/sdi/sfx.sdi
@@ -1922,6 +1922,25 @@ SfxVoidItem TipOfTheDay SID_TIPOFTHEDAY
]


SfxVoidItem BasicIDEZoom SID_BASICIDE_CURRENT_ZOOM
()
[
    AutoUpdate = FALSE,
    FastCall = FALSE,
    ReadOnlyDoc = TRUE,
    Toggle = FALSE,
    Container = FALSE,
    RecordAbsolute = FALSE,
    RecordPerSet;
    Asynchron;

    AccelConfig = FALSE,
    MenuConfig = FALSE,
    ToolBoxConfig = FALSE,
    GroupId = SfxGroupId::View;
]


SfxVoidItem HideCurPage SID_BASICIDE_HIDECURPAGE
()
[