1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <com/sun/star/accessibility/AccessibleRole.hpp>
21 
22 #include <osl/diagnose.h>
23 #include <vcl/event.hxx>
24 #include <vcl/settings.hxx>
25 #include <vcl/ptrstyle.hxx>
26 
27 #include "ipwin.hxx"
28 #include <hatchwindow.hxx>
29 
30 /************************************************************************/
31 /*************************************************************************
32 |*    SvResizeHelper::SvResizeHelper()
33 |*
34 |*    Description
35 *************************************************************************/
SvResizeHelper()36 SvResizeHelper::SvResizeHelper()
37     : aBorder( 5, 5 )
38     , nGrab( -1 )
39 {
40 }
41 
42 /*************************************************************************
43 |*    SvResizeHelper::FillHandleRects()
44 |*
45 |*    Description: the eight handles to magnify
46 *************************************************************************/
FillHandleRectsPixel() const47 std::array<tools::Rectangle,8> SvResizeHelper::FillHandleRectsPixel() const
48 {
49     std::array<tools::Rectangle,8> aRects;
50 
51     // only because of EMPTY_RECT
52     Point aBottomRight = aOuter.BottomRight();
53 
54     // upper left
55     aRects[ 0 ] = tools::Rectangle( aOuter.TopLeft(), aBorder );
56     // upper middle
57     aRects[ 1 ] = tools::Rectangle( Point( aOuter.Center().X() - aBorder.Width() / 2,
58                                     aOuter.Top() ),
59                             aBorder );
60     // upper right
61     aRects[ 2 ] = tools::Rectangle( Point( aBottomRight.X() - aBorder.Width() +1,
62                                     aOuter.Top() ),
63                             aBorder );
64     // middle right
65     aRects[ 3 ] = tools::Rectangle( Point( aBottomRight.X() - aBorder.Width() +1,
66                                     aOuter.Center().Y() - aBorder.Height() / 2 ),
67                             aBorder );
68     // lower right
69     aRects[ 4 ] = tools::Rectangle( Point( aBottomRight.X() - aBorder.Width() +1,
70                                     aBottomRight.Y() - aBorder.Height() +1 ),
71                             aBorder );
72     // lower middle
73     aRects[ 5 ] = tools::Rectangle( Point( aOuter.Center().X() - aBorder.Width() / 2,
74                                     aBottomRight.Y() - aBorder.Height() +1),
75                             aBorder );
76     // lower left
77     aRects[ 6 ] = tools::Rectangle( Point( aOuter.Left(),
78                                     aBottomRight.Y() - aBorder.Height() +1),
79                             aBorder );
80     // middle left
81     aRects[ 7 ] = tools::Rectangle( Point( aOuter.Left(),
82                                     aOuter.Center().Y() - aBorder.Height() / 2 ),
83                             aBorder );
84     return aRects;
85 }
86 
87 /*************************************************************************
88 |*    SvResizeHelper::FillMoveRectsPixel()
89 |*
90 |*    Description: the four edges are calculated
91 *************************************************************************/
FillMoveRectsPixel() const92 std::array<tools::Rectangle,4> SvResizeHelper::FillMoveRectsPixel() const
93 {
94     std::array<tools::Rectangle,4> aRects;
95 
96     // upper
97     aRects[ 0 ] = aOuter;
98     aRects[ 0 ].SetBottom( aRects[ 0 ].Top() + aBorder.Height() -1 );
99     // right
100     aRects[ 1 ] = aOuter;
101     if (!aOuter.IsWidthEmpty())
102         aRects[ 1 ].SetLeft( aRects[ 1 ].Right() - aBorder.Width() -1 );
103     // lower
104     aRects[ 2 ] = aOuter;
105     if (!aOuter.IsHeightEmpty())
106         aRects[ 2 ].SetTop( aRects[ 2 ].Bottom() - aBorder.Height() -1 );
107     // left
108     aRects[ 3 ] = aOuter;
109     aRects[ 3 ].SetRight( aRects[ 3 ].Left() + aBorder.Width() -1 );
110 
111     return aRects;
112 }
113 
114 /*************************************************************************
115 |*    SvResizeHelper::Draw()
116 |*
117 |*    Description
118 *************************************************************************/
Draw(vcl::RenderContext & rRenderContext)119 void SvResizeHelper::Draw(vcl::RenderContext& rRenderContext)
120 {
121     rRenderContext.Push();
122     rRenderContext.SetMapMode( MapMode() );
123 
124     rRenderContext.SetFillColor( COL_LIGHTGRAY );
125     rRenderContext.SetLineColor();
126 
127     std::array<tools::Rectangle,4> aMoveRects = FillMoveRectsPixel();
128     sal_uInt16 i;
129     for (i = 0; i < 4; i++)
130         rRenderContext.DrawRect(aMoveRects[i]);
131     // draw handles
132     rRenderContext.SetFillColor(Color()); // black
133     std::array<tools::Rectangle,8> aRects = FillHandleRectsPixel();
134     for (i = 0; i < 8; i++)
135         rRenderContext.DrawRect( aRects[ i ] );
136     rRenderContext.Pop();
137 }
138 
139 /*************************************************************************
140 |*    SvResizeHelper::InvalidateBorder()
141 |*
142 |*    Description
143 *************************************************************************/
InvalidateBorder(vcl::Window * pWin)144 void SvResizeHelper::InvalidateBorder( vcl::Window * pWin )
145 {
146     std::array<tools::Rectangle,4> aMoveRects = FillMoveRectsPixel();
147     for(const auto & rMoveRect : aMoveRects)
148         pWin->Invalidate( rMoveRect );
149 }
150 
151 /*************************************************************************
152 |*    SvResizeHelper::SelectBegin()
153 |*
154 |*    Description
155 *************************************************************************/
SelectBegin(vcl::Window * pWin,const Point & rPos)156 bool SvResizeHelper::SelectBegin( vcl::Window * pWin, const Point & rPos )
157 {
158     if( -1 == nGrab )
159     {
160         nGrab = SelectMove( pWin, rPos );
161         if( -1 != nGrab )
162         {
163             aSelPos = rPos; // store start position
164             pWin->CaptureMouse();
165             return true;
166         }
167     }
168     return false;
169 }
170 
171 /*************************************************************************
172 |*    SvResizeHelper::SelectMove()
173 |*
174 |*    Description
175 *************************************************************************/
SelectMove(vcl::Window * pWin,const Point & rPos)176 short SvResizeHelper::SelectMove( vcl::Window * pWin, const Point & rPos )
177 {
178     if( -1 == nGrab )
179     {
180         std::array<tools::Rectangle,8> aRects = FillHandleRectsPixel();
181         for( sal_uInt16 i = 0; i < 8; i++ )
182             if( aRects[ i ].IsInside( rPos ) )
183                 return i;
184         // Move-Rect overlaps Handles
185         std::array<tools::Rectangle,4> aMoveRects = FillMoveRectsPixel();
186         for(const auto & rMoveRect : aMoveRects)
187             if( rMoveRect.IsInside( rPos ) )
188                 return 8;
189     }
190     else
191     {
192         tools::Rectangle aRect( GetTrackRectPixel( rPos ) );
193         aRect.SetSize( pWin->PixelToLogic( aRect.GetSize() ) );
194         aRect.SetPos( pWin->PixelToLogic( aRect.TopLeft() ) );
195         pWin->ShowTracking( aRect );
196     }
197     return nGrab;
198 }
199 
GetTrackPosPixel(const tools::Rectangle & rRect) const200 Point SvResizeHelper::GetTrackPosPixel( const tools::Rectangle & rRect ) const
201 {
202     // not important how the rectangle is returned, it is important
203     // which handle has been touched
204     Point aPos;
205     tools::Rectangle aRect( rRect );
206     aRect.Justify();
207     // only because of EMPTY_RECT
208     Point aBR = aOuter.BottomRight();
209     Point aTR = aOuter.TopRight();
210     Point aBL = aOuter.BottomLeft();
211     bool bRTL = AllSettings::GetLayoutRTL();
212     switch( nGrab )
213     {
214         case 0:
215             // FIXME: disable it for RTL because it's wrong calculations
216             if( bRTL )
217                 break;
218             aPos = aRect.TopLeft() - aOuter.TopLeft();
219             break;
220         case 1:
221             aPos.setY(  aRect.Top() - aOuter.Top() );
222             break;
223         case 2:
224             // FIXME: disable it for RTL because it's wrong calculations
225             if( bRTL )
226                 break;
227             aPos =  aRect.TopRight() - aTR;
228             break;
229         case 3:
230             if( bRTL )
231                 aPos.setX( aRect.Left() - aTR.X() );
232             else
233                 aPos.setX( aRect.Right() - aTR.X() );
234             break;
235         case 4:
236             // FIXME: disable it for RTL because it's wrong calculations
237             if( bRTL )
238                 break;
239             aPos =  aRect.BottomRight() - aBR;
240             break;
241         case 5:
242             aPos.setY( aRect.Bottom() - aBR.Y() );
243             break;
244         case 6:
245             // FIXME: disable it for RTL because it's wrong calculations
246             if( bRTL )
247                 break;
248             aPos =  aRect.BottomLeft() - aBL;
249             break;
250         case 7:
251             if( bRTL )
252                 aPos.setX( aRect.Right() + aOuter.Right() - aOuter.TopRight().X() );
253             else
254                 aPos.setX( aRect.Left() - aOuter.Left() );
255             break;
256         case 8:
257             aPos = aRect.TopLeft() - aOuter.TopLeft();
258             break;
259     }
260     return aPos += aSelPos;
261 }
262 
263 /*************************************************************************
264 |*    SvResizeHelper::GetTrackRectPixel()
265 |*
266 |*    Description
267 *************************************************************************/
GetTrackRectPixel(const Point & rTrackPos) const268 tools::Rectangle SvResizeHelper::GetTrackRectPixel( const Point & rTrackPos ) const
269 {
270     tools::Rectangle aTrackRect;
271     if( -1 != nGrab )
272     {
273         Point aDiff = rTrackPos - aSelPos;
274         aTrackRect = aOuter;
275         Point aBR = aOuter.BottomRight();
276         bool bRTL = AllSettings::GetLayoutRTL();
277         switch( nGrab )
278         {
279             case 0:
280                 aTrackRect.AdjustTop(aDiff.Y() );
281                 // ugly solution for resizing OLE objects in RTL
282                 if( bRTL )
283                     aTrackRect.SetRight( aBR.X() - aDiff.X() );
284                 else
285                     aTrackRect.AdjustLeft(aDiff.X() );
286                 break;
287             case 1:
288                 aTrackRect.AdjustTop(aDiff.Y() );
289                 break;
290             case 2:
291                 aTrackRect.AdjustTop(aDiff.Y() );
292                 // ugly solution for resizing OLE objects in RTL
293                 if( bRTL )
294                     aTrackRect.AdjustLeft( -(aDiff.X()) );
295                 else
296                     aTrackRect.SetRight( aBR.X() + aDiff.X() );
297                 break;
298             case 3:
299                 // ugly solution for resizing OLE objects in RTL
300                 if( bRTL )
301                     aTrackRect.AdjustLeft( -(aDiff.X()) );
302                 else
303                     aTrackRect.SetRight( aBR.X() + aDiff.X() );
304                 break;
305             case 4:
306                 aTrackRect.SetBottom( aBR.Y() + aDiff.Y() );
307                 // ugly solution for resizing OLE objects in RTL
308                 if( bRTL )
309                     aTrackRect.AdjustLeft( -(aDiff.X()) );
310                 else
311                     aTrackRect.SetRight( aBR.X() + aDiff.X() );
312                 break;
313             case 5:
314                 aTrackRect.SetBottom( aBR.Y() + aDiff.Y() );
315                 break;
316             case 6:
317                 aTrackRect.SetBottom( aBR.Y() + aDiff.Y() );
318                 // ugly solution for resizing OLE objects in RTL
319                 if( bRTL )
320                     aTrackRect.SetRight( aBR.X() - aDiff.X() );
321                 else
322                     aTrackRect.AdjustLeft(aDiff.X() );
323                 break;
324             case 7:
325                 // ugly solution for resizing OLE objects in RTL
326                 if( bRTL )
327                     aTrackRect.SetRight( aBR.X() - aDiff.X() );
328                 else
329                     aTrackRect.AdjustLeft(aDiff.X() );
330                 break;
331             case 8:
332                 if( bRTL )
333                     aDiff.setX( -aDiff.X() ); // workaround for move in RTL mode
334                 aTrackRect.SetPos( aTrackRect.TopLeft() + aDiff );
335                 break;
336         }
337     }
338     return aTrackRect;
339 }
340 
ValidateRect(tools::Rectangle & rValidate) const341 void SvResizeHelper::ValidateRect( tools::Rectangle & rValidate ) const
342 {
343     switch( nGrab )
344     {
345         case 0:
346             if( rValidate.Top() > rValidate.Bottom() )
347                 rValidate.SetTop( rValidate.Bottom() );
348             if( rValidate.Left() > rValidate.Right() )
349                 rValidate.SetLeft( rValidate.Right() );
350             break;
351         case 1:
352             if( rValidate.Top() > rValidate.Bottom() )
353                 rValidate.SetTop( rValidate.Bottom() );
354             break;
355         case 2:
356             if( rValidate.Top() > rValidate.Bottom() )
357                 rValidate.SetTop( rValidate.Bottom() );
358             if( rValidate.Left() > rValidate.Right() )
359                 rValidate.SetRight( rValidate.Left() );
360             break;
361         case 3:
362             if( rValidate.Left() > rValidate.Right() )
363                 rValidate.SetRight( rValidate.Left() );
364             break;
365         case 4:
366             if( rValidate.Top() > rValidate.Bottom() )
367                 rValidate.SetBottom( rValidate.Top() );
368             if( rValidate.Left() > rValidate.Right() )
369                 rValidate.SetRight( rValidate.Left() );
370             break;
371         case 5:
372             if( rValidate.Top() > rValidate.Bottom() )
373                 rValidate.SetBottom( rValidate.Top() );
374             break;
375         case 6:
376             if( rValidate.Top() > rValidate.Bottom() )
377                 rValidate.SetBottom( rValidate.Top() );
378             if( rValidate.Left() > rValidate.Right() )
379                 rValidate.SetLeft( rValidate.Right() );
380             break;
381         case 7:
382             if( rValidate.Left() > rValidate.Right() )
383                 rValidate.SetLeft( rValidate.Right() );
384             break;
385     }
386 
387     // Mindestgr"osse 5 x 5
388     if( rValidate.Left() + 5 > rValidate.Right() )
389         rValidate.SetRight( rValidate.Left() + 5 );
390     if( rValidate.Top() + 5 > rValidate.Bottom() )
391         rValidate.SetBottom( rValidate.Top() + 5 );
392 }
393 
394 /*************************************************************************
395 |*    SvResizeHelper::SelectRelease()
396 |*
397 |*    Description
398 *************************************************************************/
SelectRelease(vcl::Window * pWin,const Point & rPos,tools::Rectangle & rOutPosSize)399 bool SvResizeHelper::SelectRelease( vcl::Window * pWin, const Point & rPos,
400                                     tools::Rectangle & rOutPosSize )
401 {
402     if( -1 != nGrab )
403     {
404         rOutPosSize = GetTrackRectPixel( rPos );
405         rOutPosSize.Justify();
406         nGrab = -1;
407         pWin->ReleaseMouse();
408         pWin->HideTracking();
409         return true;
410     }
411     return false;
412 }
413 
414 /*************************************************************************
415 |*    SvResizeHelper::Release()
416 |*
417 |*    Description
418 *************************************************************************/
Release(vcl::Window * pWin)419 void SvResizeHelper::Release( vcl::Window * pWin )
420 {
421     if( nGrab != -1 )
422     {
423         pWin->ReleaseMouse();
424         pWin->HideTracking();
425         nGrab = -1;
426     }
427 }
428 
429 /*************************************************************************
430 |*    SvResizeWindow::SvResizeWindow()
431 |*
432 |*    Description
433 *************************************************************************/
SvResizeWindow(vcl::Window * pParent,VCLXHatchWindow * pWrapper)434 SvResizeWindow::SvResizeWindow
435 (
436     vcl::Window * pParent,
437     VCLXHatchWindow* pWrapper
438 )
439     : Window( pParent, WB_CLIPCHILDREN )
440     , m_aOldPointer(PointerStyle::Arrow)
441     , m_nMoveGrab( -1 )
442     , m_bActive( false )
443     , m_pWrapper( pWrapper )
444 {
445     OSL_ENSURE( pParent != nullptr && pWrapper != nullptr, "Wrong initialization of hatch window!" );
446     SetBackground();
447     SetAccessibleRole( css::accessibility::AccessibleRole::EMBEDDED_OBJECT );
448     m_aResizer.SetOuterRectPixel( tools::Rectangle( Point(), GetOutputSizePixel() ) );
449 }
450 
451 /*************************************************************************
452 |*    SvResizeWindow::SetHatchBorderPixel()
453 |*
454 |*    Description
455 *************************************************************************/
SetHatchBorderPixel(const Size & rSize)456 void SvResizeWindow::SetHatchBorderPixel( const Size & rSize )
457 {
458      m_aResizer.SetBorderPixel( rSize );
459 }
460 
461 /*************************************************************************
462 |*    SvResizeWindow::SelectMouse()
463 |*
464 |*    Description
465 *************************************************************************/
SelectMouse(const Point & rPos)466 void SvResizeWindow::SelectMouse( const Point & rPos )
467 {
468     short nGrab = m_aResizer.SelectMove( this, rPos );
469     if( nGrab >= 4 )
470         nGrab -= 4;
471     if( m_nMoveGrab == nGrab )
472         return;
473 
474     // Pointer did change
475     if( -1 == nGrab )
476         SetPointer( m_aOldPointer );
477     else
478     {
479         PointerStyle aStyle = PointerStyle::Move;
480         if( nGrab == 3 )
481             aStyle = PointerStyle::ESize;
482         else if( nGrab == 2 )
483             aStyle = PointerStyle::NESize;
484         else if( nGrab == 1 )
485             aStyle = PointerStyle::SSize;
486         else if( nGrab == 0 )
487             aStyle = PointerStyle::SESize;
488         if( m_nMoveGrab == -1 ) // the first time
489         {
490             m_aOldPointer = GetPointer();
491             SetPointer( aStyle );
492         }
493         else
494             SetPointer( aStyle );
495     }
496     m_nMoveGrab = nGrab;
497 }
498 
499 /*************************************************************************
500 |*    SvResizeWindow::MouseButtonDown()
501 |*
502 |*    Description
503 *************************************************************************/
MouseButtonDown(const MouseEvent & rEvt)504 void SvResizeWindow::MouseButtonDown( const MouseEvent & rEvt )
505 {
506     if( m_aResizer.SelectBegin( this, rEvt.GetPosPixel() ) )
507         SelectMouse( rEvt.GetPosPixel() );
508 }
509 
510 /*************************************************************************
511 |*    SvResizeWindow::MouseMove()
512 |*
513 |*    Description
514 *************************************************************************/
MouseMove(const MouseEvent & rEvt)515 void SvResizeWindow::MouseMove( const MouseEvent & rEvt )
516 {
517     if( m_aResizer.GetGrab() == -1 )
518         SelectMouse( rEvt.GetPosPixel() );
519     else
520     {
521         tools::Rectangle aRect( m_aResizer.GetTrackRectPixel( rEvt.GetPosPixel() ) );
522         Point aDiff = GetPosPixel();
523         aRect.SetPos( aRect.TopLeft() + aDiff );
524         m_aResizer.ValidateRect( aRect );
525 
526         m_pWrapper->QueryObjAreaPixel( aRect );
527         aRect.SetPos( aRect.TopLeft() - aDiff );
528         Point aPos = m_aResizer.GetTrackPosPixel( aRect );
529 
530         SelectMouse( aPos );
531     }
532 }
533 
534 /*************************************************************************
535 |*    SvResizeWindow::MouseButtonUp()
536 |*
537 |*    Description
538 *************************************************************************/
MouseButtonUp(const MouseEvent & rEvt)539 void SvResizeWindow::MouseButtonUp( const MouseEvent & rEvt )
540 {
541     if( m_aResizer.GetGrab() == -1 )
542         return;
543 
544     tools::Rectangle aRect( m_aResizer.GetTrackRectPixel( rEvt.GetPosPixel() ) );
545     Point aDiff = GetPosPixel();
546     aRect.SetPos( aRect.TopLeft() + aDiff );
547     // aRect -= GetAllBorderPixel();
548     m_aResizer.ValidateRect( aRect );
549 
550     m_pWrapper->QueryObjAreaPixel( aRect );
551 
552     tools::Rectangle aOutRect;
553     if( m_aResizer.SelectRelease( this, rEvt.GetPosPixel(), aOutRect ) )
554     {
555         m_nMoveGrab = -1;
556         SetPointer( m_aOldPointer );
557         m_pWrapper->RequestObjAreaPixel( aRect );
558     }
559 }
560 
561 /*************************************************************************
562 |*    SvResizeWindow::KeyEvent()
563 |*
564 |*    Description
565 *************************************************************************/
KeyInput(const KeyEvent & rEvt)566 void SvResizeWindow::KeyInput( const KeyEvent & rEvt )
567 {
568     if( rEvt.GetKeyCode().GetCode() == KEY_ESCAPE )
569     {
570         m_aResizer.Release( this );
571         m_pWrapper->InplaceDeactivate();
572     }
573 }
574 
575 /*************************************************************************
576 |*    SvResizeWindow::Resize()
577 |*
578 |*    Description
579 *************************************************************************/
Resize()580 void SvResizeWindow::Resize()
581 {
582     m_aResizer.InvalidateBorder( this ); // old area
583     m_aResizer.SetOuterRectPixel( tools::Rectangle( Point(), GetOutputSizePixel() ) );
584     m_aResizer.InvalidateBorder( this ); // new area
585 }
586 
587 /*************************************************************************
588 |*    SvResizeWindow::Paint()
589 |*
590 |*    Description
591 *************************************************************************/
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle &)592 void SvResizeWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle & /*rRect*/ )
593 {
594     m_aResizer.Draw(rRenderContext);
595 }
596 
PreNotify(NotifyEvent & rEvt)597 bool SvResizeWindow::PreNotify( NotifyEvent& rEvt )
598 {
599     if ( rEvt.GetType() == MouseNotifyEvent::GETFOCUS && !m_bActive )
600     {
601         m_bActive = true;
602         m_pWrapper->Activated();
603     }
604 
605     return Window::PreNotify(rEvt);
606 }
607 
EventNotify(NotifyEvent & rEvt)608 bool SvResizeWindow::EventNotify( NotifyEvent& rEvt )
609 {
610     if ( rEvt.GetType() == MouseNotifyEvent::LOSEFOCUS && m_bActive )
611     {
612         bool bHasFocus = HasChildPathFocus(true);
613         if ( !bHasFocus )
614         {
615             m_bActive = false;
616             m_pWrapper->Deactivated();
617         }
618     }
619 
620     return Window::EventNotify(rEvt);
621 }
622 
623 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
624