1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <tools/poly.hxx>
22 #include <vcl/event.hxx>
23 #include <vcl/split.hxx>
24 #include <vcl/svapp.hxx>
25 #include <vcl/syswin.hxx>
26 #include <vcl/taskpanelist.hxx>
27 #include <vcl/lineinfo.hxx>
28 #include <vcl/settings.hxx>
29 #include <vcl/ptrstyle.hxx>
31 #include <rtl/instance.hxx>
38 : public rtl::StaticWithInit
<Wallpaper
, ImplBlackWall
> {
39 Wallpaper
operator () () {
40 return Wallpaper(COL_BLACK
);
44 : public rtl::StaticWithInit
<Wallpaper
, ImplWhiteWall
> {
45 Wallpaper
operator () () {
46 return Wallpaper(COL_LIGHTGRAY
);
51 // Should only be called from an ImplInit method for initialization or
52 // after checking bNew is different from the current mbHorzSplit value.
53 // The public method that does that check is Splitter::SetHorizontal().
54 void Splitter::ImplInitHorVer(bool bNew
)
58 PointerStyle ePointerStyle
;
59 const StyleSettings
& rSettings
= GetSettings().GetStyleSettings();
63 ePointerStyle
= PointerStyle::HSplit
;
64 SetSizePixel( Size( StyleSettings::GetSplitSize(), rSettings
.GetScrollBarSize() ) );
68 ePointerStyle
= PointerStyle::VSplit
;
69 SetSizePixel( Size( rSettings
.GetScrollBarSize(), StyleSettings::GetSplitSize() ) );
72 SetPointer( ePointerStyle
);
75 void Splitter::ImplInit( vcl::Window
* pParent
, WinBits nWinStyle
)
77 Window::ImplInit( pParent
, nWinStyle
, nullptr );
81 ImplInitHorVer(nWinStyle
& WB_HSCROLL
);
83 if( GetSettings().GetStyleSettings().GetFaceColor().IsDark() )
84 SetBackground( ImplWhiteWall::get() );
86 SetBackground( ImplBlackWall::get() );
88 TaskPaneList
*pTList
= GetSystemWindow()->GetTaskPaneList();
89 pTList
->AddWindow( this );
92 void Splitter::ImplSplitMousePos( Point
& rPos
)
96 if ( rPos
.X() > maDragRect
.Right()-1 )
97 rPos
.setX( maDragRect
.Right()-1 );
98 if ( rPos
.X() < maDragRect
.Left()+1 )
99 rPos
.setX( maDragRect
.Left()+1 );
103 if ( rPos
.Y() > maDragRect
.Bottom()-1 )
104 rPos
.setY( maDragRect
.Bottom()-1 );
105 if ( rPos
.Y() < maDragRect
.Top()+1 )
106 rPos
.setY( maDragRect
.Top()+1 );
110 void Splitter::ImplDrawSplitter()
112 tools::Rectangle
aInvRect( maDragRect
);
116 aInvRect
.SetLeft( maDragPos
.X() - 1 );
117 aInvRect
.SetRight( maDragPos
.X() + 1 );
121 aInvRect
.SetTop( maDragPos
.Y() - 1 );
122 aInvRect
.SetBottom( maDragPos
.Y() + 1 );
125 mpRefWin
->InvertTracking( mpRefWin
->PixelToLogic(aInvRect
), ShowTrackFlags::Split
);
128 Splitter::Splitter( vcl::Window
* pParent
, WinBits nStyle
) :
129 Window( WindowType::SPLITTER
),
133 mnStartSplitPos( 0 ),
135 mbKbdSplitting( false ),
136 mbInKeyEvent( false ),
137 mnKeyboardStepSize( SPLITTER_DEFAULTSTEPSIZE
)
139 ImplGetWindowImpl()->mbSplitter
= true;
141 ImplInit( pParent
, nStyle
);
147 Splitter::~Splitter()
152 void Splitter::dispose()
154 SystemWindow
*pSysWin
= GetSystemWindow();
157 TaskPaneList
*pTList
= pSysWin
->GetTaskPaneList();
158 pTList
->RemoveWindow(this);
164 void Splitter::SetHorizontal(bool bNew
)
166 if(bNew
!= mbHorzSplit
)
168 ImplInitHorVer(bNew
);
172 void Splitter::SetKeyboardStepSize( long nStepSize
)
174 mnKeyboardStepSize
= nStepSize
;
177 Splitter
* Splitter::ImplFindSibling()
179 // look for another splitter with the same parent but different orientation
180 vcl::Window
*pWin
= GetParent()->GetWindow( GetWindowType::FirstChild
);
181 Splitter
*pSplitter
= nullptr;
184 if( pWin
->ImplIsSplitter() )
186 pSplitter
= static_cast<Splitter
*>(pWin
);
187 if( pSplitter
!= this && IsHorizontal() != pSplitter
->IsHorizontal() )
190 pWin
= pWin
->GetWindow( GetWindowType::Next
);
195 bool Splitter::ImplSplitterActive()
197 // is splitter in document or at scrollbar handle ?
200 const StyleSettings
& rSettings
= GetSettings().GetStyleSettings();
201 long nA
= rSettings
.GetScrollBarSize();
202 long nB
= StyleSettings::GetSplitSize();
204 Size aSize
= GetOutputSize();
207 if( aSize
.Width() == nB
&& aSize
.Height() == nA
)
212 if( aSize
.Width() == nA
&& aSize
.Height() == nB
)
218 void Splitter::MouseButtonDown( const MouseEvent
& rMEvt
)
220 if ( rMEvt
.GetClicks() == 2 )
222 if ( mnLastSplitPos
!= mnSplitPos
)
225 Point aPos
= rMEvt
.GetPosPixel();
227 aPos
.setX( mnLastSplitPos
);
229 aPos
.setY( mnLastSplitPos
);
230 ImplSplitMousePos( aPos
);
231 long nTemp
= mnSplitPos
;
233 SetSplitPosPixel( aPos
.X() );
235 SetSplitPosPixel( aPos
.Y() );
236 mnLastSplitPos
= nTemp
;
245 void Splitter::Tracking( const TrackingEvent
& rTEvt
)
247 if ( rTEvt
.IsTrackingEnded() )
252 if ( !rTEvt
.IsTrackingCanceled() )
256 nNewPos
= maDragPos
.X();
258 nNewPos
= maDragPos
.Y();
259 if ( nNewPos
!= mnStartSplitPos
)
261 SetSplitPosPixel( nNewPos
);
267 else if ( mbDragFull
)
269 SetSplitPosPixel( mnStartSplitPos
);
276 //Point aNewPos = mpRefWin->ScreenToOutputPixel( OutputToScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) );
277 Point aNewPos
= mpRefWin
->NormalizedScreenToOutputPixel( OutputToNormalizedScreenPixel( rTEvt
.GetMouseEvent().GetPosPixel() ) );
278 ImplSplitMousePos( aNewPos
);
282 if ( aNewPos
.X() == maDragPos
.X() )
287 if ( aNewPos
.Y() == maDragPos
.Y() )
296 nNewPos
= maDragPos
.X();
298 nNewPos
= maDragPos
.Y();
299 if ( nNewPos
!= mnSplitPos
)
301 SetSplitPosPixel( nNewPos
);
306 GetParent()->Update();
317 void Splitter::ImplKbdTracking( vcl::KeyCode aKeyCode
)
319 sal_uInt16 nCode
= aKeyCode
.GetCode();
320 if ( nCode
== KEY_ESCAPE
|| nCode
== KEY_RETURN
)
322 if( !mbKbdSplitting
)
325 mbKbdSplitting
= false;
327 if ( nCode
!= KEY_ESCAPE
)
331 nNewPos
= maDragPos
.X();
333 nNewPos
= maDragPos
.Y();
334 if ( nNewPos
!= mnStartSplitPos
)
336 SetSplitPosPixel( nNewPos
);
343 SetSplitPosPixel( mnStartSplitPos
);
352 Size aSize
= mpRefWin
->GetOutputSize();
353 Point aPos
= GetPosPixel();
354 // depending on the position calc allows continuous moves or snaps to row/columns
355 // continuous mode is active when position is at the origin or end of the splitter
356 // otherwise snap mode is active
357 // default here is snap, holding shift sets continuous mode
359 aNewPos
= Point( ImplSplitterActive() ? aPos
.X() : mnSplitPos
, aKeyCode
.IsShift() ? 0 : aSize
.Height()/2);
361 aNewPos
= Point( aKeyCode
.IsShift() ? 0 : aSize
.Width()/2, ImplSplitterActive() ? aPos
.Y() : mnSplitPos
);
363 Point aOldWindowPos
= GetPosPixel();
365 int maxiter
= 500; // avoid endless loop
367 int delta_step
= mbHorzSplit
? aSize
.Width()/10 : aSize
.Height()/10;
369 // use the specified step size if it was set
370 if( mnKeyboardStepSize
!= SPLITTER_DEFAULTSTEPSIZE
)
371 delta_step
= mnKeyboardStepSize
;
373 while( maxiter
-- && aOldWindowPos
== GetPosPixel() )
375 // inc/dec position until application performs changes
376 // thus a single key press really moves the splitter
377 if( aKeyCode
.IsShift() )
385 aNewPos
.AdjustX( -delta
);
388 aNewPos
.AdjustX(delta
);
391 aNewPos
.AdjustY( -delta
);
394 aNewPos
.AdjustY(delta
);
397 maxiter
= 0; // leave loop
400 ImplSplitMousePos( aNewPos
);
404 if ( aNewPos
.X() == maDragPos
.X() )
409 if ( aNewPos
.Y() == maDragPos
.Y() )
416 nNewPos
= maDragPos
.X();
418 nNewPos
= maDragPos
.Y();
419 if ( nNewPos
!= mnSplitPos
)
421 SetSplitPosPixel( nNewPos
);
425 GetParent()->Update();
430 void Splitter::StartSplit()
432 maStartSplitHdl
.Call( this );
435 void Splitter::Split()
437 maSplitHdl
.Call( this );
440 void Splitter::EndSplit()
442 maEndSplitHdl
.Call( this );
445 void Splitter::SetDragRectPixel( const tools::Rectangle
& rDragRect
, vcl::Window
* _pRefWin
)
447 maDragRect
= rDragRect
;
449 mpRefWin
= GetParent();
454 void Splitter::SetSplitPosPixel( long nNewPos
)
456 mnSplitPos
= nNewPos
;
459 void Splitter::StartDrag()
469 // Start-Position ermitteln
470 maDragPos
= mpRefWin
->GetPointerPosPixel();
471 ImplSplitMousePos( maDragPos
);
473 mnStartSplitPos
= maDragPos
.X();
475 mnStartSplitPos
= maDragPos
.Y();
477 mbDragFull
= bool(Application::GetSettings().GetStyleSettings().GetDragFullOptions() & DragFullOptions::Split
);
482 void Splitter::ImplStartKbdSplitting()
487 mbKbdSplitting
= true;
491 // determine start position
492 // because we have no mouse position we take either the position
493 // of the splitter window or the last split position
494 // the other coordinate is just the center of the reference window
495 Size aSize
= mpRefWin
->GetOutputSize();
496 Point aPos
= GetPosPixel();
498 maDragPos
= Point( ImplSplitterActive() ? aPos
.X() : mnSplitPos
, aSize
.Height()/2 );
500 maDragPos
= Point( aSize
.Width()/2, ImplSplitterActive() ? aPos
.Y() : mnSplitPos
);
501 ImplSplitMousePos( maDragPos
);
503 mnStartSplitPos
= maDragPos
.X();
505 mnStartSplitPos
= maDragPos
.Y();
508 void Splitter::ImplRestoreSplitter()
510 // set splitter in the center of the ref window
512 Size aSize
= mpRefWin
->GetOutputSize();
513 Point
aPos( aSize
.Width()/2 , aSize
.Height()/2);
514 if ( mnLastSplitPos
!= mnSplitPos
&& mnLastSplitPos
> 5 )
516 // restore last pos if it was a useful position (>5)
518 aPos
.setX( mnLastSplitPos
);
520 aPos
.setY( mnLastSplitPos
);
523 ImplSplitMousePos( aPos
);
524 long nTemp
= mnSplitPos
;
526 SetSplitPosPixel( aPos
.X() );
528 SetSplitPosPixel( aPos
.Y() );
529 mnLastSplitPos
= nTemp
;
534 void Splitter::GetFocus()
536 if( !ImplSplitterActive() )
537 ImplRestoreSplitter();
542 void Splitter::LoseFocus()
546 vcl::KeyCode
aReturnKey( KEY_RETURN
);
547 ImplKbdTracking( aReturnKey
);
548 mbKbdSplitting
= false;
553 void Splitter::KeyInput( const KeyEvent
& rKEvt
)
560 Splitter
*pSibling
= ImplFindSibling();
561 vcl::KeyCode aKeyCode
= rKEvt
.GetKeyCode();
562 sal_uInt16 nCode
= aKeyCode
.GetCode();
569 ImplStartKbdSplitting();
570 ImplKbdTracking( aKeyCode
);
576 pSibling
->GrabFocus();
577 pSibling
->KeyInput( rKEvt
);
585 ImplStartKbdSplitting();
586 ImplKbdTracking( aKeyCode
);
592 pSibling
->GrabFocus();
593 pSibling
->KeyInput( rKEvt
);
599 if( ImplSplitterActive() )
603 vcl::KeyCode
aKey( KEY_ESCAPE
);
604 ImplKbdTracking( aKey
);
613 ImplSplitMousePos( aPos
);
614 long nTemp
= mnSplitPos
;
616 SetSplitPosPixel( aPos
.X() );
618 SetSplitPosPixel( aPos
.Y() );
619 mnLastSplitPos
= nTemp
;
623 // Shift-Del deletes both splitters
624 if( aKeyCode
.IsShift() && pSibling
)
625 pSibling
->KeyInput( rKEvt
);
627 GrabFocusToDocument();
633 ImplKbdTracking( aKeyCode
);
635 GrabFocusToDocument();
639 ImplKbdTracking( aKeyCode
);
640 GrabFocusToDocument();
642 default: // let any key input fix the splitter
643 Window::KeyInput( rKEvt
);
644 GrabFocusToDocument();
647 mbInKeyEvent
= false;
650 void Splitter::DataChanged( const DataChangedEvent
& rDCEvt
)
652 Window::DataChanged( rDCEvt
);
653 if( rDCEvt
.GetType() == DataChangedEventType::SETTINGS
)
655 const AllSettings
* pOldSettings
= rDCEvt
.GetOldSettings();
659 Color oldFaceColor
= pOldSettings
->GetStyleSettings().GetFaceColor();
660 Color newFaceColor
= Application::GetSettings().GetStyleSettings().GetFaceColor();
661 if( oldFaceColor
.IsDark() != newFaceColor
.IsDark() )
663 if( newFaceColor
.IsDark() )
664 SetBackground( ImplWhiteWall::get() );
666 SetBackground( ImplBlackWall::get() );
671 void Splitter::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rPaintRect
)
673 rRenderContext
.DrawRect(rPaintRect
);
675 tools::Polygon
aPoly(rPaintRect
);
676 tools::PolyPolygon
aPolyPoly(aPoly
);
677 rRenderContext
.DrawTransparent(aPolyPoly
, 85);
681 LineInfo
aInfo( LineStyle::Dash
);
682 //aInfo.SetDashLen( 2 );
683 //aInfo.SetDashCount( 1 );
684 aInfo
.SetDistance( 1 );
685 aInfo
.SetDotLen( 2 );
686 aInfo
.SetDotCount( 3 );
688 rRenderContext
.DrawPolyLine( aPoly
, aInfo
);
692 rRenderContext
.DrawRect(rPaintRect
);
696 Size
Splitter::GetOptimalSize() const
698 return LogicToPixel(Size(3, 3), MapMode(MapUnit::MapAppFont
));
701 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */