diff options
Diffstat (limited to 'vcl/source/window/split.cxx')
-rw-r--r-- | vcl/source/window/split.cxx | 698 |
1 files changed, 698 insertions, 0 deletions
diff --git a/vcl/source/window/split.cxx b/vcl/source/window/split.cxx new file mode 100644 index 0000000000..df631b270b --- /dev/null +++ b/vcl/source/window/split.cxx @@ -0,0 +1,698 @@ +/* -*- 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 <tools/poly.hxx> + +#include <vcl/event.hxx> +#include <vcl/split.hxx> +#include <vcl/svapp.hxx> +#include <vcl/syswin.hxx> +#include <vcl/taskpanelist.hxx> +#include <vcl/lineinfo.hxx> +#include <vcl/settings.hxx> +#include <vcl/ptrstyle.hxx> +#include <vcl/lazydelete.hxx> + +#include <window.h> + +namespace +{ + Wallpaper& ImplBlackWall() + { + static vcl::DeleteOnDeinit< Wallpaper > SINGLETON(COL_BLACK); + return *SINGLETON.get(); + } + Wallpaper& ImplWhiteWall() + { + static vcl::DeleteOnDeinit< Wallpaper > SINGLETON(COL_LIGHTGRAY); + return *SINGLETON.get(); + } +} + +// Should only be called from an ImplInit method for initialization or +// after checking bNew is different from the current mbHorzSplit value. +// The public method that does that check is Splitter::SetHorizontal(). +void Splitter::ImplInitHorVer(bool bNew) +{ + mbHorzSplit = bNew; + + PointerStyle ePointerStyle; + const StyleSettings& rSettings = GetSettings().GetStyleSettings(); + + if ( mbHorzSplit ) + { + ePointerStyle = PointerStyle::HSplit; + SetSizePixel( Size( StyleSettings::GetSplitSize(), rSettings.GetScrollBarSize() ) ); + } + else + { + ePointerStyle = PointerStyle::VSplit; + SetSizePixel( Size( rSettings.GetScrollBarSize(), StyleSettings::GetSplitSize() ) ); + } + + SetPointer( ePointerStyle ); +} + +void Splitter::ImplInit( vcl::Window* pParent, WinBits nWinStyle ) +{ + Window::ImplInit( pParent, nWinStyle, nullptr ); + + mpRefWin = pParent; + + ImplInitHorVer(nWinStyle & WB_HSCROLL); + + if( GetSettings().GetStyleSettings().GetFaceColor().IsDark() ) + SetBackground( ImplWhiteWall() ); + else + SetBackground( ImplBlackWall() ); + + TaskPaneList *pTList = GetSystemWindow()->GetTaskPaneList(); + pTList->AddWindow( this ); +} + +void Splitter::ImplSplitMousePos( Point& rPos ) +{ + if ( mbHorzSplit ) + { + if ( rPos.X() > maDragRect.Right()-1 ) + rPos.setX( maDragRect.Right()-1 ); + if ( rPos.X() < maDragRect.Left()+1 ) + rPos.setX( maDragRect.Left()+1 ); + } + else + { + if ( rPos.Y() > maDragRect.Bottom()-1 ) + rPos.setY( maDragRect.Bottom()-1 ); + if ( rPos.Y() < maDragRect.Top()+1 ) + rPos.setY( maDragRect.Top()+1 ); + } +} + +void Splitter::ImplDrawSplitter() +{ + tools::Rectangle aInvRect( maDragRect ); + + if ( mbHorzSplit ) + { + aInvRect.SetLeft( maDragPos.X() - 1 ); + aInvRect.SetRight( maDragPos.X() + 1 ); + } + else + { + aInvRect.SetTop( maDragPos.Y() - 1 ); + aInvRect.SetBottom( maDragPos.Y() + 1 ); + } + + mpRefWin->InvertTracking( mpRefWin->PixelToLogic(aInvRect), ShowTrackFlags::Split ); +} + +Splitter::Splitter( vcl::Window* pParent, WinBits nStyle ) : + Window( WindowType::SPLITTER ), + mpRefWin( nullptr ), + mnSplitPos( 0 ), + mnLastSplitPos( 0 ), + mnStartSplitPos( 0 ), + mbDragFull( false ), + mbKbdSplitting( false ), + mbInKeyEvent( false ), + mnKeyboardStepSize( SPLITTER_DEFAULTSTEPSIZE ) +{ + ImplGetWindowImpl()->mbSplitter = true; + + ImplInit( pParent, nStyle ); + + GetOutDev()->SetLineColor(); + GetOutDev()->SetFillColor(); +} + +Splitter::~Splitter() +{ + disposeOnce(); +} + +void Splitter::dispose() +{ + SystemWindow *pSysWin = GetSystemWindow(); + if(pSysWin) + { + TaskPaneList *pTList = pSysWin->GetTaskPaneList(); + pTList->RemoveWindow(this); + } + mpRefWin.clear(); + Window::dispose(); +} + +void Splitter::SetHorizontal(bool bNew) +{ + if(bNew != mbHorzSplit) + { + ImplInitHorVer(bNew); + } +} + +void Splitter::SetKeyboardStepSize( tools::Long nStepSize ) +{ + mnKeyboardStepSize = nStepSize; +} + +Splitter* Splitter::ImplFindSibling() +{ + // look for another splitter with the same parent but different orientation + vcl::Window *pWin = GetParent()->GetWindow( GetWindowType::FirstChild ); + Splitter *pSplitter = nullptr; + while( pWin ) + { + if( pWin->ImplIsSplitter() ) + { + pSplitter = static_cast<Splitter*>(pWin); + if( pSplitter != this && IsHorizontal() != pSplitter->IsHorizontal() ) + return pSplitter; + } + pWin = pWin->GetWindow( GetWindowType::Next ); + } + return nullptr; +} + +bool Splitter::ImplSplitterActive() +{ + // is splitter in document or at scrollbar handle ? + + bool bActive = true; + const StyleSettings& rSettings = GetSettings().GetStyleSettings(); + tools::Long nA = rSettings.GetScrollBarSize(); + tools::Long nB = StyleSettings::GetSplitSize(); + + Size aSize = GetOutDev()->GetOutputSize(); + if ( mbHorzSplit ) + { + if( aSize.Width() == nB && aSize.Height() == nA ) + bActive = false; + } + else + { + if( aSize.Width() == nA && aSize.Height() == nB ) + bActive = false; + } + return bActive; +} + +void Splitter::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if ( rMEvt.GetClicks() == 2 ) + { + if ( mnLastSplitPos != mnSplitPos ) + { + StartSplit(); + Point aPos = rMEvt.GetPosPixel(); + if ( mbHorzSplit ) + aPos.setX( mnLastSplitPos ); + else + aPos.setY( mnLastSplitPos ); + ImplSplitMousePos( aPos ); + tools::Long nTemp = mnSplitPos; + if ( mbHorzSplit ) + SetSplitPosPixel( aPos.X() ); + else + SetSplitPosPixel( aPos.Y() ); + mnLastSplitPos = nTemp; + Split(); + EndSplit(); + } + } + else + StartDrag(); +} + +void Splitter::Tracking( const TrackingEvent& rTEvt ) +{ + if ( rTEvt.IsTrackingEnded() ) + { + if ( !mbDragFull ) + ImplDrawSplitter(); + + if ( !rTEvt.IsTrackingCanceled() ) + { + tools::Long nNewPos; + if ( mbHorzSplit ) + nNewPos = maDragPos.X(); + else + nNewPos = maDragPos.Y(); + if ( nNewPos != mnStartSplitPos ) + { + SetSplitPosPixel( nNewPos ); + mnLastSplitPos = 0; + Split(); + } + EndSplit(); + } + else if ( mbDragFull ) + { + SetSplitPosPixel( mnStartSplitPos ); + Split(); + } + mnStartSplitPos = 0; + } + else + { + //Point aNewPos = mpRefWin->ScreenToOutputPixel( OutputToScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) ); + Point aNewPos = mpRefWin->NormalizedScreenToOutputPixel( OutputToNormalizedScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) ); + ImplSplitMousePos( aNewPos ); + + if ( mbHorzSplit ) + { + if ( aNewPos.X() == maDragPos.X() ) + return; + } + else + { + if ( aNewPos.Y() == maDragPos.Y() ) + return; + } + + if ( mbDragFull ) + { + maDragPos = aNewPos; + tools::Long nNewPos; + if ( mbHorzSplit ) + nNewPos = maDragPos.X(); + else + nNewPos = maDragPos.Y(); + if ( nNewPos != mnSplitPos ) + { + SetSplitPosPixel( nNewPos ); + mnLastSplitPos = 0; + Split(); + } + + GetParent()->PaintImmediately(); + } + else + { + ImplDrawSplitter(); + maDragPos = aNewPos; + ImplDrawSplitter(); + } + } +} + +void Splitter::ImplKbdTracking( vcl::KeyCode aKeyCode ) +{ + sal_uInt16 nCode = aKeyCode.GetCode(); + if ( nCode == KEY_ESCAPE || nCode == KEY_RETURN ) + { + if( !mbKbdSplitting ) + return; + else + mbKbdSplitting = false; + + if ( nCode != KEY_ESCAPE ) + { + tools::Long nNewPos; + if ( mbHorzSplit ) + nNewPos = maDragPos.X(); + else + nNewPos = maDragPos.Y(); + if ( nNewPos != mnStartSplitPos ) + { + SetSplitPosPixel( nNewPos ); + mnLastSplitPos = 0; + Split(); + } + } + else + { + SetSplitPosPixel( mnStartSplitPos ); + Split(); + EndSplit(); + } + mnStartSplitPos = 0; + } + else + { + Point aNewPos; + Size aSize = mpRefWin->GetOutDev()->GetOutputSize(); + Point aPos = GetPosPixel(); + // depending on the position calc allows continuous moves or snaps to row/columns + // continuous mode is active when position is at the origin or end of the splitter + // otherwise snap mode is active + // default here is snap, holding shift sets continuous mode + if( mbHorzSplit ) + aNewPos = Point( ImplSplitterActive() ? aPos.X() : mnSplitPos, aKeyCode.IsShift() ? 0 : aSize.Height()/2); + else + aNewPos = Point( aKeyCode.IsShift() ? 0 : aSize.Width()/2, ImplSplitterActive() ? aPos.Y() : mnSplitPos ); + + Point aOldWindowPos = GetPosPixel(); + + int maxiter = 500; // avoid endless loop + int delta=0; + int delta_step = mbHorzSplit ? aSize.Width()/10 : aSize.Height()/10; + + // use the specified step size if it was set + if( mnKeyboardStepSize != SPLITTER_DEFAULTSTEPSIZE ) + delta_step = mnKeyboardStepSize; + + while( maxiter-- && aOldWindowPos == GetPosPixel() ) + { + // inc/dec position until application performs changes + // thus a single key press really moves the splitter + if( aKeyCode.IsShift() ) + delta++; + else + delta += delta_step; + + switch( nCode ) + { + case KEY_LEFT: + aNewPos.AdjustX( -delta ); + break; + case KEY_RIGHT: + aNewPos.AdjustX(delta ); + break; + case KEY_UP: + aNewPos.AdjustY( -delta ); + break; + case KEY_DOWN: + aNewPos.AdjustY(delta ); + break; + default: + maxiter = 0; // leave loop + break; + } + ImplSplitMousePos( aNewPos ); + + if ( mbHorzSplit ) + { + if ( aNewPos.X() == maDragPos.X() ) + continue; + } + else + { + if ( aNewPos.Y() == maDragPos.Y() ) + continue; + } + + maDragPos = aNewPos; + tools::Long nNewPos; + if ( mbHorzSplit ) + nNewPos = maDragPos.X(); + else + nNewPos = maDragPos.Y(); + if ( nNewPos != mnSplitPos ) + { + SetSplitPosPixel( nNewPos ); + mnLastSplitPos = 0; + Split(); + } + GetParent()->PaintImmediately(); + } + } +} + +void Splitter::StartSplit() +{ + maStartSplitHdl.Call( this ); +} + +void Splitter::Split() +{ + maSplitHdl.Call( this ); +} + +void Splitter::EndSplit() +{ + maEndSplitHdl.Call( this ); +} + +void Splitter::SetDragRectPixel( const tools::Rectangle& rDragRect, vcl::Window* _pRefWin ) +{ + maDragRect = rDragRect; + if ( !_pRefWin ) + mpRefWin = GetParent(); + else + mpRefWin = _pRefWin; +} + +void Splitter::SetSplitPosPixel( tools::Long nNewPos ) +{ + mnSplitPos = nNewPos; +} + +void Splitter::StartDrag() +{ + if ( IsTracking() ) + return; + + StartSplit(); + + // Tracking starten + StartTracking(); + + // Start-Position ermitteln + maDragPos = mpRefWin->GetPointerPosPixel(); + ImplSplitMousePos( maDragPos ); + if ( mbHorzSplit ) + mnStartSplitPos = maDragPos.X(); + else + mnStartSplitPos = maDragPos.Y(); + + mbDragFull = bool(Application::GetSettings().GetStyleSettings().GetDragFullOptions() & DragFullOptions::Split); + if ( !mbDragFull ) + ImplDrawSplitter(); +} + +void Splitter::ImplStartKbdSplitting() +{ + if( mbKbdSplitting ) + return; + + mbKbdSplitting = true; + + StartSplit(); + + // determine start position + // because we have no mouse position we take either the position + // of the splitter window or the last split position + // the other coordinate is just the center of the reference window + Size aSize = mpRefWin->GetOutDev()->GetOutputSize(); + Point aPos = GetPosPixel(); + if( mbHorzSplit ) + maDragPos = Point( ImplSplitterActive() ? aPos.X() : mnSplitPos, aSize.Height()/2 ); + else + maDragPos = Point( aSize.Width()/2, ImplSplitterActive() ? aPos.Y() : mnSplitPos ); + ImplSplitMousePos( maDragPos ); + if ( mbHorzSplit ) + mnStartSplitPos = maDragPos.X(); + else + mnStartSplitPos = maDragPos.Y(); +} + +void Splitter::ImplRestoreSplitter() +{ + // set splitter in the center of the ref window + StartSplit(); + Size aSize = mpRefWin->GetOutDev()->GetOutputSize(); + Point aPos( aSize.Width()/2 , aSize.Height()/2); + if ( mnLastSplitPos != mnSplitPos && mnLastSplitPos > 5 ) + { + // restore last pos if it was a useful position (>5) + if ( mbHorzSplit ) + aPos.setX( mnLastSplitPos ); + else + aPos.setY( mnLastSplitPos ); + } + + ImplSplitMousePos( aPos ); + tools::Long nTemp = mnSplitPos; + if ( mbHorzSplit ) + SetSplitPosPixel( aPos.X() ); + else + SetSplitPosPixel( aPos.Y() ); + mnLastSplitPos = nTemp; + Split(); + EndSplit(); +} + +void Splitter::GetFocus() +{ + if( !ImplSplitterActive() ) + ImplRestoreSplitter(); + + Invalidate(); +} + +void Splitter::LoseFocus() +{ + if( mbKbdSplitting ) + { + vcl::KeyCode aReturnKey( KEY_RETURN ); + ImplKbdTracking( aReturnKey ); + mbKbdSplitting = false; + } + Invalidate(); +} + +void Splitter::KeyInput( const KeyEvent& rKEvt ) +{ + if( mbInKeyEvent ) + return; + + mbInKeyEvent = true; + + Splitter *pSibling = ImplFindSibling(); + vcl::KeyCode aKeyCode = rKEvt.GetKeyCode(); + sal_uInt16 nCode = aKeyCode.GetCode(); + switch ( nCode ) + { + case KEY_UP: + case KEY_DOWN: + if( !mbHorzSplit ) + { + ImplStartKbdSplitting(); + ImplKbdTracking( aKeyCode ); + } + else + { + if( pSibling ) + { + pSibling->GrabFocus(); + pSibling->KeyInput( rKEvt ); + } + } + break; + case KEY_RIGHT: + case KEY_LEFT: + if( mbHorzSplit ) + { + ImplStartKbdSplitting(); + ImplKbdTracking( aKeyCode ); + } + else + { + if( pSibling ) + { + pSibling->GrabFocus(); + pSibling->KeyInput( rKEvt ); + } + } + break; + + case KEY_DELETE: + if( ImplSplitterActive() ) + { + if( mbKbdSplitting ) + { + vcl::KeyCode aKey( KEY_ESCAPE ); + ImplKbdTracking( aKey ); + } + + StartSplit(); + Point aPos; + if ( mbHorzSplit ) + aPos.setX( 0 ); + else + aPos.setY( 0 ); + ImplSplitMousePos( aPos ); + tools::Long nTemp = mnSplitPos; + if ( mbHorzSplit ) + SetSplitPosPixel( aPos.X() ); + else + SetSplitPosPixel( aPos.Y() ); + mnLastSplitPos = nTemp; + Split(); + EndSplit(); + + // Shift-Del deletes both splitters + if( aKeyCode.IsShift() && pSibling ) + pSibling->KeyInput( rKEvt ); + + GrabFocusToDocument(); + } + break; + + case KEY_ESCAPE: + if( mbKbdSplitting ) + ImplKbdTracking( aKeyCode ); + else + GrabFocusToDocument(); + break; + + case KEY_RETURN: + ImplKbdTracking( aKeyCode ); + GrabFocusToDocument(); + break; + default: // let any key input fix the splitter + Window::KeyInput( rKEvt ); + GrabFocusToDocument(); + break; + } + mbInKeyEvent = false; +} + +void Splitter::DataChanged( const DataChangedEvent& rDCEvt ) +{ + Window::DataChanged( rDCEvt ); + if( rDCEvt.GetType() != DataChangedEventType::SETTINGS ) + return; + + const AllSettings* pOldSettings = rDCEvt.GetOldSettings(); + if(!pOldSettings) + return; + + Color oldFaceColor = pOldSettings->GetStyleSettings().GetFaceColor(); + Color newFaceColor = Application::GetSettings().GetStyleSettings().GetFaceColor(); + if( oldFaceColor.IsDark() != newFaceColor.IsDark() ) + { + if( newFaceColor.IsDark() ) + SetBackground( ImplWhiteWall() ); + else + SetBackground( ImplBlackWall() ); + } +} + +void Splitter::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rPaintRect) +{ + rRenderContext.DrawRect(rPaintRect); + + tools::Polygon aPoly(rPaintRect); + tools::PolyPolygon aPolyPoly(aPoly); + rRenderContext.DrawTransparent(aPolyPoly, 85); + + if (mbKbdSplitting) + { + LineInfo aInfo( LineStyle::Dash ); + //aInfo.SetDashLen( 2 ); + //aInfo.SetDashCount( 1 ); + aInfo.SetDistance( 1 ); + aInfo.SetDotLen( 2 ); + aInfo.SetDotCount( 3 ); + + rRenderContext.DrawPolyLine( aPoly, aInfo ); + } + else + { + rRenderContext.DrawRect(rPaintRect); + } +} + +Size Splitter::GetOptimalSize() const +{ + return LogicToPixel(Size(3, 3), MapMode(MapUnit::MapAppFont)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |