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