build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / window / split.cxx
blobeb6da6e758fa4d26457e5f6f0f6a4b49d9e3bedb
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/rc.h>
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>
34 #include <window.h>
36 namespace
38 struct ImplBlackWall
39 : public rtl::StaticWithInit<Wallpaper, ImplBlackWall> {
40 Wallpaper operator () () {
41 return Wallpaper(COL_BLACK);
44 struct ImplWhiteWall
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)
57 mbHorzSplit = bNew;
59 PointerStyle ePointerStyle;
60 const StyleSettings& rSettings = GetSettings().GetStyleSettings();
62 if ( mbHorzSplit )
64 ePointerStyle = PointerStyle::HSplit;
65 SetSizePixel( Size( rSettings.GetSplitSize(), rSettings.GetScrollBarSize() ) );
67 else
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 );
80 mpRefWin = pParent;
82 ImplInitHorVer(nWinStyle & WB_HSCROLL);
84 if( GetSettings().GetStyleSettings().GetFaceColor().IsDark() )
85 SetBackground( ImplWhiteWall::get() );
86 else
87 SetBackground( ImplBlackWall::get() );
89 TaskPaneList *pTList = GetSystemWindow()->GetTaskPaneList();
90 pTList->AddWindow( this );
93 void Splitter::ImplSplitMousePos( Point& rPos )
95 if ( mbHorzSplit )
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;
102 else
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 );
115 if ( mbHorzSplit )
117 aInvRect.Left() = maDragPos.X() - 1;
118 aInvRect.Right() = maDragPos.X() + 1;
120 else
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 ),
131 mpRefWin( nullptr ),
132 mnSplitPos( 0 ),
133 mnLastSplitPos( 0 ),
134 mnStartSplitPos( 0 ),
135 mbDragFull( false ),
136 mbKbdSplitting( false ),
137 mbInKeyEvent( 0 ),
138 mnKeyboardStepSize( SPLITTER_DEFAULTSTEPSIZE )
140 ImplGetWindowImpl()->mbSplitter = true;
142 ImplInit( pParent, nStyle );
144 SetLineColor();
145 SetFillColor();
148 Splitter::~Splitter()
150 disposeOnce();
153 void Splitter::dispose()
155 SystemWindow *pSysWin = GetSystemWindow();
156 if(pSysWin)
158 TaskPaneList *pTList = pSysWin->GetTaskPaneList();
159 pTList->RemoveWindow(this);
161 mpRefWin.clear();
162 Window::dispose();
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;
183 while( pWin )
185 if( pWin->ImplIsSplitter() )
187 pSplitter = static_cast<Splitter*>(pWin);
188 if( pSplitter != this && IsHorizontal() != pSplitter->IsHorizontal() )
189 return pSplitter;
191 pWin = pWin->GetWindow( GetWindowType::Next );
193 return nullptr;
196 bool Splitter::ImplSplitterActive()
198 // is splitter in document or at scrollbar handle ?
200 bool bActive = true;
201 const StyleSettings& rSettings = GetSettings().GetStyleSettings();
202 long nA = rSettings.GetScrollBarSize();
203 long nB = rSettings.GetSplitSize();
205 Size aSize = GetOutputSize();
206 if ( mbHorzSplit )
208 if( aSize.Width() == nB && aSize.Height() == nA )
209 bActive = false;
211 else
213 if( aSize.Width() == nA && aSize.Height() == nB )
214 bActive = false;
216 return bActive;
219 void Splitter::MouseButtonDown( const MouseEvent& rMEvt )
221 if ( rMEvt.GetClicks() == 2 )
223 if ( mnLastSplitPos != mnSplitPos )
225 StartSplit();
226 Point aPos = rMEvt.GetPosPixel();
227 if ( mbHorzSplit )
228 aPos.X() = mnLastSplitPos;
229 else
230 aPos.Y() = mnLastSplitPos;
231 ImplSplitMousePos( aPos );
232 long nTemp = mnSplitPos;
233 if ( mbHorzSplit )
234 SetSplitPosPixel( aPos.X() );
235 else
236 SetSplitPosPixel( aPos.Y() );
237 mnLastSplitPos = nTemp;
238 Split();
239 EndSplit();
242 else
243 StartDrag();
246 void Splitter::Tracking( const TrackingEvent& rTEvt )
248 if ( rTEvt.IsTrackingEnded() )
250 if ( !mbDragFull )
251 ImplDrawSplitter();
253 if ( !rTEvt.IsTrackingCanceled() )
255 long nNewPos;
256 if ( mbHorzSplit )
257 nNewPos = maDragPos.X();
258 else
259 nNewPos = maDragPos.Y();
260 if ( nNewPos != mnStartSplitPos )
262 SetSplitPosPixel( nNewPos );
263 mnLastSplitPos = 0;
264 Split();
266 EndSplit();
268 else if ( mbDragFull )
270 SetSplitPosPixel( mnStartSplitPos );
271 Split();
273 mnStartSplitPos = 0;
275 else
277 //Point aNewPos = mpRefWin->ScreenToOutputPixel( OutputToScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) );
278 Point aNewPos = mpRefWin->NormalizedScreenToOutputPixel( OutputToNormalizedScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) );
279 ImplSplitMousePos( aNewPos );
281 if ( mbHorzSplit )
283 if ( aNewPos.X() == maDragPos.X() )
284 return;
286 else
288 if ( aNewPos.Y() == maDragPos.Y() )
289 return;
292 if ( mbDragFull )
294 maDragPos = aNewPos;
295 long nNewPos;
296 if ( mbHorzSplit )
297 nNewPos = maDragPos.X();
298 else
299 nNewPos = maDragPos.Y();
300 if ( nNewPos != mnSplitPos )
302 SetSplitPosPixel( nNewPos );
303 mnLastSplitPos = 0;
304 Split();
307 GetParent()->Update();
309 else
311 ImplDrawSplitter();
312 maDragPos = aNewPos;
313 ImplDrawSplitter();
318 void Splitter::ImplKbdTracking( vcl::KeyCode aKeyCode )
320 sal_uInt16 nCode = aKeyCode.GetCode();
321 if ( nCode == KEY_ESCAPE || nCode == KEY_RETURN )
323 if( !mbKbdSplitting )
324 return;
325 else
326 mbKbdSplitting = false;
328 if ( nCode != KEY_ESCAPE )
330 long nNewPos;
331 if ( mbHorzSplit )
332 nNewPos = maDragPos.X();
333 else
334 nNewPos = maDragPos.Y();
335 if ( nNewPos != mnStartSplitPos )
337 SetSplitPosPixel( nNewPos );
338 mnLastSplitPos = 0;
339 Split();
342 else
344 SetSplitPosPixel( mnStartSplitPos );
345 Split();
346 EndSplit();
348 mnStartSplitPos = 0;
350 else
352 Point aNewPos;
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
359 if( mbHorzSplit )
360 aNewPos = Point( ImplSplitterActive() ? aPos.X() : mnSplitPos, aKeyCode.IsShift() ? 0 : aSize.Height()/2);
361 else
362 aNewPos = Point( aKeyCode.IsShift() ? 0 : aSize.Width()/2, ImplSplitterActive() ? aPos.Y() : mnSplitPos );
364 Point aOldWindowPos = GetPosPixel();
366 int maxiter = 500; // avoid endless loop
367 int delta=0;
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() )
379 delta++;
380 else
381 delta += delta_step;
383 switch( nCode )
385 case KEY_LEFT:
386 aNewPos.X()-=delta;
387 break;
388 case KEY_RIGHT:
389 aNewPos.X()+=delta;
390 break;
391 case KEY_UP:
392 aNewPos.Y()-=delta;
393 break;
394 case KEY_DOWN:
395 aNewPos.Y()+=delta;
396 break;
397 default:
398 maxiter = 0; // leave loop
399 break;
401 ImplSplitMousePos( aNewPos );
403 if ( mbHorzSplit )
405 if ( aNewPos.X() == maDragPos.X() )
406 continue;
408 else
410 if ( aNewPos.Y() == maDragPos.Y() )
411 continue;
414 maDragPos = aNewPos;
415 long nNewPos;
416 if ( mbHorzSplit )
417 nNewPos = maDragPos.X();
418 else
419 nNewPos = maDragPos.Y();
420 if ( nNewPos != mnSplitPos )
422 SetSplitPosPixel( nNewPos );
423 mnLastSplitPos = 0;
424 Split();
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;
449 if ( !_pRefWin )
450 mpRefWin = GetParent();
451 else
452 mpRefWin = _pRefWin;
455 void Splitter::SetSplitPosPixel( long nNewPos )
457 mnSplitPos = nNewPos;
460 void Splitter::StartDrag()
462 if ( IsTracking() )
463 return;
465 StartSplit();
467 // Tracking starten
468 StartTracking();
470 // Start-Position ermitteln
471 maDragPos = mpRefWin->GetPointerPosPixel();
472 ImplSplitMousePos( maDragPos );
473 if ( mbHorzSplit )
474 mnStartSplitPos = maDragPos.X();
475 else
476 mnStartSplitPos = maDragPos.Y();
478 mbDragFull = bool(Application::GetSettings().GetStyleSettings().GetDragFullOptions() & DragFullOptions::Split);
479 if ( !mbDragFull )
480 ImplDrawSplitter();
483 void Splitter::ImplStartKbdSplitting()
485 if( mbKbdSplitting )
486 return;
488 mbKbdSplitting = true;
490 StartSplit();
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();
498 if( mbHorzSplit )
499 maDragPos = Point( ImplSplitterActive() ? aPos.X() : mnSplitPos, aSize.Height()/2 );
500 else
501 maDragPos = Point( aSize.Width()/2, ImplSplitterActive() ? aPos.Y() : mnSplitPos );
502 ImplSplitMousePos( maDragPos );
503 if ( mbHorzSplit )
504 mnStartSplitPos = maDragPos.X();
505 else
506 mnStartSplitPos = maDragPos.Y();
509 void Splitter::ImplRestoreSplitter()
511 // set splitter in the center of the ref window
512 StartSplit();
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)
518 if ( mbHorzSplit )
519 aPos.X() = mnLastSplitPos;
520 else
521 aPos.Y() = mnLastSplitPos;
524 ImplSplitMousePos( aPos );
525 long nTemp = mnSplitPos;
526 if ( mbHorzSplit )
527 SetSplitPosPixel( aPos.X() );
528 else
529 SetSplitPosPixel( aPos.Y() );
530 mnLastSplitPos = nTemp;
531 Split();
532 EndSplit();
535 void Splitter::GetFocus()
537 if( !ImplSplitterActive() )
538 ImplRestoreSplitter();
540 Invalidate();
543 void Splitter::LoseFocus()
545 if( mbKbdSplitting )
547 vcl::KeyCode aReturnKey( KEY_RETURN );
548 ImplKbdTracking( aReturnKey );
549 mbKbdSplitting = false;
551 Invalidate();
554 void Splitter::KeyInput( const KeyEvent& rKEvt )
556 if( mbInKeyEvent )
557 return;
559 mbInKeyEvent = 1;
561 Splitter *pSibling = ImplFindSibling();
562 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
563 sal_uInt16 nCode = aKeyCode.GetCode();
564 switch ( nCode )
566 case KEY_UP:
567 case KEY_DOWN:
568 if( !mbHorzSplit )
570 ImplStartKbdSplitting();
571 ImplKbdTracking( aKeyCode );
573 else
575 if( pSibling )
577 pSibling->GrabFocus();
578 pSibling->KeyInput( rKEvt );
581 break;
582 case KEY_RIGHT:
583 case KEY_LEFT:
584 if( mbHorzSplit )
586 ImplStartKbdSplitting();
587 ImplKbdTracking( aKeyCode );
589 else
591 if( pSibling )
593 pSibling->GrabFocus();
594 pSibling->KeyInput( rKEvt );
597 break;
599 case KEY_DELETE:
600 if( ImplSplitterActive() )
602 if( mbKbdSplitting )
604 vcl::KeyCode aKey( KEY_ESCAPE );
605 ImplKbdTracking( aKey );
608 StartSplit();
609 Point aPos;
610 if ( mbHorzSplit )
611 aPos.X() = 0;
612 else
613 aPos.Y() = 0;
614 ImplSplitMousePos( aPos );
615 long nTemp = mnSplitPos;
616 if ( mbHorzSplit )
617 SetSplitPosPixel( aPos.X() );
618 else
619 SetSplitPosPixel( aPos.Y() );
620 mnLastSplitPos = nTemp;
621 Split();
622 EndSplit();
624 // Shift-Del deletes both splitters
625 if( aKeyCode.IsShift() && pSibling )
626 pSibling->KeyInput( rKEvt );
628 GrabFocusToDocument();
630 break;
632 case KEY_ESCAPE:
633 if( mbKbdSplitting )
634 ImplKbdTracking( aKeyCode );
635 else
636 GrabFocusToDocument();
637 break;
639 case KEY_RETURN:
640 ImplKbdTracking( aKeyCode );
641 GrabFocusToDocument();
642 break;
643 default: // let any key input fix the splitter
644 Window::KeyInput( rKEvt );
645 GrabFocusToDocument();
646 break;
648 mbInKeyEvent = 0;
651 void Splitter::DataChanged( const DataChangedEvent& rDCEvt )
653 Window::DataChanged( rDCEvt );
654 if( rDCEvt.GetType() == DataChangedEventType::SETTINGS )
656 const AllSettings* pOldSettings = rDCEvt.GetOldSettings();
657 if(!pOldSettings)
658 return;
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() );
666 else
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);
680 if (mbKbdSplitting)
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 );
691 else
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: */