summaryrefslogtreecommitdiffstats
path: root/svx/source/dialog/frmsel.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /svx/source/dialog/frmsel.cxx
parentInitial commit. (diff)
downloadlibreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz
libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--svx/source/dialog/frmsel.cxx1277
1 files changed, 1277 insertions, 0 deletions
diff --git a/svx/source/dialog/frmsel.cxx b/svx/source/dialog/frmsel.cxx
new file mode 100644
index 000000000..b4d80e2f7
--- /dev/null
+++ b/svx/source/dialog/frmsel.cxx
@@ -0,0 +1,1277 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/safeint.hxx>
+#include <svx/frmsel.hxx>
+#include <vcl/event.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+
+#include <algorithm>
+#include <math.h>
+#include <frmselimpl.hxx>
+#include <AccessibleFrameSelector.hxx>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+
+#include <bitmaps.hlst>
+
+using namespace ::com::sun::star;
+using namespace ::editeng;
+
+namespace svx {
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::accessibility::XAccessible;
+using namespace ::com::sun::star::accessibility;
+
+// global functions from framebordertype.hxx
+
+FrameBorderType GetFrameBorderTypeFromIndex( size_t nIndex )
+{
+ DBG_ASSERT( nIndex < o3tl::make_unsigned(FRAMEBORDERTYPE_COUNT),
+ "svx::GetFrameBorderTypeFromIndex - invalid index" );
+ return static_cast< FrameBorderType >( nIndex + 1 );
+}
+
+size_t GetIndexFromFrameBorderType( FrameBorderType eBorder )
+{
+ DBG_ASSERT( eBorder != FrameBorderType::NONE,
+ "svx::GetIndexFromFrameBorderType - invalid frame border type" );
+ return static_cast< size_t >( eBorder ) - 1;
+}
+
+namespace
+{
+
+/** Space between outer control border and any graphical element of the control. */
+const long FRAMESEL_GEOM_OUTER = 2;
+
+/** Space between arrows and usable inner area. */
+const long FRAMESEL_GEOM_INNER = 3;
+
+/** Maximum width to draw a frame border style. */
+const long FRAMESEL_GEOM_WIDTH = 9;
+
+/** Additional margin for click area of outer lines. */
+const long FRAMESEL_GEOM_ADD_CLICK_OUTER = 5;
+
+/** Additional margin for click area of inner lines. */
+const long FRAMESEL_GEOM_ADD_CLICK_INNER = 2;
+
+
+/** Returns the corresponding flag for a frame border. */
+FrameSelFlags lclGetFlagFromType( FrameBorderType eBorder )
+{
+ switch( eBorder )
+ {
+ case FrameBorderType::Left: return FrameSelFlags::Left;
+ case FrameBorderType::Right: return FrameSelFlags::Right;
+ case FrameBorderType::Top: return FrameSelFlags::Top;
+ case FrameBorderType::Bottom: return FrameSelFlags::Bottom;
+ case FrameBorderType::Horizontal: return FrameSelFlags::InnerHorizontal;
+ case FrameBorderType::Vertical: return FrameSelFlags::InnerVertical;
+ case FrameBorderType::TLBR: return FrameSelFlags::DiagonalTLBR;
+ case FrameBorderType::BLTR: return FrameSelFlags::DiagonalBLTR;
+ case FrameBorderType::NONE : break;
+ }
+ return FrameSelFlags::NONE;
+}
+
+/** Merges the rSource polypolygon into the rDest polypolygon. */
+void lclPolyPolyUnion( tools::PolyPolygon& rDest, const tools::PolyPolygon& rSource )
+{
+ const tools::PolyPolygon aTmp( rDest );
+ aTmp.GetUnion( rSource, rDest );
+}
+
+} // namespace
+
+FrameBorder::FrameBorder( FrameBorderType eType ) :
+ meType( eType ),
+ meState( FrameBorderState::Hide ),
+ meKeyLeft( FrameBorderType::NONE ),
+ meKeyRight( FrameBorderType::NONE ),
+ meKeyTop( FrameBorderType::NONE ),
+ meKeyBottom( FrameBorderType::NONE ),
+ mbEnabled( false ),
+ mbSelected( false )
+{
+}
+
+void FrameBorder::Enable( FrameSelFlags nFlags )
+{
+ mbEnabled = bool(nFlags & lclGetFlagFromType( meType ));
+ if( !mbEnabled )
+ SetState( FrameBorderState::Hide );
+}
+
+void FrameBorder::SetCoreStyle( const SvxBorderLine* pStyle )
+{
+ if( pStyle )
+ maCoreStyle = *pStyle;
+ else
+ maCoreStyle = SvxBorderLine();
+
+ // from twips to points
+ maUIStyle.Set( &maCoreStyle, FrameBorder::GetDefaultPatternScale(), FRAMESEL_GEOM_WIDTH );
+ meState = maUIStyle.IsUsed() ? FrameBorderState::Show : FrameBorderState::Hide;
+}
+
+void FrameBorder::SetState( FrameBorderState eState )
+{
+ meState = eState;
+ switch( meState )
+ {
+ case FrameBorderState::Show:
+ SAL_WARN( "svx.dialog", "svx::FrameBorder::SetState - use SetCoreStyle to make border visible" );
+ break;
+ case FrameBorderState::Hide:
+ maCoreStyle = SvxBorderLine();
+ maUIStyle.Clear();
+ break;
+ case FrameBorderState::DontCare:
+ maCoreStyle = SvxBorderLine();
+ maUIStyle = frame::Style(3, 0, 0, SvxBorderLineStyle::SOLID, FrameBorder::GetDefaultPatternScale()); //OBJ_FRAMESTYLE_DONTCARE
+ break;
+ }
+}
+
+void FrameBorder::AddFocusPolygon( const tools::Polygon& rFocus )
+{
+ lclPolyPolyUnion( maFocusArea, rFocus );
+}
+
+void FrameBorder::MergeFocusToPolyPolygon( tools::PolyPolygon& rPPoly ) const
+{
+ lclPolyPolyUnion( rPPoly, maFocusArea );
+}
+
+void FrameBorder::AddClickRect( const tools::Rectangle& rRect )
+{
+ lclPolyPolyUnion( maClickArea, tools::Polygon( rRect ) );
+}
+
+bool FrameBorder::ContainsClickPoint( const Point& rPos ) const
+{
+ return vcl::Region( maClickArea ).IsInside( rPos );
+}
+
+tools::Rectangle FrameBorder::GetClickBoundRect() const
+{
+ return maClickArea.GetBoundRect();
+}
+
+void FrameBorder::SetKeyboardNeighbors(
+ FrameBorderType eLeft, FrameBorderType eRight, FrameBorderType eTop, FrameBorderType eBottom )
+{
+ meKeyLeft = eLeft;
+ meKeyRight = eRight;
+ meKeyTop = eTop;
+ meKeyBottom = eBottom;
+}
+
+FrameBorderType FrameBorder::GetKeyboardNeighbor( sal_uInt16 nKeyCode ) const
+{
+ FrameBorderType eBorder = FrameBorderType::NONE;
+ switch( nKeyCode )
+ {
+ case KEY_LEFT: eBorder = meKeyLeft; break;
+ case KEY_RIGHT: eBorder = meKeyRight; break;
+ case KEY_UP: eBorder = meKeyTop; break;
+ case KEY_DOWN: eBorder = meKeyBottom; break;
+ default: SAL_WARN( "svx.dialog", "svx::FrameBorder::GetKeyboardNeighbor - unknown key code" );
+ }
+ return eBorder;
+}
+
+FrameSelectorImpl::FrameSelectorImpl( FrameSelector& rFrameSel ) :
+ mrFrameSel( rFrameSel ),
+ mpVirDev( VclPtr<VirtualDevice>::Create() ),
+ maLeft( FrameBorderType::Left ),
+ maRight( FrameBorderType::Right ),
+ maTop( FrameBorderType::Top ),
+ maBottom( FrameBorderType::Bottom ),
+ maHor( FrameBorderType::Horizontal ),
+ maVer( FrameBorderType::Vertical ),
+ maTLBR( FrameBorderType::TLBR ),
+ maBLTR( FrameBorderType::BLTR ),
+ mnFlags( FrameSelFlags::Outer ),
+ mnCtrlSize( 0 ),
+ mnArrowSize( 0 ),
+ mnLine1( 0 ),
+ mnLine2( 0 ),
+ mnLine3( 0 ),
+ mnFocusOffs( 0 ),
+ mbHor( false ),
+ mbVer( false ),
+ mbTLBR( false ),
+ mbBLTR( false ),
+ mbFullRepaint( true ),
+ mbAutoSelect( true ),
+ mbHCMode( false ),
+ maChildVec( 8 )
+{
+ maAllBorders.resize( FRAMEBORDERTYPE_COUNT, nullptr );
+ maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Left ) ] = &maLeft;
+ maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Right ) ] = &maRight;
+ maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Top ) ] = &maTop;
+ maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Bottom ) ] = &maBottom;
+ maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Horizontal ) ] = &maHor;
+ maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Vertical ) ] = &maVer;
+ maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::TLBR ) ] = &maTLBR;
+ maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::BLTR ) ] = &maBLTR;
+#if OSL_DEBUG_LEVEL >= 2
+ {
+ bool bOk = true;
+ for( FrameBorderCIter aIt( maAllBorders ); bOk && aIt.Is(); bOk = (*aIt != 0), ++aIt );
+ DBG_ASSERT( bOk, "svx::FrameSelectorImpl::FrameSelectorImpl - missing entry in maAllBorders" );
+ }
+#endif
+ // left neighbor right neighbor upper neighbor lower neighbor
+ maLeft.SetKeyboardNeighbors( FrameBorderType::NONE, FrameBorderType::TLBR, FrameBorderType::Top, FrameBorderType::Bottom );
+ maRight.SetKeyboardNeighbors( FrameBorderType::BLTR, FrameBorderType::NONE, FrameBorderType::Top, FrameBorderType::Bottom );
+ maTop.SetKeyboardNeighbors( FrameBorderType::Left, FrameBorderType::Right, FrameBorderType::NONE, FrameBorderType::TLBR );
+ maBottom.SetKeyboardNeighbors( FrameBorderType::Left, FrameBorderType::Right, FrameBorderType::BLTR, FrameBorderType::NONE );
+ maHor.SetKeyboardNeighbors( FrameBorderType::Left, FrameBorderType::Right, FrameBorderType::TLBR, FrameBorderType::BLTR );
+ maVer.SetKeyboardNeighbors( FrameBorderType::TLBR, FrameBorderType::BLTR, FrameBorderType::Top, FrameBorderType::Bottom );
+ maTLBR.SetKeyboardNeighbors( FrameBorderType::Left, FrameBorderType::Vertical, FrameBorderType::Top, FrameBorderType::Horizontal );
+ maBLTR.SetKeyboardNeighbors( FrameBorderType::Vertical, FrameBorderType::Right, FrameBorderType::Horizontal, FrameBorderType::Bottom );
+
+ Initialize(mnFlags);
+}
+
+FrameSelectorImpl::~FrameSelectorImpl()
+
+{
+ for( auto& rpChild : maChildVec )
+ if( rpChild.is() )
+ rpChild->Invalidate();
+}
+
+// initialization
+void FrameSelectorImpl::Initialize( FrameSelFlags nFlags )
+{
+ mnFlags = nFlags;
+
+ maEnabBorders.clear();
+ for( FrameBorderIter aIt( maAllBorders ); aIt.Is(); ++aIt )
+ {
+ (*aIt)->Enable( mnFlags );
+ if( (*aIt)->IsEnabled() )
+ maEnabBorders.push_back( *aIt );
+ }
+ mbHor = maHor.IsEnabled();
+ mbVer = maVer.IsEnabled();
+ mbTLBR = maTLBR.IsEnabled();
+ mbBLTR = maBLTR.IsEnabled();
+
+ InitVirtualDevice();
+}
+
+void FrameSelectorImpl::InitColors()
+{
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ maBackCol = rSettings.GetFieldColor();
+ mbHCMode = rSettings.GetHighContrastMode();
+ maArrowCol = rSettings.GetFieldTextColor();
+ maMarkCol.operator=(maBackCol).Merge(maArrowCol, mbHCMode ? 0x80 : 0xC0);
+ maHCLineCol = rSettings.GetLabelTextColor();
+}
+
+static const OUStringLiteral aImageIds[] =
+{
+ RID_SVXBMP_FRMSEL_ARROW1,
+ RID_SVXBMP_FRMSEL_ARROW2,
+ RID_SVXBMP_FRMSEL_ARROW3,
+ RID_SVXBMP_FRMSEL_ARROW4,
+ RID_SVXBMP_FRMSEL_ARROW5,
+ RID_SVXBMP_FRMSEL_ARROW6,
+ RID_SVXBMP_FRMSEL_ARROW7,
+ RID_SVXBMP_FRMSEL_ARROW8,
+ RID_SVXBMP_FRMSEL_ARROW9,
+ RID_SVXBMP_FRMSEL_ARROW10,
+ RID_SVXBMP_FRMSEL_ARROW11,
+ RID_SVXBMP_FRMSEL_ARROW12,
+ RID_SVXBMP_FRMSEL_ARROW13,
+ RID_SVXBMP_FRMSEL_ARROW14,
+ RID_SVXBMP_FRMSEL_ARROW15,
+ RID_SVXBMP_FRMSEL_ARROW16
+};
+
+void FrameSelectorImpl::InitArrowImageList()
+{
+ maArrows.clear();
+
+ /* Build the arrow images bitmap with current colors. */
+ Color pColorAry1[3];
+ Color pColorAry2[3];
+ pColorAry1[0] = Color( 0, 0, 0 );
+ pColorAry2[0] = maArrowCol; // black -> arrow color
+ pColorAry1[1] = Color( 0, 255, 0 );
+ pColorAry2[1] = maMarkCol; // green -> marker color
+ pColorAry1[2] = Color( 255, 0, 255 );
+ pColorAry2[2] = maBackCol; // magenta -> background
+
+ assert(SAL_N_ELEMENTS(aImageIds) == 16);
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aImageIds); ++i)
+ {
+ BitmapEx aBmpEx(aImageIds[i]);
+ aBmpEx.Replace(pColorAry1, pColorAry2, 3);
+ maArrows.emplace_back(aBmpEx);
+ }
+ assert(maArrows.size() == 16);
+
+ mnArrowSize = maArrows[0].GetSizePixel().Height();
+}
+
+void FrameSelectorImpl::InitGlobalGeometry()
+{
+ Size aCtrlSize(mrFrameSel.GetOutputSizePixel());
+ /* nMinSize is the lower of width and height (control will always be squarish).
+ FRAMESEL_GEOM_OUTER is the minimal distance between inner control border
+ and any element. */
+ long nMinSize = std::min( aCtrlSize.Width(), aCtrlSize.Height() ) - 2 * FRAMESEL_GEOM_OUTER;
+ /* nFixedSize is the size all existing elements need in one direction:
+ the diag. arrow, space betw. arrow and frame border, outer frame border,
+ inner frame border, other outer frame border, space betw. frame border
+ and arrow, the other arrow. */
+ long nFixedSize = 2 * mnArrowSize + 2 * FRAMESEL_GEOM_INNER + 3 * FRAMESEL_GEOM_WIDTH;
+ /* nBetwBordersSize contains the size between an outer and inner frame border (made odd). */
+ long nBetwBordersSize = (((nMinSize - nFixedSize) / 2) - 1) | 1;
+
+ /* The final size of the usable area. At least do not get negative */
+ mnCtrlSize = 2 * nBetwBordersSize + nFixedSize;
+ mnCtrlSize = std::max(mnCtrlSize, static_cast<long>(0));
+ mpVirDev->SetOutputSizePixel( Size( mnCtrlSize, mnCtrlSize ) );
+
+ /* Center the virtual device in the control. */
+ maVirDevPos = Point( (aCtrlSize.Width() - mnCtrlSize) / 2, (aCtrlSize.Height() - mnCtrlSize) / 2 );
+}
+
+void FrameSelectorImpl::InitBorderGeometry()
+{
+ size_t nCol, nCols, nRow, nRows;
+
+ // Global border geometry values
+ /* mnLine* is the middle point inside a frame border (i.e. mnLine1 is mid X inside left border). */
+ mnLine1 = mnArrowSize + FRAMESEL_GEOM_INNER + FRAMESEL_GEOM_WIDTH / 2;
+ mnLine2 = mnCtrlSize / 2;
+ mnLine3 = 2 * mnLine2 - mnLine1;
+
+ // Frame helper array
+ maArray.Initialize( mbVer ? 2 : 1, mbHor ? 2 : 1 );
+
+ maArray.SetXOffset( mnLine1 );
+ maArray.SetAllColWidths( (mbVer ? mnLine2 : mnLine3) - mnLine1 );
+
+ maArray.SetYOffset( mnLine1 );
+ maArray.SetAllRowHeights( (mbHor ? mnLine2 : mnLine3) - mnLine1 );
+
+ // Focus polygons
+ /* Width for focus rectangles from center of frame borders. */
+ mnFocusOffs = FRAMESEL_GEOM_WIDTH / 2 + 1;
+
+ maLeft.ClearFocusArea();
+ maVer.ClearFocusArea();
+ maRight.ClearFocusArea();
+ maTop.ClearFocusArea();
+ maHor.ClearFocusArea();
+ maBottom.ClearFocusArea();
+
+ maLeft.AddFocusPolygon( tools::Rectangle( mnLine1 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine1 + mnFocusOffs, mnLine3 + mnFocusOffs ) );
+ maVer.AddFocusPolygon( tools::Rectangle( mnLine2 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine2 + mnFocusOffs, mnLine3 + mnFocusOffs ) );
+ maRight.AddFocusPolygon( tools::Rectangle( mnLine3 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine3 + mnFocusOffs ) );
+ maTop.AddFocusPolygon( tools::Rectangle( mnLine1 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine1 + mnFocusOffs ) );
+ maHor.AddFocusPolygon( tools::Rectangle( mnLine1 - mnFocusOffs, mnLine2 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine2 + mnFocusOffs ) );
+ maBottom.AddFocusPolygon( tools::Rectangle( mnLine1 - mnFocusOffs, mnLine3 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine3 + mnFocusOffs ) );
+
+ for( nCol = 0, nCols = maArray.GetColCount(); nCol < nCols; ++nCol )
+ {
+ for( nRow = 0, nRows = maArray.GetRowCount(); nRow < nRows; ++nRow )
+ {
+ const basegfx::B2DRange aCellRange(maArray.GetCellRange( nCol, nRow, true ));
+ const tools::Rectangle aRect(
+ basegfx::fround(aCellRange.getMinX()), basegfx::fround(aCellRange.getMinY()),
+ basegfx::fround(aCellRange.getMaxX()), basegfx::fround(aCellRange.getMaxY()));
+ const double fHorDiagAngle(atan2(fabs(aCellRange.getHeight()), fabs(aCellRange.getWidth())));
+ const double fVerDiagAngle(fHorDiagAngle > 0.0 ? F_PI2 - fHorDiagAngle : 0.0);
+ const long nDiagFocusOffsX(basegfx::fround(-mnFocusOffs / tan(fHorDiagAngle) + mnFocusOffs / sin(fHorDiagAngle)));
+ const long nDiagFocusOffsY(basegfx::fround(-mnFocusOffs / tan(fVerDiagAngle) + mnFocusOffs / sin(fVerDiagAngle)));
+
+ std::vector< Point > aFocusVec;
+ aFocusVec.emplace_back( aRect.Left() - mnFocusOffs, aRect.Top() + nDiagFocusOffsY );
+ aFocusVec.emplace_back( aRect.Left() - mnFocusOffs, aRect.Top() - mnFocusOffs );
+ aFocusVec.emplace_back( aRect.Left() + nDiagFocusOffsX, aRect.Top() - mnFocusOffs );
+ aFocusVec.emplace_back( aRect.Right() + mnFocusOffs, aRect.Bottom() - nDiagFocusOffsY );
+ aFocusVec.emplace_back( aRect.Right() + mnFocusOffs, aRect.Bottom() + mnFocusOffs );
+ aFocusVec.emplace_back( aRect.Right() - nDiagFocusOffsX, aRect.Bottom() + mnFocusOffs );
+ maTLBR.AddFocusPolygon( tools::Polygon( static_cast< sal_uInt16 >( aFocusVec.size() ), aFocusVec.data() ) );
+
+ aFocusVec.clear();
+ aFocusVec.emplace_back( aRect.Right() + mnFocusOffs, aRect.Top() + nDiagFocusOffsY );
+ aFocusVec.emplace_back( aRect.Right() + mnFocusOffs, aRect.Top() - mnFocusOffs );
+ aFocusVec.emplace_back( aRect.Right() - nDiagFocusOffsX, aRect.Top() - mnFocusOffs );
+ aFocusVec.emplace_back( aRect.Left() - mnFocusOffs, aRect.Bottom() - nDiagFocusOffsY );
+ aFocusVec.emplace_back( aRect.Left() - mnFocusOffs, aRect.Bottom() + mnFocusOffs );
+ aFocusVec.emplace_back( aRect.Left() + nDiagFocusOffsX, aRect.Bottom() + mnFocusOffs );
+ maBLTR.AddFocusPolygon( tools::Polygon( static_cast< sal_uInt16 >( aFocusVec.size() ), aFocusVec.data() ) );
+ }
+ }
+
+ // Click areas
+ for( FrameBorderIter aIt( maAllBorders ); aIt.Is(); ++aIt )
+ (*aIt)->ClearClickArea();
+
+ /* Additional space for click area: is added to the space available to draw
+ the frame borders. For instance left frame border:
+ - To left, top, and bottom always big additional space (outer area).
+ - To right: Dependent on existence of inner vertical frame border
+ (if enabled, use less space).
+ */
+ long nClO = FRAMESEL_GEOM_WIDTH / 2 + FRAMESEL_GEOM_ADD_CLICK_OUTER;
+ long nClI = (mbTLBR && mbBLTR) ? (FRAMESEL_GEOM_WIDTH / 2 + FRAMESEL_GEOM_ADD_CLICK_INNER) : nClO;
+ long nClH = mbHor ? nClI : nClO; // additional space dependent of horizontal inner border
+ long nClV = mbVer ? nClI : nClO; // additional space dependent of vertical inner border
+
+ maLeft.AddClickRect( tools::Rectangle( mnLine1 - nClO, mnLine1 - nClO, mnLine1 + nClV, mnLine3 + nClO ) );
+ maVer.AddClickRect( tools::Rectangle( mnLine2 - nClI, mnLine1 - nClO, mnLine2 + nClI, mnLine3 + nClO ) );
+ maRight.AddClickRect( tools::Rectangle( mnLine3 - nClV, mnLine1 - nClO, mnLine3 + nClO, mnLine3 + nClO ) );
+ maTop.AddClickRect( tools::Rectangle( mnLine1 - nClO, mnLine1 - nClO, mnLine3 + nClO, mnLine1 + nClH ) );
+ maHor.AddClickRect( tools::Rectangle( mnLine1 - nClO, mnLine2 - nClI, mnLine3 + nClO, mnLine2 + nClI ) );
+ maBottom.AddClickRect( tools::Rectangle( mnLine1 - nClO, mnLine3 - nClH, mnLine3 + nClO, mnLine3 + nClO ) );
+
+ /* Diagonal frame borders use the remaining space between outer and inner frame borders. */
+ if( mbTLBR || mbBLTR )
+ {
+ for( nCol = 0, nCols = maArray.GetColCount(); nCol < nCols; ++nCol )
+ {
+ for( nRow = 0, nRows = maArray.GetRowCount(); nRow < nRows; ++nRow )
+ {
+ // the usable area between horizontal/vertical frame borders of current quadrant
+ const basegfx::B2DRange aCellRange(maArray.GetCellRange( nCol, nRow, true ));
+ const tools::Rectangle aRect(
+ basegfx::fround(aCellRange.getMinX()) + nClV + 1, basegfx::fround(aCellRange.getMinY()) + nClH + 1,
+ basegfx::fround(aCellRange.getMaxX()) - nClV + 1, basegfx::fround(aCellRange.getMaxY()) - nClH + 1);
+
+ /* Both diagonal frame borders enabled. */
+ if( mbTLBR && mbBLTR )
+ {
+ // single areas
+ Point aMid( aRect.Center() );
+ maTLBR.AddClickRect( tools::Rectangle( aRect.TopLeft(), aMid ) );
+ maTLBR.AddClickRect( tools::Rectangle( aMid + Point( 1, 1 ), aRect.BottomRight() ) );
+ maBLTR.AddClickRect( tools::Rectangle( aRect.Left(), aMid.Y() + 1, aMid.X(), aRect.Bottom() ) );
+ maBLTR.AddClickRect( tools::Rectangle( aMid.X() + 1, aRect.Top(), aRect.Right(), aMid.Y() ) );
+ // centered rectangle for both frame borders
+ tools::Rectangle aMidRect( aRect.TopLeft(), Size( aRect.GetWidth() / 3, aRect.GetHeight() / 3 ) );
+ aMidRect.Move( (aRect.GetWidth() - aMidRect.GetWidth()) / 2, (aRect.GetHeight() - aMidRect.GetHeight()) / 2 );
+ maTLBR.AddClickRect( aMidRect );
+ maBLTR.AddClickRect( aMidRect );
+ }
+ /* One of the diagonal frame borders enabled - use entire rectangle. */
+ else if( mbTLBR && !mbBLTR ) // top-left to bottom-right only
+ maTLBR.AddClickRect( aRect );
+ else if( !mbTLBR && mbBLTR ) // bottom-left to top-right only
+ maBLTR.AddClickRect( aRect );
+ }
+ }
+ }
+}
+
+void FrameSelectorImpl::InitVirtualDevice()
+{
+ // initialize resources
+ InitColors();
+ InitArrowImageList();
+
+ sizeChanged();
+}
+
+void FrameSelectorImpl::sizeChanged()
+{
+ // initialize geometry
+ InitGlobalGeometry();
+ InitBorderGeometry();
+
+ DoInvalidate( true );
+}
+
+// frame border access
+const FrameBorder& FrameSelectorImpl::GetBorder( FrameBorderType eBorder ) const
+{
+ size_t nIndex = GetIndexFromFrameBorderType( eBorder );
+ if( nIndex < maAllBorders.size() )
+ return *maAllBorders[ nIndex ];
+ SAL_WARN( "svx.dialog", "svx::FrameSelectorImpl::GetBorder - unknown border type" );
+ return maTop;
+}
+
+FrameBorder& FrameSelectorImpl::GetBorderAccess( FrameBorderType eBorder )
+{
+ return const_cast< FrameBorder& >( GetBorder( eBorder ) );
+}
+
+// drawing
+void FrameSelectorImpl::DrawBackground()
+{
+ // clear the area
+ mpVirDev->SetLineColor();
+ mpVirDev->SetFillColor( maBackCol );
+ mpVirDev->DrawRect( tools::Rectangle( Point( 0, 0 ), mpVirDev->GetOutputSizePixel() ) );
+
+ // draw the inner gray (or whatever color) rectangle
+ mpVirDev->SetLineColor();
+ mpVirDev->SetFillColor( maMarkCol );
+ mpVirDev->DrawRect( tools::Rectangle(
+ mnLine1 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine3 + mnFocusOffs ) );
+
+ // draw the white space for enabled frame borders
+ tools::PolyPolygon aPPoly;
+ for( FrameBorderCIter aIt( maEnabBorders ); aIt.Is(); ++aIt )
+ (*aIt)->MergeFocusToPolyPolygon( aPPoly );
+ aPPoly.Optimize( PolyOptimizeFlags::CLOSE );
+ mpVirDev->SetLineColor( maBackCol );
+ mpVirDev->SetFillColor( maBackCol );
+ mpVirDev->DrawPolyPolygon( aPPoly );
+}
+
+void FrameSelectorImpl::DrawArrows( const FrameBorder& rBorder )
+{
+ DBG_ASSERT( rBorder.IsEnabled(), "svx::FrameSelectorImpl::DrawArrows - access to disabled border" );
+
+ long nLinePos = 0;
+ switch( rBorder.GetType() )
+ {
+ case FrameBorderType::Left:
+ case FrameBorderType::Top: nLinePos = mnLine1; break;
+ case FrameBorderType::Vertical:
+ case FrameBorderType::Horizontal: nLinePos = mnLine2; break;
+ case FrameBorderType::Right:
+ case FrameBorderType::Bottom: nLinePos = mnLine3; break;
+ default: ; //prevent warning
+ }
+ nLinePos -= mnArrowSize / 2;
+
+ long nTLPos = 0;
+ long nBRPos = mnCtrlSize - mnArrowSize;
+ Point aPos1, aPos2;
+ int nImgIndex1 = -1, nImgIndex2 = -1;
+ switch( rBorder.GetType() )
+ {
+ case FrameBorderType::Left:
+ case FrameBorderType::Right:
+ case FrameBorderType::Vertical:
+ aPos1 = Point( nLinePos, nTLPos ); nImgIndex1 = 0;
+ aPos2 = Point( nLinePos, nBRPos ); nImgIndex2 = 1;
+ break;
+
+ case FrameBorderType::Top:
+ case FrameBorderType::Bottom:
+ case FrameBorderType::Horizontal:
+ aPos1 = Point( nTLPos, nLinePos ); nImgIndex1 = 2;
+ aPos2 = Point( nBRPos, nLinePos ); nImgIndex2 = 3;
+ break;
+
+ case FrameBorderType::TLBR:
+ aPos1 = Point( nTLPos, nTLPos ); nImgIndex1 = 4;
+ aPos2 = Point( nBRPos, nBRPos ); nImgIndex2 = 5;
+ break;
+ case FrameBorderType::BLTR:
+ aPos1 = Point( nTLPos, nBRPos ); nImgIndex1 = 6;
+ aPos2 = Point( nBRPos, nTLPos ); nImgIndex2 = 7;
+ break;
+ default: ; //prevent warning
+ }
+
+ // Arrow or marker? Do not draw arrows into disabled control.
+ sal_uInt16 nSelectAdd = (mrFrameSel.IsEnabled() && rBorder.IsSelected()) ? 0 : 8;
+ if (nImgIndex1 >= 0)
+ mpVirDev->DrawImage(aPos1, maArrows[nImgIndex1 + nSelectAdd]);
+ if (nImgIndex2 >= 0)
+ mpVirDev->DrawImage(aPos2, maArrows[nImgIndex2 + nSelectAdd]);
+}
+
+Color FrameSelectorImpl::GetDrawLineColor( const Color& rColor ) const
+{
+ Color aColor( mbHCMode ? maHCLineCol : rColor );
+ if( aColor == maBackCol )
+ aColor.Invert();
+ return aColor;
+}
+
+void FrameSelectorImpl::DrawAllFrameBorders()
+{
+ // Translate core colors to current UI colors (regards current background and HC mode).
+ for( FrameBorderIter aIt( maEnabBorders ); aIt.Is(); ++aIt )
+ {
+ Color aCoreColorPrim = ((*aIt)->GetState() == FrameBorderState::DontCare) ? maMarkCol : (*aIt)->GetCoreStyle().GetColorOut();
+ Color aCoreColorSecn = ((*aIt)->GetState() == FrameBorderState::DontCare) ? maMarkCol : (*aIt)->GetCoreStyle().GetColorIn();
+ (*aIt)->SetUIColorPrim( GetDrawLineColor( aCoreColorPrim ) );
+ (*aIt)->SetUIColorSecn( GetDrawLineColor( aCoreColorSecn ) );
+ }
+
+ // Copy all frame border styles to the helper array
+ maArray.SetColumnStyleLeft( 0, maLeft.GetUIStyle() );
+ if( mbVer ) maArray.SetColumnStyleLeft( 1, maVer.GetUIStyle() );
+
+ // Invert the style for the right line
+ const frame::Style rRightStyle = maRight.GetUIStyle( );
+ frame::Style rInvertedRight( rRightStyle.GetColorPrim(),
+ rRightStyle.GetColorSecn(), rRightStyle.GetColorGap(),
+ rRightStyle.UseGapColor(),
+ rRightStyle.Secn(), rRightStyle.Dist(), rRightStyle.Prim( ),
+ rRightStyle.Type( ), rRightStyle.PatternScale() );
+ maArray.SetColumnStyleRight( mbVer ? 1 : 0, rInvertedRight );
+
+ maArray.SetRowStyleTop( 0, maTop.GetUIStyle() );
+ if( mbHor )
+ {
+ // Invert the style for the hor line to match the real borders
+ const frame::Style rHorStyle = maHor.GetUIStyle();
+ frame::Style rInvertedHor( rHorStyle.GetColorPrim(),
+ rHorStyle.GetColorSecn(), rHorStyle.GetColorGap(),
+ rHorStyle.UseGapColor(),
+ rHorStyle.Secn(), rHorStyle.Dist(), rHorStyle.Prim( ),
+ rHorStyle.Type(), rHorStyle.PatternScale() );
+ maArray.SetRowStyleTop( 1, rInvertedHor );
+ }
+
+ // Invert the style for the bottom line
+ const frame::Style rBottomStyle = maBottom.GetUIStyle( );
+ frame::Style rInvertedBottom( rBottomStyle.GetColorPrim(),
+ rBottomStyle.GetColorSecn(), rBottomStyle.GetColorGap(),
+ rBottomStyle.UseGapColor(),
+ rBottomStyle.Secn(), rBottomStyle.Dist(), rBottomStyle.Prim( ),
+ rBottomStyle.Type(), rBottomStyle.PatternScale() );
+ maArray.SetRowStyleBottom( mbHor ? 1 : 0, rInvertedBottom );
+
+ for( size_t nCol = 0; nCol < maArray.GetColCount(); ++nCol )
+ for( size_t nRow = 0; nRow < maArray.GetRowCount(); ++nRow )
+ maArray.SetCellStyleDiag( nCol, nRow, maTLBR.GetUIStyle(), maBLTR.GetUIStyle() );
+
+ // This is used in the dialog/control for 'Border' attributes. When using
+ // the original paint below instead of primitives, the advantage currently
+ // is the correct visualization of diagonal line(s) including overlaying,
+ // but the rest is bad. Since the edit views use primitives and the preview
+ // should be 'real' I opt for also changing this to primitives. I will
+ // keep the old solution and add a switch (above) based on a static bool so
+ // that interested people may test this out in the debugger.
+ // This is one more hint to enhance the primitive visualization further to
+ // support diagonals better - that's the way to go.
+ const drawinglayer::geometry::ViewInformation2D aNewViewInformation2D;
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D(
+ drawinglayer::processor2d::createPixelProcessor2DFromOutputDevice(
+ *mpVirDev,
+ aNewViewInformation2D));
+
+ if (pProcessor2D)
+ {
+ pProcessor2D->process(maArray.CreateB2DPrimitiveArray());
+ pProcessor2D.reset();
+ }
+}
+
+void FrameSelectorImpl::DrawVirtualDevice()
+{
+ DrawBackground();
+ for(FrameBorderCIter aIt(maEnabBorders); aIt.Is(); ++aIt)
+ DrawArrows(**aIt);
+ DrawAllFrameBorders();
+ mbFullRepaint = false;
+}
+
+void FrameSelectorImpl::CopyVirDevToControl(vcl::RenderContext& rRenderContext)
+{
+ if (mbFullRepaint)
+ DrawVirtualDevice();
+ rRenderContext.DrawBitmapEx(maVirDevPos, mpVirDev->GetBitmapEx(Point(0, 0), mpVirDev->GetOutputSizePixel()));
+}
+
+void FrameSelectorImpl::DrawAllTrackingRects(vcl::RenderContext& rRenderContext)
+{
+ tools::PolyPolygon aPPoly;
+ if (mrFrameSel.IsAnyBorderSelected())
+ {
+ for(SelFrameBorderCIter aIt( maEnabBorders ); aIt.Is(); ++aIt)
+ (*aIt)->MergeFocusToPolyPolygon(aPPoly);
+ aPPoly.Move(maVirDevPos.X(), maVirDevPos.Y());
+ }
+ else
+ // no frame border selected -> draw tracking rectangle around entire control
+ aPPoly.Insert( tools::Polygon(tools::Rectangle(maVirDevPos, mpVirDev->GetOutputSizePixel())));
+
+ aPPoly.Optimize(PolyOptimizeFlags::CLOSE);
+
+ for(sal_uInt16 nIdx = 0, nCount = aPPoly.Count(); nIdx < nCount; ++nIdx)
+ rRenderContext.Invert(aPPoly.GetObject(nIdx), InvertFlags::TrackFrame);
+}
+
+Point FrameSelectorImpl::GetDevPosFromMousePos( const Point& rMousePos ) const
+{
+ return rMousePos - maVirDevPos;
+}
+
+void FrameSelectorImpl::DoInvalidate( bool bFullRepaint )
+{
+ mbFullRepaint |= bFullRepaint;
+ mrFrameSel.Invalidate();
+}
+
+// frame border state and style
+void FrameSelectorImpl::SetBorderState( FrameBorder& rBorder, FrameBorderState eState )
+{
+ DBG_ASSERT( rBorder.IsEnabled(), "svx::FrameSelectorImpl::SetBorderState - access to disabled border" );
+ Any aOld;
+ Any aNew;
+ Any& rMod = eState == FrameBorderState::Show ? aNew : aOld;
+ rMod <<= AccessibleStateType::CHECKED;
+ rtl::Reference< a11y::AccFrameSelectorChild > xRet;
+ size_t nVecIdx = static_cast< size_t >( rBorder.GetType() );
+ if( GetBorder(rBorder.GetType()).IsEnabled() && (1 <= nVecIdx) && (nVecIdx <= maChildVec.size()) )
+ xRet = maChildVec[ --nVecIdx ].get();
+
+ if( eState == FrameBorderState::Show )
+ SetBorderCoreStyle( rBorder, &maCurrStyle );
+ else
+ rBorder.SetState( eState );
+ if (xRet.is())
+ xRet->NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOld, aNew );
+ DoInvalidate( true );
+}
+
+void FrameSelectorImpl::SetBorderCoreStyle( FrameBorder& rBorder, const SvxBorderLine* pStyle )
+{
+ DBG_ASSERT( rBorder.IsEnabled(), "svx::FrameSelectorImpl::SetBorderCoreStyle - access to disabled border" );
+ rBorder.SetCoreStyle( pStyle );
+ DoInvalidate( true );
+}
+
+void FrameSelectorImpl::ToggleBorderState( FrameBorder& rBorder )
+{
+ bool bDontCare = mrFrameSel.SupportsDontCareState();
+ switch( rBorder.GetState() )
+ {
+ // same order as tristate check box: visible -> don't care -> hidden
+ case FrameBorderState::Show:
+ SetBorderState( rBorder, bDontCare ? FrameBorderState::DontCare : FrameBorderState::Hide );
+ break;
+ case FrameBorderState::Hide:
+ SetBorderState( rBorder, FrameBorderState::Show );
+ break;
+ case FrameBorderState::DontCare:
+ SetBorderState( rBorder, FrameBorderState::Hide );
+ break;
+ }
+}
+
+// frame border selection
+void FrameSelectorImpl::SelectBorder( FrameBorder& rBorder, bool bSelect )
+{
+ DBG_ASSERT( rBorder.IsEnabled(), "svx::FrameSelectorImpl::SelectBorder - access to disabled border" );
+ rBorder.Select( bSelect );
+ DrawArrows( rBorder );
+ DoInvalidate( false );
+}
+
+void FrameSelectorImpl::SilentGrabFocus()
+{
+ bool bOldAuto = mbAutoSelect;
+ mbAutoSelect = false;
+ mrFrameSel.GrabFocus();
+ mbAutoSelect = bOldAuto;
+}
+
+bool FrameSelectorImpl::SelectedBordersEqual() const
+{
+ bool bEqual = true;
+ SelFrameBorderCIter aIt( maEnabBorders );
+ if( aIt.Is() )
+ {
+ const SvxBorderLine& rFirstStyle = (*aIt)->GetCoreStyle();
+ for( ++aIt; bEqual && aIt.Is(); ++aIt )
+ bEqual = ((*aIt)->GetCoreStyle() == rFirstStyle);
+ }
+ return bEqual;
+}
+
+FrameSelector::FrameSelector()
+{
+}
+
+void FrameSelector::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ Size aPrefSize = pDrawingArea->get_ref_device().LogicToPixel(Size(61, 65), MapMode(MapUnit::MapAppFont));
+ pDrawingArea->set_size_request(aPrefSize.Width(), aPrefSize.Height());
+ mxImpl.reset( new FrameSelectorImpl( *this ) );
+ EnableRTL( false ); // #107808# don't mirror the mouse handling
+}
+
+FrameSelector::~FrameSelector()
+{
+ if( mxAccess.is() )
+ mxAccess->Invalidate();
+}
+
+void FrameSelector::Initialize( FrameSelFlags nFlags )
+{
+ mxImpl->Initialize( nFlags );
+ Show();
+}
+
+// enabled frame borders
+bool FrameSelector::IsBorderEnabled( FrameBorderType eBorder ) const
+{
+ return mxImpl->GetBorder( eBorder ).IsEnabled();
+}
+
+sal_Int32 FrameSelector::GetEnabledBorderCount() const
+{
+ return static_cast< sal_Int32 >( mxImpl->maEnabBorders.size() );
+}
+
+FrameBorderType FrameSelector::GetEnabledBorderType( sal_Int32 nIndex ) const
+{
+ FrameBorderType eBorder = FrameBorderType::NONE;
+ if( nIndex >= 0 )
+ {
+ size_t nVecIdx = static_cast< size_t >( nIndex );
+ if( nVecIdx < mxImpl->maEnabBorders.size() )
+ eBorder = mxImpl->maEnabBorders[ nVecIdx ]->GetType();
+ }
+ return eBorder;
+}
+
+// frame border state and style
+bool FrameSelector::SupportsDontCareState() const
+{
+ return bool(mxImpl->mnFlags & FrameSelFlags::DontCare);
+}
+
+FrameBorderState FrameSelector::GetFrameBorderState( FrameBorderType eBorder ) const
+{
+ return mxImpl->GetBorder( eBorder ).GetState();
+}
+
+const SvxBorderLine* FrameSelector::GetFrameBorderStyle( FrameBorderType eBorder ) const
+{
+ const SvxBorderLine& rStyle = mxImpl->GetBorder( eBorder ).GetCoreStyle();
+ // rest of the world uses null pointer for invisible frame border
+ return rStyle.GetOutWidth() ? &rStyle : nullptr;
+}
+
+void FrameSelector::ShowBorder( FrameBorderType eBorder, const SvxBorderLine* pStyle )
+{
+ mxImpl->SetBorderCoreStyle( mxImpl->GetBorderAccess( eBorder ), pStyle );
+}
+
+void FrameSelector::SetBorderDontCare( FrameBorderType eBorder )
+{
+ mxImpl->SetBorderState( mxImpl->GetBorderAccess( eBorder ), FrameBorderState::DontCare );
+}
+
+bool FrameSelector::IsAnyBorderVisible() const
+{
+ bool bIsSet = false;
+ for( FrameBorderCIter aIt( mxImpl->maEnabBorders ); !bIsSet && aIt.Is(); ++aIt )
+ bIsSet = ((*aIt)->GetState() == FrameBorderState::Show);
+ return bIsSet;
+}
+
+void FrameSelector::HideAllBorders()
+{
+ for( FrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ mxImpl->SetBorderState( **aIt, FrameBorderState::Hide );
+}
+
+bool FrameSelector::GetVisibleWidth( long& rnWidth, SvxBorderLineStyle& rnStyle ) const
+{
+ VisFrameBorderCIter aIt( mxImpl->maEnabBorders );
+ if( !aIt.Is() )
+ return false;
+
+ const SvxBorderLine& rStyle = (*aIt)->GetCoreStyle();
+ bool bFound = true;
+ for( ++aIt; bFound && aIt.Is(); ++aIt )
+ {
+ bFound =
+ (rStyle.GetWidth() == (*aIt)->GetCoreStyle().GetWidth()) &&
+ (rStyle.GetBorderLineStyle() ==
+ (*aIt)->GetCoreStyle().GetBorderLineStyle());
+ }
+
+ if( bFound )
+ {
+ rnWidth = rStyle.GetWidth();
+ rnStyle = rStyle.GetBorderLineStyle();
+ }
+ return bFound;
+}
+
+bool FrameSelector::GetVisibleColor( Color& rColor ) const
+{
+ VisFrameBorderCIter aIt( mxImpl->maEnabBorders );
+ if( !aIt.Is() )
+ return false;
+
+ const SvxBorderLine& rStyle = (*aIt)->GetCoreStyle();
+ bool bFound = true;
+ for( ++aIt; bFound && aIt.Is(); ++aIt )
+ bFound = (rStyle.GetColor() == (*aIt)->GetCoreStyle().GetColor());
+
+ if( bFound )
+ rColor = rStyle.GetColor();
+ return bFound;
+}
+
+// frame border selection
+const Link<LinkParamNone*,void>& FrameSelector::GetSelectHdl() const
+{
+ return mxImpl->maSelectHdl;
+}
+
+void FrameSelector::SetSelectHdl( const Link<LinkParamNone*,void>& rHdl )
+{
+ mxImpl->maSelectHdl = rHdl;
+}
+
+bool FrameSelector::IsBorderSelected( FrameBorderType eBorder ) const
+{
+ return mxImpl->GetBorder( eBorder ).IsSelected();
+}
+
+void FrameSelector::SelectBorder( FrameBorderType eBorder )
+{
+ mxImpl->SelectBorder( mxImpl->GetBorderAccess( eBorder ), true/*bSelect*/ );
+ // MT: bFireFox as API parameter is ugly...
+ // if (bFocus)
+ {
+ rtl::Reference< a11y::AccFrameSelectorChild > xRet = GetChildAccessible(eBorder);
+ if (xRet.is())
+ {
+ Any aOldValue, aNewValue;
+ aNewValue <<= AccessibleStateType::FOCUSED;
+ xRet->NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue );
+ }
+ }
+}
+
+bool FrameSelector::IsAnyBorderSelected() const
+{
+ // Construct an iterator for selected borders. If it is valid, there is a selected border.
+ return SelFrameBorderCIter( mxImpl->maEnabBorders ).Is();
+}
+
+void FrameSelector::SelectAllBorders( bool bSelect )
+{
+ for( FrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ mxImpl->SelectBorder( **aIt, bSelect );
+}
+
+void FrameSelector::SelectAllVisibleBorders()
+{
+ for( VisFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ mxImpl->SelectBorder( **aIt, true/*bSelect*/ );
+}
+
+void FrameSelector::SetStyleToSelection( long nWidth, SvxBorderLineStyle nStyle )
+{
+ mxImpl->maCurrStyle.SetBorderLineStyle( nStyle );
+ mxImpl->maCurrStyle.SetWidth( nWidth );
+ for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ mxImpl->SetBorderState( **aIt, FrameBorderState::Show );
+}
+
+void FrameSelector::SetColorToSelection( const Color& rColor )
+{
+ mxImpl->maCurrStyle.SetColor( rColor );
+ for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ mxImpl->SetBorderState( **aIt, FrameBorderState::Show );
+}
+
+// accessibility
+Reference< XAccessible > FrameSelector::CreateAccessible()
+{
+ if( !mxAccess.is() )
+ mxAccess = new a11y::AccFrameSelector(*this);
+ return mxAccess.get();
+}
+
+rtl::Reference< a11y::AccFrameSelectorChild > FrameSelector::GetChildAccessible( FrameBorderType eBorder )
+{
+ rtl::Reference< a11y::AccFrameSelectorChild > xRet;
+ size_t nVecIdx = static_cast< size_t >( eBorder );
+ if( IsBorderEnabled( eBorder ) && (1 <= nVecIdx) && (nVecIdx <= mxImpl->maChildVec.size()) )
+ {
+ --nVecIdx;
+ if( !mxImpl->maChildVec[ nVecIdx ].is() )
+ mxImpl->maChildVec[ nVecIdx ] = new a11y::AccFrameSelectorChild( *this, eBorder );
+ xRet = mxImpl->maChildVec[ nVecIdx ].get();
+ }
+ return xRet;
+}
+
+Reference< XAccessible > FrameSelector::GetChildAccessible( sal_Int32 nIndex )
+{
+ return GetChildAccessible( GetEnabledBorderType( nIndex ) ).get();
+}
+
+Reference< XAccessible > FrameSelector::GetChildAccessible( const Point& rPos )
+{
+ Reference< XAccessible > xRet;
+ for( FrameBorderCIter aIt( mxImpl->maEnabBorders ); !xRet.is() && aIt.Is(); ++aIt )
+ if( (*aIt)->ContainsClickPoint( rPos ) )
+ xRet = GetChildAccessible( (*aIt)->GetType() ).get();
+ return xRet;
+}
+
+tools::Rectangle FrameSelector::GetClickBoundRect( FrameBorderType eBorder ) const
+{
+ tools::Rectangle aRect;
+ const FrameBorder& rBorder = mxImpl->GetBorder( eBorder );
+ if( rBorder.IsEnabled() )
+ aRect = rBorder.GetClickBoundRect();
+ return aRect;
+}
+
+// virtual functions from base class
+void FrameSelector::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ mxImpl->CopyVirDevToControl(rRenderContext);
+ if (HasFocus())
+ mxImpl->DrawAllTrackingRects(rRenderContext);
+}
+
+bool FrameSelector::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ /* Mouse handling:
+ * Click on an unselected frame border:
+ Set current style/color, make frame border visible, deselect all
+ other frame borders.
+ * Click on a selected frame border:
+ Toggle state of the frame border (visible -> don't care -> hidden),
+ deselect all other frame borders.
+ * SHIFT+Click or CTRL+Click on an unselected frame border:
+ Extend selection, set current style/color to all selected frame
+ borders independent of the state/style/color of the borders.
+ * SHIFT+Click or CTRL+Click on a selected frame border:
+ If all frame borders have same style/color, toggle state of all
+ borders (see above), otherwise set current style/color to all
+ borders.
+ * Click on unused area: Do not modify selection and selected frame
+ borders.
+ */
+
+ // #107394# do not auto-select a frame border
+ mxImpl->SilentGrabFocus();
+
+ if( rMEvt.IsLeft() )
+ {
+ Point aPos( mxImpl->GetDevPosFromMousePos( rMEvt.GetPosPixel() ) );
+ FrameBorderPtrVec aDeselectBorders;
+
+ bool bAnyClicked = false; // Any frame border clicked?
+ bool bNewSelected = false; // Any unselected frame border selected?
+
+ /* If frame borders are set to "don't care" and the control does not
+ support this state, hide them on first mouse click.
+ DR 2004-01-30: Why are the borders set to "don't care" then?!? */
+ bool bHideDontCare = !SupportsDontCareState();
+
+ for( FrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ {
+ if( (*aIt)->ContainsClickPoint( aPos ) )
+ {
+ // frame border is clicked
+ bAnyClicked = true;
+ if( !(*aIt)->IsSelected() )
+ {
+ bNewSelected = true;
+ //mxImpl->SelectBorder( **aIt, true );
+ SelectBorder((**aIt).GetType());
+ }
+ }
+ else
+ {
+ // hide a "don't care" frame border only if it is not clicked
+ if( bHideDontCare && ((*aIt)->GetState() == FrameBorderState::DontCare) )
+ mxImpl->SetBorderState( **aIt, FrameBorderState::Hide );
+
+ // deselect frame borders not clicked (if SHIFT or CTRL are not pressed)
+ if( !rMEvt.IsShift() && !rMEvt.IsMod1() )
+ aDeselectBorders.push_back( *aIt );
+ }
+ }
+
+ if( bAnyClicked )
+ {
+ // any valid frame border clicked? -> deselect other frame borders
+ for( FrameBorderIter aIt( aDeselectBorders ); aIt.Is(); ++aIt )
+ mxImpl->SelectBorder( **aIt, false );
+
+ if( bNewSelected || !mxImpl->SelectedBordersEqual() )
+ {
+ // new frame border selected, selection extended, or selected borders different? -> show
+ for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ // SetBorderState() sets current style and color to the frame border
+ mxImpl->SetBorderState( **aIt, FrameBorderState::Show );
+ }
+ else
+ {
+ // all selected frame borders are equal -> toggle state
+ for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ mxImpl->ToggleBorderState( **aIt );
+ }
+
+ GetSelectHdl().Call( nullptr );
+ }
+ }
+
+ return true;
+}
+
+bool FrameSelector::KeyInput( const KeyEvent& rKEvt )
+{
+ bool bHandled = false;
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+ if( !aKeyCode.GetModifier() )
+ {
+ sal_uInt16 nCode = aKeyCode.GetCode();
+ switch( nCode )
+ {
+ case KEY_SPACE:
+ {
+ for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ mxImpl->ToggleBorderState( **aIt );
+ bHandled = true;
+ }
+ break;
+
+ case KEY_UP:
+ case KEY_DOWN:
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ {
+ if( !mxImpl->maEnabBorders.empty() )
+ {
+ // start from first selected frame border
+ SelFrameBorderCIter aIt( mxImpl->maEnabBorders );
+ FrameBorderType eBorder = aIt.Is() ? (*aIt)->GetType() : mxImpl->maEnabBorders.front()->GetType();
+
+ // search for next enabled frame border
+ do
+ {
+ eBorder = mxImpl->GetBorder( eBorder ).GetKeyboardNeighbor( nCode );
+ }
+ while( (eBorder != FrameBorderType::NONE) && !IsBorderEnabled( eBorder ) );
+
+ // select the frame border
+ if( eBorder != FrameBorderType::NONE )
+ {
+ DeselectAllBorders();
+ SelectBorder( eBorder );
+ }
+ bHandled = true;
+ }
+ }
+ break;
+ }
+ }
+ if (bHandled)
+ return true;
+ return CustomWidgetController::KeyInput(rKEvt);
+}
+
+void FrameSelector::GetFocus()
+{
+ // auto-selection of a frame border, if focus reaches control, and nothing is selected
+ if( mxImpl->mbAutoSelect && !IsAnyBorderSelected() && !mxImpl->maEnabBorders.empty() )
+ mxImpl->SelectBorder( *mxImpl->maEnabBorders.front(), true );
+
+ mxImpl->DoInvalidate( false );
+ if (IsAnyBorderSelected())
+ {
+ FrameBorderType borderType = FrameBorderType::NONE;
+ if (mxImpl->maLeft.IsSelected())
+ borderType = FrameBorderType::Left;
+ else if (mxImpl->maRight.IsSelected())
+ borderType = FrameBorderType::Right;
+ else if (mxImpl->maTop.IsSelected())
+ borderType = FrameBorderType::Top;
+ else if (mxImpl->maBottom.IsSelected())
+ borderType = FrameBorderType::Bottom;
+ else if (mxImpl->maHor.IsSelected())
+ borderType = FrameBorderType::Horizontal;
+ else if (mxImpl->maVer.IsSelected())
+ borderType = FrameBorderType::Vertical;
+ else if (mxImpl->maTLBR.IsSelected())
+ borderType = FrameBorderType::TLBR;
+ else if (mxImpl->maBLTR.IsSelected())
+ borderType = FrameBorderType::BLTR;
+ SelectBorder(borderType);
+ }
+ for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ mxImpl->SetBorderState( **aIt, FrameBorderState::Show );
+ CustomWidgetController::GetFocus();
+}
+
+void FrameSelector::LoseFocus()
+{
+ mxImpl->DoInvalidate( false );
+ CustomWidgetController::LoseFocus();
+}
+
+void FrameSelector::StyleUpdated()
+{
+ mxImpl->InitVirtualDevice();
+ CustomWidgetController::StyleUpdated();
+}
+
+void FrameSelector::Resize()
+{
+ CustomWidgetController::Resize();
+ mxImpl->sizeChanged();
+}
+
+template< typename Cont, typename Iter, typename Pred >
+FrameBorderIterBase< Cont, Iter, Pred >::FrameBorderIterBase( container_type& rCont ) :
+ maIt( rCont.begin() ),
+ maEnd( rCont.end() )
+{
+ while( Is() && !maPred( *maIt ) ) ++maIt;
+}
+
+template< typename Cont, typename Iter, typename Pred >
+FrameBorderIterBase< Cont, Iter, Pred >& FrameBorderIterBase< Cont, Iter, Pred >::operator++()
+{
+ do { ++maIt; } while( Is() && !maPred( *maIt ) );
+ return *this;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */