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 .
21 #include <tools/poly.hxx>
23 #include <vcl/event.hxx>
24 #include <vcl/split.hxx>
25 #include <vcl/svapp.hxx>
26 #include <vcl/syswin.hxx>
27 #include <vcl/taskpanelist.hxx>
28 #include <vcl/gradient.hxx>
29 #include <vcl/lineinfo.hxx>
30 #include <vcl/settings.hxx>
32 #include <rtl/instance.hxx>
39 : public rtl::StaticWithInit
<Wallpaper
, ImplBlackWall
> {
40 Wallpaper
operator () () {
41 return Wallpaper(COL_BLACK
);
45 : public rtl::StaticWithInit
<Wallpaper
, ImplWhiteWall
> {
46 Wallpaper
operator () () {
47 return Wallpaper(COL_LIGHTGRAY
);
52 // Should only be called from a ImplInit method for initialization or
53 // after checking bNew is different from the current mbHorzSplit value.
54 // The public method that does that check is Splitter::SetHorizontal().
55 void Splitter::ImplInitHorVer(bool bNew
)
59 PointerStyle ePointerStyle
;
60 const StyleSettings
& rSettings
= GetSettings().GetStyleSettings();
64 ePointerStyle
= PointerStyle::HSplit
;
65 SetSizePixel( Size( rSettings
.GetSplitSize(), rSettings
.GetScrollBarSize() ) );
69 ePointerStyle
= PointerStyle::VSplit
;
70 SetSizePixel( Size( rSettings
.GetScrollBarSize(), rSettings
.GetSplitSize() ) );
73 SetPointer( Pointer( ePointerStyle
) );
76 void Splitter::ImplInit( vcl::Window
* pParent
, WinBits nWinStyle
)
78 Window::ImplInit( pParent
, nWinStyle
, nullptr );
82 ImplInitHorVer(nWinStyle
& WB_HSCROLL
);
84 if( GetSettings().GetStyleSettings().GetFaceColor().IsDark() )
85 SetBackground( ImplWhiteWall::get() );
87 SetBackground( ImplBlackWall::get() );
89 TaskPaneList
*pTList
= GetSystemWindow()->GetTaskPaneList();
90 pTList
->AddWindow( this );
93 void Splitter::ImplSplitMousePos( Point
& rPos
)
97 if ( rPos
.X() > maDragRect
.Right()-1 )
98 rPos
.X() = maDragRect
.Right()-1;
99 if ( rPos
.X() < maDragRect
.Left()+1 )
100 rPos
.X() = maDragRect
.Left()+1;
104 if ( rPos
.Y() > maDragRect
.Bottom()-1 )
105 rPos
.Y() = maDragRect
.Bottom()-1;
106 if ( rPos
.Y() < maDragRect
.Top()+1 )
107 rPos
.Y() = maDragRect
.Top()+1;
111 void Splitter::ImplDrawSplitter()
113 Rectangle
aInvRect( maDragRect
);
117 aInvRect
.Left() = maDragPos
.X() - 1;
118 aInvRect
.Right() = maDragPos
.X() + 1;
122 aInvRect
.Top() = maDragPos
.Y() - 1;
123 aInvRect
.Bottom() = maDragPos
.Y() + 1;
126 mpRefWin
->InvertTracking( mpRefWin
->PixelToLogic(aInvRect
), ShowTrackFlags::Split
);
129 Splitter::Splitter( vcl::Window
* pParent
, WinBits nStyle
) :
130 Window( WINDOW_SPLITTER
),
134 mnStartSplitPos( 0 ),
136 mbKbdSplitting( false ),
138 mnKeyboardStepSize( SPLITTER_DEFAULTSTEPSIZE
)
140 ImplGetWindowImpl()->mbSplitter
= true;
142 ImplInit( pParent
, nStyle
);
148 Splitter::~Splitter()
153 void Splitter::dispose()
155 SystemWindow
*pSysWin
= GetSystemWindow();
158 TaskPaneList
*pTList
= pSysWin
->GetTaskPaneList();
159 pTList
->RemoveWindow(this);
165 void Splitter::SetHorizontal(bool bNew
)
167 if(bNew
!= (bool)mbHorzSplit
)
169 ImplInitHorVer(bNew
);
173 void Splitter::SetKeyboardStepSize( long nStepSize
)
175 mnKeyboardStepSize
= nStepSize
;
178 Splitter
* Splitter::ImplFindSibling()
180 // look for another splitter with the same parent but different orientation
181 vcl::Window
*pWin
= GetParent()->GetWindow( GetWindowType::FirstChild
);
182 Splitter
*pSplitter
= nullptr;
185 if( pWin
->ImplIsSplitter() )
187 pSplitter
= static_cast<Splitter
*>(pWin
);
188 if( pSplitter
!= this && IsHorizontal() != pSplitter
->IsHorizontal() )
191 pWin
= pWin
->GetWindow( GetWindowType::Next
);
196 bool Splitter::ImplSplitterActive()
198 // is splitter in document or at scrollbar handle ?
201 const StyleSettings
& rSettings
= GetSettings().GetStyleSettings();
202 long nA
= rSettings
.GetScrollBarSize();
203 long nB
= rSettings
.GetSplitSize();
205 Size aSize
= GetOutputSize();
208 if( aSize
.Width() == nB
&& aSize
.Height() == nA
)
213 if( aSize
.Width() == nA
&& aSize
.Height() == nB
)
219 void Splitter::MouseButtonDown( const MouseEvent
& rMEvt
)
221 if ( rMEvt
.GetClicks() == 2 )
223 if ( mnLastSplitPos
!= mnSplitPos
)
226 Point aPos
= rMEvt
.GetPosPixel();
228 aPos
.X() = mnLastSplitPos
;
230 aPos
.Y() = mnLastSplitPos
;
231 ImplSplitMousePos( aPos
);
232 long nTemp
= mnSplitPos
;
234 SetSplitPosPixel( aPos
.X() );
236 SetSplitPosPixel( aPos
.Y() );
237 mnLastSplitPos
= nTemp
;
246 void Splitter::Tracking( const TrackingEvent
& rTEvt
)
248 if ( rTEvt
.IsTrackingEnded() )
253 if ( !rTEvt
.IsTrackingCanceled() )
257 nNewPos
= maDragPos
.X();
259 nNewPos
= maDragPos
.Y();
260 if ( nNewPos
!= mnStartSplitPos
)
262 SetSplitPosPixel( nNewPos
);
268 else if ( mbDragFull
)
270 SetSplitPosPixel( mnStartSplitPos
);
277 //Point aNewPos = mpRefWin->ScreenToOutputPixel( OutputToScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) );
278 Point aNewPos
= mpRefWin
->NormalizedScreenToOutputPixel( OutputToNormalizedScreenPixel( rTEvt
.GetMouseEvent().GetPosPixel() ) );
279 ImplSplitMousePos( aNewPos
);
283 if ( aNewPos
.X() == maDragPos
.X() )
288 if ( aNewPos
.Y() == maDragPos
.Y() )
297 nNewPos
= maDragPos
.X();
299 nNewPos
= maDragPos
.Y();
300 if ( nNewPos
!= mnSplitPos
)
302 SetSplitPosPixel( nNewPos
);
307 GetParent()->Update();
318 void Splitter::ImplKbdTracking( vcl::KeyCode aKeyCode
)
320 sal_uInt16 nCode
= aKeyCode
.GetCode();
321 if ( nCode
== KEY_ESCAPE
|| nCode
== KEY_RETURN
)
323 if( !mbKbdSplitting
)
326 mbKbdSplitting
= false;
328 if ( nCode
!= KEY_ESCAPE
)
332 nNewPos
= maDragPos
.X();
334 nNewPos
= maDragPos
.Y();
335 if ( nNewPos
!= mnStartSplitPos
)
337 SetSplitPosPixel( nNewPos
);
344 SetSplitPosPixel( mnStartSplitPos
);
353 Size aSize
= mpRefWin
->GetOutputSize();
354 Point aPos
= GetPosPixel();
355 // depending on the position calc allows continuous moves or snaps to row/columns
356 // continuous mode is active when position is at the origin or end of the splitter
357 // otherwise snap mode is active
358 // default here is snap, holding shift sets continuous mode
360 aNewPos
= Point( ImplSplitterActive() ? aPos
.X() : mnSplitPos
, aKeyCode
.IsShift() ? 0 : aSize
.Height()/2);
362 aNewPos
= Point( aKeyCode
.IsShift() ? 0 : aSize
.Width()/2, ImplSplitterActive() ? aPos
.Y() : mnSplitPos
);
364 Point aOldWindowPos
= GetPosPixel();
366 int maxiter
= 500; // avoid endless loop
368 int delta_step
= mbHorzSplit
? aSize
.Width()/10 : aSize
.Height()/10;
370 // use the specified step size if it was set
371 if( mnKeyboardStepSize
!= SPLITTER_DEFAULTSTEPSIZE
)
372 delta_step
= mnKeyboardStepSize
;
374 while( maxiter
-- && aOldWindowPos
== GetPosPixel() )
376 // inc/dec position until application performs changes
377 // thus a single key press really moves the splitter
378 if( aKeyCode
.IsShift() )
398 maxiter
= 0; // leave loop
401 ImplSplitMousePos( aNewPos
);
405 if ( aNewPos
.X() == maDragPos
.X() )
410 if ( aNewPos
.Y() == maDragPos
.Y() )
417 nNewPos
= maDragPos
.X();
419 nNewPos
= maDragPos
.Y();
420 if ( nNewPos
!= mnSplitPos
)
422 SetSplitPosPixel( nNewPos
);
426 GetParent()->Update();
431 void Splitter::StartSplit()
433 maStartSplitHdl
.Call( this );
436 void Splitter::Split()
438 maSplitHdl
.Call( this );
441 void Splitter::EndSplit()
443 maEndSplitHdl
.Call( this );
446 void Splitter::SetDragRectPixel( const Rectangle
& rDragRect
, vcl::Window
* _pRefWin
)
448 maDragRect
= rDragRect
;
450 mpRefWin
= GetParent();
455 void Splitter::SetSplitPosPixel( long nNewPos
)
457 mnSplitPos
= nNewPos
;
460 void Splitter::StartDrag()
470 // Start-Position ermitteln
471 maDragPos
= mpRefWin
->GetPointerPosPixel();
472 ImplSplitMousePos( maDragPos
);
474 mnStartSplitPos
= maDragPos
.X();
476 mnStartSplitPos
= maDragPos
.Y();
478 mbDragFull
= bool(Application::GetSettings().GetStyleSettings().GetDragFullOptions() & DragFullOptions::Split
);
483 void Splitter::ImplStartKbdSplitting()
488 mbKbdSplitting
= true;
492 // determine start position
493 // because we have no mouse position we take either the position
494 // of the splitter window or the last split position
495 // the other coordinate is just the center of the reference window
496 Size aSize
= mpRefWin
->GetOutputSize();
497 Point aPos
= GetPosPixel();
499 maDragPos
= Point( ImplSplitterActive() ? aPos
.X() : mnSplitPos
, aSize
.Height()/2 );
501 maDragPos
= Point( aSize
.Width()/2, ImplSplitterActive() ? aPos
.Y() : mnSplitPos
);
502 ImplSplitMousePos( maDragPos
);
504 mnStartSplitPos
= maDragPos
.X();
506 mnStartSplitPos
= maDragPos
.Y();
509 void Splitter::ImplRestoreSplitter()
511 // set splitter in the center of the ref window
513 Size aSize
= mpRefWin
->GetOutputSize();
514 Point aPos
= Point( aSize
.Width()/2 , aSize
.Height()/2);
515 if ( mnLastSplitPos
!= mnSplitPos
&& mnLastSplitPos
> 5 )
517 // restore last pos if it was a useful position (>5)
519 aPos
.X() = mnLastSplitPos
;
521 aPos
.Y() = mnLastSplitPos
;
524 ImplSplitMousePos( aPos
);
525 long nTemp
= mnSplitPos
;
527 SetSplitPosPixel( aPos
.X() );
529 SetSplitPosPixel( aPos
.Y() );
530 mnLastSplitPos
= nTemp
;
535 void Splitter::GetFocus()
537 if( !ImplSplitterActive() )
538 ImplRestoreSplitter();
543 void Splitter::LoseFocus()
547 vcl::KeyCode
aReturnKey( KEY_RETURN
);
548 ImplKbdTracking( aReturnKey
);
549 mbKbdSplitting
= false;
554 void Splitter::KeyInput( const KeyEvent
& rKEvt
)
561 Splitter
*pSibling
= ImplFindSibling();
562 vcl::KeyCode aKeyCode
= rKEvt
.GetKeyCode();
563 sal_uInt16 nCode
= aKeyCode
.GetCode();
570 ImplStartKbdSplitting();
571 ImplKbdTracking( aKeyCode
);
577 pSibling
->GrabFocus();
578 pSibling
->KeyInput( rKEvt
);
586 ImplStartKbdSplitting();
587 ImplKbdTracking( aKeyCode
);
593 pSibling
->GrabFocus();
594 pSibling
->KeyInput( rKEvt
);
600 if( ImplSplitterActive() )
604 vcl::KeyCode
aKey( KEY_ESCAPE
);
605 ImplKbdTracking( aKey
);
614 ImplSplitMousePos( aPos
);
615 long nTemp
= mnSplitPos
;
617 SetSplitPosPixel( aPos
.X() );
619 SetSplitPosPixel( aPos
.Y() );
620 mnLastSplitPos
= nTemp
;
624 // Shift-Del deletes both splitters
625 if( aKeyCode
.IsShift() && pSibling
)
626 pSibling
->KeyInput( rKEvt
);
628 GrabFocusToDocument();
634 ImplKbdTracking( aKeyCode
);
636 GrabFocusToDocument();
640 ImplKbdTracking( aKeyCode
);
641 GrabFocusToDocument();
643 default: // let any key input fix the splitter
644 Window::KeyInput( rKEvt
);
645 GrabFocusToDocument();
651 void Splitter::DataChanged( const DataChangedEvent
& rDCEvt
)
653 Window::DataChanged( rDCEvt
);
654 if( rDCEvt
.GetType() == DataChangedEventType::SETTINGS
)
656 const AllSettings
* pOldSettings
= rDCEvt
.GetOldSettings();
660 Color oldFaceColor
= pOldSettings
->GetStyleSettings().GetFaceColor();
661 Color newFaceColor
= Application::GetSettings().GetStyleSettings().GetFaceColor();
662 if( oldFaceColor
.IsDark() != newFaceColor
.IsDark() )
664 if( newFaceColor
.IsDark() )
665 SetBackground( ImplWhiteWall::get() );
667 SetBackground( ImplBlackWall::get() );
672 void Splitter::Paint(vcl::RenderContext
& rRenderContext
, const Rectangle
& rPaintRect
)
674 rRenderContext
.DrawRect(rPaintRect
);
676 tools::Polygon
aPoly(rPaintRect
);
677 tools::PolyPolygon
aPolyPoly(aPoly
);
678 rRenderContext
.DrawTransparent(aPolyPoly
, 85);
682 LineInfo
aInfo( LineStyle::Dash
);
683 //aInfo.SetDashLen( 2 );
684 //aInfo.SetDashCount( 1 );
685 aInfo
.SetDistance( 1 );
686 aInfo
.SetDotLen( 2 );
687 aInfo
.SetDotCount( 3 );
689 rRenderContext
.DrawPolyLine( aPoly
, aInfo
);
693 rRenderContext
.DrawRect(rPaintRect
);
697 Size
Splitter::GetOptimalSize() const
699 return LogicToPixel(Size(3, 3), MapUnit::MapAppFont
);
702 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */