1040 lines
33 KiB
C++
1040 lines
33 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 <vcl/image.hxx>
|
|
#include <vcl/taskpanelist.hxx>
|
|
#include <vcl/settings.hxx>
|
|
#include <vcl/syswin.hxx>
|
|
#include <osl/diagnose.h>
|
|
|
|
#include <olinewin.hxx>
|
|
#include <olinetab.hxx>
|
|
#include <document.hxx>
|
|
#include <dbfunc.hxx>
|
|
#include <bitmaps.hlst>
|
|
|
|
const tools::Long SC_OL_BITMAPSIZE = 12;
|
|
const tools::Long SC_OL_POSOFFSET = 2;
|
|
|
|
const size_t SC_OL_NOLEVEL = static_cast< size_t >( -1 );
|
|
const size_t SC_OL_HEADERENTRY = static_cast< size_t >( -1 );
|
|
|
|
ScOutlineWindow::ScOutlineWindow( vcl::Window* pParent, ScOutlineMode eMode, ScViewData* pViewData, ScSplitPos eWhich ) :
|
|
Window( pParent ),
|
|
mrViewData( *pViewData ),
|
|
meWhich( eWhich ),
|
|
mbHoriz( eMode == SC_OUTLINE_HOR ),
|
|
mbMirrorEntries( false ), // updated in SetHeaderSize
|
|
mbMirrorLevels( false ), // updated in SetHeaderSize
|
|
maLineColor( COL_BLACK ),
|
|
mnHeaderSize( 0 ),
|
|
mnHeaderPos( 0 ),
|
|
mnMainFirstPos( 0 ),
|
|
mnMainLastPos( 0 ),
|
|
mbMTActive( false ),
|
|
mbMTPressed( false ),
|
|
mnFocusLevel( 0 ),
|
|
mnFocusEntry( SC_OL_HEADERENTRY ),
|
|
mbDontDrawFocus( false )
|
|
{
|
|
EnableRTL( false ); // mirroring is done manually
|
|
|
|
InitSettings();
|
|
maFocusRect.SetEmpty();
|
|
SetHeaderSize( 0 );
|
|
|
|
// insert the window into task pane list for "F6 cycling"
|
|
if( SystemWindow* pSysWin = GetSystemWindow() )
|
|
if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() )
|
|
pTaskPaneList->AddWindow( this );
|
|
}
|
|
|
|
ScOutlineWindow::~ScOutlineWindow()
|
|
{
|
|
disposeOnce();
|
|
}
|
|
|
|
void ScOutlineWindow::dispose()
|
|
{
|
|
// remove the window from task pane list
|
|
if( SystemWindow* pSysWin = GetSystemWindow() )
|
|
if( TaskPaneList* pTaskPaneList = pSysWin->GetTaskPaneList() )
|
|
pTaskPaneList->RemoveWindow( this );
|
|
vcl::Window::dispose();
|
|
}
|
|
|
|
void ScOutlineWindow::SetHeaderSize( tools::Long nNewSize )
|
|
{
|
|
bool bLayoutRTL = GetDoc().IsLayoutRTL( GetTab() );
|
|
mbMirrorEntries = bLayoutRTL && mbHoriz;
|
|
mbMirrorLevels = bLayoutRTL && !mbHoriz;
|
|
|
|
bool bNew = (nNewSize != mnHeaderSize);
|
|
mnHeaderSize = nNewSize;
|
|
mnHeaderPos = mbMirrorEntries ? (GetOutputSizeEntry() - mnHeaderSize) : 0;
|
|
mnMainFirstPos = mbMirrorEntries ? 0 : mnHeaderSize;
|
|
mnMainLastPos = GetOutputSizeEntry() - (mbMirrorEntries ? mnHeaderSize : 0) - 1;
|
|
if ( bNew )
|
|
Invalidate();
|
|
}
|
|
|
|
tools::Long ScOutlineWindow::GetDepthSize() const
|
|
{
|
|
tools::Long nSize = GetLevelCount() * SC_OL_BITMAPSIZE;
|
|
if ( nSize > 0 )
|
|
nSize += 2 * SC_OL_POSOFFSET + 1;
|
|
return nSize;
|
|
}
|
|
|
|
void ScOutlineWindow::ScrollPixel( tools::Long nDiff )
|
|
{
|
|
HideFocus();
|
|
mbDontDrawFocus = true;
|
|
|
|
tools::Long nStart = mnMainFirstPos;
|
|
tools::Long nEnd = mnMainLastPos;
|
|
|
|
tools::Long nInvStart, nInvEnd;
|
|
if (nDiff < 0)
|
|
{
|
|
nStart -= nDiff;
|
|
nInvStart = nEnd + nDiff;
|
|
nInvEnd = nEnd;
|
|
}
|
|
else
|
|
{
|
|
nEnd -= nDiff;
|
|
nInvStart = nStart;
|
|
nInvEnd = nStart + nDiff;
|
|
}
|
|
|
|
ScrollRel( nDiff, nStart, nEnd );
|
|
Invalidate( GetRectangle( 0, nInvStart, GetOutputSizeLevel() - 1, nInvEnd ) );
|
|
|
|
// if focus becomes invisible, move it to next visible button
|
|
ImplMoveFocusToVisible( nDiff < 0 );
|
|
|
|
mbDontDrawFocus = false;
|
|
ShowFocus();
|
|
}
|
|
|
|
void ScOutlineWindow::ScrollRel( tools::Long nEntryDiff, tools::Long nEntryStart, tools::Long nEntryEnd )
|
|
{
|
|
tools::Rectangle aRect( GetRectangle( 0, nEntryStart, GetOutputSizeLevel() - 1, nEntryEnd ) );
|
|
if ( mbHoriz )
|
|
Scroll( nEntryDiff, 0, aRect );
|
|
else
|
|
Scroll( 0, nEntryDiff, aRect );
|
|
}
|
|
|
|
// internal -------------------------------------------------------------------
|
|
|
|
void ScOutlineWindow::InitSettings()
|
|
{
|
|
const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
|
|
SetBackground( rStyleSettings.GetFaceColor() );
|
|
maLineColor = rStyleSettings.GetButtonTextColor();
|
|
Invalidate();
|
|
}
|
|
|
|
const ScOutlineArray* ScOutlineWindow::GetOutlineArray() const
|
|
{
|
|
const ScOutlineTable* pTable = GetDoc().GetOutlineTable( GetTab() );
|
|
if ( !pTable ) return nullptr;
|
|
return mbHoriz ? &pTable->GetColArray() : &pTable->GetRowArray();
|
|
}
|
|
|
|
const ScOutlineEntry* ScOutlineWindow::GetOutlineEntry( size_t nLevel, size_t nEntry ) const
|
|
{
|
|
const ScOutlineArray* pArray = GetOutlineArray();
|
|
return pArray ? pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) ) : nullptr;
|
|
}
|
|
|
|
bool ScOutlineWindow::IsHidden( SCCOLROW nColRowIndex ) const
|
|
{
|
|
return mbHoriz ?
|
|
GetDoc().ColHidden(static_cast<SCCOL>(nColRowIndex), GetTab()) :
|
|
GetDoc().RowHidden(static_cast<SCROW>(nColRowIndex), GetTab());
|
|
}
|
|
|
|
bool ScOutlineWindow::IsFiltered( SCCOLROW nColRowIndex ) const
|
|
{
|
|
// columns cannot be filtered
|
|
return !mbHoriz && GetDoc().RowFiltered( static_cast<SCROW>(nColRowIndex), GetTab() );
|
|
}
|
|
|
|
bool ScOutlineWindow::IsFirstVisible( SCCOLROW nColRowIndex ) const
|
|
{
|
|
bool bAllHidden = true;
|
|
for ( SCCOLROW nPos = 0; (nPos < nColRowIndex) && bAllHidden; ++nPos )
|
|
bAllHidden = IsHidden( nPos );
|
|
return bAllHidden;
|
|
}
|
|
|
|
void ScOutlineWindow::GetVisibleRange( SCCOLROW& rnColRowStart, SCCOLROW& rnColRowEnd ) const
|
|
{
|
|
if ( mbHoriz )
|
|
{
|
|
rnColRowStart = mrViewData.GetPosX( WhichH( meWhich ) );
|
|
rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsX( WhichH( meWhich ) );
|
|
}
|
|
else
|
|
{
|
|
rnColRowStart = mrViewData.GetPosY( WhichV( meWhich ) );
|
|
rnColRowEnd = rnColRowStart + mrViewData.VisibleCellsY( WhichV( meWhich ) );
|
|
}
|
|
|
|
// include collapsed columns/rows in front of visible range
|
|
while ( (rnColRowStart > 0) && IsHidden( rnColRowStart - 1 ) )
|
|
--rnColRowStart;
|
|
}
|
|
|
|
Point ScOutlineWindow::GetPoint( tools::Long nLevelPos, tools::Long nEntryPos ) const
|
|
{
|
|
return mbHoriz ? Point( nEntryPos, nLevelPos ) : Point( nLevelPos, nEntryPos );
|
|
}
|
|
|
|
tools::Rectangle ScOutlineWindow::GetRectangle(
|
|
tools::Long nLevelStart, tools::Long nEntryStart, tools::Long nLevelEnd, tools::Long nEntryEnd ) const
|
|
{
|
|
return tools::Rectangle( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) );
|
|
}
|
|
|
|
tools::Long ScOutlineWindow::GetOutputSizeLevel() const
|
|
{
|
|
Size aSize( GetOutputSizePixel() );
|
|
return mbHoriz ? aSize.Height() : aSize.Width();
|
|
}
|
|
|
|
tools::Long ScOutlineWindow::GetOutputSizeEntry() const
|
|
{
|
|
Size aSize( GetOutputSizePixel() );
|
|
return mbHoriz ? aSize.Width() : aSize.Height();
|
|
}
|
|
|
|
size_t ScOutlineWindow::GetLevelCount() const
|
|
{
|
|
const ScOutlineArray* pArray = GetOutlineArray();
|
|
size_t nLevelCount = pArray ? pArray->GetDepth() : 0;
|
|
return nLevelCount ? (nLevelCount + 1) : 0;
|
|
}
|
|
|
|
tools::Long ScOutlineWindow::GetLevelPos( size_t nLevel ) const
|
|
{
|
|
// #i51970# must always return the *left* edge of the area used by a level
|
|
tools::Long nPos = static_cast< tools::Long >( SC_OL_POSOFFSET + nLevel * SC_OL_BITMAPSIZE );
|
|
return mbMirrorLevels ? (GetOutputSizeLevel() - nPos - SC_OL_BITMAPSIZE) : nPos;
|
|
}
|
|
|
|
size_t ScOutlineWindow::GetLevelFromPos( tools::Long nLevelPos ) const
|
|
{
|
|
if( mbMirrorLevels ) nLevelPos = GetOutputSizeLevel() - nLevelPos - 1;
|
|
tools::Long nStart = SC_OL_POSOFFSET;
|
|
if ( nLevelPos < nStart ) return SC_OL_NOLEVEL;
|
|
size_t nLevel = static_cast< size_t >( (nLevelPos - nStart) / SC_OL_BITMAPSIZE );
|
|
return (nLevel < GetLevelCount()) ? nLevel : SC_OL_NOLEVEL;
|
|
}
|
|
|
|
tools::Long ScOutlineWindow::GetColRowPos( SCCOLROW nColRowIndex ) const
|
|
{
|
|
tools::Long nDocPos = mbHoriz ?
|
|
mrViewData.GetScrPos( static_cast<SCCOL>(nColRowIndex), 0, meWhich, true ).X() :
|
|
mrViewData.GetScrPos( 0, static_cast<SCROW>(nColRowIndex), meWhich, true ).Y();
|
|
return mnMainFirstPos + nDocPos;
|
|
}
|
|
|
|
tools::Long ScOutlineWindow::GetHeaderEntryPos() const
|
|
{
|
|
return mnHeaderPos + (mnHeaderSize - SC_OL_BITMAPSIZE) / 2;
|
|
}
|
|
|
|
bool ScOutlineWindow::GetEntryPos(
|
|
size_t nLevel, size_t nEntry,
|
|
tools::Long& rnStartPos, tools::Long& rnEndPos, tools::Long& rnImagePos ) const
|
|
{
|
|
const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
|
|
if ( !pEntry || !pEntry->IsVisible() )
|
|
return false;
|
|
|
|
SCCOLROW nStart = pEntry->GetStart();
|
|
SCCOLROW nEnd = pEntry->GetEnd();
|
|
|
|
tools::Long nEntriesSign = mbMirrorEntries ? -1 : 1;
|
|
|
|
// --- common calculation ---
|
|
|
|
rnStartPos = GetColRowPos( nStart );
|
|
rnEndPos = GetColRowPos( nEnd + 1 );
|
|
|
|
bool bHidden = IsHidden( nStart );
|
|
rnImagePos = bHidden ?
|
|
(rnStartPos - ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign) :
|
|
rnStartPos + nEntriesSign;
|
|
tools::Long nCenter = (rnStartPos + rnEndPos - SC_OL_BITMAPSIZE * nEntriesSign +
|
|
( mbMirrorEntries ? 1 : 0 )) / 2;
|
|
rnImagePos = mbMirrorEntries ? std::max( rnImagePos, nCenter ) : std::min( rnImagePos, nCenter );
|
|
|
|
// --- refinements ---
|
|
|
|
// do not cut leftmost/topmost image
|
|
if ( bHidden && IsFirstVisible( nStart ) )
|
|
rnImagePos = rnStartPos;
|
|
|
|
// do not cover previous collapsed image
|
|
bool bDoNoCover = !bHidden && nEntry;
|
|
const ScOutlineEntry* pPrevEntry = bDoNoCover ? GetOutlineEntry(nLevel, nEntry - 1) : nullptr;
|
|
if (pPrevEntry)
|
|
{
|
|
SCCOLROW nPrevEnd = pPrevEntry->GetEnd();
|
|
if ( (nPrevEnd + 1 == nStart) && IsHidden( nPrevEnd ) )
|
|
{
|
|
if ( IsFirstVisible( pPrevEntry->GetStart() ) )
|
|
rnStartPos += SC_OL_BITMAPSIZE * nEntriesSign;
|
|
else
|
|
rnStartPos += ( SC_OL_BITMAPSIZE / 2 ) * nEntriesSign;
|
|
rnImagePos = rnStartPos;
|
|
}
|
|
}
|
|
|
|
// restrict rnStartPos...rnEndPos to valid area
|
|
rnStartPos = std::max( rnStartPos, mnMainFirstPos );
|
|
rnEndPos = std::max( rnEndPos, mnMainFirstPos );
|
|
|
|
if ( mbMirrorEntries )
|
|
rnImagePos -= SC_OL_BITMAPSIZE - 1; // start pos aligns with right edge of bitmap
|
|
|
|
// --- all rows filtered? ---
|
|
|
|
bool bVisible = true;
|
|
if ( !mbHoriz )
|
|
{
|
|
bVisible = false;
|
|
for ( SCCOLROW nRow = nStart; (nRow <= nEnd) && !bVisible; ++nRow )
|
|
bVisible = !IsFiltered( nRow );
|
|
}
|
|
return bVisible;
|
|
}
|
|
|
|
bool ScOutlineWindow::GetImagePos( size_t nLevel, size_t nEntry, Point& rPos ) const
|
|
{
|
|
bool bRet = nLevel < GetLevelCount();
|
|
if ( bRet )
|
|
{
|
|
tools::Long nLevelPos = GetLevelPos( nLevel );
|
|
if ( nEntry == SC_OL_HEADERENTRY )
|
|
rPos = GetPoint( nLevelPos, GetHeaderEntryPos() );
|
|
else
|
|
{
|
|
tools::Long nStartPos, nEndPos, nImagePos;
|
|
bRet = GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos );
|
|
rPos = GetPoint( nLevelPos, nImagePos );
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
bool ScOutlineWindow::IsButtonVisible( size_t nLevel, size_t nEntry ) const
|
|
{
|
|
bool bRet = false;
|
|
if ( nEntry == SC_OL_HEADERENTRY )
|
|
bRet = (mnHeaderSize > 0) && (nLevel < GetLevelCount());
|
|
else
|
|
{
|
|
const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
|
|
if ( pEntry && pEntry->IsVisible() )
|
|
{
|
|
SCCOLROW nStart, nEnd;
|
|
GetVisibleRange( nStart, nEnd );
|
|
bRet = (nStart <= pEntry->GetStart()) && (pEntry->GetStart() <= nEnd);
|
|
}
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
bool ScOutlineWindow::ItemHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry, bool& rbButton ) const
|
|
{
|
|
const ScOutlineArray* pArray = GetOutlineArray();
|
|
if ( !pArray ) return false;
|
|
|
|
SCCOLROW nStartIndex, nEndIndex;
|
|
GetVisibleRange( nStartIndex, nEndIndex );
|
|
|
|
size_t nLevel = GetLevelFromPos( mbHoriz ? rPos.Y() : rPos.X() );
|
|
if ( nLevel == SC_OL_NOLEVEL )
|
|
return false;
|
|
|
|
tools::Long nEntryMousePos = mbHoriz ? rPos.X() : rPos.Y();
|
|
|
|
// --- level buttons ---
|
|
|
|
if ( mnHeaderSize > 0 )
|
|
{
|
|
tools::Long nImagePos = GetHeaderEntryPos();
|
|
if ( (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) )
|
|
{
|
|
rnLevel = nLevel;
|
|
rnEntry = SC_OL_HEADERENTRY;
|
|
rbButton = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// --- expand/collapse buttons and expanded lines ---
|
|
|
|
// search outline entries backwards
|
|
size_t nEntry = pArray->GetCount( sal::static_int_cast<sal_uInt16>(nLevel) );
|
|
while ( nEntry )
|
|
{
|
|
--nEntry;
|
|
|
|
const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
|
|
sal::static_int_cast<sal_uInt16>(nEntry) );
|
|
SCCOLROW nStart = pEntry->GetStart();
|
|
SCCOLROW nEnd = pEntry->GetEnd();
|
|
|
|
if ( (nEnd >= nStartIndex) && (nStart <= nEndIndex) )
|
|
{
|
|
tools::Long nStartPos, nEndPos, nImagePos;
|
|
if ( GetEntryPos( nLevel, nEntry, nStartPos, nEndPos, nImagePos ) )
|
|
{
|
|
rnLevel = nLevel;
|
|
rnEntry = nEntry;
|
|
|
|
// button?
|
|
if ( (nStart >= nStartIndex) && (nImagePos <= nEntryMousePos) && (nEntryMousePos < nImagePos + SC_OL_BITMAPSIZE) )
|
|
{
|
|
rbButton = true;
|
|
return true;
|
|
}
|
|
|
|
// line?
|
|
if ( mbMirrorEntries )
|
|
::std::swap( nStartPos, nEndPos ); // in RTL mode, nStartPos is the larger value
|
|
if ( (nStartPos <= nEntryMousePos) && (nEntryMousePos <= nEndPos) )
|
|
{
|
|
rbButton = false;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ScOutlineWindow::ButtonHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const
|
|
{
|
|
bool bButton;
|
|
bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton );
|
|
return bRet && bButton;
|
|
}
|
|
|
|
bool ScOutlineWindow::LineHit( const Point& rPos, size_t& rnLevel, size_t& rnEntry ) const
|
|
{
|
|
bool bButton;
|
|
bool bRet = ItemHit( rPos, rnLevel, rnEntry, bButton );
|
|
return bRet && !bButton;
|
|
}
|
|
|
|
void ScOutlineWindow::DoFunction( size_t nLevel, size_t nEntry ) const
|
|
{
|
|
ScDBFunc& rFunc = *mrViewData.GetView();
|
|
if ( nEntry == SC_OL_HEADERENTRY )
|
|
rFunc.SelectLevel( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel) );
|
|
else
|
|
{
|
|
const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
|
|
if ( pEntry )
|
|
{
|
|
if ( pEntry->IsHidden() )
|
|
rFunc.ShowOutline( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
|
|
else
|
|
rFunc.HideOutline( mbHoriz, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScOutlineWindow::DoExpand( size_t nLevel, size_t nEntry ) const
|
|
{
|
|
const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
|
|
if ( pEntry && pEntry->IsHidden() )
|
|
DoFunction( nLevel, nEntry );
|
|
}
|
|
|
|
void ScOutlineWindow::DoCollapse( size_t nLevel, size_t nEntry ) const
|
|
{
|
|
const ScOutlineEntry* pEntry = GetOutlineEntry( nLevel, nEntry );
|
|
if ( pEntry && !pEntry->IsHidden() )
|
|
DoFunction( nLevel, nEntry );
|
|
}
|
|
|
|
void ScOutlineWindow::Resize()
|
|
{
|
|
Window::Resize();
|
|
SetHeaderSize( mnHeaderSize ); // recalculates header/group positions
|
|
if ( !IsFocusButtonVisible() )
|
|
{
|
|
HideFocus();
|
|
ShowFocus(); // calculates valid position
|
|
}
|
|
}
|
|
|
|
void ScOutlineWindow::DataChanged( const DataChangedEvent& rDCEvt )
|
|
{
|
|
if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
|
|
(rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
|
|
{
|
|
InitSettings();
|
|
Invalidate();
|
|
}
|
|
Window::DataChanged( rDCEvt );
|
|
}
|
|
|
|
// drawing --------------------------------------------------------------------
|
|
|
|
void ScOutlineWindow::SetEntryAreaClipRegion()
|
|
{
|
|
GetOutDev()->SetClipRegion( vcl::Region(tools::Rectangle(
|
|
GetPoint( 0, mnMainFirstPos ),
|
|
GetPoint( GetOutputSizeLevel() - 1, mnMainLastPos ))));
|
|
}
|
|
|
|
void ScOutlineWindow::DrawLineRel(
|
|
tools::Long nLevelStart, tools::Long nEntryStart, tools::Long nLevelEnd, tools::Long nEntryEnd )
|
|
{
|
|
GetOutDev()->DrawLine( GetPoint( nLevelStart, nEntryStart ), GetPoint( nLevelEnd, nEntryEnd ) );
|
|
}
|
|
|
|
void ScOutlineWindow::DrawRectRel(
|
|
tools::Long nLevelStart, tools::Long nEntryStart, tools::Long nLevelEnd, tools::Long nEntryEnd )
|
|
{
|
|
GetOutDev()->DrawRect( GetRectangle( nLevelStart, nEntryStart, nLevelEnd, nEntryEnd ) );
|
|
}
|
|
|
|
namespace
|
|
{
|
|
Image GetImage(const OUString& rId)
|
|
{
|
|
return Image(StockImage::Yes, rId);
|
|
}
|
|
}
|
|
|
|
void ScOutlineWindow::DrawImageRel(tools::Long nLevelPos, tools::Long nEntryPos, const OUString& rId)
|
|
{
|
|
const Image aImage = GetImage(rId);
|
|
GetOutDev()->SetLineColor();
|
|
GetOutDev()->SetFillColor( GetBackground().GetColor() );
|
|
Point aPos( GetPoint( nLevelPos, nEntryPos ) );
|
|
GetOutDev()->DrawRect( tools::Rectangle( aPos, aImage.GetSizePixel() ) );
|
|
GetOutDev()->DrawImage( aPos, aImage );
|
|
}
|
|
|
|
void ScOutlineWindow::DrawBorderRel( size_t nLevel, size_t nEntry, bool bPressed )
|
|
{
|
|
Point aPos;
|
|
if ( GetImagePos( nLevel, nEntry, aPos ) )
|
|
{
|
|
OUString sId = bPressed ? RID_BMP_PRESSED : RID_BMP_NOTPRESSED;
|
|
bool bClip = (nEntry != SC_OL_HEADERENTRY);
|
|
if ( bClip )
|
|
SetEntryAreaClipRegion();
|
|
GetOutDev()->DrawImage(aPos, GetImage(sId));
|
|
if ( bClip )
|
|
GetOutDev()->SetClipRegion();
|
|
}
|
|
mbMTPressed = bPressed;
|
|
}
|
|
|
|
void ScOutlineWindow::ShowFocus()
|
|
{
|
|
if ( !HasFocus() )
|
|
return;
|
|
|
|
// first move to a visible position
|
|
ImplMoveFocusToVisible( true );
|
|
|
|
if ( !IsFocusButtonVisible() )
|
|
return;
|
|
|
|
Point aPos;
|
|
if ( GetImagePos( mnFocusLevel, mnFocusEntry, aPos ) )
|
|
{
|
|
aPos += Point( 1, 1 );
|
|
maFocusRect = tools::Rectangle( aPos, Size( SC_OL_BITMAPSIZE - 2, SC_OL_BITMAPSIZE - 2 ) );
|
|
bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY);
|
|
if ( bClip )
|
|
SetEntryAreaClipRegion();
|
|
InvertTracking( maFocusRect, ShowTrackFlags::Small | ShowTrackFlags::TrackWindow );
|
|
if ( bClip )
|
|
GetOutDev()->SetClipRegion();
|
|
}
|
|
}
|
|
|
|
void ScOutlineWindow::HideFocus()
|
|
{
|
|
if ( !maFocusRect.IsEmpty() )
|
|
{
|
|
bool bClip = (mnFocusEntry != SC_OL_HEADERENTRY);
|
|
if ( bClip )
|
|
SetEntryAreaClipRegion();
|
|
InvertTracking( maFocusRect, ShowTrackFlags::Small | ShowTrackFlags::TrackWindow );
|
|
if ( bClip )
|
|
GetOutDev()->SetClipRegion();
|
|
maFocusRect.SetEmpty();
|
|
}
|
|
}
|
|
|
|
constexpr OUString aLevelBmps[]=
|
|
{
|
|
RID_BMP_LEVEL1,
|
|
RID_BMP_LEVEL2,
|
|
RID_BMP_LEVEL3,
|
|
RID_BMP_LEVEL4,
|
|
RID_BMP_LEVEL5,
|
|
RID_BMP_LEVEL6,
|
|
RID_BMP_LEVEL7,
|
|
RID_BMP_LEVEL8
|
|
};
|
|
|
|
void ScOutlineWindow::Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& /* rRect */ )
|
|
{
|
|
tools::Long nEntriesSign = mbMirrorEntries ? -1 : 1;
|
|
tools::Long nLevelsSign = mbMirrorLevels ? -1 : 1;
|
|
|
|
Size aSize = GetOutputSizePixel();
|
|
tools::Long nLevelEnd = (mbHoriz ? aSize.Height() : aSize.Width()) - 1;
|
|
tools::Long nEntryEnd = (mbHoriz ? aSize.Width() : aSize.Height()) - 1;
|
|
|
|
GetOutDev()->SetLineColor( maLineColor );
|
|
tools::Long nBorderPos = mbMirrorLevels ? 0 : nLevelEnd;
|
|
DrawLineRel( nBorderPos, 0, nBorderPos, nEntryEnd );
|
|
|
|
const ScOutlineArray* pArray = GetOutlineArray();
|
|
if ( !pArray ) return;
|
|
|
|
size_t nLevelCount = GetLevelCount();
|
|
|
|
// --- draw header images ---
|
|
|
|
if ( mnHeaderSize > 0 )
|
|
{
|
|
tools::Long nEntryPos = GetHeaderEntryPos();
|
|
for ( size_t nLevel = 0; nLevel < nLevelCount; ++nLevel )
|
|
DrawImageRel(GetLevelPos(nLevel), nEntryPos, aLevelBmps[nLevel]);
|
|
|
|
GetOutDev()->SetLineColor( maLineColor );
|
|
tools::Long nLinePos = mnHeaderPos + (mbMirrorEntries ? 0 : (mnHeaderSize - 1));
|
|
DrawLineRel( 0, nLinePos, nLevelEnd, nLinePos );
|
|
}
|
|
|
|
// --- draw lines & collapse/expand images ---
|
|
|
|
SetEntryAreaClipRegion();
|
|
|
|
SCCOLROW nStartIndex, nEndIndex;
|
|
GetVisibleRange( nStartIndex, nEndIndex );
|
|
|
|
for ( size_t nLevel = 0; nLevel + 1 < nLevelCount; ++nLevel )
|
|
{
|
|
tools::Long nLevelPos = GetLevelPos( nLevel );
|
|
tools::Long nEntryPos1 = 0, nEntryPos2 = 0, nImagePos = 0;
|
|
|
|
size_t nEntryCount = pArray->GetCount( sal::static_int_cast<sal_uInt16>(nLevel) );
|
|
size_t nEntry;
|
|
|
|
// first draw all lines in the current level
|
|
GetOutDev()->SetLineColor();
|
|
GetOutDev()->SetFillColor( maLineColor );
|
|
for ( nEntry = 0; nEntry < nEntryCount; ++nEntry )
|
|
{
|
|
const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
|
|
sal::static_int_cast<sal_uInt16>(nEntry) );
|
|
SCCOLROW nStart = pEntry->GetStart();
|
|
SCCOLROW nEnd = pEntry->GetEnd();
|
|
|
|
// visible range?
|
|
bool bDraw = (nEnd >= nStartIndex) && (nStart <= nEndIndex);
|
|
// find output coordinates
|
|
if ( bDraw )
|
|
bDraw = GetEntryPos( nLevel, nEntry, nEntryPos1, nEntryPos2, nImagePos );
|
|
// draw, if not collapsed
|
|
if ( bDraw && !pEntry->IsHidden() )
|
|
{
|
|
if ( nStart >= nStartIndex )
|
|
nEntryPos1 += nEntriesSign;
|
|
nEntryPos2 -= 2 * nEntriesSign;
|
|
tools::Long nLinePos = nLevelPos;
|
|
if ( mbMirrorLevels )
|
|
nLinePos += SC_OL_BITMAPSIZE - 1; // align with right edge of bitmap
|
|
DrawRectRel( nLinePos, nEntryPos1, nLinePos + nLevelsSign, nEntryPos2 );
|
|
|
|
if ( nEnd <= nEndIndex )
|
|
DrawRectRel( nLinePos, nEntryPos2 - nEntriesSign,
|
|
nLinePos + ( SC_OL_BITMAPSIZE / 3 ) * nLevelsSign, nEntryPos2 );
|
|
}
|
|
}
|
|
|
|
// draw all images in the level from last to first
|
|
nEntry = nEntryCount;
|
|
while ( nEntry )
|
|
{
|
|
--nEntry;
|
|
|
|
const ScOutlineEntry* pEntry = pArray->GetEntry( sal::static_int_cast<sal_uInt16>(nLevel),
|
|
sal::static_int_cast<sal_uInt16>(nEntry) );
|
|
SCCOLROW nStart = pEntry->GetStart();
|
|
|
|
// visible range?
|
|
bool bDraw = (nStartIndex <= nStart) && (nStart <= nEndIndex + 1);
|
|
// find output coordinates
|
|
if ( bDraw )
|
|
bDraw = GetEntryPos( nLevel, nEntry, nEntryPos1, nEntryPos2, nImagePos );
|
|
// draw, if not hidden by higher levels
|
|
if ( bDraw )
|
|
{
|
|
OUString sImageId = pEntry->IsHidden() ? RID_BMP_PLUS : RID_BMP_MINUS;
|
|
DrawImageRel(nLevelPos, nImagePos, sImageId);
|
|
}
|
|
}
|
|
}
|
|
|
|
GetOutDev()->SetClipRegion();
|
|
|
|
if ( !mbDontDrawFocus )
|
|
ShowFocus();
|
|
}
|
|
|
|
// focus ----------------------------------------------------------------------
|
|
|
|
/** Increments or decrements a value and wraps at the specified limits.
|
|
@return true = value wrapped. */
|
|
static bool lcl_RotateValue( size_t& rnValue, size_t nMin, size_t nMax, bool bForward )
|
|
{
|
|
OSL_ENSURE( nMin <= nMax, "lcl_RotateValue - invalid range" );
|
|
OSL_ENSURE( nMax < static_cast< size_t >( -1 ), "lcl_RotateValue - range overflow" );
|
|
bool bWrap = false;
|
|
if ( bForward )
|
|
{
|
|
if ( rnValue < nMax )
|
|
++rnValue;
|
|
else
|
|
{
|
|
rnValue = nMin;
|
|
bWrap = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( rnValue > nMin )
|
|
--rnValue;
|
|
else
|
|
{
|
|
rnValue = nMax;
|
|
bWrap = true;
|
|
}
|
|
}
|
|
return bWrap;
|
|
}
|
|
|
|
bool ScOutlineWindow::IsFocusButtonVisible() const
|
|
{
|
|
return IsButtonVisible( mnFocusLevel, mnFocusEntry );
|
|
}
|
|
|
|
bool ScOutlineWindow::ImplMoveFocusByEntry( bool bForward, bool bFindVisible )
|
|
{
|
|
const ScOutlineArray* pArray = GetOutlineArray();
|
|
if ( !pArray )
|
|
return false;
|
|
|
|
bool bWrapped = false;
|
|
size_t nEntryCount = pArray->GetCount( sal::static_int_cast<sal_uInt16>(mnFocusLevel) );
|
|
// #i29530# entry count may be decreased after changing active sheet
|
|
if( mnFocusEntry >= nEntryCount )
|
|
mnFocusEntry = SC_OL_HEADERENTRY;
|
|
size_t nOldEntry = mnFocusEntry;
|
|
|
|
do
|
|
{
|
|
if ( mnFocusEntry == SC_OL_HEADERENTRY )
|
|
{
|
|
// move from header to first or last entry
|
|
if ( nEntryCount > 0 )
|
|
mnFocusEntry = bForward ? 0 : (nEntryCount - 1);
|
|
/* wrapped, if forward from right header to first entry,
|
|
or if backward from left header to last entry */
|
|
// Header and entries are now always in consistent order,
|
|
// so there's no need to check for mirroring here.
|
|
if ( !nEntryCount || !bForward )
|
|
bWrapped = true;
|
|
}
|
|
else if ( lcl_RotateValue( mnFocusEntry, 0, nEntryCount - 1, bForward ) )
|
|
{
|
|
// lcl_RotateValue returns true -> wrapped the entry range -> move to header
|
|
mnFocusEntry = SC_OL_HEADERENTRY;
|
|
/* wrapped, if forward from last entry to left header,
|
|
or if backward from first entry to right header */
|
|
if ( bForward )
|
|
bWrapped = true;
|
|
}
|
|
}
|
|
while ( bFindVisible && !IsFocusButtonVisible() && (nOldEntry != mnFocusEntry) );
|
|
|
|
return bWrapped;
|
|
}
|
|
|
|
bool ScOutlineWindow::ImplMoveFocusByLevel( bool bForward )
|
|
{
|
|
const ScOutlineArray* pArray = GetOutlineArray();
|
|
if ( !pArray )
|
|
return false;
|
|
|
|
bool bWrapped = false;
|
|
size_t nLevelCount = GetLevelCount();
|
|
|
|
if ( mnFocusEntry == SC_OL_HEADERENTRY )
|
|
{
|
|
if ( nLevelCount > 0 )
|
|
bWrapped = lcl_RotateValue( mnFocusLevel, 0, nLevelCount - 1, bForward );
|
|
}
|
|
else
|
|
{
|
|
const ScOutlineEntry* pEntry = pArray->GetEntry(
|
|
mnFocusLevel, mnFocusEntry);
|
|
|
|
if ( pEntry )
|
|
{
|
|
SCCOLROW nStart = pEntry->GetStart();
|
|
SCCOLROW nEnd = pEntry->GetEnd();
|
|
size_t nNewLevel = mnFocusLevel;
|
|
size_t nNewEntry = 0;
|
|
|
|
bool bFound = false;
|
|
if ( bForward && (mnFocusLevel + 2 < nLevelCount) )
|
|
{
|
|
// next level -> find first child entry
|
|
nNewLevel = mnFocusLevel + 1;
|
|
bFound = pArray->GetEntryIndexInRange(nNewLevel, nStart, nEnd, nNewEntry);
|
|
}
|
|
else if ( !bForward && (mnFocusLevel > 0) )
|
|
{
|
|
// previous level -> find parent entry
|
|
nNewLevel = mnFocusLevel - 1;
|
|
bFound = pArray->GetEntryIndex(nNewLevel, nStart, nNewEntry);
|
|
}
|
|
|
|
if ( bFound && IsButtonVisible( nNewLevel, nNewEntry ) )
|
|
{
|
|
mnFocusLevel = nNewLevel;
|
|
mnFocusEntry = nNewEntry;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bWrapped;
|
|
}
|
|
|
|
bool ScOutlineWindow::ImplMoveFocusByTabOrder( bool bForward )
|
|
{
|
|
bool bRet = false;
|
|
size_t nOldLevel = mnFocusLevel;
|
|
size_t nOldEntry = mnFocusEntry;
|
|
|
|
do
|
|
{
|
|
/* one level up, if backward from left header,
|
|
or one level down, if forward from right header */
|
|
if ( (!bForward) && (mnFocusEntry == SC_OL_HEADERENTRY) )
|
|
bRet |= ImplMoveFocusByLevel( bForward );
|
|
// move to next/previous entry
|
|
bool bWrapInLevel = ImplMoveFocusByEntry( bForward, false );
|
|
bRet |= bWrapInLevel;
|
|
/* one level up, if wrapped backward to right header,
|
|
or one level down, if wrapped forward to right header */
|
|
if ( bForward && bWrapInLevel )
|
|
bRet |= ImplMoveFocusByLevel( bForward );
|
|
}
|
|
while ( !IsFocusButtonVisible() && ((nOldLevel != mnFocusLevel) || (nOldEntry != mnFocusEntry)) );
|
|
|
|
return bRet;
|
|
}
|
|
|
|
void ScOutlineWindow::ImplMoveFocusToVisible( bool bForward )
|
|
{
|
|
// first try to find an entry in the same level
|
|
if ( !IsFocusButtonVisible() )
|
|
ImplMoveFocusByEntry( bForward, true );
|
|
// then try to find any other entry
|
|
if ( !IsFocusButtonVisible() )
|
|
ImplMoveFocusByTabOrder( bForward );
|
|
}
|
|
|
|
void ScOutlineWindow::MoveFocusByEntry( bool bForward )
|
|
{
|
|
HideFocus();
|
|
ImplMoveFocusByEntry( bForward, true );
|
|
ShowFocus();
|
|
}
|
|
|
|
void ScOutlineWindow::MoveFocusByLevel( bool bForward )
|
|
{
|
|
HideFocus();
|
|
ImplMoveFocusByLevel( bForward );
|
|
ShowFocus();
|
|
}
|
|
|
|
void ScOutlineWindow::MoveFocusByTabOrder( bool bForward )
|
|
{
|
|
HideFocus();
|
|
ImplMoveFocusByTabOrder( bForward );
|
|
ShowFocus();
|
|
}
|
|
|
|
void ScOutlineWindow::GetFocus()
|
|
{
|
|
Window::GetFocus();
|
|
ShowFocus();
|
|
}
|
|
|
|
void ScOutlineWindow::LoseFocus()
|
|
{
|
|
HideFocus();
|
|
Window::LoseFocus();
|
|
}
|
|
|
|
// mouse ----------------------------------------------------------------------
|
|
|
|
void ScOutlineWindow::StartMouseTracking( size_t nLevel, size_t nEntry )
|
|
{
|
|
mbMTActive = true;
|
|
mnMTLevel = nLevel;
|
|
mnMTEntry = nEntry;
|
|
DrawBorderRel( nLevel, nEntry, true );
|
|
}
|
|
|
|
void ScOutlineWindow::EndMouseTracking()
|
|
{
|
|
if ( mbMTPressed )
|
|
DrawBorderRel( mnMTLevel, mnMTEntry, false );
|
|
mbMTActive = false;
|
|
}
|
|
|
|
void ScOutlineWindow::MouseMove( const MouseEvent& rMEvt )
|
|
{
|
|
if ( IsMouseTracking() )
|
|
{
|
|
size_t nLevel, nEntry;
|
|
bool bHit = false;
|
|
|
|
if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) )
|
|
bHit = (nLevel == mnMTLevel) && (nEntry == mnMTEntry);
|
|
|
|
if ( bHit != mbMTPressed )
|
|
DrawBorderRel( mnMTLevel, mnMTEntry, bHit );
|
|
}
|
|
}
|
|
|
|
void ScOutlineWindow::MouseButtonUp( const MouseEvent& rMEvt )
|
|
{
|
|
if ( IsMouseTracking() )
|
|
{
|
|
EndMouseTracking();
|
|
|
|
size_t nLevel, nEntry;
|
|
if ( ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry ) )
|
|
if ( (nLevel == mnMTLevel) && (nEntry == mnMTEntry) )
|
|
DoFunction( nLevel, nEntry );
|
|
}
|
|
}
|
|
|
|
void ScOutlineWindow::MouseButtonDown( const MouseEvent& rMEvt )
|
|
{
|
|
size_t nLevel, nEntry;
|
|
bool bHit = ButtonHit( rMEvt.GetPosPixel(), nLevel, nEntry );
|
|
if ( bHit )
|
|
StartMouseTracking( nLevel, nEntry );
|
|
else if ( rMEvt.GetClicks() == 2 )
|
|
{
|
|
bHit = LineHit( rMEvt.GetPosPixel(), nLevel, nEntry );
|
|
if ( bHit )
|
|
DoFunction( nLevel, nEntry );
|
|
}
|
|
|
|
// if an item has been hit and window is focused, move focus to this item
|
|
if ( bHit && HasFocus() )
|
|
{
|
|
HideFocus();
|
|
mnFocusLevel = nLevel;
|
|
mnFocusEntry = nEntry;
|
|
ShowFocus();
|
|
}
|
|
}
|
|
|
|
// keyboard -------------------------------------------------------------------
|
|
|
|
void ScOutlineWindow::KeyInput( const KeyEvent& rKEvt )
|
|
{
|
|
const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
|
|
bool bNoMod = !rKCode.GetModifier();
|
|
bool bShift = (rKCode.GetModifier() == KEY_SHIFT);
|
|
bool bCtrl = (rKCode.GetModifier() == KEY_MOD1);
|
|
|
|
sal_uInt16 nCode = rKCode.GetCode();
|
|
bool bUpDownKey = (nCode == KEY_UP) || (nCode == KEY_DOWN);
|
|
bool bLeftRightKey = (nCode == KEY_LEFT) || (nCode == KEY_RIGHT);
|
|
|
|
// TAB key
|
|
if ( (nCode == KEY_TAB) && (bNoMod || bShift) )
|
|
// move forward without SHIFT key
|
|
MoveFocusByTabOrder( bNoMod ); // TAB uses logical order, regardless of mirroring
|
|
|
|
// LEFT/RIGHT/UP/DOWN keys
|
|
else if ( bNoMod && (bUpDownKey || bLeftRightKey) )
|
|
{
|
|
bool bForward = (nCode == KEY_DOWN) || (nCode == KEY_RIGHT);
|
|
if ( mbHoriz == bLeftRightKey )
|
|
// move inside level with LEFT/RIGHT in horizontal and with UP/DOWN in vertical
|
|
MoveFocusByEntry( bForward != mbMirrorEntries );
|
|
else
|
|
// move to next/prev level with LEFT/RIGHT in vertical and with UP/DOWN in horizontal
|
|
MoveFocusByLevel( bForward != mbMirrorLevels );
|
|
}
|
|
|
|
// CTRL + number
|
|
else if ( bCtrl && (nCode >= KEY_1) && (nCode <= KEY_9) )
|
|
{
|
|
size_t nLevel = static_cast< size_t >( nCode - KEY_1 );
|
|
if ( nLevel < GetLevelCount() )
|
|
DoFunction( nLevel, SC_OL_HEADERENTRY );
|
|
}
|
|
|
|
// other key codes
|
|
else switch ( rKCode.GetFullCode() )
|
|
{
|
|
case KEY_ADD: DoExpand( mnFocusLevel, mnFocusEntry ); break;
|
|
case KEY_SUBTRACT: DoCollapse( mnFocusLevel, mnFocusEntry ); break;
|
|
case KEY_SPACE:
|
|
case KEY_RETURN: DoFunction( mnFocusLevel, mnFocusEntry ); break;
|
|
default: Window::KeyInput( rKEvt );
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|