Fix typo
[LibreOffice.git] / vcl / source / window / split.cxx
blobadb82ad575c3dfbdc4c42f67eb3e907a145ecf2a
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>
30 #include <tools/lazydelete.hxx>
32 #include <window.h>
34 namespace
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)
53 mbHorzSplit = bNew;
55 PointerStyle ePointerStyle;
56 const StyleSettings& rSettings = GetSettings().GetStyleSettings();
58 if ( mbHorzSplit )
60 ePointerStyle = PointerStyle::HSplit;
61 SetSizePixel( Size( StyleSettings::GetSplitSize(), rSettings.GetScrollBarSize() ) );
63 else
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 );
76 mpRefWin = pParent;
78 ImplInitHorVer(nWinStyle & WB_HSCROLL);
80 if( GetSettings().GetStyleSettings().GetFaceColor().IsDark() )
81 SetBackground( ImplWhiteWall() );
82 else
83 SetBackground( ImplBlackWall() );
85 TaskPaneList *pTList = GetSystemWindow()->GetTaskPaneList();
86 pTList->AddWindow( this );
89 void Splitter::ImplSplitMousePos( Point& rPos )
91 if ( mbHorzSplit )
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 );
98 else
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 );
111 if ( mbHorzSplit )
113 aInvRect.SetLeft( maDragPos.X() - 1 );
114 aInvRect.SetRight( maDragPos.X() + 1 );
116 else
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 ),
127 mpRefWin( nullptr ),
128 mnSplitPos( 0 ),
129 mnLastSplitPos( 0 ),
130 mnStartSplitPos( 0 ),
131 mbDragFull( false ),
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()
146 disposeOnce();
149 void Splitter::dispose()
151 SystemWindow *pSysWin = GetSystemWindow();
152 if(pSysWin)
154 TaskPaneList *pTList = pSysWin->GetTaskPaneList();
155 pTList->RemoveWindow(this);
157 mpRefWin.clear();
158 Window::dispose();
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;
179 while( pWin )
181 if( pWin->ImplIsSplitter() )
183 pSplitter = static_cast<Splitter*>(pWin);
184 if( pSplitter != this && IsHorizontal() != pSplitter->IsHorizontal() )
185 return pSplitter;
187 pWin = pWin->GetWindow( GetWindowType::Next );
189 return nullptr;
192 bool Splitter::ImplSplitterActive()
194 // is splitter in document or at scrollbar handle ?
196 bool bActive = true;
197 const StyleSettings& rSettings = GetSettings().GetStyleSettings();
198 tools::Long nA = rSettings.GetScrollBarSize();
199 tools::Long nB = StyleSettings::GetSplitSize();
201 Size aSize = GetOutDev()->GetOutputSize();
202 if ( mbHorzSplit )
204 if( aSize.Width() == nB && aSize.Height() == nA )
205 bActive = false;
207 else
209 if( aSize.Width() == nA && aSize.Height() == nB )
210 bActive = false;
212 return bActive;
215 void Splitter::MouseButtonDown( const MouseEvent& rMEvt )
217 if ( rMEvt.GetClicks() == 2 )
219 if ( mnLastSplitPos != mnSplitPos )
221 StartSplit();
222 Point aPos = rMEvt.GetPosPixel();
223 if ( mbHorzSplit )
224 aPos.setX( mnLastSplitPos );
225 else
226 aPos.setY( mnLastSplitPos );
227 ImplSplitMousePos( aPos );
228 tools::Long nTemp = mnSplitPos;
229 if ( mbHorzSplit )
230 SetSplitPosPixel( aPos.X() );
231 else
232 SetSplitPosPixel( aPos.Y() );
233 mnLastSplitPos = nTemp;
234 Split();
235 EndSplit();
238 else
239 StartDrag();
242 void Splitter::Tracking( const TrackingEvent& rTEvt )
244 if ( rTEvt.IsTrackingEnded() )
246 if ( !mbDragFull )
247 ImplDrawSplitter();
249 if ( !rTEvt.IsTrackingCanceled() )
251 tools::Long nNewPos;
252 if ( mbHorzSplit )
253 nNewPos = maDragPos.X();
254 else
255 nNewPos = maDragPos.Y();
256 if ( nNewPos != mnStartSplitPos )
258 SetSplitPosPixel( nNewPos );
259 mnLastSplitPos = 0;
260 Split();
262 EndSplit();
264 else if ( mbDragFull )
266 SetSplitPosPixel( mnStartSplitPos );
267 Split();
269 mnStartSplitPos = 0;
271 else
273 //Point aNewPos = mpRefWin->ScreenToOutputPixel( OutputToScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) );
274 Point aNewPos = mpRefWin->NormalizedScreenToOutputPixel( OutputToNormalizedScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) );
275 ImplSplitMousePos( aNewPos );
277 if ( mbHorzSplit )
279 if ( aNewPos.X() == maDragPos.X() )
280 return;
282 else
284 if ( aNewPos.Y() == maDragPos.Y() )
285 return;
288 if ( mbDragFull )
290 maDragPos = aNewPos;
291 tools::Long nNewPos;
292 if ( mbHorzSplit )
293 nNewPos = maDragPos.X();
294 else
295 nNewPos = maDragPos.Y();
296 if ( nNewPos != mnSplitPos )
298 SetSplitPosPixel( nNewPos );
299 mnLastSplitPos = 0;
300 Split();
303 GetParent()->PaintImmediately();
305 else
307 ImplDrawSplitter();
308 maDragPos = aNewPos;
309 ImplDrawSplitter();
314 void Splitter::ImplKbdTracking( vcl::KeyCode aKeyCode )
316 sal_uInt16 nCode = aKeyCode.GetCode();
317 if ( nCode == KEY_ESCAPE || nCode == KEY_RETURN )
319 if( !mbKbdSplitting )
320 return;
321 else
322 mbKbdSplitting = false;
324 if ( nCode != KEY_ESCAPE )
326 tools::Long nNewPos;
327 if ( mbHorzSplit )
328 nNewPos = maDragPos.X();
329 else
330 nNewPos = maDragPos.Y();
331 if ( nNewPos != mnStartSplitPos )
333 SetSplitPosPixel( nNewPos );
334 mnLastSplitPos = 0;
335 Split();
338 else
340 SetSplitPosPixel( mnStartSplitPos );
341 Split();
342 EndSplit();
344 mnStartSplitPos = 0;
346 else
348 Point aNewPos;
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
355 if( mbHorzSplit )
356 aNewPos = Point( ImplSplitterActive() ? aPos.X() : mnSplitPos, aKeyCode.IsShift() ? 0 : aSize.Height()/2);
357 else
358 aNewPos = Point( aKeyCode.IsShift() ? 0 : aSize.Width()/2, ImplSplitterActive() ? aPos.Y() : mnSplitPos );
360 Point aOldWindowPos = GetPosPixel();
362 int maxiter = 500; // avoid endless loop
363 int delta=0;
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() )
375 delta++;
376 else
377 delta += delta_step;
379 switch( nCode )
381 case KEY_LEFT:
382 aNewPos.AdjustX( -delta );
383 break;
384 case KEY_RIGHT:
385 aNewPos.AdjustX(delta );
386 break;
387 case KEY_UP:
388 aNewPos.AdjustY( -delta );
389 break;
390 case KEY_DOWN:
391 aNewPos.AdjustY(delta );
392 break;
393 default:
394 maxiter = 0; // leave loop
395 break;
397 ImplSplitMousePos( aNewPos );
399 if ( mbHorzSplit )
401 if ( aNewPos.X() == maDragPos.X() )
402 continue;
404 else
406 if ( aNewPos.Y() == maDragPos.Y() )
407 continue;
410 maDragPos = aNewPos;
411 tools::Long nNewPos;
412 if ( mbHorzSplit )
413 nNewPos = maDragPos.X();
414 else
415 nNewPos = maDragPos.Y();
416 if ( nNewPos != mnSplitPos )
418 SetSplitPosPixel( nNewPos );
419 mnLastSplitPos = 0;
420 Split();
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;
445 if ( !_pRefWin )
446 mpRefWin = GetParent();
447 else
448 mpRefWin = _pRefWin;
451 void Splitter::SetSplitPosPixel( tools::Long nNewPos )
453 mnSplitPos = nNewPos;
456 void Splitter::StartDrag()
458 if ( IsTracking() )
459 return;
461 StartSplit();
463 // Start tracking
464 StartTracking();
466 // Determine start position
467 maDragPos = mpRefWin->GetPointerPosPixel();
468 ImplSplitMousePos( maDragPos );
469 if ( mbHorzSplit )
470 mnStartSplitPos = maDragPos.X();
471 else
472 mnStartSplitPos = maDragPos.Y();
474 mbDragFull = bool(Application::GetSettings().GetStyleSettings().GetDragFullOptions() & DragFullOptions::Split);
475 if ( !mbDragFull )
476 ImplDrawSplitter();
479 void Splitter::ImplStartKbdSplitting()
481 if( mbKbdSplitting )
482 return;
484 mbKbdSplitting = true;
486 StartSplit();
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();
494 if( mbHorzSplit )
495 maDragPos = Point( ImplSplitterActive() ? aPos.X() : mnSplitPos, aSize.Height()/2 );
496 else
497 maDragPos = Point( aSize.Width()/2, ImplSplitterActive() ? aPos.Y() : mnSplitPos );
498 ImplSplitMousePos( maDragPos );
499 if ( mbHorzSplit )
500 mnStartSplitPos = maDragPos.X();
501 else
502 mnStartSplitPos = maDragPos.Y();
505 void Splitter::ImplRestoreSplitter()
507 // set splitter in the center of the ref window
508 StartSplit();
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)
514 if ( mbHorzSplit )
515 aPos.setX( mnLastSplitPos );
516 else
517 aPos.setY( mnLastSplitPos );
520 ImplSplitMousePos( aPos );
521 tools::Long nTemp = mnSplitPos;
522 if ( mbHorzSplit )
523 SetSplitPosPixel( aPos.X() );
524 else
525 SetSplitPosPixel( aPos.Y() );
526 mnLastSplitPos = nTemp;
527 Split();
528 EndSplit();
531 void Splitter::GetFocus()
533 if( !ImplSplitterActive() )
534 ImplRestoreSplitter();
536 Invalidate();
539 void Splitter::LoseFocus()
541 if( mbKbdSplitting )
543 vcl::KeyCode aReturnKey( KEY_RETURN );
544 ImplKbdTracking( aReturnKey );
545 mbKbdSplitting = false;
547 Invalidate();
550 void Splitter::KeyInput( const KeyEvent& rKEvt )
552 if( mbInKeyEvent )
553 return;
555 mbInKeyEvent = true;
557 Splitter *pSibling = ImplFindSibling();
558 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
559 sal_uInt16 nCode = aKeyCode.GetCode();
560 switch ( nCode )
562 case KEY_UP:
563 case KEY_DOWN:
564 if( !mbHorzSplit )
566 ImplStartKbdSplitting();
567 ImplKbdTracking( aKeyCode );
569 else
571 if( pSibling )
573 pSibling->GrabFocus();
574 pSibling->KeyInput( rKEvt );
577 break;
578 case KEY_RIGHT:
579 case KEY_LEFT:
580 if( mbHorzSplit )
582 ImplStartKbdSplitting();
583 ImplKbdTracking( aKeyCode );
585 else
587 if( pSibling )
589 pSibling->GrabFocus();
590 pSibling->KeyInput( rKEvt );
593 break;
595 case KEY_DELETE:
596 if( ImplSplitterActive() )
598 if( mbKbdSplitting )
600 vcl::KeyCode aKey( KEY_ESCAPE );
601 ImplKbdTracking( aKey );
604 StartSplit();
605 Point aPos;
606 if ( mbHorzSplit )
607 aPos.setX( 0 );
608 else
609 aPos.setY( 0 );
610 ImplSplitMousePos( aPos );
611 tools::Long nTemp = mnSplitPos;
612 if ( mbHorzSplit )
613 SetSplitPosPixel( aPos.X() );
614 else
615 SetSplitPosPixel( aPos.Y() );
616 mnLastSplitPos = nTemp;
617 Split();
618 EndSplit();
620 // Shift-Del deletes both splitters
621 if( aKeyCode.IsShift() && pSibling )
622 pSibling->KeyInput( rKEvt );
624 GrabFocusToDocument();
626 break;
628 case KEY_ESCAPE:
629 if( mbKbdSplitting )
630 ImplKbdTracking( aKeyCode );
631 else
632 GrabFocusToDocument();
633 break;
635 case KEY_RETURN:
636 ImplKbdTracking( aKeyCode );
637 GrabFocusToDocument();
638 break;
639 default: // let any key input fix the splitter
640 Window::KeyInput( rKEvt );
641 GrabFocusToDocument();
642 break;
644 mbInKeyEvent = false;
647 void Splitter::DataChanged( const DataChangedEvent& rDCEvt )
649 Window::DataChanged( rDCEvt );
650 if( rDCEvt.GetType() != DataChangedEventType::SETTINGS )
651 return;
653 const AllSettings* pOldSettings = rDCEvt.GetOldSettings();
654 if(!pOldSettings)
655 return;
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() );
663 else
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);
676 if (mbKbdSplitting)
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 );
687 else
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: */