Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / source / window / split.cxx
blob6a12d0cada6c50442065a2140c940b97fb395019
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
33 #include <window.h>
35 namespace
37 struct ImplBlackWall
38 : public rtl::StaticWithInit<Wallpaper, ImplBlackWall> {
39 Wallpaper operator () () {
40 return Wallpaper(COL_BLACK);
43 struct ImplWhiteWall
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)
56 mbHorzSplit = bNew;
58 PointerStyle ePointerStyle;
59 const StyleSettings& rSettings = GetSettings().GetStyleSettings();
61 if ( mbHorzSplit )
63 ePointerStyle = PointerStyle::HSplit;
64 SetSizePixel( Size( StyleSettings::GetSplitSize(), rSettings.GetScrollBarSize() ) );
66 else
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 );
79 mpRefWin = pParent;
81 ImplInitHorVer(nWinStyle & WB_HSCROLL);
83 if( GetSettings().GetStyleSettings().GetFaceColor().IsDark() )
84 SetBackground( ImplWhiteWall::get() );
85 else
86 SetBackground( ImplBlackWall::get() );
88 TaskPaneList *pTList = GetSystemWindow()->GetTaskPaneList();
89 pTList->AddWindow( this );
92 void Splitter::ImplSplitMousePos( Point& rPos )
94 if ( mbHorzSplit )
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 );
101 else
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 );
114 if ( mbHorzSplit )
116 aInvRect.SetLeft( maDragPos.X() - 1 );
117 aInvRect.SetRight( maDragPos.X() + 1 );
119 else
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 ),
130 mpRefWin( nullptr ),
131 mnSplitPos( 0 ),
132 mnLastSplitPos( 0 ),
133 mnStartSplitPos( 0 ),
134 mbDragFull( false ),
135 mbKbdSplitting( false ),
136 mbInKeyEvent( false ),
137 mnKeyboardStepSize( SPLITTER_DEFAULTSTEPSIZE )
139 ImplGetWindowImpl()->mbSplitter = true;
141 ImplInit( pParent, nStyle );
143 SetLineColor();
144 SetFillColor();
147 Splitter::~Splitter()
149 disposeOnce();
152 void Splitter::dispose()
154 SystemWindow *pSysWin = GetSystemWindow();
155 if(pSysWin)
157 TaskPaneList *pTList = pSysWin->GetTaskPaneList();
158 pTList->RemoveWindow(this);
160 mpRefWin.clear();
161 Window::dispose();
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;
182 while( pWin )
184 if( pWin->ImplIsSplitter() )
186 pSplitter = static_cast<Splitter*>(pWin);
187 if( pSplitter != this && IsHorizontal() != pSplitter->IsHorizontal() )
188 return pSplitter;
190 pWin = pWin->GetWindow( GetWindowType::Next );
192 return nullptr;
195 bool Splitter::ImplSplitterActive()
197 // is splitter in document or at scrollbar handle ?
199 bool bActive = true;
200 const StyleSettings& rSettings = GetSettings().GetStyleSettings();
201 long nA = rSettings.GetScrollBarSize();
202 long nB = StyleSettings::GetSplitSize();
204 Size aSize = GetOutputSize();
205 if ( mbHorzSplit )
207 if( aSize.Width() == nB && aSize.Height() == nA )
208 bActive = false;
210 else
212 if( aSize.Width() == nA && aSize.Height() == nB )
213 bActive = false;
215 return bActive;
218 void Splitter::MouseButtonDown( const MouseEvent& rMEvt )
220 if ( rMEvt.GetClicks() == 2 )
222 if ( mnLastSplitPos != mnSplitPos )
224 StartSplit();
225 Point aPos = rMEvt.GetPosPixel();
226 if ( mbHorzSplit )
227 aPos.setX( mnLastSplitPos );
228 else
229 aPos.setY( mnLastSplitPos );
230 ImplSplitMousePos( aPos );
231 long nTemp = mnSplitPos;
232 if ( mbHorzSplit )
233 SetSplitPosPixel( aPos.X() );
234 else
235 SetSplitPosPixel( aPos.Y() );
236 mnLastSplitPos = nTemp;
237 Split();
238 EndSplit();
241 else
242 StartDrag();
245 void Splitter::Tracking( const TrackingEvent& rTEvt )
247 if ( rTEvt.IsTrackingEnded() )
249 if ( !mbDragFull )
250 ImplDrawSplitter();
252 if ( !rTEvt.IsTrackingCanceled() )
254 long nNewPos;
255 if ( mbHorzSplit )
256 nNewPos = maDragPos.X();
257 else
258 nNewPos = maDragPos.Y();
259 if ( nNewPos != mnStartSplitPos )
261 SetSplitPosPixel( nNewPos );
262 mnLastSplitPos = 0;
263 Split();
265 EndSplit();
267 else if ( mbDragFull )
269 SetSplitPosPixel( mnStartSplitPos );
270 Split();
272 mnStartSplitPos = 0;
274 else
276 //Point aNewPos = mpRefWin->ScreenToOutputPixel( OutputToScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) );
277 Point aNewPos = mpRefWin->NormalizedScreenToOutputPixel( OutputToNormalizedScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) );
278 ImplSplitMousePos( aNewPos );
280 if ( mbHorzSplit )
282 if ( aNewPos.X() == maDragPos.X() )
283 return;
285 else
287 if ( aNewPos.Y() == maDragPos.Y() )
288 return;
291 if ( mbDragFull )
293 maDragPos = aNewPos;
294 long nNewPos;
295 if ( mbHorzSplit )
296 nNewPos = maDragPos.X();
297 else
298 nNewPos = maDragPos.Y();
299 if ( nNewPos != mnSplitPos )
301 SetSplitPosPixel( nNewPos );
302 mnLastSplitPos = 0;
303 Split();
306 GetParent()->Update();
308 else
310 ImplDrawSplitter();
311 maDragPos = aNewPos;
312 ImplDrawSplitter();
317 void Splitter::ImplKbdTracking( vcl::KeyCode aKeyCode )
319 sal_uInt16 nCode = aKeyCode.GetCode();
320 if ( nCode == KEY_ESCAPE || nCode == KEY_RETURN )
322 if( !mbKbdSplitting )
323 return;
324 else
325 mbKbdSplitting = false;
327 if ( nCode != KEY_ESCAPE )
329 long nNewPos;
330 if ( mbHorzSplit )
331 nNewPos = maDragPos.X();
332 else
333 nNewPos = maDragPos.Y();
334 if ( nNewPos != mnStartSplitPos )
336 SetSplitPosPixel( nNewPos );
337 mnLastSplitPos = 0;
338 Split();
341 else
343 SetSplitPosPixel( mnStartSplitPos );
344 Split();
345 EndSplit();
347 mnStartSplitPos = 0;
349 else
351 Point aNewPos;
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
358 if( mbHorzSplit )
359 aNewPos = Point( ImplSplitterActive() ? aPos.X() : mnSplitPos, aKeyCode.IsShift() ? 0 : aSize.Height()/2);
360 else
361 aNewPos = Point( aKeyCode.IsShift() ? 0 : aSize.Width()/2, ImplSplitterActive() ? aPos.Y() : mnSplitPos );
363 Point aOldWindowPos = GetPosPixel();
365 int maxiter = 500; // avoid endless loop
366 int delta=0;
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() )
378 delta++;
379 else
380 delta += delta_step;
382 switch( nCode )
384 case KEY_LEFT:
385 aNewPos.AdjustX( -delta );
386 break;
387 case KEY_RIGHT:
388 aNewPos.AdjustX(delta );
389 break;
390 case KEY_UP:
391 aNewPos.AdjustY( -delta );
392 break;
393 case KEY_DOWN:
394 aNewPos.AdjustY(delta );
395 break;
396 default:
397 maxiter = 0; // leave loop
398 break;
400 ImplSplitMousePos( aNewPos );
402 if ( mbHorzSplit )
404 if ( aNewPos.X() == maDragPos.X() )
405 continue;
407 else
409 if ( aNewPos.Y() == maDragPos.Y() )
410 continue;
413 maDragPos = aNewPos;
414 long nNewPos;
415 if ( mbHorzSplit )
416 nNewPos = maDragPos.X();
417 else
418 nNewPos = maDragPos.Y();
419 if ( nNewPos != mnSplitPos )
421 SetSplitPosPixel( nNewPos );
422 mnLastSplitPos = 0;
423 Split();
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;
448 if ( !_pRefWin )
449 mpRefWin = GetParent();
450 else
451 mpRefWin = _pRefWin;
454 void Splitter::SetSplitPosPixel( long nNewPos )
456 mnSplitPos = nNewPos;
459 void Splitter::StartDrag()
461 if ( IsTracking() )
462 return;
464 StartSplit();
466 // Tracking starten
467 StartTracking();
469 // Start-Position ermitteln
470 maDragPos = mpRefWin->GetPointerPosPixel();
471 ImplSplitMousePos( maDragPos );
472 if ( mbHorzSplit )
473 mnStartSplitPos = maDragPos.X();
474 else
475 mnStartSplitPos = maDragPos.Y();
477 mbDragFull = bool(Application::GetSettings().GetStyleSettings().GetDragFullOptions() & DragFullOptions::Split);
478 if ( !mbDragFull )
479 ImplDrawSplitter();
482 void Splitter::ImplStartKbdSplitting()
484 if( mbKbdSplitting )
485 return;
487 mbKbdSplitting = true;
489 StartSplit();
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();
497 if( mbHorzSplit )
498 maDragPos = Point( ImplSplitterActive() ? aPos.X() : mnSplitPos, aSize.Height()/2 );
499 else
500 maDragPos = Point( aSize.Width()/2, ImplSplitterActive() ? aPos.Y() : mnSplitPos );
501 ImplSplitMousePos( maDragPos );
502 if ( mbHorzSplit )
503 mnStartSplitPos = maDragPos.X();
504 else
505 mnStartSplitPos = maDragPos.Y();
508 void Splitter::ImplRestoreSplitter()
510 // set splitter in the center of the ref window
511 StartSplit();
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)
517 if ( mbHorzSplit )
518 aPos.setX( mnLastSplitPos );
519 else
520 aPos.setY( mnLastSplitPos );
523 ImplSplitMousePos( aPos );
524 long nTemp = mnSplitPos;
525 if ( mbHorzSplit )
526 SetSplitPosPixel( aPos.X() );
527 else
528 SetSplitPosPixel( aPos.Y() );
529 mnLastSplitPos = nTemp;
530 Split();
531 EndSplit();
534 void Splitter::GetFocus()
536 if( !ImplSplitterActive() )
537 ImplRestoreSplitter();
539 Invalidate();
542 void Splitter::LoseFocus()
544 if( mbKbdSplitting )
546 vcl::KeyCode aReturnKey( KEY_RETURN );
547 ImplKbdTracking( aReturnKey );
548 mbKbdSplitting = false;
550 Invalidate();
553 void Splitter::KeyInput( const KeyEvent& rKEvt )
555 if( mbInKeyEvent )
556 return;
558 mbInKeyEvent = true;
560 Splitter *pSibling = ImplFindSibling();
561 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
562 sal_uInt16 nCode = aKeyCode.GetCode();
563 switch ( nCode )
565 case KEY_UP:
566 case KEY_DOWN:
567 if( !mbHorzSplit )
569 ImplStartKbdSplitting();
570 ImplKbdTracking( aKeyCode );
572 else
574 if( pSibling )
576 pSibling->GrabFocus();
577 pSibling->KeyInput( rKEvt );
580 break;
581 case KEY_RIGHT:
582 case KEY_LEFT:
583 if( mbHorzSplit )
585 ImplStartKbdSplitting();
586 ImplKbdTracking( aKeyCode );
588 else
590 if( pSibling )
592 pSibling->GrabFocus();
593 pSibling->KeyInput( rKEvt );
596 break;
598 case KEY_DELETE:
599 if( ImplSplitterActive() )
601 if( mbKbdSplitting )
603 vcl::KeyCode aKey( KEY_ESCAPE );
604 ImplKbdTracking( aKey );
607 StartSplit();
608 Point aPos;
609 if ( mbHorzSplit )
610 aPos.setX( 0 );
611 else
612 aPos.setY( 0 );
613 ImplSplitMousePos( aPos );
614 long nTemp = mnSplitPos;
615 if ( mbHorzSplit )
616 SetSplitPosPixel( aPos.X() );
617 else
618 SetSplitPosPixel( aPos.Y() );
619 mnLastSplitPos = nTemp;
620 Split();
621 EndSplit();
623 // Shift-Del deletes both splitters
624 if( aKeyCode.IsShift() && pSibling )
625 pSibling->KeyInput( rKEvt );
627 GrabFocusToDocument();
629 break;
631 case KEY_ESCAPE:
632 if( mbKbdSplitting )
633 ImplKbdTracking( aKeyCode );
634 else
635 GrabFocusToDocument();
636 break;
638 case KEY_RETURN:
639 ImplKbdTracking( aKeyCode );
640 GrabFocusToDocument();
641 break;
642 default: // let any key input fix the splitter
643 Window::KeyInput( rKEvt );
644 GrabFocusToDocument();
645 break;
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();
656 if(!pOldSettings)
657 return;
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() );
665 else
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);
679 if (mbKbdSplitting)
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 );
690 else
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: */