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 <svx/frmsel.hxx>
21 #include <vcl/event.hxx>
22 #include <sal/log.hxx>
23 #include <tools/debug.hxx>
24 
25 #include <algorithm>
26 #include <math.h>
27 #include <frmselimpl.hxx>
28 #include <AccessibleFrameSelector.hxx>
29 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
30 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
31 #include <vcl/settings.hxx>
32 #include <vcl/svapp.hxx>
33 #include <drawinglayer/processor2d/processor2dtools.hxx>
34 #include <drawinglayer/processor2d/baseprocessor2d.hxx>
35 
36 #include <bitmaps.hlst>
37 
38 using namespace ::com::sun::star;
39 using namespace ::editeng;
40 
41 namespace svx {
42 
43 using ::com::sun::star::uno::Reference;
44 using ::com::sun::star::uno::Any;
45 using ::com::sun::star::accessibility::XAccessible;
46 using namespace ::com::sun::star::accessibility;
47 
48 // global functions from framebordertype.hxx
49 
GetFrameBorderTypeFromIndex(size_t nIndex)50 FrameBorderType GetFrameBorderTypeFromIndex( size_t nIndex )
51 {
52     DBG_ASSERT( nIndex < size_t(FRAMEBORDERTYPE_COUNT),
53         "svx::GetFrameBorderTypeFromIndex - invalid index" );
54     return static_cast< FrameBorderType >( nIndex + 1 );
55 }
56 
GetIndexFromFrameBorderType(FrameBorderType eBorder)57 size_t GetIndexFromFrameBorderType( FrameBorderType eBorder )
58 {
59     DBG_ASSERT( eBorder != FrameBorderType::NONE,
60         "svx::GetIndexFromFrameBorderType - invalid frame border type" );
61     return static_cast< size_t >( eBorder ) - 1;
62 }
63 
64 namespace
65 {
66 
67 /** Space between outer control border and any graphical element of the control. */
68 const long FRAMESEL_GEOM_OUTER    = 2;
69 
70 /** Space between arrows and usable inner area. */
71 const long FRAMESEL_GEOM_INNER    = 3;
72 
73 /** Maximum width to draw a frame border style. */
74 const long FRAMESEL_GEOM_WIDTH    = 9;
75 
76 /** Additional margin for click area of outer lines. */
77 const long FRAMESEL_GEOM_ADD_CLICK_OUTER = 5;
78 
79 /** Additional margin for click area of inner lines. */
80 const long FRAMESEL_GEOM_ADD_CLICK_INNER = 2;
81 
82 
83 /** Returns the corresponding flag for a frame border. */
lclGetFlagFromType(FrameBorderType eBorder)84 FrameSelFlags lclGetFlagFromType( FrameBorderType eBorder )
85 {
86     switch( eBorder )
87     {
88         case FrameBorderType::Left:      return FrameSelFlags::Left;
89         case FrameBorderType::Right:     return FrameSelFlags::Right;
90         case FrameBorderType::Top:       return FrameSelFlags::Top;
91         case FrameBorderType::Bottom:    return FrameSelFlags::Bottom;
92         case FrameBorderType::Horizontal:       return FrameSelFlags::InnerHorizontal;
93         case FrameBorderType::Vertical:       return FrameSelFlags::InnerVertical;
94         case FrameBorderType::TLBR:      return FrameSelFlags::DiagonalTLBR;
95         case FrameBorderType::BLTR:      return FrameSelFlags::DiagonalBLTR;
96         case FrameBorderType::NONE : break;
97     }
98     return FrameSelFlags::NONE;
99 }
100 
101 /** Merges the rSource polypolygon into the rDest polypolygon. */
lclPolyPolyUnion(tools::PolyPolygon & rDest,const tools::PolyPolygon & rSource)102 void lclPolyPolyUnion( tools::PolyPolygon& rDest, const tools::PolyPolygon& rSource )
103 {
104     const tools::PolyPolygon aTmp( rDest );
105     aTmp.GetUnion( rSource, rDest );
106 }
107 
108 } // namespace
109 
FrameBorder(FrameBorderType eType)110 FrameBorder::FrameBorder( FrameBorderType eType ) :
111     meType( eType ),
112     meState( FrameBorderState::Hide ),
113     meKeyLeft( FrameBorderType::NONE ),
114     meKeyRight( FrameBorderType::NONE ),
115     meKeyTop( FrameBorderType::NONE ),
116     meKeyBottom( FrameBorderType::NONE ),
117     mbEnabled( false ),
118     mbSelected( false )
119 {
120 }
121 
Enable(FrameSelFlags nFlags)122 void FrameBorder::Enable( FrameSelFlags nFlags )
123 {
124     mbEnabled = bool(nFlags & lclGetFlagFromType( meType ));
125     if( !mbEnabled )
126         SetState( FrameBorderState::Hide );
127 }
128 
SetCoreStyle(const SvxBorderLine * pStyle)129 void FrameBorder::SetCoreStyle( const SvxBorderLine* pStyle )
130 {
131     if( pStyle )
132         maCoreStyle = *pStyle;
133     else
134         maCoreStyle = SvxBorderLine();
135 
136     // from twips to points
137     maUIStyle.Set( &maCoreStyle, FrameBorder::GetDefaultPatternScale(), FRAMESEL_GEOM_WIDTH );
138     meState = maUIStyle.IsUsed() ? FrameBorderState::Show : FrameBorderState::Hide;
139 }
140 
SetState(FrameBorderState eState)141 void FrameBorder::SetState( FrameBorderState eState )
142 {
143     meState = eState;
144     switch( meState )
145     {
146         case FrameBorderState::Show:
147             SAL_WARN( "svx.dialog", "svx::FrameBorder::SetState - use SetCoreStyle to make border visible" );
148         break;
149         case FrameBorderState::Hide:
150             maCoreStyle = SvxBorderLine();
151             maUIStyle.Clear();
152         break;
153         case FrameBorderState::DontCare:
154             maCoreStyle = SvxBorderLine();
155             maUIStyle = frame::Style(3, 0, 0, SvxBorderLineStyle::SOLID, FrameBorder::GetDefaultPatternScale()); //OBJ_FRAMESTYLE_DONTCARE
156         break;
157     }
158 }
159 
AddFocusPolygon(const tools::Polygon & rFocus)160 void FrameBorder::AddFocusPolygon( const tools::Polygon& rFocus )
161 {
162     lclPolyPolyUnion( maFocusArea, rFocus );
163 }
164 
MergeFocusToPolyPolygon(tools::PolyPolygon & rPPoly) const165 void FrameBorder::MergeFocusToPolyPolygon( tools::PolyPolygon& rPPoly ) const
166 {
167     lclPolyPolyUnion( rPPoly, maFocusArea );
168 }
169 
AddClickRect(const tools::Rectangle & rRect)170 void FrameBorder::AddClickRect( const tools::Rectangle& rRect )
171 {
172     lclPolyPolyUnion( maClickArea, tools::Polygon( rRect ) );
173 }
174 
ContainsClickPoint(const Point & rPos) const175 bool FrameBorder::ContainsClickPoint( const Point& rPos ) const
176 {
177     return vcl::Region( maClickArea ).IsInside( rPos );
178 }
179 
GetClickBoundRect() const180 tools::Rectangle FrameBorder::GetClickBoundRect() const
181 {
182     return maClickArea.GetBoundRect();
183 }
184 
SetKeyboardNeighbors(FrameBorderType eLeft,FrameBorderType eRight,FrameBorderType eTop,FrameBorderType eBottom)185 void FrameBorder::SetKeyboardNeighbors(
186         FrameBorderType eLeft, FrameBorderType eRight, FrameBorderType eTop, FrameBorderType eBottom )
187 {
188     meKeyLeft = eLeft;
189     meKeyRight = eRight;
190     meKeyTop = eTop;
191     meKeyBottom = eBottom;
192 }
193 
GetKeyboardNeighbor(sal_uInt16 nKeyCode) const194 FrameBorderType FrameBorder::GetKeyboardNeighbor( sal_uInt16 nKeyCode ) const
195 {
196     FrameBorderType eBorder = FrameBorderType::NONE;
197     switch( nKeyCode )
198     {
199         case KEY_LEFT:  eBorder = meKeyLeft;      break;
200         case KEY_RIGHT: eBorder = meKeyRight;     break;
201         case KEY_UP:    eBorder = meKeyTop;       break;
202         case KEY_DOWN:  eBorder = meKeyBottom;    break;
203         default:        SAL_WARN( "svx.dialog", "svx::FrameBorder::GetKeyboardNeighbor - unknown key code" );
204     }
205     return eBorder;
206 }
207 
FrameSelectorImpl(FrameSelector & rFrameSel)208 FrameSelectorImpl::FrameSelectorImpl( FrameSelector& rFrameSel ) :
209     mrFrameSel( rFrameSel ),
210     mpVirDev( VclPtr<VirtualDevice>::Create() ),
211     maLeft( FrameBorderType::Left ),
212     maRight( FrameBorderType::Right ),
213     maTop( FrameBorderType::Top ),
214     maBottom( FrameBorderType::Bottom ),
215     maHor( FrameBorderType::Horizontal ),
216     maVer( FrameBorderType::Vertical ),
217     maTLBR( FrameBorderType::TLBR ),
218     maBLTR( FrameBorderType::BLTR ),
219     mnFlags( FrameSelFlags::Outer ),
220     mnCtrlSize( 0 ),
221     mnArrowSize( 0 ),
222     mnLine1( 0 ),
223     mnLine2( 0 ),
224     mnLine3( 0 ),
225     mnFocusOffs( 0 ),
226     mbHor( false ),
227     mbVer( false ),
228     mbTLBR( false ),
229     mbBLTR( false ),
230     mbFullRepaint( true ),
231     mbAutoSelect( true ),
232     mbHCMode( false ),
233     maChildVec( 8 )
234 {
235     maAllBorders.resize( FRAMEBORDERTYPE_COUNT, nullptr );
236     maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Left   ) ] = &maLeft;
237     maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Right  ) ] = &maRight;
238     maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Top    ) ] = &maTop;
239     maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Bottom ) ] = &maBottom;
240     maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Horizontal    ) ] = &maHor;
241     maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Vertical    ) ] = &maVer;
242     maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::TLBR   ) ] = &maTLBR;
243     maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::BLTR   ) ] = &maBLTR;
244 #if OSL_DEBUG_LEVEL >= 2
245     {
246         bool bOk = true;
247         for( FrameBorderCIter aIt( maAllBorders ); bOk && aIt.Is(); bOk = (*aIt != 0), ++aIt );
248         DBG_ASSERT( bOk, "svx::FrameSelectorImpl::FrameSelectorImpl - missing entry in maAllBorders" );
249     }
250 #endif
251     //                             left neighbor     right neighbor     upper neighbor    lower neighbor
252     maLeft.SetKeyboardNeighbors(   FrameBorderType::NONE, FrameBorderType::TLBR,  FrameBorderType::Top,  FrameBorderType::Bottom );
253     maRight.SetKeyboardNeighbors(  FrameBorderType::BLTR, FrameBorderType::NONE,  FrameBorderType::Top,  FrameBorderType::Bottom );
254     maTop.SetKeyboardNeighbors(    FrameBorderType::Left, FrameBorderType::Right, FrameBorderType::NONE, FrameBorderType::TLBR );
255     maBottom.SetKeyboardNeighbors( FrameBorderType::Left, FrameBorderType::Right, FrameBorderType::BLTR, FrameBorderType::NONE );
256     maHor.SetKeyboardNeighbors(    FrameBorderType::Left, FrameBorderType::Right, FrameBorderType::TLBR, FrameBorderType::BLTR );
257     maVer.SetKeyboardNeighbors(    FrameBorderType::TLBR, FrameBorderType::BLTR,  FrameBorderType::Top,  FrameBorderType::Bottom );
258     maTLBR.SetKeyboardNeighbors(   FrameBorderType::Left, FrameBorderType::Vertical,   FrameBorderType::Top,  FrameBorderType::Horizontal );
259     maBLTR.SetKeyboardNeighbors(   FrameBorderType::Vertical,  FrameBorderType::Right, FrameBorderType::Horizontal,  FrameBorderType::Bottom );
260 
261     Initialize(mnFlags);
262 }
263 
~FrameSelectorImpl()264 FrameSelectorImpl::~FrameSelectorImpl()
265 
266 {
267     for( auto& rpChild : maChildVec )
268         if( rpChild.is() )
269             rpChild->Invalidate();
270 }
271 
272 // initialization
Initialize(FrameSelFlags nFlags)273 void FrameSelectorImpl::Initialize( FrameSelFlags nFlags )
274 {
275     mnFlags = nFlags;
276 
277     maEnabBorders.clear();
278     for( FrameBorderIter aIt( maAllBorders ); aIt.Is(); ++aIt )
279     {
280         (*aIt)->Enable( mnFlags );
281         if( (*aIt)->IsEnabled() )
282             maEnabBorders.push_back( *aIt );
283     }
284     mbHor = maHor.IsEnabled();
285     mbVer = maVer.IsEnabled();
286     mbTLBR = maTLBR.IsEnabled();
287     mbBLTR = maBLTR.IsEnabled();
288 
289     InitVirtualDevice();
290 }
291 
InitColors()292 void FrameSelectorImpl::InitColors()
293 {
294     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
295     maBackCol = rSettings.GetFieldColor();
296     mbHCMode = rSettings.GetHighContrastMode();
297     maArrowCol = rSettings.GetFieldTextColor();
298     maMarkCol.operator=(maBackCol).Merge(maArrowCol, mbHCMode ? 0x80 : 0xC0);
299     maHCLineCol = rSettings.GetLabelTextColor();
300 }
301 
302 static const OUStringLiteral aImageIds[] =
303 {
304     RID_SVXBMP_FRMSEL_ARROW1,
305     RID_SVXBMP_FRMSEL_ARROW2,
306     RID_SVXBMP_FRMSEL_ARROW3,
307     RID_SVXBMP_FRMSEL_ARROW4,
308     RID_SVXBMP_FRMSEL_ARROW5,
309     RID_SVXBMP_FRMSEL_ARROW6,
310     RID_SVXBMP_FRMSEL_ARROW7,
311     RID_SVXBMP_FRMSEL_ARROW8,
312     RID_SVXBMP_FRMSEL_ARROW9,
313     RID_SVXBMP_FRMSEL_ARROW10,
314     RID_SVXBMP_FRMSEL_ARROW11,
315     RID_SVXBMP_FRMSEL_ARROW12,
316     RID_SVXBMP_FRMSEL_ARROW13,
317     RID_SVXBMP_FRMSEL_ARROW14,
318     RID_SVXBMP_FRMSEL_ARROW15,
319     RID_SVXBMP_FRMSEL_ARROW16
320 };
321 
InitArrowImageList()322 void FrameSelectorImpl::InitArrowImageList()
323 {
324     maArrows.clear();
325 
326     /* Build the arrow images bitmap with current colors. */
327     Color pColorAry1[3];
328     Color pColorAry2[3];
329     pColorAry1[0] = Color( 0, 0, 0 );
330     pColorAry2[0] = maArrowCol;       // black -> arrow color
331     pColorAry1[1] = Color( 0, 255, 0 );
332     pColorAry2[1] = maMarkCol;        // green -> marker color
333     pColorAry1[2] = Color( 255, 0, 255 );
334     pColorAry2[2] = maBackCol;       // magenta -> background
335 
336     assert(SAL_N_ELEMENTS(aImageIds) == 16);
337     for (size_t i = 0; i < SAL_N_ELEMENTS(aImageIds); ++i)
338     {
339         BitmapEx aBmpEx(aImageIds[i]);
340         aBmpEx.Replace(pColorAry1, pColorAry2, 3);
341         maArrows.emplace_back(aBmpEx);
342     }
343     assert(maArrows.size() == 16);
344 
345     mnArrowSize = maArrows[0].GetSizePixel().Height();
346 }
347 
InitGlobalGeometry()348 void FrameSelectorImpl::InitGlobalGeometry()
349 {
350     Size aCtrlSize(mrFrameSel.GetOutputSizePixel());
351     /*  nMinSize is the lower of width and height (control will always be squarish).
352         FRAMESEL_GEOM_OUTER is the minimal distance between inner control border
353         and any element. */
354     long nMinSize = std::min( aCtrlSize.Width(), aCtrlSize.Height() ) - 2 * FRAMESEL_GEOM_OUTER;
355     /*  nFixedSize is the size all existing elements need in one direction:
356         the diag. arrow, space betw. arrow and frame border, outer frame border,
357         inner frame border, other outer frame border, space betw. frame border
358         and arrow, the other arrow. */
359     long nFixedSize = 2 * mnArrowSize + 2 * FRAMESEL_GEOM_INNER + 3 * FRAMESEL_GEOM_WIDTH;
360     /*  nBetwBordersSize contains the size between an outer and inner frame border (made odd). */
361     long nBetwBordersSize = (((nMinSize - nFixedSize) / 2) - 1) | 1;
362 
363     /*  The final size of the usable area. At least do not get negative */
364     mnCtrlSize = 2 * nBetwBordersSize + nFixedSize;
365     mnCtrlSize = std::max(mnCtrlSize, static_cast<long>(0));
366     mpVirDev->SetOutputSizePixel( Size( mnCtrlSize, mnCtrlSize ) );
367 
368     /*  Center the virtual device in the control. */
369     maVirDevPos = Point( (aCtrlSize.Width() - mnCtrlSize) / 2, (aCtrlSize.Height() - mnCtrlSize) / 2 );
370 }
371 
InitBorderGeometry()372 void FrameSelectorImpl::InitBorderGeometry()
373 {
374     size_t nCol, nCols, nRow, nRows;
375 
376     // Global border geometry values
377     /*  mnLine* is the middle point inside a frame border (i.e. mnLine1 is mid X inside left border). */
378     mnLine1 = mnArrowSize + FRAMESEL_GEOM_INNER + FRAMESEL_GEOM_WIDTH / 2;
379     mnLine2 = mnCtrlSize / 2;
380     mnLine3 = 2 * mnLine2 - mnLine1;
381 
382     // Frame helper array
383     maArray.Initialize( mbVer ? 2 : 1, mbHor ? 2 : 1 );
384 
385     maArray.SetXOffset( mnLine1 );
386     maArray.SetAllColWidths( (mbVer ? mnLine2 : mnLine3) - mnLine1 );
387 
388     maArray.SetYOffset( mnLine1 );
389     maArray.SetAllRowHeights( (mbHor ? mnLine2 : mnLine3) - mnLine1 );
390 
391     // Focus polygons
392     /*  Width for focus rectangles from center of frame borders. */
393     mnFocusOffs = FRAMESEL_GEOM_WIDTH / 2 + 1;
394 
395     maLeft.ClearFocusArea();
396     maVer.ClearFocusArea();
397     maRight.ClearFocusArea();
398     maTop.ClearFocusArea();
399     maHor.ClearFocusArea();
400     maBottom.ClearFocusArea();
401 
402     maLeft.AddFocusPolygon(   tools::Rectangle( mnLine1 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine1 + mnFocusOffs, mnLine3 + mnFocusOffs ) );
403     maVer.AddFocusPolygon(    tools::Rectangle( mnLine2 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine2 + mnFocusOffs, mnLine3 + mnFocusOffs ) );
404     maRight.AddFocusPolygon(  tools::Rectangle( mnLine3 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine3 + mnFocusOffs ) );
405     maTop.AddFocusPolygon(    tools::Rectangle( mnLine1 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine1 + mnFocusOffs ) );
406     maHor.AddFocusPolygon(    tools::Rectangle( mnLine1 - mnFocusOffs, mnLine2 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine2 + mnFocusOffs ) );
407     maBottom.AddFocusPolygon( tools::Rectangle( mnLine1 - mnFocusOffs, mnLine3 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine3 + mnFocusOffs ) );
408 
409     for( nCol = 0, nCols = maArray.GetColCount(); nCol < nCols; ++nCol )
410     {
411         for( nRow = 0, nRows = maArray.GetRowCount(); nRow < nRows; ++nRow )
412         {
413             const basegfx::B2DRange aCellRange(maArray.GetCellRange( nCol, nRow, true ));
414             const tools::Rectangle aRect(
415                 basegfx::fround(aCellRange.getMinX()), basegfx::fround(aCellRange.getMinY()),
416                 basegfx::fround(aCellRange.getMaxX()), basegfx::fround(aCellRange.getMaxY()));
417             const double fHorDiagAngle(atan2(fabs(aCellRange.getHeight()), fabs(aCellRange.getWidth())));
418             const double fVerDiagAngle(fHorDiagAngle > 0.0 ? F_PI2 - fHorDiagAngle : 0.0);
419             const long nDiagFocusOffsX(basegfx::fround(-mnFocusOffs / tan(fHorDiagAngle) + mnFocusOffs / sin(fHorDiagAngle)));
420             const long nDiagFocusOffsY(basegfx::fround(-mnFocusOffs / tan(fVerDiagAngle) + mnFocusOffs / sin(fVerDiagAngle)));
421 
422             std::vector< Point > aFocusVec;
423             aFocusVec.emplace_back( aRect.Left()  - mnFocusOffs,     aRect.Top()    + nDiagFocusOffsY );
424             aFocusVec.emplace_back( aRect.Left()  - mnFocusOffs,     aRect.Top()    - mnFocusOffs     );
425             aFocusVec.emplace_back( aRect.Left()  + nDiagFocusOffsX, aRect.Top()    - mnFocusOffs     );
426             aFocusVec.emplace_back( aRect.Right() + mnFocusOffs,     aRect.Bottom() - nDiagFocusOffsY );
427             aFocusVec.emplace_back( aRect.Right() + mnFocusOffs,     aRect.Bottom() + mnFocusOffs     );
428             aFocusVec.emplace_back( aRect.Right() - nDiagFocusOffsX, aRect.Bottom() + mnFocusOffs     );
429             maTLBR.AddFocusPolygon( tools::Polygon( static_cast< sal_uInt16 >( aFocusVec.size() ), aFocusVec.data() ) );
430 
431             aFocusVec.clear();
432             aFocusVec.emplace_back( aRect.Right() + mnFocusOffs,     aRect.Top()    + nDiagFocusOffsY );
433             aFocusVec.emplace_back( aRect.Right() + mnFocusOffs,     aRect.Top()    - mnFocusOffs     );
434             aFocusVec.emplace_back( aRect.Right() - nDiagFocusOffsX, aRect.Top()    - mnFocusOffs     );
435             aFocusVec.emplace_back( aRect.Left()  - mnFocusOffs,     aRect.Bottom() - nDiagFocusOffsY );
436             aFocusVec.emplace_back( aRect.Left()  - mnFocusOffs,     aRect.Bottom() + mnFocusOffs     );
437             aFocusVec.emplace_back( aRect.Left()  + nDiagFocusOffsX, aRect.Bottom() + mnFocusOffs     );
438             maBLTR.AddFocusPolygon( tools::Polygon( static_cast< sal_uInt16 >( aFocusVec.size() ), aFocusVec.data() ) );
439         }
440     }
441 
442     // Click areas
443     for( FrameBorderIter aIt( maAllBorders ); aIt.Is(); ++aIt )
444         (*aIt)->ClearClickArea();
445 
446     /*  Additional space for click area: is added to the space available to draw
447         the frame borders. For instance left frame border:
448         - To left, top, and bottom always big additional space (outer area).
449         - To right: Dependent on existence of inner vertical frame border
450             (if enabled, use less space).
451      */
452     long nClO = FRAMESEL_GEOM_WIDTH / 2 + FRAMESEL_GEOM_ADD_CLICK_OUTER;
453     long nClI = (mbTLBR && mbBLTR) ? (FRAMESEL_GEOM_WIDTH / 2 + FRAMESEL_GEOM_ADD_CLICK_INNER) : nClO;
454     long nClH = mbHor ? nClI : nClO;            // additional space dependent of horizontal inner border
455     long nClV = mbVer ? nClI : nClO;            // additional space dependent of vertical inner border
456 
457     maLeft.AddClickRect(   tools::Rectangle( mnLine1 - nClO, mnLine1 - nClO, mnLine1 + nClV, mnLine3 + nClO ) );
458     maVer.AddClickRect(    tools::Rectangle( mnLine2 - nClI, mnLine1 - nClO, mnLine2 + nClI, mnLine3 + nClO ) );
459     maRight.AddClickRect(  tools::Rectangle( mnLine3 - nClV, mnLine1 - nClO, mnLine3 + nClO, mnLine3 + nClO ) );
460     maTop.AddClickRect(    tools::Rectangle( mnLine1 - nClO, mnLine1 - nClO, mnLine3 + nClO, mnLine1 + nClH ) );
461     maHor.AddClickRect(    tools::Rectangle( mnLine1 - nClO, mnLine2 - nClI, mnLine3 + nClO, mnLine2 + nClI ) );
462     maBottom.AddClickRect( tools::Rectangle( mnLine1 - nClO, mnLine3 - nClH, mnLine3 + nClO, mnLine3 + nClO ) );
463 
464     /*  Diagonal frame borders use the remaining space between outer and inner frame borders. */
465     if( mbTLBR || mbBLTR )
466     {
467         for( nCol = 0, nCols = maArray.GetColCount(); nCol < nCols; ++nCol )
468         {
469             for( nRow = 0, nRows = maArray.GetRowCount(); nRow < nRows; ++nRow )
470             {
471                 // the usable area between horizontal/vertical frame borders of current quadrant
472                 const basegfx::B2DRange aCellRange(maArray.GetCellRange( nCol, nRow, true ));
473                 const tools::Rectangle aRect(
474                     basegfx::fround(aCellRange.getMinX()) + nClV + 1, basegfx::fround(aCellRange.getMinY()) + nClH + 1,
475                     basegfx::fround(aCellRange.getMaxX()) - nClV + 1, basegfx::fround(aCellRange.getMaxY()) - nClH + 1);
476 
477                 /*  Both diagonal frame borders enabled. */
478                 if( mbTLBR && mbBLTR )
479                 {
480                     // single areas
481                     Point aMid( aRect.Center() );
482                     maTLBR.AddClickRect( tools::Rectangle( aRect.TopLeft(), aMid ) );
483                     maTLBR.AddClickRect( tools::Rectangle( aMid + Point( 1, 1 ), aRect.BottomRight() ) );
484                     maBLTR.AddClickRect( tools::Rectangle( aRect.Left(), aMid.Y() + 1, aMid.X(), aRect.Bottom() ) );
485                     maBLTR.AddClickRect( tools::Rectangle( aMid.X() + 1, aRect.Top(), aRect.Right(), aMid.Y() ) );
486                     // centered rectangle for both frame borders
487                     tools::Rectangle aMidRect( aRect.TopLeft(), Size( aRect.GetWidth() / 3, aRect.GetHeight() / 3 ) );
488                     aMidRect.Move( (aRect.GetWidth() - aMidRect.GetWidth()) / 2, (aRect.GetHeight() - aMidRect.GetHeight()) / 2 );
489                     maTLBR.AddClickRect( aMidRect );
490                     maBLTR.AddClickRect( aMidRect );
491                 }
492                 /*  One of the diagonal frame borders enabled - use entire rectangle. */
493                 else if( mbTLBR && !mbBLTR )    // top-left to bottom-right only
494                     maTLBR.AddClickRect( aRect );
495                 else if( !mbTLBR && mbBLTR )    // bottom-left to top-right only
496                     maBLTR.AddClickRect( aRect );
497             }
498         }
499     }
500 }
501 
InitVirtualDevice()502 void FrameSelectorImpl::InitVirtualDevice()
503 {
504     // initialize resources
505     InitColors();
506     InitArrowImageList();
507 
508     sizeChanged();
509 }
510 
sizeChanged()511 void FrameSelectorImpl::sizeChanged()
512 {
513     // initialize geometry
514     InitGlobalGeometry();
515     InitBorderGeometry();
516 
517     DoInvalidate( true );
518 }
519 
520 // frame border access
GetBorder(FrameBorderType eBorder) const521 const FrameBorder& FrameSelectorImpl::GetBorder( FrameBorderType eBorder ) const
522 {
523     size_t nIndex = GetIndexFromFrameBorderType( eBorder );
524     if( nIndex < maAllBorders.size() )
525         return *maAllBorders[ nIndex ];
526     SAL_WARN( "svx.dialog", "svx::FrameSelectorImpl::GetBorder - unknown border type" );
527     return maTop;
528 }
529 
GetBorderAccess(FrameBorderType eBorder)530 FrameBorder& FrameSelectorImpl::GetBorderAccess( FrameBorderType eBorder )
531 {
532     return const_cast< FrameBorder& >( GetBorder( eBorder ) );
533 }
534 
535 // drawing
DrawBackground()536 void FrameSelectorImpl::DrawBackground()
537 {
538     // clear the area
539     mpVirDev->SetLineColor();
540     mpVirDev->SetFillColor( maBackCol );
541     mpVirDev->DrawRect( tools::Rectangle( Point( 0, 0 ), mpVirDev->GetOutputSizePixel() ) );
542 
543     // draw the inner gray (or whatever color) rectangle
544     mpVirDev->SetLineColor();
545     mpVirDev->SetFillColor( maMarkCol );
546     mpVirDev->DrawRect( tools::Rectangle(
547         mnLine1 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine3 + mnFocusOffs ) );
548 
549     // draw the white space for enabled frame borders
550     tools::PolyPolygon aPPoly;
551     for( FrameBorderCIter aIt( maEnabBorders ); aIt.Is(); ++aIt )
552         (*aIt)->MergeFocusToPolyPolygon( aPPoly );
553     aPPoly.Optimize( PolyOptimizeFlags::CLOSE );
554     mpVirDev->SetLineColor( maBackCol );
555     mpVirDev->SetFillColor( maBackCol );
556     mpVirDev->DrawPolyPolygon( aPPoly );
557 }
558 
DrawArrows(const FrameBorder & rBorder)559 void FrameSelectorImpl::DrawArrows( const FrameBorder& rBorder )
560 {
561     DBG_ASSERT( rBorder.IsEnabled(), "svx::FrameSelectorImpl::DrawArrows - access to disabled border" );
562 
563     long nLinePos = 0;
564     switch( rBorder.GetType() )
565     {
566         case FrameBorderType::Left:
567         case FrameBorderType::Top:       nLinePos = mnLine1; break;
568         case FrameBorderType::Vertical:
569         case FrameBorderType::Horizontal:       nLinePos = mnLine2; break;
570         case FrameBorderType::Right:
571         case FrameBorderType::Bottom:    nLinePos = mnLine3; break;
572         default: ; //prevent warning
573     }
574     nLinePos -= mnArrowSize / 2;
575 
576     long nTLPos = 0;
577     long nBRPos = mnCtrlSize - mnArrowSize;
578     Point aPos1, aPos2;
579     int nImgIndex1 = -1, nImgIndex2 = -1;
580     switch( rBorder.GetType() )
581     {
582         case FrameBorderType::Left:
583         case FrameBorderType::Right:
584         case FrameBorderType::Vertical:
585             aPos1 = Point( nLinePos, nTLPos ); nImgIndex1 = 0;
586             aPos2 = Point( nLinePos, nBRPos ); nImgIndex2 = 1;
587         break;
588 
589         case FrameBorderType::Top:
590         case FrameBorderType::Bottom:
591         case FrameBorderType::Horizontal:
592             aPos1 = Point( nTLPos, nLinePos ); nImgIndex1 = 2;
593             aPos2 = Point( nBRPos, nLinePos ); nImgIndex2 = 3;
594         break;
595 
596         case FrameBorderType::TLBR:
597             aPos1 = Point( nTLPos, nTLPos ); nImgIndex1 = 4;
598             aPos2 = Point( nBRPos, nBRPos ); nImgIndex2 = 5;
599         break;
600         case FrameBorderType::BLTR:
601             aPos1 = Point( nTLPos, nBRPos ); nImgIndex1 = 6;
602             aPos2 = Point( nBRPos, nTLPos ); nImgIndex2 = 7;
603         break;
604         default: ; //prevent warning
605     }
606 
607     // Arrow or marker? Do not draw arrows into disabled control.
608     sal_uInt16 nSelectAdd = (mrFrameSel.IsEnabled() && rBorder.IsSelected()) ? 0 : 8;
609     if (nImgIndex1 >= 0)
610         mpVirDev->DrawImage(aPos1, maArrows[nImgIndex1 + nSelectAdd]);
611     if (nImgIndex2 >= 0)
612         mpVirDev->DrawImage(aPos2, maArrows[nImgIndex2 + nSelectAdd]);
613 }
614 
GetDrawLineColor(const Color & rColor) const615 Color FrameSelectorImpl::GetDrawLineColor( const Color& rColor ) const
616 {
617     Color aColor( mbHCMode ? maHCLineCol : rColor );
618     if( aColor == maBackCol )
619         aColor.Invert();
620     return aColor;
621 }
622 
DrawAllFrameBorders()623 void FrameSelectorImpl::DrawAllFrameBorders()
624 {
625     // Translate core colors to current UI colors (regards current background and HC mode).
626     for( FrameBorderIter aIt( maEnabBorders ); aIt.Is(); ++aIt )
627     {
628         Color aCoreColorPrim = ((*aIt)->GetState() == FrameBorderState::DontCare) ? maMarkCol : (*aIt)->GetCoreStyle().GetColorOut();
629         Color aCoreColorSecn = ((*aIt)->GetState() == FrameBorderState::DontCare) ? maMarkCol : (*aIt)->GetCoreStyle().GetColorIn();
630         (*aIt)->SetUIColorPrim( GetDrawLineColor( aCoreColorPrim ) );
631         (*aIt)->SetUIColorSecn( GetDrawLineColor( aCoreColorSecn ) );
632     }
633 
634     // Copy all frame border styles to the helper array
635     maArray.SetColumnStyleLeft( 0, maLeft.GetUIStyle() );
636     if( mbVer ) maArray.SetColumnStyleLeft( 1, maVer.GetUIStyle() );
637 
638     // Invert the style for the right line
639     const frame::Style rRightStyle = maRight.GetUIStyle( );
640     frame::Style rInvertedRight( rRightStyle.GetColorPrim(),
641             rRightStyle.GetColorSecn(), rRightStyle.GetColorGap(),
642             rRightStyle.UseGapColor(),
643             rRightStyle.Secn(), rRightStyle.Dist(), rRightStyle.Prim( ),
644             rRightStyle.Type( ), rRightStyle.PatternScale() );
645     maArray.SetColumnStyleRight( mbVer ? 1 : 0, rInvertedRight );
646 
647     maArray.SetRowStyleTop( 0, maTop.GetUIStyle() );
648     if( mbHor )
649     {
650         // Invert the style for the hor line to match the real borders
651         const frame::Style rHorStyle = maHor.GetUIStyle();
652         frame::Style rInvertedHor( rHorStyle.GetColorPrim(),
653             rHorStyle.GetColorSecn(), rHorStyle.GetColorGap(),
654             rHorStyle.UseGapColor(),
655             rHorStyle.Secn(), rHorStyle.Dist(), rHorStyle.Prim( ),
656             rHorStyle.Type(), rHorStyle.PatternScale() );
657         maArray.SetRowStyleTop( 1, rInvertedHor );
658     }
659 
660     // Invert the style for the bottom line
661     const frame::Style rBottomStyle = maBottom.GetUIStyle( );
662     frame::Style rInvertedBottom( rBottomStyle.GetColorPrim(),
663             rBottomStyle.GetColorSecn(), rBottomStyle.GetColorGap(),
664             rBottomStyle.UseGapColor(),
665             rBottomStyle.Secn(), rBottomStyle.Dist(), rBottomStyle.Prim( ),
666             rBottomStyle.Type(), rBottomStyle.PatternScale() );
667     maArray.SetRowStyleBottom( mbHor ? 1 : 0, rInvertedBottom );
668 
669     for( size_t nCol = 0; nCol < maArray.GetColCount(); ++nCol )
670         for( size_t nRow = 0; nRow < maArray.GetRowCount(); ++nRow )
671             maArray.SetCellStyleDiag( nCol, nRow, maTLBR.GetUIStyle(), maBLTR.GetUIStyle() );
672 
673     // This is used in the dialog/control for 'Border' attributes. When using
674     // the original paint below instead of primitives, the advantage currently
675     // is the correct visualization of diagonal line(s) including overlaying,
676     // but the rest is bad. Since the edit views use primitives and the preview
677     // should be 'real' I opt for also changing this to primitives. I will
678     // keep the old solution and add a switch (above) based on a static bool so
679     // that interested people may test this out in the debugger.
680     // This is one more hint to enhance the primitive visualization further to
681     // support diagonals better - that's the way to go.
682     const drawinglayer::geometry::ViewInformation2D aNewViewInformation2D;
683     std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D(
684         drawinglayer::processor2d::createPixelProcessor2DFromOutputDevice(
685             *mpVirDev,
686             aNewViewInformation2D));
687 
688     if (pProcessor2D)
689     {
690         pProcessor2D->process(maArray.CreateB2DPrimitiveArray());
691         pProcessor2D.reset();
692     }
693 }
694 
DrawVirtualDevice()695 void FrameSelectorImpl::DrawVirtualDevice()
696 {
697     DrawBackground();
698     for(FrameBorderCIter aIt(maEnabBorders); aIt.Is(); ++aIt)
699         DrawArrows(**aIt);
700     DrawAllFrameBorders();
701     mbFullRepaint = false;
702 }
703 
CopyVirDevToControl(vcl::RenderContext & rRenderContext)704 void FrameSelectorImpl::CopyVirDevToControl(vcl::RenderContext& rRenderContext)
705 {
706     if (mbFullRepaint)
707         DrawVirtualDevice();
708     rRenderContext.DrawBitmapEx(maVirDevPos, mpVirDev->GetBitmapEx(Point(0, 0), mpVirDev->GetOutputSizePixel()));
709 }
710 
DrawAllTrackingRects(vcl::RenderContext & rRenderContext)711 void FrameSelectorImpl::DrawAllTrackingRects(vcl::RenderContext& rRenderContext)
712 {
713     tools::PolyPolygon aPPoly;
714     if (mrFrameSel.IsAnyBorderSelected())
715     {
716         for(SelFrameBorderCIter aIt( maEnabBorders ); aIt.Is(); ++aIt)
717             (*aIt)->MergeFocusToPolyPolygon(aPPoly);
718         aPPoly.Move(maVirDevPos.X(), maVirDevPos.Y());
719     }
720     else
721         // no frame border selected -> draw tracking rectangle around entire control
722         aPPoly.Insert( tools::Polygon(tools::Rectangle(maVirDevPos, mpVirDev->GetOutputSizePixel())));
723 
724     aPPoly.Optimize(PolyOptimizeFlags::CLOSE);
725 
726     for(sal_uInt16 nIdx = 0, nCount = aPPoly.Count(); nIdx < nCount; ++nIdx)
727         rRenderContext.Invert(aPPoly.GetObject(nIdx), InvertFlags::TrackFrame);
728 }
729 
GetDevPosFromMousePos(const Point & rMousePos) const730 Point FrameSelectorImpl::GetDevPosFromMousePos( const Point& rMousePos ) const
731 {
732     return rMousePos - maVirDevPos;
733 }
734 
DoInvalidate(bool bFullRepaint)735 void FrameSelectorImpl::DoInvalidate( bool bFullRepaint )
736 {
737     mbFullRepaint |= bFullRepaint;
738     mrFrameSel.Invalidate();
739 }
740 
741 // frame border state and style
SetBorderState(FrameBorder & rBorder,FrameBorderState eState)742 void FrameSelectorImpl::SetBorderState( FrameBorder& rBorder, FrameBorderState eState )
743 {
744     DBG_ASSERT( rBorder.IsEnabled(), "svx::FrameSelectorImpl::SetBorderState - access to disabled border" );
745     Any aOld;
746     Any aNew;
747     Any& rMod = eState == FrameBorderState::Show ? aNew : aOld;
748     rMod <<= AccessibleStateType::CHECKED;
749     rtl::Reference< a11y::AccFrameSelectorChild > xRet;
750     size_t nVecIdx = static_cast< size_t >( rBorder.GetType() );
751     if( GetBorder(rBorder.GetType()).IsEnabled() && (1 <= nVecIdx) && (nVecIdx <= maChildVec.size()) )
752         xRet = maChildVec[ --nVecIdx ].get();
753 
754     if( eState == FrameBorderState::Show )
755         SetBorderCoreStyle( rBorder, &maCurrStyle );
756     else
757         rBorder.SetState( eState );
758     if (xRet.is())
759         xRet->NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOld, aNew );
760     DoInvalidate( true );
761 }
762 
SetBorderCoreStyle(FrameBorder & rBorder,const SvxBorderLine * pStyle)763 void FrameSelectorImpl::SetBorderCoreStyle( FrameBorder& rBorder, const SvxBorderLine* pStyle )
764 {
765     DBG_ASSERT( rBorder.IsEnabled(), "svx::FrameSelectorImpl::SetBorderCoreStyle - access to disabled border" );
766     rBorder.SetCoreStyle( pStyle );
767     DoInvalidate( true );
768 }
769 
ToggleBorderState(FrameBorder & rBorder)770 void FrameSelectorImpl::ToggleBorderState( FrameBorder& rBorder )
771 {
772     bool bDontCare = mrFrameSel.SupportsDontCareState();
773     switch( rBorder.GetState() )
774     {
775         // same order as tristate check box: visible -> don't care -> hidden
776         case FrameBorderState::Show:
777             SetBorderState( rBorder, bDontCare ? FrameBorderState::DontCare : FrameBorderState::Hide );
778         break;
779         case FrameBorderState::Hide:
780             SetBorderState( rBorder, FrameBorderState::Show );
781         break;
782         case FrameBorderState::DontCare:
783             SetBorderState( rBorder, FrameBorderState::Hide );
784         break;
785     }
786 }
787 
788 // frame border selection
SelectBorder(FrameBorder & rBorder,bool bSelect)789 void FrameSelectorImpl::SelectBorder( FrameBorder& rBorder, bool bSelect )
790 {
791     DBG_ASSERT( rBorder.IsEnabled(), "svx::FrameSelectorImpl::SelectBorder - access to disabled border" );
792     rBorder.Select( bSelect );
793     DrawArrows( rBorder );
794     DoInvalidate( false );
795 }
796 
SilentGrabFocus()797 void FrameSelectorImpl::SilentGrabFocus()
798 {
799     bool bOldAuto = mbAutoSelect;
800     mbAutoSelect = false;
801     mrFrameSel.GrabFocus();
802     mbAutoSelect = bOldAuto;
803 }
804 
SelectedBordersEqual() const805 bool FrameSelectorImpl::SelectedBordersEqual() const
806 {
807     bool bEqual = true;
808     SelFrameBorderCIter aIt( maEnabBorders );
809     if( aIt.Is() )
810     {
811         const SvxBorderLine& rFirstStyle = (*aIt)->GetCoreStyle();
812         for( ++aIt; bEqual && aIt.Is(); ++aIt )
813             bEqual = ((*aIt)->GetCoreStyle() == rFirstStyle);
814     }
815     return bEqual;
816 }
817 
FrameSelector()818 FrameSelector::FrameSelector()
819 {
820 }
821 
SetDrawingArea(weld::DrawingArea * pDrawingArea)822 void FrameSelector::SetDrawingArea(weld::DrawingArea* pDrawingArea)
823 {
824     CustomWidgetController::SetDrawingArea(pDrawingArea);
825     Size aPrefSize = pDrawingArea->get_ref_device().LogicToPixel(Size(61, 65), MapMode(MapUnit::MapAppFont));
826     pDrawingArea->set_size_request(aPrefSize.Width(), aPrefSize.Height());
827     mxImpl.reset( new FrameSelectorImpl( *this ) );
828     EnableRTL( false ); // #107808# don't mirror the mouse handling
829 }
830 
~FrameSelector()831 FrameSelector::~FrameSelector()
832 {
833     if( mxAccess.is() )
834         mxAccess->Invalidate();
835 }
836 
Initialize(FrameSelFlags nFlags)837 void FrameSelector::Initialize( FrameSelFlags nFlags )
838 {
839     mxImpl->Initialize( nFlags );
840     Show();
841 }
842 
843 // enabled frame borders
IsBorderEnabled(FrameBorderType eBorder) const844 bool FrameSelector::IsBorderEnabled( FrameBorderType eBorder ) const
845 {
846     return mxImpl->GetBorder( eBorder ).IsEnabled();
847 }
848 
GetEnabledBorderCount() const849 sal_Int32 FrameSelector::GetEnabledBorderCount() const
850 {
851     return static_cast< sal_Int32 >( mxImpl->maEnabBorders.size() );
852 }
853 
GetEnabledBorderType(sal_Int32 nIndex) const854 FrameBorderType FrameSelector::GetEnabledBorderType( sal_Int32 nIndex ) const
855 {
856     FrameBorderType eBorder = FrameBorderType::NONE;
857     if( nIndex >= 0 )
858     {
859         size_t nVecIdx = static_cast< size_t >( nIndex );
860         if( nVecIdx < mxImpl->maEnabBorders.size() )
861             eBorder = mxImpl->maEnabBorders[ nVecIdx ]->GetType();
862     }
863     return eBorder;
864 }
865 
866 // frame border state and style
SupportsDontCareState() const867 bool FrameSelector::SupportsDontCareState() const
868 {
869     return bool(mxImpl->mnFlags & FrameSelFlags::DontCare);
870 }
871 
GetFrameBorderState(FrameBorderType eBorder) const872 FrameBorderState FrameSelector::GetFrameBorderState( FrameBorderType eBorder ) const
873 {
874     return mxImpl->GetBorder( eBorder ).GetState();
875 }
876 
GetFrameBorderStyle(FrameBorderType eBorder) const877 const SvxBorderLine* FrameSelector::GetFrameBorderStyle( FrameBorderType eBorder ) const
878 {
879     const SvxBorderLine& rStyle = mxImpl->GetBorder( eBorder ).GetCoreStyle();
880     // rest of the world uses null pointer for invisible frame border
881     return rStyle.GetOutWidth() ? &rStyle : nullptr;
882 }
883 
ShowBorder(FrameBorderType eBorder,const SvxBorderLine * pStyle)884 void FrameSelector::ShowBorder( FrameBorderType eBorder, const SvxBorderLine* pStyle )
885 {
886     mxImpl->SetBorderCoreStyle( mxImpl->GetBorderAccess( eBorder ), pStyle );
887 }
888 
SetBorderDontCare(FrameBorderType eBorder)889 void FrameSelector::SetBorderDontCare( FrameBorderType eBorder )
890 {
891     mxImpl->SetBorderState( mxImpl->GetBorderAccess( eBorder ), FrameBorderState::DontCare );
892 }
893 
IsAnyBorderVisible() const894 bool FrameSelector::IsAnyBorderVisible() const
895 {
896     bool bIsSet = false;
897     for( FrameBorderCIter aIt( mxImpl->maEnabBorders ); !bIsSet && aIt.Is(); ++aIt )
898         bIsSet = ((*aIt)->GetState() == FrameBorderState::Show);
899     return bIsSet;
900 }
901 
HideAllBorders()902 void FrameSelector::HideAllBorders()
903 {
904     for( FrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
905         mxImpl->SetBorderState( **aIt, FrameBorderState::Hide );
906 }
907 
GetVisibleWidth(long & rnWidth,SvxBorderLineStyle & rnStyle) const908 bool FrameSelector::GetVisibleWidth( long& rnWidth, SvxBorderLineStyle& rnStyle ) const
909 {
910     VisFrameBorderCIter aIt( mxImpl->maEnabBorders );
911     if( !aIt.Is() )
912         return false;
913 
914     const SvxBorderLine& rStyle = (*aIt)->GetCoreStyle();
915     bool bFound = true;
916     for( ++aIt; bFound && aIt.Is(); ++aIt )
917     {
918         bFound =
919             (rStyle.GetWidth() == (*aIt)->GetCoreStyle().GetWidth()) &&
920             (rStyle.GetBorderLineStyle() ==
921                 (*aIt)->GetCoreStyle().GetBorderLineStyle());
922     }
923 
924     if( bFound )
925     {
926         rnWidth = rStyle.GetWidth();
927         rnStyle = rStyle.GetBorderLineStyle();
928     }
929     return bFound;
930 }
931 
GetVisibleColor(Color & rColor) const932 bool FrameSelector::GetVisibleColor( Color& rColor ) const
933 {
934     VisFrameBorderCIter aIt( mxImpl->maEnabBorders );
935     if( !aIt.Is() )
936         return false;
937 
938     const SvxBorderLine& rStyle = (*aIt)->GetCoreStyle();
939     bool bFound = true;
940     for( ++aIt; bFound && aIt.Is(); ++aIt )
941         bFound = (rStyle.GetColor() == (*aIt)->GetCoreStyle().GetColor());
942 
943     if( bFound )
944         rColor = rStyle.GetColor();
945     return bFound;
946 }
947 
948 // frame border selection
GetSelectHdl() const949 const Link<LinkParamNone*,void>& FrameSelector::GetSelectHdl() const
950 {
951     return mxImpl->maSelectHdl;
952 }
953 
SetSelectHdl(const Link<LinkParamNone *,void> & rHdl)954 void FrameSelector::SetSelectHdl( const Link<LinkParamNone*,void>& rHdl )
955 {
956     mxImpl->maSelectHdl = rHdl;
957 }
958 
IsBorderSelected(FrameBorderType eBorder) const959 bool FrameSelector::IsBorderSelected( FrameBorderType eBorder ) const
960 {
961     return mxImpl->GetBorder( eBorder ).IsSelected();
962 }
963 
SelectBorder(FrameBorderType eBorder)964 void FrameSelector::SelectBorder( FrameBorderType eBorder )
965 {
966     mxImpl->SelectBorder( mxImpl->GetBorderAccess( eBorder ), true/*bSelect*/ );
967     // MT: bFireFox as API parameter is ugly...
968     // if (bFocus)
969     {
970         rtl::Reference< a11y::AccFrameSelectorChild > xRet = GetChildAccessible(eBorder);
971         if (xRet.is())
972         {
973             Any aOldValue, aNewValue;
974             aNewValue <<= AccessibleStateType::FOCUSED;
975             xRet->NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue );
976         }
977     }
978 }
979 
IsAnyBorderSelected() const980 bool FrameSelector::IsAnyBorderSelected() const
981 {
982     // Construct an iterator for selected borders. If it is valid, there is a selected border.
983     return SelFrameBorderCIter( mxImpl->maEnabBorders ).Is();
984 }
985 
SelectAllBorders(bool bSelect)986 void FrameSelector::SelectAllBorders( bool bSelect )
987 {
988     for( FrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
989         mxImpl->SelectBorder( **aIt, bSelect );
990 }
991 
SelectAllVisibleBorders()992 void FrameSelector::SelectAllVisibleBorders()
993 {
994     for( VisFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
995         mxImpl->SelectBorder( **aIt, true/*bSelect*/ );
996 }
997 
SetStyleToSelection(long nWidth,SvxBorderLineStyle nStyle)998 void FrameSelector::SetStyleToSelection( long nWidth, SvxBorderLineStyle nStyle )
999 {
1000     mxImpl->maCurrStyle.SetBorderLineStyle( nStyle );
1001     mxImpl->maCurrStyle.SetWidth( nWidth );
1002     for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
1003         mxImpl->SetBorderState( **aIt, FrameBorderState::Show );
1004 }
1005 
SetColorToSelection(const Color & rColor)1006 void FrameSelector::SetColorToSelection( const Color& rColor )
1007 {
1008     mxImpl->maCurrStyle.SetColor( rColor );
1009     for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
1010         mxImpl->SetBorderState( **aIt, FrameBorderState::Show );
1011 }
1012 
1013 // accessibility
CreateAccessible()1014 Reference< XAccessible > FrameSelector::CreateAccessible()
1015 {
1016     if( !mxAccess.is() )
1017         mxAccess = new a11y::AccFrameSelector(*this);
1018     return mxAccess.get();
1019 }
1020 
GetChildAccessible(FrameBorderType eBorder)1021 rtl::Reference< a11y::AccFrameSelectorChild > FrameSelector::GetChildAccessible( FrameBorderType eBorder )
1022 {
1023     rtl::Reference< a11y::AccFrameSelectorChild > xRet;
1024     size_t nVecIdx = static_cast< size_t >( eBorder );
1025     if( IsBorderEnabled( eBorder ) && (1 <= nVecIdx) && (nVecIdx <= mxImpl->maChildVec.size()) )
1026     {
1027         --nVecIdx;
1028         if( !mxImpl->maChildVec[ nVecIdx ].is() )
1029             mxImpl->maChildVec[ nVecIdx ] = new a11y::AccFrameSelectorChild( *this, eBorder );
1030         xRet = mxImpl->maChildVec[ nVecIdx ].get();
1031     }
1032     return xRet;
1033 }
1034 
GetChildAccessible(sal_Int32 nIndex)1035 Reference< XAccessible > FrameSelector::GetChildAccessible( sal_Int32 nIndex )
1036 {
1037     return GetChildAccessible( GetEnabledBorderType( nIndex ) ).get();
1038 }
1039 
GetChildAccessible(const Point & rPos)1040 Reference< XAccessible > FrameSelector::GetChildAccessible( const Point& rPos )
1041 {
1042     Reference< XAccessible > xRet;
1043     for( FrameBorderCIter aIt( mxImpl->maEnabBorders ); !xRet.is() && aIt.Is(); ++aIt )
1044         if( (*aIt)->ContainsClickPoint( rPos ) )
1045             xRet = GetChildAccessible( (*aIt)->GetType() ).get();
1046     return xRet;
1047 }
1048 
GetClickBoundRect(FrameBorderType eBorder) const1049 tools::Rectangle FrameSelector::GetClickBoundRect( FrameBorderType eBorder ) const
1050 {
1051     tools::Rectangle aRect;
1052     const FrameBorder& rBorder = mxImpl->GetBorder( eBorder );
1053     if( rBorder.IsEnabled() )
1054         aRect = rBorder.GetClickBoundRect();
1055     return aRect;
1056 }
1057 
1058 // virtual functions from base class
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle &)1059 void FrameSelector::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
1060 {
1061     mxImpl->CopyVirDevToControl(rRenderContext);
1062     if (HasFocus())
1063         mxImpl->DrawAllTrackingRects(rRenderContext);
1064 }
1065 
MouseButtonDown(const MouseEvent & rMEvt)1066 bool FrameSelector::MouseButtonDown( const MouseEvent& rMEvt )
1067 {
1068     /*  Mouse handling:
1069         * Click on an unselected frame border:
1070             Set current style/color, make frame border visible, deselect all
1071             other frame borders.
1072         * Click on a selected frame border:
1073             Toggle state of the frame border (visible -> don't care -> hidden),
1074             deselect all other frame borders.
1075         * SHIFT+Click or CTRL+Click on an unselected frame border:
1076             Extend selection, set current style/color to all selected frame
1077             borders independent of the state/style/color of the borders.
1078         * SHIFT+Click or CTRL+Click on a selected frame border:
1079             If all frame borders have same style/color, toggle state of all
1080             borders (see above), otherwise set current style/color to all
1081             borders.
1082         * Click on unused area: Do not modify selection and selected frame
1083             borders.
1084      */
1085 
1086     // #107394# do not auto-select a frame border
1087     mxImpl->SilentGrabFocus();
1088 
1089     if( rMEvt.IsLeft() )
1090     {
1091         Point aPos( mxImpl->GetDevPosFromMousePos( rMEvt.GetPosPixel() ) );
1092         FrameBorderPtrVec aDeselectBorders;
1093 
1094         bool bAnyClicked = false;   // Any frame border clicked?
1095         bool bNewSelected = false;  // Any unselected frame border selected?
1096 
1097         /*  If frame borders are set to "don't care" and the control does not
1098             support this state, hide them on first mouse click.
1099             DR 2004-01-30: Why are the borders set to "don't care" then?!? */
1100         bool bHideDontCare = !SupportsDontCareState();
1101 
1102         for( FrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
1103         {
1104             if( (*aIt)->ContainsClickPoint( aPos ) )
1105             {
1106                 // frame border is clicked
1107                 bAnyClicked = true;
1108                 if( !(*aIt)->IsSelected() )
1109                 {
1110                     bNewSelected = true;
1111                     //mxImpl->SelectBorder( **aIt, true );
1112                     SelectBorder((**aIt).GetType());
1113                 }
1114             }
1115             else
1116             {
1117                 // hide a "don't care" frame border only if it is not clicked
1118                 if( bHideDontCare && ((*aIt)->GetState() == FrameBorderState::DontCare) )
1119                     mxImpl->SetBorderState( **aIt, FrameBorderState::Hide );
1120 
1121                 // deselect frame borders not clicked (if SHIFT or CTRL are not pressed)
1122                 if( !rMEvt.IsShift() && !rMEvt.IsMod1() )
1123                     aDeselectBorders.push_back( *aIt );
1124             }
1125         }
1126 
1127         if( bAnyClicked )
1128         {
1129             // any valid frame border clicked? -> deselect other frame borders
1130             for( FrameBorderIter aIt( aDeselectBorders ); aIt.Is(); ++aIt )
1131                 mxImpl->SelectBorder( **aIt, false );
1132 
1133             if( bNewSelected || !mxImpl->SelectedBordersEqual() )
1134             {
1135                 // new frame border selected, selection extended, or selected borders different? -> show
1136                 for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
1137                     // SetBorderState() sets current style and color to the frame border
1138                     mxImpl->SetBorderState( **aIt, FrameBorderState::Show );
1139             }
1140             else
1141             {
1142                 // all selected frame borders are equal -> toggle state
1143                 for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
1144                     mxImpl->ToggleBorderState( **aIt );
1145             }
1146 
1147             GetSelectHdl().Call( nullptr );
1148         }
1149     }
1150 
1151     return true;
1152 }
1153 
KeyInput(const KeyEvent & rKEvt)1154 bool FrameSelector::KeyInput( const KeyEvent& rKEvt )
1155 {
1156     bool bHandled = false;
1157     vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1158     if( !aKeyCode.GetModifier() )
1159     {
1160         sal_uInt16 nCode = aKeyCode.GetCode();
1161         switch( nCode )
1162         {
1163             case KEY_SPACE:
1164             {
1165                 for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
1166                     mxImpl->ToggleBorderState( **aIt );
1167                 bHandled = true;
1168             }
1169             break;
1170 
1171             case KEY_UP:
1172             case KEY_DOWN:
1173             case KEY_LEFT:
1174             case KEY_RIGHT:
1175             {
1176                 if( !mxImpl->maEnabBorders.empty() )
1177                 {
1178                     // start from first selected frame border
1179                     SelFrameBorderCIter aIt( mxImpl->maEnabBorders );
1180                     FrameBorderType eBorder = aIt.Is() ? (*aIt)->GetType() : mxImpl->maEnabBorders.front()->GetType();
1181 
1182                     // search for next enabled frame border
1183                     do
1184                     {
1185                         eBorder = mxImpl->GetBorder( eBorder ).GetKeyboardNeighbor( nCode );
1186                     }
1187                     while( (eBorder != FrameBorderType::NONE) && !IsBorderEnabled( eBorder ) );
1188 
1189                     // select the frame border
1190                     if( eBorder != FrameBorderType::NONE )
1191                     {
1192                         DeselectAllBorders();
1193                         SelectBorder( eBorder );
1194                     }
1195                     bHandled = true;
1196                 }
1197             }
1198             break;
1199         }
1200     }
1201     if (bHandled)
1202         return true;
1203     return CustomWidgetController::KeyInput(rKEvt);
1204 }
1205 
GetFocus()1206 void FrameSelector::GetFocus()
1207 {
1208     // auto-selection of a frame border, if focus reaches control, and nothing is selected
1209     if( mxImpl->mbAutoSelect && !IsAnyBorderSelected() && !mxImpl->maEnabBorders.empty() )
1210         mxImpl->SelectBorder( *mxImpl->maEnabBorders.front(), true );
1211 
1212     mxImpl->DoInvalidate( false );
1213     if (IsAnyBorderSelected())
1214     {
1215         FrameBorderType borderType = FrameBorderType::NONE;
1216         if (mxImpl->maLeft.IsSelected())
1217             borderType = FrameBorderType::Left;
1218         else if (mxImpl->maRight.IsSelected())
1219             borderType = FrameBorderType::Right;
1220         else if (mxImpl->maTop.IsSelected())
1221             borderType = FrameBorderType::Top;
1222         else if (mxImpl->maBottom.IsSelected())
1223             borderType = FrameBorderType::Bottom;
1224         else if (mxImpl->maHor.IsSelected())
1225             borderType = FrameBorderType::Horizontal;
1226         else if (mxImpl->maVer.IsSelected())
1227             borderType = FrameBorderType::Vertical;
1228         else if (mxImpl->maTLBR.IsSelected())
1229             borderType = FrameBorderType::TLBR;
1230         else if (mxImpl->maBLTR.IsSelected())
1231             borderType = FrameBorderType::BLTR;
1232         SelectBorder(borderType);
1233     }
1234     for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
1235             mxImpl->SetBorderState( **aIt, FrameBorderState::Show );
1236     CustomWidgetController::GetFocus();
1237 }
1238 
LoseFocus()1239 void FrameSelector::LoseFocus()
1240 {
1241     mxImpl->DoInvalidate( false );
1242     CustomWidgetController::LoseFocus();
1243 }
1244 
StyleUpdated()1245 void FrameSelector::StyleUpdated()
1246 {
1247     mxImpl->InitVirtualDevice();
1248     CustomWidgetController::StyleUpdated();
1249 }
1250 
Resize()1251 void FrameSelector::Resize()
1252 {
1253     CustomWidgetController::Resize();
1254     mxImpl->sizeChanged();
1255 }
1256 
1257 template< typename Cont, typename Iter, typename Pred >
FrameBorderIterBase(container_type & rCont)1258 FrameBorderIterBase< Cont, Iter, Pred >::FrameBorderIterBase( container_type& rCont ) :
1259     maIt( rCont.begin() ),
1260     maEnd( rCont.end() )
1261 {
1262     while( Is() && !maPred( *maIt ) ) ++maIt;
1263 }
1264 
1265 template< typename Cont, typename Iter, typename Pred >
operator ++()1266 FrameBorderIterBase< Cont, Iter, Pred >& FrameBorderIterBase< Cont, Iter, Pred >::operator++()
1267 {
1268     do { ++maIt; } while( Is() && !maPred( *maIt ) );
1269     return *this;
1270 }
1271 
1272 }
1273 
1274 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1275