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