Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/vcl/source/treelist/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 20 kB image not shown  

Quelle  iconviewimpl.cxx   Sprache: 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 <vcl/svapp.hxx>
#include <vcl/toolkit/treelistentry.hxx>
#include <tools/debug.hxx>
#include <iconview.hxx>
#include "iconviewimpl.hxx"

IconViewImpl::IconViewImpl( SvTreeListBox* pTreeListBox, SvTreeList* pTreeList, WinBits nWinStyle )
: SvImpLBox( pTreeListBox, pTreeList, nWinStyle )
{
}

static bool IsSeparator(const SvTreeListEntry* entry)
{
    return entry && entry->GetFlags() & SvTLEntryFlags::IS_SEPARATOR;
}

Size IconViewImpl::GetEntrySize(const SvTreeListEntry& entry) const
{
    return static_cast<const IconView*>(m_pView.get())->GetEntrySize(entry);
}

void IconViewImpl::IterateVisibleEntryAreas(const IterateEntriesFunc& f, bool fromStartEntry) const
{
    tools::Long x = 0, y = 0;
    short column = 0;
    const tools::Long rowWidth = m_pView->GetEntryWidth() * m_pView->GetColumnsCount();
    tools::Long nPrevHeight = 0;
    for (auto entry = fromStartEntry ? m_pStartEntry : m_pView->FirstVisible(); entry;
         entry = m_pView->NextVisible(entry))
    {
        const Size s = GetEntrySize(*entry);
        if (x >= rowWidth || IsSeparator(entry))
        {
            column = 0;
            x = 0;
            y += nPrevHeight;
        }
        EntryAreaInfo info{ entry, column, tools::Rectangle{ Point{ x, y }, s } };
        const auto result = f(info);
        if (result == CallbackResult::Stop)
            return;
        ++column;
        x += s.Width();
        nPrevHeight = s.Height();
    }
}

tools::Long IconViewImpl::GetEntryRow(const SvTreeListEntry* entry) const
{
    tools::Long nEntryRow = -1;
    auto GetRow = [entry, &nEntryRow, row = -1](const EntryAreaInfo& info) mutable
    {
        if (info.column == 0 && !IsSeparator(info.entry))
            ++row;
        if (info.entry != entry)
            return CallbackResult::Continue;
        nEntryRow = row;
        return CallbackResult::Stop;
    };
    IterateVisibleEntryAreas(GetRow);
    return nEntryRow;
}

void IconViewImpl::SetStartEntry(SvTreeListEntry* entry)
{
    const tools::Long max = m_aVerSBar->GetRangeMax() - m_aVerSBar->GetVisibleSize();
    tools::Long row = -1;
    auto GetEntryAndRow = [&entry, &row, max, found = entry](const EntryAreaInfo& info) mutable
    {
        if (info.column == 0 && !IsSeparator(info.entry))
        {
            found = info.entry;
            ++row;
        }
        if (row >= max || info.entry == entry)
        {
            entry = found;
            return CallbackResult::Stop;
        }
        return CallbackResult::Continue;
    };
    IterateVisibleEntryAreas(GetEntryAndRow);

    m_pStartEntry = entry;
    m_aVerSBar->SetThumbPos(row);
    m_pView->Invalidate(GetVisibleArea());
}

void IconViewImpl::ScrollTo(SvTreeListEntry* entry)
{
    if (!m_aVerSBar->IsVisible())
        return;
    const tools::Long entryRow = GetEntryRow(entry);
    const tools::Long oldStartRow = m_aVerSBar->GetThumbPos();
    if (entryRow < oldStartRow)
        IconViewImpl::SetStartEntry(entry);
    const tools::Long visibleRows = m_aVerSBar->GetVisibleSize();
    const tools::Long posRelativeToBottom = entryRow - (oldStartRow + visibleRows) + 1;
    if (posRelativeToBottom > 0)
        IconViewImpl::SetStartEntry(GoToNextRow(m_pStartEntry, posRelativeToBottom));
}

SvTreeListEntry* IconViewImpl::GoToPrevRow(SvTreeListEntry* pEntry, int nRows) const
{
    SvTreeListEntry* pPrev = pEntry;
    auto FindPrev = [this, pEntry, nRows, &pPrev,
                     prevs = std::vector<SvTreeListEntry*>()](const EntryAreaInfo& info) mutable
    {
        if (info.column == 0 && !IsSeparator(info.entry))
            prevs.push_back(info.entry);
        if (pEntry == info.entry)
        {
            if (prevs.size() > 1)
            {
                int i = std::max(0, static_cast<int>(prevs.size()) - nRows - 1);
                pPrev = prevs[i];
                for (short column = info.column; column; --column)
                {
                    SvTreeListEntry* pNext = m_pView->NextVisible(pPrev);
                    if (!pNext || IsSeparator(pNext))
                        break;
                    pPrev = pNext;
                }
            }
            return CallbackResult::Stop;
        }
        return CallbackResult::Continue;
    };
    IterateVisibleEntryAreas(FindPrev);

    return pPrev;
}

SvTreeListEntry* IconViewImpl::GoToNextRow(SvTreeListEntry* pEntry, int nRows) const
{
    SvTreeListEntry* pNext = pEntry;
    auto FindNext
        = [pEntry, nRows, &pNext, column = -1](const EntryAreaInfo& info) mutable
    {
        if (info.column <= column && !IsSeparator(info.entry))
        {
            if (info.column == 0 && --nRows < 0)
                return CallbackResult::Stop;
            pNext = info.entry;
            if (info.column == column && nRows == 0)
                return CallbackResult::Stop;
        }
        else if (pEntry == info.entry)
        {
            column = info.column;
        }
        return CallbackResult::Continue;
    };
    IterateVisibleEntryAreas(FindNext);

    return pNext;
}

void IconViewImpl::CursorUp()
{
    if (!m_pStartEntry)
        return;

    SvTreeListEntry* pPrevFirstToDraw = GoToPrevRow(m_pStartEntry, 1);

    m_nFlags &= ~LBoxFlags::Filling;
    ShowCursor( false );
    SetStartEntry(pPrevFirstToDraw);
    ShowCursor( true );
    m_pView->NotifyScrolled();
}

void IconViewImpl::CursorDown()
{
    if (!m_pStartEntry)
        return;

    SvTreeListEntry* pNextFirstToDraw = GoToNextRow(m_pStartEntry, 1);

    m_nFlags &= ~LBoxFlags::Filling;
    ShowCursor( false );
    SetStartEntry(pNextFirstToDraw);
    ShowCursor( true );
    m_pView->NotifyScrolled();
}

void IconViewImpl::PageDown( sal_uInt16 nDelta )
{
    if( !nDelta )
        return;

    if (!m_pStartEntry)
        return;

    SvTreeListEntry* pNext = GoToNextRow(m_pStartEntry, nDelta);

    ShowCursor( false );

    m_nFlags &= ~LBoxFlags::Filling;
    SetStartEntry(pNext);

    ShowCursor( true );
}

void IconViewImpl::PageUp( sal_uInt16 nDelta )
{
    if( !nDelta )
        return;

    if (!m_pStartEntry)
        return;

    SvTreeListEntry* pPrev = GoToPrevRow(m_pStartEntry, nDelta);

    m_nFlags &= ~LBoxFlags::Filling;
    ShowCursor( false );

    SetStartEntry(pPrev);

    ShowCursor( true );
}

void IconViewImpl::KeyDown( bool bPageDown )
{
    if( !m_aVerSBar->IsVisible() )
        return;

    tools::Long nDelta;
    if( bPageDown )
        nDelta = m_aVerSBar->GetPageSize();
    else
        nDelta = 1;

    if( nDelta <= 0 )
        return;

    m_nFlags &= ~LBoxFlags::Filling;

    if( bPageDown )
        PageDown( static_cast<short>(nDelta) );
    else
        CursorDown();
}

void IconViewImpl::KeyUp( bool bPageUp )
{
    if( !m_aVerSBar->IsVisible() )
        return;

    tools::Long nDelta;
    if( bPageUp )
        nDelta = m_aVerSBar->GetPageSize();
    else
        nDelta = 1;

    m_nFlags &= ~LBoxFlags::Filling;

    if( bPageUp )
        PageUp( static_cast<short>(nDelta) );
    else
        CursorUp();
}

tools::Long IconViewImpl::GetEntryLine(const SvTreeListEntry* pEntry) const
{
    if(!m_pStartEntry )
        return -1; // invisible position

    return IconViewImpl::GetEntryPosition(pEntry).Y();
}

Point IconViewImpl::GetEntryPosition(const SvTreeListEntry* pEntry) const
{
    Point result{ -m_pView->GetEntryWidth(), -m_pView->GetEntryHeight() }; // invisible
    auto FindEntryPos = [pEntry, &result](const EntryAreaInfo& info)
    {
        if (pEntry == info.entry)
        {
            result = info.area.TopLeft();
            return CallbackResult::Stop;
        }
        return CallbackResult::Continue;
    };
    IterateVisibleEntryAreas(FindEntryPos, true);

    return result;
}

// Returns the last entry (in respective row) if position is just past the last entry
SvTreeListEntry* IconViewImpl::GetClickedEntry( const Point& rPoint ) const
{
    DBG_ASSERT( m_pView->GetModel(), "IconViewImpl::GetClickedEntry: how can this ever happen?" );
    if ( !m_pView->GetModel() )
        return nullptr;
    if( m_pView->GetEntryCount() == 0 || !m_pStartEntry || !m_pView->GetEntryHeight() || !m_pView->GetEntryWidth())
        return nullptr;

    SvTreeListEntry* pEntry = nullptr;
    auto FindEntryByPos = [&pEntry, &rPoint](const EntryAreaInfo& info)
    {
        if (info.area.Contains(rPoint))
        {
            pEntry = info.entry;
            return CallbackResult::Stop;
        }
        else if (info.area.Top() > rPoint.Y())
        {
            return CallbackResult::Stop; // we are already below the clicked row
        }
        else if (info.area.Bottom() > rPoint.Y())
        {
            pEntry = info.entry; // Same row; store the entry in case the click is past all entries
        }
        return CallbackResult::Continue;
    };
    IterateVisibleEntryAreas(FindEntryByPos, true);

    return pEntry;
}

bool IconViewImpl::IsEntryInView( SvTreeListEntry* pEntry ) const
{
    // parent collapsed
    if( !m_pView->IsEntryVisible(pEntry) )
        return false;

    tools::Long nY = GetEntryLine( pEntry );
    if( nY < 0 )
        return false;

    tools::Long height = GetEntrySize(*pEntry).Height();
    if (nY + height > m_aOutputSize.Height())
        return false;

    return true;
}

void IconViewImpl::AdjustScrollBars( Size& rSize )
{
    tools::Long nEntryHeight = m_pView->GetEntryHeight();
    if( !nEntryHeight )
        return;

    sal_uInt16 nResult = 0;

    Size aOSize( m_pView->Control::GetOutputSizePixel() );

    const WinBits nWindowStyle = m_pView->GetStyle();
    bool bVerSBar = ( nWindowStyle & WB_VSCROLL ) != 0;

    // number of entries visible within the view
    const tools::Long nVisibleRows = aOSize.Height() / nEntryHeight;
    m_nVisibleCount = nVisibleRows * m_pView->GetColumnsCount();

    tools::Long nTotalRows = 0;
    tools::Long totalHeight = 0;
    auto CountRowsAndHeight = [&nTotalRows, &totalHeight](const EntryAreaInfo& info)
    {
        totalHeight = std::max(totalHeight, info.area.Bottom());
        if (info.column == 0 && !IsSeparator(info.entry))
            ++nTotalRows;
        return CallbackResult::Continue;
    };
    IterateVisibleEntryAreas(CountRowsAndHeight);

    // do we need a vertical scrollbar?
    if( bVerSBar || totalHeight > aOSize.Height())
    {
        nResult = 1;
    }

    // do we need a Horizontal scrollbar?
    bool bHorSBar = (nWindowStyle & WB_HSCROLL) != 0;
    if (bHorSBar || m_pView->GetEntryWidth() > aOSize.Width())
    {
        nResult += 2;
        m_aHorSBar->SetRange(Range(0, m_pView->GetEntryWidth()));
        m_aHorSBar->SetVisibleSize(aOSize.Width());
    }

    PositionScrollBars( aOSize, nResult );

    // adapt Range, VisibleRange etc.

    // refresh output size, in case we have to scroll
    tools::Rectangle aRect;
    aRect.SetSize( aOSize );
    m_aSelEng.SetVisibleArea( aRect );

    // vertical scrollbar
    if( !m_bInVScrollHdl )
    {
        m_aVerSBar->SetRange(Range(0, nTotalRows));
        m_aVerSBar->SetPageSize(nVisibleRows);
        m_aVerSBar->SetVisibleSize(nVisibleRows);
    }
    else
    {
        m_nFlags |= LBoxFlags::EndScrollSetVisSize;
    }

    if( nResult & 0x0001 )
        m_aVerSBar->Show();
    else
        m_aVerSBar->Hide();

    if (nResult & 0x0002)
        m_aHorSBar->Show();
    else
        m_aHorSBar->Hide();

    rSize = aOSize;
}

// returns 0 if position is just past the last entry
SvTreeListEntry* IconViewImpl::GetEntry( const Point& rPoint ) const
{
    if( (m_pView->GetEntryCount() == 0) || !m_pStartEntry ||
        (rPoint.Y() > m_aOutputSize.Height())
        || !m_pView->GetEntryHeight()
        || !m_pView->GetEntryWidth())
        return nullptr;

    SvTreeListEntry* pEntry = nullptr;
    auto FindEntryByPos = [&pEntry, &rPoint](const EntryAreaInfo& info)
    {
        if (info.area.Contains(rPoint))
        {
            pEntry = info.entry;
            return CallbackResult::Stop;
        }
        else if (info.area.Top() > rPoint.Y())
        {
            return CallbackResult::Stop; // we are already below the clicked row
        }
        return CallbackResult::Continue;
    };
    IterateVisibleEntryAreas(FindEntryByPos, true);

    return pEntry;
}

void IconViewImpl::SyncVerThumb()
{
    m_aVerSBar->SetThumbPos(GetEntryRow(m_pStartEntry));
}

void IconViewImpl::UpdateAll()
{
    FindMostRight();
    AdjustScrollBars(m_aOutputSize);
    SyncVerThumb();
    FillView();
    ShowVerSBar();
    if( m_bSimpleTravel && m_pCursor && m_pView->HasFocus() )
        m_pView->Select( m_pCursor );
    ShowCursor( true );
    m_pView->Invalidate( GetVisibleArea() );
}

void IconViewImpl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
{
    if (!m_pView->GetVisibleCount())
        return;

    m_nFlags |= LBoxFlags::InPaint;

    if (m_nFlags & LBoxFlags::Filling)
    {
        SvTreeListEntry* pFirst = m_pView->First();
        if (pFirst != m_pStartEntry)
        {
            ShowCursor(false);
            m_pStartEntry = m_pView->First();
            m_aVerSBar->SetThumbPos( 0 );
            StopUserEvent();
            ShowCursor(true);
            m_nCurUserEvent = Application::PostUserEvent(LINK(this, SvImpLBox, MyUserEvent),
                                                       reinterpret_cast<void*>(1));
            return;
        }
    }

    if (!m_pStartEntry)
    {
        m_pStartEntry = m_pView->First();
    }

    if (!m_pCursor && !mbNoAutoCurEntry)
    {
        // do not select if multiselection or explicit set
        bool bNotSelect = (m_aSelEng.GetSelectionMode() == SelectionMode::Multiple ) || ((m_nStyle & WB_NOINITIALSELECTION) == WB_NOINITIALSELECTION);
        SetCursor(m_pStartEntry, bNotSelect);
    }

    auto PaintEntry = [iconView = static_cast<IconView*>(m_pView.get()), &rRect,
                       &rRenderContext](const EntryAreaInfo& info)
    {
        if (!info.area.GetIntersection(rRect).IsEmpty())
        {
            iconView->PaintEntry(*info.entry, info.area.Left(), info.area.Top(), rRenderContext);
        }
        else if (info.area.Top() > rRect.Bottom())
        {
            return CallbackResult::Stop; // we are already below the last visible row
        }
        return CallbackResult::Continue;
    };
    IterateVisibleEntryAreas(PaintEntry, true);

    m_nFlags &= ~LBoxFlags::DeselectAll;
    rRenderContext.SetClipRegion();
    m_nFlags &= ~LBoxFlags::InPaint;
}

void IconViewImpl::InvalidateEntry( tools::Long nId ) const
{
    if( m_nFlags & LBoxFlags::InPaint )
        return;
    if (nId < 0)
        return;

    // nId is a Y coordinate of the top of the element, coming from GetEntryLine
    tools::Rectangle aRect( GetVisibleArea() );
    if (nId > aRect.Bottom())
        return;
    aRect.SetTop(nId); // Invalidate everything below
    m_pView->Invalidate( aRect );
}

bool IconViewImpl::KeyInput( const KeyEvent& rKEvt )
{
    const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();

    if( rKeyCode.IsMod2() )
        return false// don't evaluate Alt key

    m_nFlags &= ~LBoxFlags::Filling;

    if( !m_pCursor )
        m_pCursor = m_pStartEntry;
    if( !m_pCursor )
        return false;

    sal_uInt16  aCode = rKeyCode.GetCode();

    bool    bShift = rKeyCode.IsShift();
    bool    bMod1 = rKeyCode.IsMod1();

    SvTreeListEntry* pNewCursor;

    bool bHandled = true;

    switch( aCode )
    {
        case KEY_LEFT:
            if( !IsEntryInView( m_pCursor ) )
                MakeVisible( m_pCursor );

            pNewCursor = m_pCursor;
            do
            {
                pNewCursor = m_pView->PrevVisible(pNewCursor);
            } while( pNewCursor && !IsSelectable(pNewCursor) );

            // if there is no next entry, take the current one
            // this ensures that in case of _one_ entry in the list, this entry is selected when pressing
            // the cursor key
            if (!pNewCursor)
                pNewCursor = m_pCursor;

            m_aSelEng.CursorPosChanging( bShift, bMod1 );
            SetCursor( pNewCursor, bMod1 );     // no selection, when Ctrl is on
            if( !IsEntryInView( pNewCursor ) )
                KeyUp( false );
            break;

        case KEY_RIGHT:
            if( !IsEntryInView( m_pCursor ) )
                MakeVisible( m_pCursor );

            pNewCursor = m_pCursor;
            do
            {
                pNewCursor = m_pView->NextVisible(pNewCursor);
            } while( pNewCursor && !IsSelectable(pNewCursor) );

            // if there is no next entry, take the current one
            // this ensures that in case of _one_ entry in the list, this entry is selected when pressing
            // the cursor key
            if ( !pNewCursor && m_pCursor )
                pNewCursor = m_pCursor;

            if( pNewCursor )
            {
                m_aSelEng.CursorPosChanging( bShift, bMod1 );
                if( IsEntryInView( pNewCursor ) )
                    SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
                else
                {
                    if( m_pCursor )
                        m_pView->Select( m_pCursor, false );
                    KeyDown( false );
                    SetCursor( pNewCursor, bMod1 ); // no selection, when Ctrl is on
                }
            }
            else
                KeyDown( false );   // because scrollbar range might still
                                        // allow scrolling
            break;

        case KEY_UP:
        {
            pNewCursor = GoToPrevRow(m_pCursor, 1);

            if( pNewCursor )
            {
                m_aSelEng.CursorPosChanging( bShift, bMod1 );
                SetCursor( pNewCursor, bMod1 );     // no selection, when Ctrl is on
                ScrollTo(pNewCursor);
            }
            break;
        }

        case KEY_DOWN:
        {
            pNewCursor = GoToNextRow(m_pCursor, 1);

            if( pNewCursor )
            {
                m_aSelEng.CursorPosChanging( bShift, bMod1 );
                ScrollTo(pNewCursor);
                SetCursor(pNewCursor, bMod1); // no selection, when Ctrl is on
            }
            else
                KeyDown( false );   // because scrollbar range might still
                                        // allow scrolling
            break;
        }

        case KEY_PAGEUP:
            if (!bMod1)
            {
                const sal_uInt16 nDelta = m_aVerSBar->GetPageSize();
                pNewCursor = GoToPrevRow(m_pCursor, nDelta);

                if (pNewCursor)
                {
                    m_aSelEng.CursorPosChanging(bShift, bMod1);
                    ScrollTo(pNewCursor);
                    SetCursor(pNewCursor);
                }
            }
            else
                bHandled = false;
            break;

        case KEY_PAGEDOWN:
            if (!bMod1)
            {
                const sal_uInt16 nDelta = m_aVerSBar->GetPageSize();
                pNewCursor = GoToNextRow(m_pCursor, nDelta);

                if (pNewCursor)
                {
                    m_aSelEng.CursorPosChanging(bShift, bMod1);
                    ScrollTo(pNewCursor);
                    SetCursor(pNewCursor);
                }
                else
                    KeyDown(false);
            }
            else
                bHandled = false;
            break;

        case KEY_RETURN:
        case KEY_SPACE:
        {
            bHandled = !m_pView->aDoubleClickHdl.Call(m_pView);
            break;
        }

        case KEY_END:
        {
            pNewCursor = m_pView->GetModel()->Last();

            while( pNewCursor && !IsSelectable(pNewCursor) )
            {
                pNewCursor = m_pView->PrevVisible(pNewCursor);
            }

            SetStartEntry(pNewCursor);

            if( pNewCursor && pNewCursor != m_pCursor)
            {
//              SelAllDestrAnch( false );
                m_aSelEng.CursorPosChanging( bShift, bMod1 );
                SetCursor( pNewCursor );
            }

            bHandled = true;

            break;
        }

        default:
        {
            bHandled = false;
            break;
        }
    }

    if(!bHandled)
        return SvImpLBox::KeyInput( rKEvt );

    return true;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=95 H=96 G=95

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.