tdf#65587 SM handle page keys in the ElementControl

The page handling implementation is a little bit tricky, because
the elemnt list is not handled like a grid but a list. Normally
one would keep the horizontal cell and just scroll vertically.
Instead this implements a kind of circle.

Vertical offset is consistet, so you have the same amount of
steps for up and down, but up runs left and down runs right.

Change-Id: I296a46e98f7e00a59fd0a0ba358c981b49ac86cd
Reviewed-on: https://gerrit.libreoffice.org/72793
Tested-by: Jenkins
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
diff --git a/starmath/inc/ElementsDockingWindow.hxx b/starmath/inc/ElementsDockingWindow.hxx
index e102250..874735a 100644
--- a/starmath/inc/ElementsDockingWindow.hxx
+++ b/starmath/inc/ElementsDockingWindow.hxx
@@ -99,7 +99,12 @@ class SmElementsControl : public Control
    void addElements(const std::pair<const char*, const char*> aElementsArray[], sal_uInt16 size);
    SmElement* current() const;
    bool hasRollover() const { return m_nCurrentRolloverElement != SAL_MAX_UINT16; }

    void stepFocus(const bool bBackward);
    void pageFocus(const bool bBackward);
    // common code of page and step focus
    inline void scrollToElement(const bool, const SmElement*);
    inline sal_uInt16 nextElement(const bool, const sal_uInt16, const sal_uInt16);

    void build();

diff --git a/starmath/source/ElementsDockingWindow.cxx b/starmath/source/ElementsDockingWindow.cxx
index cbf8c80..385d33d 100644
--- a/starmath/source/ElementsDockingWindow.cxx
+++ b/starmath/source/ElementsDockingWindow.cxx
@@ -601,27 +601,23 @@ void SmElementsControl::LoseFocus()
    Invalidate();
}

void SmElementsControl::stepFocus(const bool bBackward)
sal_uInt16 SmElementsControl::nextElement(const bool bBackward, const sal_uInt16 nStartPos, const sal_uInt16 nLastElement)
{
    const sal_uInt16 nStartPos = m_nCurrentElement;
    const sal_uInt16 nElementCount = maElementList.size();
    sal_uInt16 nPos = nStartPos;
    assert(nPos < nElementCount);

    while (true)
    {
        if (bBackward)
        {
            if (nPos == 0)
                nPos = nElementCount - 1;
            else
                nPos--;
                break;
            nPos--;
        }
        else
        {
            if (nPos == nLastElement)
                break;
            nPos++;
            if (nPos == nElementCount)
                nPos = 0;
        }

        if (nStartPos == nPos)
@@ -630,22 +626,94 @@ void SmElementsControl::stepFocus(const bool bBackward)
            break;
    }

    return nPos;
}

void SmElementsControl::scrollToElement(const bool bBackward, const SmElement *pCur)
{
    long nScrollPos = mxScroll->GetThumbPos();
    if (mbVerticalMode)
    {
        nScrollPos += pCur->mBoxLocation.X();
        if (!bBackward)
            nScrollPos += pCur->mBoxSize.Width() - GetOutputSizePixel().Width();
    }
    else
    {
        nScrollPos += pCur->mBoxLocation.Y();
        if (!bBackward)
            nScrollPos += pCur->mBoxSize.Height() - GetOutputSizePixel().Height();
    }
    mxScroll->DoScroll(nScrollPos);
}

void SmElementsControl::stepFocus(const bool bBackward)
{
    const sal_uInt16 nStartPos = m_nCurrentElement;
    const sal_uInt16 nLastElement = (maElementList.size() ? maElementList.size() - 1 : 0);
    assert(nStartPos <= nLastElement);

    sal_uInt16 nPos = nextElement(bBackward, nStartPos, nLastElement);
    if (nStartPos != nPos)
    {
        m_nCurrentElement = nPos;
        if (!hasRollover())
        m_nCurrentRolloverElement = SAL_MAX_UINT16;

        const tools::Rectangle outputRect(Point(0,0), GetOutputSizePixel());
        const SmElement *pCur = maElementList[nPos].get();
        tools::Rectangle elementRect(pCur->mBoxLocation, pCur->mBoxSize);
        if (!outputRect.IsInside(elementRect))
            scrollToElement(bBackward, pCur);
        Invalidate();
    }
}

void SmElementsControl::pageFocus(const bool bBackward)
{
    const sal_uInt16 nStartPos = m_nCurrentElement;
    const sal_uInt16 nLastElement = (maElementList.size() ? maElementList.size() - 1 : 0);
    assert(nStartPos <= nLastElement);
    tools::Rectangle outputRect(Point(0,0), GetOutputSizePixel());
    sal_uInt16 nPrevPos = nStartPos;
    sal_uInt16 nPos = nPrevPos;

    bool bMoved = false;
    while (true)
    {
        nPrevPos = nPos;
        nPos = nextElement(bBackward, nPrevPos, nLastElement);
        if (nPrevPos == nPos)
            break;

        m_nCurrentRolloverElement = SAL_MAX_UINT16;

        SmElement *pCur = maElementList[nPos].get();
        tools::Rectangle elementRect(pCur->mBoxLocation, pCur->mBoxSize);
        if (!outputRect.IsInside(elementRect))
        {
            const SmElement *pCur = current();
            tools::Rectangle elementRect(pCur->mBoxLocation, pCur->mBoxSize);
            tools::Rectangle outputRect(Point(0,0), GetOutputSizePixel());
            if (!outputRect.IsInside(elementRect))
            if (nPrevPos != nStartPos)
            {
                long nScrollPos = mxScroll->GetThumbPos() + pCur->mBoxLocation.Y();
                if (!bBackward)
                    nScrollPos += pCur->mBoxSize.Height() - GetOutputSizePixel().Height();
                mxScroll->DoScroll(nScrollPos);
                nPos = nPrevPos;
                break;
            }
            if (bMoved)
                break;
            pCur = maElementList[nPrevPos].get();

            elementRect = tools::Rectangle(pCur->mBoxLocation, pCur->mBoxSize);
            if (mbVerticalMode)
                outputRect.Move(bBackward ? -outputRect.GetWidth() + elementRect.Right() : elementRect.Left(), 0);
            else
                outputRect.Move(0, bBackward ? -outputRect.GetHeight() + elementRect.Bottom() : elementRect.Top());
            bMoved = true;
        }
    }

    if (nStartPos != nPos)
    {
        m_nCurrentElement = nPos;
        if (bMoved)
            scrollToElement(bBackward, maElementList[nPos].get());
        Invalidate();
    }
}
@@ -692,6 +760,13 @@ void SmElementsControl::KeyInput(const KeyEvent& rKEvt)
            mxScroll->DoScroll(mxScroll->GetRangeMax());
            break;

        case KEY_PAGEUP:
            pageFocus(true);
            break;
        case KEY_PAGEDOWN:
            pageFocus(false);
            break;

        default:
            Control::KeyInput( rKEvt );
            break;