bump product version to 4.1.6.2
[LibreOffice.git] / vcl / source / window / split.cxx
blob13cc48663aa068d9ad513c4ecc92c3d819aa20f6
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>
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 // =======================================================================
53 void Splitter::ImplInitSplitterData()
55 ImplGetWindowImpl()->mbSplitter = sal_True;
56 mpRefWin = NULL;
57 mnSplitPos = 0;
58 mnLastSplitPos = 0;
59 mnStartSplitPos = 0;
60 mbDragFull = sal_False;
61 mbKbdSplitting = sal_False;
62 mbInKeyEvent = 0;
63 mnKeyboardStepSize = SPLITTER_DEFAULTSTEPSIZE;
66 // -----------------------------------------------------------------------
68 // Should only be called from a ImplInit method for initialization or
69 // after checking bNew is different from the current mbHorzSplit value.
70 // The public method that does that check is Splitter::SetHorizontal().
71 void Splitter::ImplInitHorVer(bool bNew)
73 mbHorzSplit = bNew;
75 PointerStyle ePointerStyle;
76 const StyleSettings& rSettings = GetSettings().GetStyleSettings();
78 if ( mbHorzSplit )
80 ePointerStyle = POINTER_HSPLIT;
81 SetSizePixel( Size( rSettings.GetSplitSize(), rSettings.GetScrollBarSize() ) );
83 else
85 ePointerStyle = POINTER_VSPLIT;
86 SetSizePixel( Size( rSettings.GetScrollBarSize(), rSettings.GetSplitSize() ) );
89 SetPointer( Pointer( ePointerStyle ) );
92 // -----------------------------------------------------------------------
94 void Splitter::ImplInit( Window* pParent, WinBits nWinStyle )
96 Window::ImplInit( pParent, nWinStyle, NULL );
98 mpRefWin = pParent;
100 ImplInitHorVer(nWinStyle & WB_HSCROLL);
102 if( GetSettings().GetStyleSettings().GetFaceColor().IsDark() )
103 SetBackground( ImplWhiteWall::get() );
104 else
105 SetBackground( ImplBlackWall::get() );
107 TaskPaneList *pTList = GetSystemWindow()->GetTaskPaneList();
108 pTList->AddWindow( this );
111 // -----------------------------------------------------------------------
113 void Splitter::ImplSplitMousePos( Point& rPos )
115 if ( mbHorzSplit )
117 if ( rPos.X() > maDragRect.Right()-1 )
118 rPos.X() = maDragRect.Right()-1;
119 if ( rPos.X() < maDragRect.Left()+1 )
120 rPos.X() = maDragRect.Left()+1;
122 else
124 if ( rPos.Y() > maDragRect.Bottom()-1 )
125 rPos.Y() = maDragRect.Bottom()-1;
126 if ( rPos.Y() < maDragRect.Top()+1 )
127 rPos.Y() = maDragRect.Top()+1;
131 // -----------------------------------------------------------------------
133 void Splitter::ImplDrawSplitter()
135 Rectangle aInvRect( maDragRect );
137 if ( mbHorzSplit )
139 aInvRect.Left() = maDragPos.X() - 1;
140 aInvRect.Right() = maDragPos.X() + 1;
142 else
144 aInvRect.Top() = maDragPos.Y() - 1;
145 aInvRect.Bottom() = maDragPos.Y() + 1;
148 mpRefWin->InvertTracking( mpRefWin->PixelToLogic(aInvRect), SHOWTRACK_SPLIT );
151 // -----------------------------------------------------------------------
153 Splitter::Splitter( Window* pParent, WinBits nStyle ) :
154 Window( WINDOW_SPLITTER )
156 ImplInitSplitterData();
157 ImplInit( pParent, nStyle );
159 SetLineColor();
160 SetFillColor();
163 // -----------------------------------------------------------------------
165 Splitter::Splitter( Window* pParent, const ResId& rResId ) :
166 Window( WINDOW_SPLITTER )
168 ImplInitSplitterData();
169 rResId.SetRT( RSC_SPLITTER );
170 WinBits nStyle = ImplInitRes( rResId );
171 ImplInit( pParent, nStyle );
172 ImplLoadRes( rResId );
174 SetLineColor();
175 SetFillColor();
177 if ( !(nStyle & WB_HIDE) )
178 Show();
181 // -----------------------------------------------------------------------
183 Splitter::~Splitter()
185 TaskPaneList *pTList = GetSystemWindow()->GetTaskPaneList();
186 pTList->RemoveWindow( this );
189 // -----------------------------------------------------------------------
191 void Splitter::SetHorizontal(bool bNew)
193 if(bNew != (bool)mbHorzSplit)
195 ImplInitHorVer(bNew);
199 // -----------------------------------------------------------------------
201 void Splitter::SetKeyboardStepSize( long nStepSize )
203 mnKeyboardStepSize = nStepSize;
206 // -----------------------------------------------------------------------
208 Splitter* Splitter::ImplFindSibling()
210 // look for another splitter with the same parent but different orientation
211 Window *pWin = GetParent()->GetWindow( WINDOW_FIRSTCHILD );
212 Splitter *pSplitter = NULL;
213 while( pWin )
215 if( pWin->ImplIsSplitter() )
217 pSplitter = (Splitter*) pWin;
218 if( pSplitter != this && IsHorizontal() != pSplitter->IsHorizontal() )
219 return pSplitter;
221 pWin = pWin->GetWindow( WINDOW_NEXT );
223 return NULL;
226 // -----------------------------------------------------------------------
228 sal_Bool Splitter::ImplSplitterActive()
230 // is splitter in document or at scrollbar handle ?
232 sal_Bool bActive = sal_True;
233 const StyleSettings& rSettings = GetSettings().GetStyleSettings();
234 long nA = rSettings.GetScrollBarSize();
235 long nB = rSettings.GetSplitSize();
237 Size aSize = GetOutputSize();
238 if ( mbHorzSplit )
240 if( aSize.Width() == nB && aSize.Height() == nA )
241 bActive = sal_False;
243 else
245 if( aSize.Width() == nA && aSize.Height() == nB )
246 bActive = sal_False;
248 return bActive;
251 // -----------------------------------------------------------------------
253 void Splitter::MouseButtonDown( const MouseEvent& rMEvt )
255 if ( rMEvt.GetClicks() == 2 )
257 if ( mnLastSplitPos != mnSplitPos )
259 StartSplit();
260 Point aPos = rMEvt.GetPosPixel();
261 if ( mbHorzSplit )
262 aPos.X() = mnLastSplitPos;
263 else
264 aPos.Y() = mnLastSplitPos;
265 ImplSplitMousePos( aPos );
266 Splitting( aPos );
267 ImplSplitMousePos( aPos );
268 long nTemp = mnSplitPos;
269 if ( mbHorzSplit )
270 SetSplitPosPixel( aPos.X() );
271 else
272 SetSplitPosPixel( aPos.Y() );
273 mnLastSplitPos = nTemp;
274 Split();
275 EndSplit();
278 else
279 StartDrag();
282 // -----------------------------------------------------------------------
284 void Splitter::Tracking( const TrackingEvent& rTEvt )
286 if ( rTEvt.IsTrackingEnded() )
288 if ( !mbDragFull )
289 ImplDrawSplitter();
291 if ( !rTEvt.IsTrackingCanceled() )
293 long nNewPos;
294 if ( mbHorzSplit )
295 nNewPos = maDragPos.X();
296 else
297 nNewPos = maDragPos.Y();
298 if ( nNewPos != mnStartSplitPos )
300 SetSplitPosPixel( nNewPos );
301 mnLastSplitPos = 0;
302 Split();
304 EndSplit();
306 else if ( mbDragFull )
308 SetSplitPosPixel( mnStartSplitPos );
309 Split();
311 mnStartSplitPos = 0;
313 else
315 //Point aNewPos = mpRefWin->ScreenToOutputPixel( OutputToScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) );
316 Point aNewPos = mpRefWin->NormalizedScreenToOutputPixel( OutputToNormalizedScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) );
317 ImplSplitMousePos( aNewPos );
318 Splitting( aNewPos );
319 ImplSplitMousePos( aNewPos );
321 if ( mbHorzSplit )
323 if ( aNewPos.X() == maDragPos.X() )
324 return;
326 else
328 if ( aNewPos.Y() == maDragPos.Y() )
329 return;
332 if ( mbDragFull )
334 maDragPos = aNewPos;
335 long nNewPos;
336 if ( mbHorzSplit )
337 nNewPos = maDragPos.X();
338 else
339 nNewPos = maDragPos.Y();
340 if ( nNewPos != mnSplitPos )
342 SetSplitPosPixel( nNewPos );
343 mnLastSplitPos = 0;
344 Split();
347 GetParent()->Update();
349 else
351 ImplDrawSplitter();
352 maDragPos = aNewPos;
353 ImplDrawSplitter();
358 // -----------------------------------------------------------------------
360 void Splitter::ImplKbdTracking( KeyCode aKeyCode )
362 sal_uInt16 nCode = aKeyCode.GetCode();
363 if ( nCode == KEY_ESCAPE || nCode == KEY_RETURN )
365 if( !mbKbdSplitting )
366 return;
367 else
368 mbKbdSplitting = sal_False;
370 if ( nCode != KEY_ESCAPE )
372 long nNewPos;
373 if ( mbHorzSplit )
374 nNewPos = maDragPos.X();
375 else
376 nNewPos = maDragPos.Y();
377 if ( nNewPos != mnStartSplitPos )
379 SetSplitPosPixel( nNewPos );
380 mnLastSplitPos = 0;
381 Split();
384 else
386 SetSplitPosPixel( mnStartSplitPos );
387 Split();
388 EndSplit();
390 mnStartSplitPos = 0;
392 else
394 Point aNewPos;
395 Size aSize = mpRefWin->GetOutputSize();
396 Point aPos = GetPosPixel();
397 // depending on the position calc allows continous moves or snaps to row/columns
398 // continous mode is active when position is at the origin or end of the splitter
399 // otherwise snap mode is active
400 // default here is snap, holding shift sets continous mode
401 if( mbHorzSplit )
402 aNewPos = Point( ImplSplitterActive() ? aPos.X() : mnSplitPos, aKeyCode.IsShift() ? 0 : aSize.Height()/2);
403 else
404 aNewPos = Point( aKeyCode.IsShift() ? 0 : aSize.Width()/2, ImplSplitterActive() ? aPos.Y() : mnSplitPos );
406 Point aOldWindowPos = GetPosPixel();
408 int maxiter = 500; // avoid endless loop
409 int delta=0;
410 int delta_step = mbHorzSplit ? aSize.Width()/10 : aSize.Height()/10;
412 // use the specified step size if it was set
413 if( mnKeyboardStepSize != SPLITTER_DEFAULTSTEPSIZE )
414 delta_step = mnKeyboardStepSize;
416 while( maxiter-- && aOldWindowPos == GetPosPixel() )
418 // inc/dec position until application performs changes
419 // thus a single key press really moves the splitter
420 if( aKeyCode.IsShift() )
421 delta++;
422 else
423 delta += delta_step;
425 switch( nCode )
427 case KEY_LEFT:
428 aNewPos.X()-=delta;
429 break;
430 case KEY_RIGHT:
431 aNewPos.X()+=delta;
432 break;
433 case KEY_UP:
434 aNewPos.Y()-=delta;
435 break;
436 case KEY_DOWN:
437 aNewPos.Y()+=delta;
438 break;
439 default:
440 maxiter = 0; // leave loop
441 break;
443 ImplSplitMousePos( aNewPos );
444 Splitting( aNewPos );
445 ImplSplitMousePos( aNewPos );
447 if ( mbHorzSplit )
449 if ( aNewPos.X() == maDragPos.X() )
450 continue;
452 else
454 if ( aNewPos.Y() == maDragPos.Y() )
455 continue;
458 maDragPos = aNewPos;
459 long nNewPos;
460 if ( mbHorzSplit )
461 nNewPos = maDragPos.X();
462 else
463 nNewPos = maDragPos.Y();
464 if ( nNewPos != mnSplitPos )
466 SetSplitPosPixel( nNewPos );
467 mnLastSplitPos = 0;
468 Split();
470 GetParent()->Update();
475 // -----------------------------------------------------------------------
477 void Splitter::StartSplit()
479 maStartSplitHdl.Call( this );
482 // -----------------------------------------------------------------------
484 void Splitter::Split()
486 maSplitHdl.Call( this );
489 // -----------------------------------------------------------------------
491 void Splitter::EndSplit()
493 if ( maEndSplitHdl.IsSet() )
494 maEndSplitHdl.Call( this );
497 // -----------------------------------------------------------------------
499 void Splitter::Splitting( Point& /* rSplitPos */ )
503 // -----------------------------------------------------------------------
505 void Splitter::SetDragRectPixel( const Rectangle& rDragRect, Window* _pRefWin )
507 maDragRect = rDragRect;
508 if ( !_pRefWin )
509 mpRefWin = GetParent();
510 else
511 mpRefWin = _pRefWin;
514 // -----------------------------------------------------------------------
516 void Splitter::SetSplitPosPixel( long nNewPos )
518 mnSplitPos = nNewPos;
521 // -----------------------------------------------------------------------
523 void Splitter::StartDrag()
525 if ( IsTracking() )
526 return;
528 StartSplit();
530 // Tracking starten
531 StartTracking();
533 // Start-Positon ermitteln
534 maDragPos = mpRefWin->GetPointerPosPixel();
535 ImplSplitMousePos( maDragPos );
536 Splitting( maDragPos );
537 ImplSplitMousePos( maDragPos );
538 if ( mbHorzSplit )
539 mnStartSplitPos = maDragPos.X();
540 else
541 mnStartSplitPos = maDragPos.Y();
543 mbDragFull = (Application::GetSettings().GetStyleSettings().GetDragFullOptions() & DRAGFULL_OPTION_SPLIT) != 0;
544 if ( !mbDragFull )
545 ImplDrawSplitter();
549 // -----------------------------------------------------------------------
551 void Splitter::ImplStartKbdSplitting()
553 if( mbKbdSplitting )
554 return;
556 mbKbdSplitting = sal_True;
558 StartSplit();
560 // determine start position
561 // because we have no mouse position we take either the position
562 // of the splitter window or the last split position
563 // the other coordinate is just the center of the reference window
564 Size aSize = mpRefWin->GetOutputSize();
565 Point aPos = GetPosPixel();
566 if( mbHorzSplit )
567 maDragPos = Point( ImplSplitterActive() ? aPos.X() : mnSplitPos, aSize.Height()/2 );
568 else
569 maDragPos = Point( aSize.Width()/2, ImplSplitterActive() ? aPos.Y() : mnSplitPos );
570 ImplSplitMousePos( maDragPos );
571 Splitting( maDragPos );
572 ImplSplitMousePos( maDragPos );
573 if ( mbHorzSplit )
574 mnStartSplitPos = maDragPos.X();
575 else
576 mnStartSplitPos = maDragPos.Y();
579 // -----------------------------------------------------------------------
581 void Splitter::ImplRestoreSplitter()
583 // set splitter in the center of the ref window
584 StartSplit();
585 Size aSize = mpRefWin->GetOutputSize();
586 Point aPos = Point( aSize.Width()/2 , aSize.Height()/2);
587 if ( mnLastSplitPos != mnSplitPos && mnLastSplitPos > 5 )
589 // restore last pos if it was a useful position (>5)
590 if ( mbHorzSplit )
591 aPos.X() = mnLastSplitPos;
592 else
593 aPos.Y() = mnLastSplitPos;
596 ImplSplitMousePos( aPos );
597 Splitting( aPos );
598 ImplSplitMousePos( aPos );
599 long nTemp = mnSplitPos;
600 if ( mbHorzSplit )
601 SetSplitPosPixel( aPos.X() );
602 else
603 SetSplitPosPixel( aPos.Y() );
604 mnLastSplitPos = nTemp;
605 Split();
606 EndSplit();
610 // -----------------------------------------------------------------------
612 void Splitter::GetFocus()
614 if( !ImplSplitterActive() )
615 ImplRestoreSplitter();
617 Invalidate();
620 // -----------------------------------------------------------------------
622 void Splitter::LoseFocus()
624 if( mbKbdSplitting )
626 KeyCode aReturnKey( KEY_RETURN );
627 ImplKbdTracking( aReturnKey );
628 mbKbdSplitting = sal_False;
630 Invalidate();
633 // -----------------------------------------------------------------------
635 void Splitter::KeyInput( const KeyEvent& rKEvt )
637 if( mbInKeyEvent )
638 return;
640 mbInKeyEvent = 1;
642 Splitter *pSibling = ImplFindSibling();
643 KeyCode aKeyCode = rKEvt.GetKeyCode();
644 sal_uInt16 nCode = aKeyCode.GetCode();
645 switch ( nCode )
647 case KEY_UP:
648 case KEY_DOWN:
649 if( !mbHorzSplit )
651 ImplStartKbdSplitting();
652 ImplKbdTracking( aKeyCode );
654 else
656 if( pSibling )
658 pSibling->GrabFocus();
659 pSibling->KeyInput( rKEvt );
662 break;
663 case KEY_RIGHT:
664 case KEY_LEFT:
665 if( mbHorzSplit )
667 ImplStartKbdSplitting();
668 ImplKbdTracking( aKeyCode );
670 else
672 if( pSibling )
674 pSibling->GrabFocus();
675 pSibling->KeyInput( rKEvt );
678 break;
680 case KEY_DELETE:
681 if( ImplSplitterActive() )
683 if( mbKbdSplitting )
685 KeyCode aKey( KEY_ESCAPE );
686 ImplKbdTracking( aKey );
689 StartSplit();
690 Point aPos;
691 if ( mbHorzSplit )
692 aPos.X() = 0;
693 else
694 aPos.Y() = 0;
695 ImplSplitMousePos( aPos );
696 Splitting( aPos );
697 ImplSplitMousePos( aPos );
698 long nTemp = mnSplitPos;
699 if ( mbHorzSplit )
700 SetSplitPosPixel( aPos.X() );
701 else
702 SetSplitPosPixel( aPos.Y() );
703 mnLastSplitPos = nTemp;
704 Split();
705 EndSplit();
707 // Shift-Del deletes both splitters
708 if( aKeyCode.IsShift() && pSibling )
709 pSibling->KeyInput( rKEvt );
711 GrabFocusToDocument();
713 break;
715 case KEY_ESCAPE:
716 if( mbKbdSplitting )
717 ImplKbdTracking( aKeyCode );
718 else
719 GrabFocusToDocument();
720 break;
722 case KEY_RETURN:
723 ImplKbdTracking( aKeyCode );
724 GrabFocusToDocument();
725 break;
726 default: // let any key input fix the splitter
727 Window::KeyInput( rKEvt );
728 GrabFocusToDocument();
729 break;
731 mbInKeyEvent = 0;
734 // -----------------------------------------------------------------------
736 long Splitter::Notify( NotifyEvent& rNEvt )
738 return Window::Notify( rNEvt );
741 // -----------------------------------------------------------------------
743 void Splitter::DataChanged( const DataChangedEvent& rDCEvt )
745 Window::DataChanged( rDCEvt );
746 if( rDCEvt.GetType() == DATACHANGED_SETTINGS )
748 const AllSettings* pOldSettings = rDCEvt.GetOldSettings();
749 if(!pOldSettings)
750 return;
752 Color oldFaceColor = pOldSettings->GetStyleSettings().GetFaceColor();
753 Color newFaceColor = Application::GetSettings().GetStyleSettings().GetFaceColor();
754 if( oldFaceColor.IsDark() != newFaceColor.IsDark() )
756 if( newFaceColor.IsDark() )
757 SetBackground( ImplWhiteWall::get() );
758 else
759 SetBackground( ImplBlackWall::get() );
764 // -----------------------------------------------------------------------
766 void Splitter::Paint( const Rectangle& rPaintRect )
768 DrawRect( rPaintRect );
770 Polygon aPoly( rPaintRect );
771 PolyPolygon aPolyPoly( aPoly );
772 DrawTransparent( aPolyPoly, 85 );
774 if( mbKbdSplitting )
776 LineInfo aInfo( LINE_DASH );
777 //aInfo.SetDashLen( 2 );
778 //aInfo.SetDashCount( 1 );
779 aInfo.SetDistance( 1 );
780 aInfo.SetDotLen( 2 );
781 aInfo.SetDotCount( 3 );
783 DrawPolyLine( aPoly, aInfo );
785 else
787 DrawRect( rPaintRect );
791 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */