/* -*- 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 #include #include #include #include #include #include #include #include #include #include namespace { Wallpaper& ImplBlackWall() { static tools::DeleteOnDeinit< Wallpaper > SINGLETON(COL_BLACK); return *SINGLETON.get(); } Wallpaper& ImplWhiteWall() { static tools::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(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(); // Start tracking StartTracking(); // Determine start position 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: */