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>
30 #include <tools/lazydelete.hxx>
36 Wallpaper
& ImplBlackWall()
38 static tools::DeleteOnDeinit
< Wallpaper
> SINGLETON(COL_BLACK
);
39 return *SINGLETON
.get();
41 Wallpaper
& ImplWhiteWall()
43 static tools::DeleteOnDeinit
< Wallpaper
> SINGLETON(COL_LIGHTGRAY
);
44 return *SINGLETON
.get();
48 // Should only be called from an ImplInit method for initialization or
49 // after checking bNew is different from the current mbHorzSplit value.
50 // The public method that does that check is Splitter::SetHorizontal().
51 void Splitter::ImplInitHorVer(bool bNew
)
55 PointerStyle ePointerStyle
;
56 const StyleSettings
& rSettings
= GetSettings().GetStyleSettings();
60 ePointerStyle
= PointerStyle::HSplit
;
61 SetSizePixel( Size( StyleSettings::GetSplitSize(), rSettings
.GetScrollBarSize() ) );
65 ePointerStyle
= PointerStyle::VSplit
;
66 SetSizePixel( Size( rSettings
.GetScrollBarSize(), StyleSettings::GetSplitSize() ) );
69 SetPointer( ePointerStyle
);
72 void Splitter::ImplInit( vcl::Window
* pParent
, WinBits nWinStyle
)
74 Window::ImplInit( pParent
, nWinStyle
, nullptr );
78 ImplInitHorVer(nWinStyle
& WB_HSCROLL
);
80 if( GetSettings().GetStyleSettings().GetFaceColor().IsDark() )
81 SetBackground( ImplWhiteWall() );
83 SetBackground( ImplBlackWall() );
85 TaskPaneList
*pTList
= GetSystemWindow()->GetTaskPaneList();
86 pTList
->AddWindow( this );
89 void Splitter::ImplSplitMousePos( Point
& rPos
)
93 if ( rPos
.X() > maDragRect
.Right()-1 )
94 rPos
.setX( maDragRect
.Right()-1 );
95 if ( rPos
.X() < maDragRect
.Left()+1 )
96 rPos
.setX( maDragRect
.Left()+1 );
100 if ( rPos
.Y() > maDragRect
.Bottom()-1 )
101 rPos
.setY( maDragRect
.Bottom()-1 );
102 if ( rPos
.Y() < maDragRect
.Top()+1 )
103 rPos
.setY( maDragRect
.Top()+1 );
107 void Splitter::ImplDrawSplitter()
109 tools::Rectangle
aInvRect( maDragRect
);
113 aInvRect
.SetLeft( maDragPos
.X() - 1 );
114 aInvRect
.SetRight( maDragPos
.X() + 1 );
118 aInvRect
.SetTop( maDragPos
.Y() - 1 );
119 aInvRect
.SetBottom( maDragPos
.Y() + 1 );
122 mpRefWin
->InvertTracking( mpRefWin
->PixelToLogic(aInvRect
), ShowTrackFlags::Split
);
125 Splitter::Splitter( vcl::Window
* pParent
, WinBits nStyle
) :
126 Window( WindowType::SPLITTER
),
130 mnStartSplitPos( 0 ),
132 mbKbdSplitting( false ),
133 mbInKeyEvent( false ),
134 mnKeyboardStepSize( SPLITTER_DEFAULTSTEPSIZE
)
136 ImplGetWindowImpl()->mbSplitter
= true;
138 ImplInit( pParent
, nStyle
);
140 GetOutDev()->SetLineColor();
141 GetOutDev()->SetFillColor();
144 Splitter::~Splitter()
149 void Splitter::dispose()
151 SystemWindow
*pSysWin
= GetSystemWindow();
154 TaskPaneList
*pTList
= pSysWin
->GetTaskPaneList();
155 pTList
->RemoveWindow(this);
161 void Splitter::SetHorizontal(bool bNew
)
163 if(bNew
!= mbHorzSplit
)
165 ImplInitHorVer(bNew
);
169 void Splitter::SetKeyboardStepSize( tools::Long nStepSize
)
171 mnKeyboardStepSize
= nStepSize
;
174 Splitter
* Splitter::ImplFindSibling()
176 // look for another splitter with the same parent but different orientation
177 vcl::Window
*pWin
= GetParent()->GetWindow( GetWindowType::FirstChild
);
178 Splitter
*pSplitter
= nullptr;
181 if( pWin
->ImplIsSplitter() )
183 pSplitter
= static_cast<Splitter
*>(pWin
);
184 if( pSplitter
!= this && IsHorizontal() != pSplitter
->IsHorizontal() )
187 pWin
= pWin
->GetWindow( GetWindowType::Next
);
192 bool Splitter::ImplSplitterActive()
194 // is splitter in document or at scrollbar handle ?
197 const StyleSettings
& rSettings
= GetSettings().GetStyleSettings();
198 tools::Long nA
= rSettings
.GetScrollBarSize();
199 tools::Long nB
= StyleSettings::GetSplitSize();
201 Size aSize
= GetOutDev()->GetOutputSize();
204 if( aSize
.Width() == nB
&& aSize
.Height() == nA
)
209 if( aSize
.Width() == nA
&& aSize
.Height() == nB
)
215 void Splitter::MouseButtonDown( const MouseEvent
& rMEvt
)
217 if ( rMEvt
.GetClicks() == 2 )
219 if ( mnLastSplitPos
!= mnSplitPos
)
222 Point aPos
= rMEvt
.GetPosPixel();
224 aPos
.setX( mnLastSplitPos
);
226 aPos
.setY( mnLastSplitPos
);
227 ImplSplitMousePos( aPos
);
228 tools::Long nTemp
= mnSplitPos
;
230 SetSplitPosPixel( aPos
.X() );
232 SetSplitPosPixel( aPos
.Y() );
233 mnLastSplitPos
= nTemp
;
242 void Splitter::Tracking( const TrackingEvent
& rTEvt
)
244 if ( rTEvt
.IsTrackingEnded() )
249 if ( !rTEvt
.IsTrackingCanceled() )
253 nNewPos
= maDragPos
.X();
255 nNewPos
= maDragPos
.Y();
256 if ( nNewPos
!= mnStartSplitPos
)
258 SetSplitPosPixel( nNewPos
);
264 else if ( mbDragFull
)
266 SetSplitPosPixel( mnStartSplitPos
);
273 //Point aNewPos = mpRefWin->ScreenToOutputPixel( OutputToScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) );
274 Point aNewPos
= mpRefWin
->NormalizedScreenToOutputPixel( OutputToNormalizedScreenPixel( rTEvt
.GetMouseEvent().GetPosPixel() ) );
275 ImplSplitMousePos( aNewPos
);
279 if ( aNewPos
.X() == maDragPos
.X() )
284 if ( aNewPos
.Y() == maDragPos
.Y() )
293 nNewPos
= maDragPos
.X();
295 nNewPos
= maDragPos
.Y();
296 if ( nNewPos
!= mnSplitPos
)
298 SetSplitPosPixel( nNewPos
);
303 GetParent()->PaintImmediately();
314 void Splitter::ImplKbdTracking( vcl::KeyCode aKeyCode
)
316 sal_uInt16 nCode
= aKeyCode
.GetCode();
317 if ( nCode
== KEY_ESCAPE
|| nCode
== KEY_RETURN
)
319 if( !mbKbdSplitting
)
322 mbKbdSplitting
= false;
324 if ( nCode
!= KEY_ESCAPE
)
328 nNewPos
= maDragPos
.X();
330 nNewPos
= maDragPos
.Y();
331 if ( nNewPos
!= mnStartSplitPos
)
333 SetSplitPosPixel( nNewPos
);
340 SetSplitPosPixel( mnStartSplitPos
);
349 Size aSize
= mpRefWin
->GetOutDev()->GetOutputSize();
350 Point aPos
= GetPosPixel();
351 // depending on the position calc allows continuous moves or snaps to row/columns
352 // continuous mode is active when position is at the origin or end of the splitter
353 // otherwise snap mode is active
354 // default here is snap, holding shift sets continuous mode
356 aNewPos
= Point( ImplSplitterActive() ? aPos
.X() : mnSplitPos
, aKeyCode
.IsShift() ? 0 : aSize
.Height()/2);
358 aNewPos
= Point( aKeyCode
.IsShift() ? 0 : aSize
.Width()/2, ImplSplitterActive() ? aPos
.Y() : mnSplitPos
);
360 Point aOldWindowPos
= GetPosPixel();
362 int maxiter
= 500; // avoid endless loop
364 int delta_step
= mbHorzSplit
? aSize
.Width()/10 : aSize
.Height()/10;
366 // use the specified step size if it was set
367 if( mnKeyboardStepSize
!= SPLITTER_DEFAULTSTEPSIZE
)
368 delta_step
= mnKeyboardStepSize
;
370 while( maxiter
-- && aOldWindowPos
== GetPosPixel() )
372 // inc/dec position until application performs changes
373 // thus a single key press really moves the splitter
374 if( aKeyCode
.IsShift() )
382 aNewPos
.AdjustX( -delta
);
385 aNewPos
.AdjustX(delta
);
388 aNewPos
.AdjustY( -delta
);
391 aNewPos
.AdjustY(delta
);
394 maxiter
= 0; // leave loop
397 ImplSplitMousePos( aNewPos
);
401 if ( aNewPos
.X() == maDragPos
.X() )
406 if ( aNewPos
.Y() == maDragPos
.Y() )
413 nNewPos
= maDragPos
.X();
415 nNewPos
= maDragPos
.Y();
416 if ( nNewPos
!= mnSplitPos
)
418 SetSplitPosPixel( nNewPos
);
422 GetParent()->PaintImmediately();
427 void Splitter::StartSplit()
429 maStartSplitHdl
.Call( this );
432 void Splitter::Split()
434 maSplitHdl
.Call( this );
437 void Splitter::EndSplit()
439 maEndSplitHdl
.Call( this );
442 void Splitter::SetDragRectPixel( const tools::Rectangle
& rDragRect
, vcl::Window
* _pRefWin
)
444 maDragRect
= rDragRect
;
446 mpRefWin
= GetParent();
451 void Splitter::SetSplitPosPixel( tools::Long nNewPos
)
453 mnSplitPos
= nNewPos
;
456 void Splitter::StartDrag()
466 // Determine start position
467 maDragPos
= mpRefWin
->GetPointerPosPixel();
468 ImplSplitMousePos( maDragPos
);
470 mnStartSplitPos
= maDragPos
.X();
472 mnStartSplitPos
= maDragPos
.Y();
474 mbDragFull
= bool(Application::GetSettings().GetStyleSettings().GetDragFullOptions() & DragFullOptions::Split
);
479 void Splitter::ImplStartKbdSplitting()
484 mbKbdSplitting
= true;
488 // determine start position
489 // because we have no mouse position we take either the position
490 // of the splitter window or the last split position
491 // the other coordinate is just the center of the reference window
492 Size aSize
= mpRefWin
->GetOutDev()->GetOutputSize();
493 Point aPos
= GetPosPixel();
495 maDragPos
= Point( ImplSplitterActive() ? aPos
.X() : mnSplitPos
, aSize
.Height()/2 );
497 maDragPos
= Point( aSize
.Width()/2, ImplSplitterActive() ? aPos
.Y() : mnSplitPos
);
498 ImplSplitMousePos( maDragPos
);
500 mnStartSplitPos
= maDragPos
.X();
502 mnStartSplitPos
= maDragPos
.Y();
505 void Splitter::ImplRestoreSplitter()
507 // set splitter in the center of the ref window
509 Size aSize
= mpRefWin
->GetOutDev()->GetOutputSize();
510 Point
aPos( aSize
.Width()/2 , aSize
.Height()/2);
511 if ( mnLastSplitPos
!= mnSplitPos
&& mnLastSplitPos
> 5 )
513 // restore last pos if it was a useful position (>5)
515 aPos
.setX( mnLastSplitPos
);
517 aPos
.setY( mnLastSplitPos
);
520 ImplSplitMousePos( aPos
);
521 tools::Long nTemp
= mnSplitPos
;
523 SetSplitPosPixel( aPos
.X() );
525 SetSplitPosPixel( aPos
.Y() );
526 mnLastSplitPos
= nTemp
;
531 void Splitter::GetFocus()
533 if( !ImplSplitterActive() )
534 ImplRestoreSplitter();
539 void Splitter::LoseFocus()
543 vcl::KeyCode
aReturnKey( KEY_RETURN
);
544 ImplKbdTracking( aReturnKey
);
545 mbKbdSplitting
= false;
550 void Splitter::KeyInput( const KeyEvent
& rKEvt
)
557 Splitter
*pSibling
= ImplFindSibling();
558 vcl::KeyCode aKeyCode
= rKEvt
.GetKeyCode();
559 sal_uInt16 nCode
= aKeyCode
.GetCode();
566 ImplStartKbdSplitting();
567 ImplKbdTracking( aKeyCode
);
573 pSibling
->GrabFocus();
574 pSibling
->KeyInput( rKEvt
);
582 ImplStartKbdSplitting();
583 ImplKbdTracking( aKeyCode
);
589 pSibling
->GrabFocus();
590 pSibling
->KeyInput( rKEvt
);
596 if( ImplSplitterActive() )
600 vcl::KeyCode
aKey( KEY_ESCAPE
);
601 ImplKbdTracking( aKey
);
610 ImplSplitMousePos( aPos
);
611 tools::Long nTemp
= mnSplitPos
;
613 SetSplitPosPixel( aPos
.X() );
615 SetSplitPosPixel( aPos
.Y() );
616 mnLastSplitPos
= nTemp
;
620 // Shift-Del deletes both splitters
621 if( aKeyCode
.IsShift() && pSibling
)
622 pSibling
->KeyInput( rKEvt
);
624 GrabFocusToDocument();
630 ImplKbdTracking( aKeyCode
);
632 GrabFocusToDocument();
636 ImplKbdTracking( aKeyCode
);
637 GrabFocusToDocument();
639 default: // let any key input fix the splitter
640 Window::KeyInput( rKEvt
);
641 GrabFocusToDocument();
644 mbInKeyEvent
= false;
647 void Splitter::DataChanged( const DataChangedEvent
& rDCEvt
)
649 Window::DataChanged( rDCEvt
);
650 if( rDCEvt
.GetType() != DataChangedEventType::SETTINGS
)
653 const AllSettings
* pOldSettings
= rDCEvt
.GetOldSettings();
657 Color oldFaceColor
= pOldSettings
->GetStyleSettings().GetFaceColor();
658 Color newFaceColor
= Application::GetSettings().GetStyleSettings().GetFaceColor();
659 if( oldFaceColor
.IsDark() != newFaceColor
.IsDark() )
661 if( newFaceColor
.IsDark() )
662 SetBackground( ImplWhiteWall() );
664 SetBackground( ImplBlackWall() );
668 void Splitter::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rPaintRect
)
670 rRenderContext
.DrawRect(rPaintRect
);
672 tools::Polygon
aPoly(rPaintRect
);
673 tools::PolyPolygon
aPolyPoly(aPoly
);
674 rRenderContext
.DrawTransparent(aPolyPoly
, 85);
678 LineInfo
aInfo( LineStyle::Dash
);
679 //aInfo.SetDashLen( 2 );
680 //aInfo.SetDashCount( 1 );
681 aInfo
.SetDistance( 1 );
682 aInfo
.SetDotLen( 2 );
683 aInfo
.SetDotCount( 3 );
685 rRenderContext
.DrawPolyLine( aPoly
, aInfo
);
689 rRenderContext
.DrawRect(rPaintRect
);
693 Size
Splitter::GetOptimalSize() const
695 return LogicToPixel(Size(3, 3), MapMode(MapUnit::MapAppFont
));
698 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */