1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sfx2/dispatch.hxx>
21 #include <vcl/commandevent.hxx>
22 #include <vcl/help.hxx>
23 #include <vcl/settings.hxx>
24 #include <svtools/colorcfg.hxx>
25 #include <osl/diagnose.h>
27 #include <tabvwsh.hxx>
28 #include <hdrcont.hxx>
31 #include <inputopt.hxx>
32 #include <gridmerg.hxx>
33 #include <document.hxx>
34 #include <markdata.hxx>
35 #include <tabview.hxx>
36 #include <viewdata.hxx>
37 #include <columnspanset.hxx>
38 #include <officecfg/Office/Common.hxx>
43 // (selection left/right must be first because the continuous lines
44 // are partly overwritten later)
46 #define SC_HDRPAINT_SEL_BOTTOM 4
47 #define SC_HDRPAINT_BOTTOM 5
48 #define SC_HDRPAINT_TEXT 6
49 #define SC_HDRPAINT_COUNT 7
51 ScHeaderControl::ScHeaderControl( vcl::Window
* pParent
, SelectionEngine
* pSelectionEngine
,
52 SCCOLROW nNewSize
, bool bNewVertical
, ScTabView
* pTab
) :
54 pSelEngine ( pSelectionEngine
),
55 aShowHelpTimer("sc HeaderControl Popover Timer"),
56 bVertical ( bNewVertical
),
65 nTipVisible ( nullptr ),
67 bIgnoreMove ( false ),
71 // RTL: no default mirroring for this window, the spreadsheet itself
72 // is also not mirrored
73 // mirror the vertical window for correct border drawing
74 // table layout depends on sheet format, not UI setting, so the
75 // borders of the vertical window have to be handled manually, too.
78 aNormFont
= GetFont();
79 aNormFont
.SetTransparent( true ); //! hard-set WEIGHT_NORMAL ???
80 aBoldFont
= aNormFont
;
81 aBoldFont
.SetWeight( WEIGHT_BOLD
);
82 aAutoFilterFont
= aNormFont
;
86 bAutoFilterSet
= false;
88 Size aSize
= LogicToPixel( Size(
89 GetTextWidth(u
"8888"_ustr
),
91 aSize
.AdjustWidth(4 ); // place for highlight border
92 aSize
.AdjustHeight(3 );
93 SetSizePixel( aSize
);
95 nWidth
= nSmallWidth
= aSize
.Width();
96 nBigWidth
= LogicToPixel( Size( GetTextWidth(u
"8888888"_ustr
), 0 ) ).Width() + 5;
98 aShowHelpTimer
.SetInvokeHandler(LINK(this, ScHeaderControl
, ShowDragHelpHdl
));
99 aShowHelpTimer
.SetTimeout(GetSettings().GetMouseSettings().GetDoubleClickTime());
104 void ScHeaderControl::dispose()
106 aShowHelpTimer
.Stop();
107 vcl::Window::dispose();
110 void ScHeaderControl::SetWidth( tools::Long nNew
)
112 OSL_ENSURE( bVertical
, "SetWidth works only on row headers" );
113 if ( nNew
!= nWidth
)
115 Size
aSize( nNew
, GetSizePixel().Height() );
116 SetSizePixel( aSize
);
124 ScHeaderControl::~ScHeaderControl()
128 void ScHeaderControl::DoPaint( SCCOLROW nStart
, SCCOLROW nEnd
)
130 bool bLayoutRTL
= IsLayoutRTL();
131 tools::Long nLayoutSign
= bLayoutRTL
? -1 : 1;
133 tools::Rectangle
aRect( Point(0,0), GetOutputSizePixel() );
136 aRect
.SetTop( GetScrPos( nStart
)-nLayoutSign
); // extra pixel for line at top of selection
137 aRect
.SetBottom( GetScrPos( nEnd
+1 )-nLayoutSign
);
141 aRect
.SetLeft( GetScrPos( nStart
)-nLayoutSign
); // extra pixel for line left of selection
142 aRect
.SetRight( GetScrPos( nEnd
+1 )-nLayoutSign
);
147 void ScHeaderControl::SetMark( bool bNewSet
, SCCOLROW nNewStart
, SCCOLROW nNewEnd
)
149 bool bEnabled
= ScModule::get()->GetInputOptions().GetMarkHeader(); //! cache?
153 bool bOldSet
= bMarkRange
;
154 SCCOLROW nOldStart
= nMarkStart
;
155 SCCOLROW nOldEnd
= nMarkEnd
;
156 PutInOrder( nNewStart
, nNewEnd
);
157 bMarkRange
= bNewSet
;
158 nMarkStart
= nNewStart
;
167 if ( nNewStart
== nOldStart
)
169 if ( nNewEnd
!= nOldEnd
)
170 DoPaint( std::min( nNewEnd
, nOldEnd
) + 1, std::max( nNewEnd
, nOldEnd
) );
172 else if ( nNewEnd
== nOldEnd
)
173 DoPaint( std::min( nNewStart
, nOldStart
), std::max( nNewStart
, nOldStart
) - 1 );
174 else if ( nNewStart
> nOldEnd
|| nNewEnd
< nOldStart
)
177 DoPaint( nOldStart
, nOldEnd
);
178 DoPaint( nNewStart
, nNewEnd
);
180 else // somehow overlapping... (it is not often)
181 DoPaint( std::min( nNewStart
, nOldStart
), std::max( nNewEnd
, nOldEnd
) );
184 DoPaint( nNewStart
, nNewEnd
); // completely new selection
187 DoPaint( nOldStart
, nOldEnd
); // cancel selection
190 tools::Long
ScHeaderControl::GetScrPos( SCCOLROW nEntryNo
) const
194 tools::Long nMax
= ( bVertical
? GetOutputSizePixel().Height() : GetOutputSizePixel().Width() ) + 1;
195 if (nEntryNo
>= nSize
)
200 for (SCCOLROW i
=GetPos(); i
<nEntryNo
&& nScrPos
<nMax
; i
++)
202 sal_uInt16 nAdd
= GetEntrySize(i
);
207 SCCOLROW nHidden
= GetHiddenCount(i
);
215 nScrPos
= nMax
- nScrPos
- 2;
220 void ScHeaderControl::Paint( vcl::RenderContext
& /*rRenderContext*/, const tools::Rectangle
& rRect
)
222 // It is important for VCL to have few calls, that is why the outer lines are
225 const StyleSettings
& rStyleSettings
= GetSettings().GetStyleSettings();
226 bool bHighContrast
= rStyleSettings
.GetHighContrastMode();
227 bool bDark
= rStyleSettings
.GetFaceColor().IsDark();
228 // Use the same distinction for bDark as in Window::DrawSelectionBackground
230 Color aTextColor
= rStyleSettings
.GetButtonTextColor();
231 Color aSelTextColor
= rStyleSettings
.GetHighlightTextColor();
232 Color aAFilterTextColor
= rStyleSettings
.GetButtonTextColor();
233 aAFilterTextColor
.Merge(COL_LIGHTBLUE
, bDark
? 150 : 10); // color of filtered row numbers
234 aNormFont
.SetColor( aTextColor
);
235 aAutoFilterFont
.SetColor(aAFilterTextColor
);
237 aBoldFont
.SetColor( aTextColor
);
239 aBoldFont
.SetColor( aSelTextColor
);
242 SetTextColor(aAFilterTextColor
);
244 SetTextColor((bBoldSet
&& !bHighContrast
) ? aSelTextColor
: aTextColor
);
246 ScModule
* mod
= ScModule::get();
247 Color aSelLineColor
= mod
->GetColorConfig().GetColorValue(svtools::CALCCELLFOCUS
).nColor
;
248 aSelLineColor
.Merge( COL_BLACK
, 0xe0 ); // darken just a little bit
250 bool bLayoutRTL
= IsLayoutRTL();
251 tools::Long nLayoutSign
= bLayoutRTL
? -1 : 1;
252 bool bMirrored
= IsMirrored();
260 nBarSize
= static_cast<sal_uInt16
>(GetSizePixel().Width());
262 nBarSize
= static_cast<sal_uInt16
>(GetSizePixel().Height());
264 SCCOLROW nPos
= GetPos();
266 tools::Long nPStart
= bVertical
? rRect
.Top() : rRect
.Left();
267 tools::Long nPEnd
= bVertical
? rRect
.Bottom() : rRect
.Right();
269 tools::Long nTransStart
= nPEnd
+ 1;
270 tools::Long nTransEnd
= 0;
272 tools::Long nInitScrPos
= 0;
275 std::swap(nPStart
, nPEnd
);
276 std::swap(nTransStart
, nTransEnd
);
277 if ( bVertical
) // start loops from the end
278 nInitScrPos
= GetSizePixel().Height() - 1;
280 nInitScrPos
= GetSizePixel().Width() - 1;
283 // complete the painting of the outer lines
284 // first find the end of the last cell
286 tools::Long nLineEnd
= nInitScrPos
- nLayoutSign
;
288 for (SCCOLROW i
=nPos
; i
<nSize
; i
++)
290 sal_uInt16 nSizePix
= GetEntrySize( i
);
293 nLineEnd
+= nSizePix
* nLayoutSign
;
295 if ( bMarkRange
&& i
>= nMarkStart
&& i
<= nMarkEnd
)
297 tools::Long nLineStart
= nLineEnd
- ( nSizePix
- 1 ) * nLayoutSign
;
298 if ( nLineStart
* nLayoutSign
< nTransStart
* nLayoutSign
)
299 nTransStart
= nLineStart
;
300 if ( nLineEnd
* nLayoutSign
> nTransEnd
* nLayoutSign
)
301 nTransEnd
= nLineEnd
;
304 if ( nLineEnd
* nLayoutSign
> nPEnd
* nLayoutSign
)
312 SCCOLROW nHidden
= GetHiddenCount(i
);
318 // background is different for entry area and behind the entries
320 tools::Rectangle aFillRect
;
321 GetOutDev()->SetLineColor();
323 if ( nLineEnd
* nLayoutSign
>= nInitScrPos
* nLayoutSign
)
325 Color
aFaceColor(rStyleSettings
.GetFaceColor());
327 aFaceColor
.IncreaseLuminance(20);
329 aFaceColor
.DecreaseLuminance(20);
330 GetOutDev()->SetFillColor( aFaceColor
);
332 aFillRect
= tools::Rectangle( 0, nInitScrPos
, nBarSize
-1, nLineEnd
);
334 aFillRect
= tools::Rectangle( nInitScrPos
, 0, nLineEnd
, nBarSize
-1 );
335 GetOutDev()->DrawRect( aFillRect
);
338 if ( nLineEnd
* nLayoutSign
< nPEnd
* nLayoutSign
)
340 GetOutDev()->SetFillColor( mod
->GetColorConfig().GetColorValue(svtools::APPBACKGROUND
).nColor
);
342 aFillRect
= tools::Rectangle( 0, nLineEnd
+nLayoutSign
, nBarSize
-1, nPEnd
);
344 aFillRect
= tools::Rectangle( nLineEnd
+nLayoutSign
, 0, nPEnd
, nBarSize
-1 );
345 GetOutDev()->DrawRect( aFillRect
);
348 if ( nLineEnd
* nLayoutSign
>= nPStart
* nLayoutSign
)
350 if ( nTransEnd
* nLayoutSign
>= nTransStart
* nLayoutSign
)
353 aFillRect
= tools::Rectangle( 0, nTransStart
, nBarSize
-1, nTransEnd
);
355 aFillRect
= tools::Rectangle( nTransStart
, 0, nTransEnd
, nBarSize
-1 );
361 // solid grey background for dark face color is drawn before lines
362 GetOutDev()->SetLineColor();
363 GetOutDev()->SetFillColor( COL_LIGHTGRAY
);
364 GetOutDev()->DrawRect( aFillRect
);
369 // background for selection
370 GetOutDev()->SetLineColor();
371 Color aColor
= mod
->GetColorConfig().GetColorValue(svtools::CALCCELLFOCUS
).nColor
;
372 // merging the highlightcolor (which is used if accent does not exist) with the background
373 // fails in many cases such as Breeze Dark (highlight is too close to background) and
374 // Breeze Light (font color is white and not readable anymore)
376 aColor
.Merge( rStyleSettings
.GetFaceColor(), 80 );
378 GetOutDev()->SetFillColor( aColor
);
379 GetOutDev()->DrawRect( aFillRect
);
383 GetOutDev()->SetLineColor( rStyleSettings
.GetShadowColor() );
386 GetOutDev()->DrawLine( Point( 0, nPStart
), Point( 0, nLineEnd
) ); //left
387 GetOutDev()->DrawLine( Point( nBarSize
-1, nPStart
), Point( nBarSize
-1, nLineEnd
) ); //right
391 GetOutDev()->DrawLine( Point( nPStart
, nBarSize
-1 ), Point( nLineEnd
, nBarSize
-1 ) ); //bottom
392 GetOutDev()->DrawLine( Point( nPStart
, 0 ), Point( nLineEnd
, 0 ) ); //top
396 // tdf#89841 Use blue row numbers when Autofilter selected
397 std::vector
<sc::ColRowSpan
> aSpans
;
398 if (bVertical
&& pTabView
)
400 SCTAB nTab
= pTabView
->GetViewData().GetTabNo();
401 ScDocument
& rDoc
= pTabView
->GetViewData().GetDocument();
403 ScDBData
* pDBData
= rDoc
.GetAnonymousDBData(nTab
);
404 if (pDBData
&& pDBData
->HasAutoFilter())
406 SCSIZE nSelected
= 0;
408 pDBData
->GetFilterSelCount(nSelected
, nTotal
);
409 if (nTotal
> nSelected
)
412 pDBData
->GetArea(aRange
);
413 SCCOLROW nStartRow
= static_cast<SCCOLROW
>(aRange
.aStart
.Row());
414 SCCOLROW nEndRow
= static_cast<SCCOLROW
>(aRange
.aEnd
.Row());
415 if (pDBData
->HasHeader())
417 aSpans
.push_back(sc::ColRowSpan(nStartRow
, nEndRow
));
421 ScDBCollection
* pDocColl
= rDoc
.GetDBCollection();
422 if (!pDocColl
->empty())
424 ScDBCollection::NamedDBs
& rDBs
= pDocColl
->getNamedDBs();
425 for (const auto& rxDB
: rDBs
)
427 if (rxDB
->GetTab() == nTab
&& rxDB
->HasAutoFilter())
429 SCSIZE nSelected
= 0;
431 rxDB
->GetFilterSelCount(nSelected
, nTotal
);
432 if (nTotal
> nSelected
)
435 rxDB
->GetArea(aRange
);
436 SCCOLROW nStartRow
= static_cast<SCCOLROW
>(aRange
.aStart
.Row());
437 SCCOLROW nEndRow
= static_cast<SCCOLROW
>(aRange
.aEnd
.Row());
438 if (rxDB
->HasHeader())
440 aSpans
.push_back(sc::ColRowSpan(nStartRow
, nEndRow
));
447 // loop through entries several times to avoid changing the line color too often
448 // and to allow merging of lines
450 ScGridMerger
aGrid( GetOutDev(), 1, 1 );
452 // start at SC_HDRPAINT_BOTTOM instead of 0 - selection doesn't get different
453 // borders, light border at top isn't used anymore
454 // use SC_HDRPAINT_SEL_BOTTOM for different color
456 for (sal_uInt16 nPass
= SC_HDRPAINT_SEL_BOTTOM
; nPass
< SC_HDRPAINT_COUNT
; nPass
++)
458 // set line color etc. before entry loop
461 case SC_HDRPAINT_SEL_BOTTOM
:
462 // same as non-selected for high contrast
463 GetOutDev()->SetLineColor( bHighContrast
? rStyleSettings
.GetShadowColor() : aSelLineColor
);
465 case SC_HDRPAINT_BOTTOM
:
466 GetOutDev()->SetLineColor( rStyleSettings
.GetShadowColor() );
468 case SC_HDRPAINT_TEXT
:
469 // DrawSelectionBackground is used only for high contrast on light background
470 if ( nTransEnd
* nLayoutSign
>= nTransStart
* nLayoutSign
&& bHighContrast
&& !bDark
)
472 // Transparent selection background is drawn after lines, before text.
473 // Use DrawSelectionBackground to make sure there is a visible
474 // difference. The case of a dark face color, where DrawSelectionBackground
475 // would just paint over the lines, is handled separately (bDark).
476 // Otherwise, GetHighlightColor is used with 80% transparency.
477 // The window's background color (SetBackground) has to be the background
478 // of the cell area, for the contrast comparison in DrawSelectionBackground.
480 tools::Rectangle aTransRect
;
482 aTransRect
= tools::Rectangle( 0, nTransStart
, nBarSize
-1, nTransEnd
);
484 aTransRect
= tools::Rectangle( nTransStart
, 0, nTransEnd
, nBarSize
-1 );
485 SetBackground( rStyleSettings
.GetFaceColor() );
486 DrawSelectionBackground( aTransRect
, 0, true, false );
493 tools::Long nScrPos
=nInitScrPos
;
497 aScrPos
= Point( 0, nScrPos
);
499 aScrPos
= Point( nScrPos
, 0 );
501 SCCOLROW nEntryNo
= nCount
+ nPos
;
502 if ( nEntryNo
>= nSize
) // rDoc.MaxCol()/rDoc.MaxRow()
503 nScrPos
= nPEnd
+ nLayoutSign
; // beyond nPEnd -> stop
506 sal_uInt16 nSizePix
= GetEntrySize( nEntryNo
);
510 SCCOLROW nHidden
= GetHiddenCount(nEntryNo
);
512 nCount
+= nHidden
- 1;
514 else if ((nScrPos
+nSizePix
*nLayoutSign
)*nLayoutSign
>= nPStart
*nLayoutSign
)
516 Point
aEndPos(aScrPos
);
518 aEndPos
= Point( aScrPos
.X()+nBarSize
-1, aScrPos
.Y()+(nSizePix
-1)*nLayoutSign
);
520 aEndPos
= Point( aScrPos
.X()+(nSizePix
-1)*nLayoutSign
, aScrPos
.Y()+nBarSize
-1 );
522 bool bMark
= bMarkRange
&& nEntryNo
>= nMarkStart
&& nEntryNo
<= nMarkEnd
;
523 bool bNextToMark
= bMarkRange
&& nEntryNo
+ 1 >= nMarkStart
&& nEntryNo
<= nMarkEnd
;
527 case SC_HDRPAINT_SEL_BOTTOM
:
528 case SC_HDRPAINT_BOTTOM
:
529 if ( nPass
== ( bNextToMark
? SC_HDRPAINT_SEL_BOTTOM
: SC_HDRPAINT_BOTTOM
) )
532 aGrid
.AddHorLine(/* here we work in pixels */ true, aScrPos
.X(), aEndPos
.X(), aEndPos
.Y());
534 aGrid
.AddVerLine(/* here we work in pixels */ true, aEndPos
.X(), aScrPos
.Y(), aEndPos
.Y());
536 // thick bottom for hidden rows
537 // (drawn directly, without aGrid)
538 if ( nEntryNo
+1 < nSize
)
539 if ( GetEntrySize(nEntryNo
+1)==0 )
542 GetOutDev()->DrawLine( Point(aScrPos
.X(),aEndPos
.Y()-nLayoutSign
),
543 Point(aEndPos
.X(),aEndPos
.Y()-nLayoutSign
) );
545 GetOutDev()->DrawLine( Point(aEndPos
.X()-nLayoutSign
,aScrPos
.Y()),
546 Point(aEndPos
.X()-nLayoutSign
,aEndPos
.Y()) );
551 case SC_HDRPAINT_TEXT
:
552 if ( nSizePix
> 1 ) // minimal check for small columns/rows
556 bool bAutoFilterPos
= false;
557 for (const auto& rSpan
: aSpans
)
559 if (nEntryNo
>= rSpan
.mnStart
&& nEntryNo
<= rSpan
.mnEnd
)
561 bAutoFilterPos
= true;
566 if (bMark
!= bBoldSet
|| bAutoFilterPos
!= bAutoFilterSet
)
570 else if (bAutoFilterPos
)
571 SetFont(aAutoFilterFont
);
575 bAutoFilterSet
= bAutoFilterPos
&& !bMark
;
580 if (bMark
!= bBoldSet
)
590 aString
= GetEntryText( nEntryNo
);
591 aTextSize
.setWidth( GetTextWidth( aString
) );
592 aTextSize
.setHeight( GetTextHeight() );
594 Point
aTxtPos(aScrPos
);
597 aTxtPos
.AdjustX((nBarSize
-aTextSize
.Width())/2 );
598 aTxtPos
.AdjustY((nSizePix
*nLayoutSign
-aTextSize
.Height())/2 );
600 aTxtPos
.AdjustX(1 ); // dark border is left instead of right
604 aTxtPos
.AdjustX((nSizePix
*nLayoutSign
-aTextSize
.Width()+1)/2 );
605 aTxtPos
.AdjustY((nBarSize
-aTextSize
.Height())/2 );
607 GetOutDev()->DrawText( aTxtPos
, aString
);
612 // when selecting the complete row/column:
613 // InvertRect( Rectangle( aScrPos, aEndPos ) );
615 nScrPos
+= nSizePix
* nLayoutSign
; // also if before the visible area
619 while ( nScrPos
* nLayoutSign
<= nPEnd
* nLayoutSign
);
625 SCCOLROW
ScHeaderControl::GetMousePos(const Point
& rPos
, bool& rBorder
) const
628 SCCOLROW nPos
= GetPos();
629 SCCOLROW nHitNo
= nPos
;
630 SCCOLROW nEntryNo
= 1 + nPos
;
632 tools::Long nMousePos
= bVertical
? rPos
.Y() : rPos
.X();
634 Size aSize
= GetOutputSizePixel();
635 tools::Long nWinSize
= bVertical
? aSize
.Height() : aSize
.Width();
637 bool bLayoutRTL
= IsLayoutRTL();
638 tools::Long nLayoutSign
= bLayoutRTL
? -1 : 1;
639 tools::Long nEndPos
= bLayoutRTL
? -1 : nWinSize
;
641 nScrPos
= GetScrPos( nPos
) - nLayoutSign
;
644 if (nEntryNo
> nSize
)
645 nScrPos
= nEndPos
+ nLayoutSign
;
647 nScrPos
+= GetEntrySize( nEntryNo
- 1 ) * nLayoutSign
; //! GetHiddenCount() ??
649 nDif
= nMousePos
- nScrPos
;
650 if (nDif
>= -5 && nDif
<= 5)
655 else if (nDif
* nLayoutSign
>= 0 && nEntryNo
< nSize
)
659 while ( nScrPos
* nLayoutSign
< nEndPos
* nLayoutSign
&& nDif
* nLayoutSign
> 0 );
665 bool ScHeaderControl::IsSelectionAllowed(SCCOLROW nPos
) const
667 ScTabViewShell
* pViewSh
= dynamic_cast<ScTabViewShell
*>(SfxViewShell::Current());
671 ScViewData
& rViewData
= pViewSh
->GetViewData();
672 sal_uInt16 nTab
= rViewData
.GetTabNo();
673 ScDocument
& rDoc
= rViewData
.GetDocument();
674 const ScTableProtection
* pProtect
= rDoc
.GetTabProtection(nTab
);
675 bool bSelectAllowed
= true;
676 if ( pProtect
&& pProtect
->isProtected() )
678 // This sheet is protected. Check if a context menu is allowed on this cell.
679 bool bCellsProtected
= false;
683 SCROW nRPos
= static_cast<SCROW
>(nPos
);
684 bCellsProtected
= rDoc
.HasAttrib(0, nRPos
, nTab
, rDoc
.MaxCol(), nRPos
, nTab
, HasAttrFlags::Protected
);
689 SCCOL nCPos
= static_cast<SCCOL
>(nPos
);
690 bCellsProtected
= rDoc
.HasAttrib(nCPos
, 0, nTab
, nCPos
, rDoc
.MaxRow(), nTab
, HasAttrFlags::Protected
);
693 bool bSelProtected
= pProtect
->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS
);
694 bool bSelUnprotected
= pProtect
->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS
);
697 bSelectAllowed
= bSelProtected
;
699 bSelectAllowed
= bSelUnprotected
;
701 return bSelectAllowed
;
704 void ScHeaderControl::MouseButtonDown( const MouseEvent
& rMEvt
)
713 SCCOLROW nHitNo
= GetMousePos(rMEvt
.GetPosPixel(), bIsBorder
);
714 if (!IsSelectionAllowed(nHitNo
))
716 if ( ! rMEvt
.IsLeft() )
718 if (ScModule::get()->IsFormulaMode())
722 SCTAB nTab
= pTabView
->GetViewData().GetTabNo();
723 if( !rMEvt
.IsShift() )
724 pTabView
->DoneRefMode( rMEvt
.IsMod1() );
725 ScDocument
& rDoc
= pTabView
->GetViewData().GetDocument();
728 pTabView
->InitRefMode( nHitNo
, 0, nTab
, SC_REFTYPE_REF
);
729 pTabView
->UpdateRef( nHitNo
, rDoc
.MaxRow(), nTab
);
733 pTabView
->InitRefMode( 0, nHitNo
, nTab
, SC_REFTYPE_REF
);
734 pTabView
->UpdateRef( rDoc
.MaxCol(), nHitNo
, nTab
);
739 if ( bIsBorder
&& ResizeAllowed() )
742 sal_uInt16 nClicks
= rMEvt
.GetClicks();
743 if ( nClicks
&& nClicks
%2==0 )
745 SetEntrySize( nDragNo
, HDR_SIZE_OPTIMUM
);
746 SetPointer( PointerStyle::Arrow
);
751 nDragStart
= rMEvt
.GetPosPixel().Y();
753 nDragStart
= rMEvt
.GetPosPixel().X();
754 nDragPos
= nDragStart
;
755 // tdf#140833 launch help tip to show after the double click time has expired
756 // so under gtk the popover isn't active when the double click is processed
757 // by gtk because under load on wayland the double click is getting handled
758 // by something else and getting sent to the window underneath our window
759 aShowHelpTimer
.Start();
760 DrawInvert( nDragPos
);
769 pSelEngine
->SetWindow( this );
770 tools::Rectangle
aVis( Point(), GetOutputSizePixel() );
773 aVis
.SetLeft( LONG_MIN
);
774 aVis
.SetRight( LONG_MAX
);
778 aVis
.SetTop( LONG_MIN
);
779 aVis
.SetBottom( LONG_MAX
);
781 pSelEngine
->SetVisibleArea( aVis
);
783 SetMarking( true ); // must precede SelMouseButtonDown
784 pSelEngine
->SelMouseButtonDown( rMEvt
);
786 // In column/row headers a simple click already is a selection.
787 // -> Call SelMouseMove to ensure CreateAnchor is called (and DestroyAnchor
788 // if the next click is somewhere else with Control key).
789 pSelEngine
->SelMouseMove( rMEvt
);
791 if (IsMouseCaptured())
793 // tracking instead of CaptureMouse, so it can be cancelled cleanly
794 //! someday SelectionEngine itself should call StartTracking!?!
801 void ScHeaderControl::MouseButtonUp( const MouseEvent
& rMEvt
)
806 if (ScModule
* mod
= ScModule::get(); mod
->IsFormulaMode())
818 DrawInvert( nDragPos
);
823 tools::Long nScrPos
= GetScrPos( nDragNo
);
824 tools::Long nMousePos
= bVertical
? rMEvt
.GetPosPixel().Y() : rMEvt
.GetPosPixel().X();
825 bool bLayoutRTL
= IsLayoutRTL();
826 tools::Long nNewWidth
= bLayoutRTL
? ( nScrPos
- nMousePos
+ 1 )
827 : ( nMousePos
+ 2 - nScrPos
);
829 if ( nNewWidth
< 0 /* && !IsSelected(nDragNo) */ )
832 SCCOLROW nEnd
= nDragNo
;
833 while (nNewWidth
< 0)
839 nNewWidth
+= GetEntrySize( nDragNo
); //! GetHiddenCount() ???
844 HideEntries( nStart
, nEnd
);
849 SetEntrySize( nDragNo
, static_cast<sal_uInt16
>(nNewWidth
) );
854 pSelEngine
->SelMouseButtonUp( rMEvt
);
859 void ScHeaderControl::MouseMove( const MouseEvent
& rMEvt
)
863 SetPointer( PointerStyle::Arrow
);
867 if (bInRefMode
&& rMEvt
.IsLeft() && ScModule::get()->IsFormulaMode())
872 SCCOLROW nHitNo
= GetMousePos(rMEvt
.GetPosPixel(), bTmp
);
873 SCTAB nTab
= pTabView
->GetViewData().GetTabNo();
874 ScDocument
& rDoc
= pTabView
->GetViewData().GetDocument();
876 pTabView
->UpdateRef( nHitNo
, rDoc
.MaxRow(), nTab
);
878 pTabView
->UpdateRef( rDoc
.MaxCol(), nHitNo
, nTab
);
885 tools::Long nNewPos
= bVertical
? rMEvt
.GetPosPixel().Y() : rMEvt
.GetPosPixel().X();
886 if ( nNewPos
!= nDragPos
)
888 DrawInvert( nDragPos
);
891 DrawInvert( nDragPos
);
893 if (nDragPos
<= nDragStart
-SC_DRAG_MIN
|| nDragPos
>= nDragStart
+SC_DRAG_MIN
)
900 (void)GetMousePos(rMEvt
.GetPosPixel(), bIsBorder
);
902 if ( bIsBorder
&& rMEvt
.GetButtons()==0 && ResizeAllowed() )
903 SetPointer( bVertical
? PointerStyle::VSizeBar
: PointerStyle::HSizeBar
);
905 SetPointer( PointerStyle::Arrow
);
908 pSelEngine
->SelMouseMove( rMEvt
);
912 void ScHeaderControl::Tracking( const TrackingEvent
& rTEvt
)
914 // Distribute the tracking events to the various MouseEvents, because
915 // SelectionEngine does not know anything about Tracking
917 if ( rTEvt
.IsTrackingCanceled() )
919 else if ( rTEvt
.IsTrackingEnded() )
920 MouseButtonUp( rTEvt
.GetMouseEvent() );
922 MouseMove( rTEvt
.GetMouseEvent() );
925 void ScHeaderControl::Command( const CommandEvent
& rCEvt
)
927 CommandEventId nCmd
= rCEvt
.GetCommand();
928 if ( nCmd
== CommandEventId::ContextMenu
)
930 StopMarking(); // finish selection / dragging
932 // execute popup menu
934 ScTabViewShell
* pViewSh
= dynamic_cast< ScTabViewShell
*>( SfxViewShell::Current() );
937 if ( rCEvt
.IsMouseEvent() )
939 // #i18735# select the column/row under the mouse pointer
940 ScViewData
& rViewData
= pViewSh
->GetViewData();
942 SelectWindow(); // also deselects drawing objects, stops draw text edit
943 if ( rViewData
.HasEditView( rViewData
.GetActivePart() ) )
944 ScModule::get()->InputEnterHandler(); // always end edit mode
947 SCCOLROW nPos
= GetMousePos(rCEvt
.GetMousePosPixel(), bBorder
);
948 if (!IsSelectionAllowed(nPos
))
949 // Selecting this cell is not allowed, neither is context menu.
952 SCTAB nTab
= rViewData
.GetTabNo();
953 ScDocument
& rDoc
= pViewSh
->GetViewData().GetDocument();
956 aNewRange
= ScRange( 0, sal::static_int_cast
<SCROW
>(nPos
), nTab
,
957 rDoc
.MaxCol(), sal::static_int_cast
<SCROW
>(nPos
), nTab
);
959 aNewRange
= ScRange( sal::static_int_cast
<SCCOL
>(nPos
), 0, nTab
,
960 sal::static_int_cast
<SCCOL
>(nPos
), rDoc
.MaxRow(), nTab
);
962 // see if any part of the range is already selected
964 rViewData
.GetMarkData().FillRangeListWithMarks( &aRanges
, false );
965 bool bSelected
= aRanges
.Intersects(aNewRange
);
967 // select the range if no part of it was selected
969 pViewSh
->MarkRange( aNewRange
);
972 pViewSh
->GetDispatcher()->ExecutePopup( bVertical
? u
"rowheader"_ustr
: u
"colheader"_ustr
);
975 else if ( nCmd
== CommandEventId::StartDrag
)
977 pSelEngine
->Command( rCEvt
);
981 void ScHeaderControl::StopMarking()
985 DrawInvert( nDragPos
);
993 // don't call pSelEngine->Reset, so selection across the parts of
994 // a split/frozen view is possible
995 if (IsMouseCaptured())
999 IMPL_LINK_NOARG(ScHeaderControl
, ShowDragHelpHdl
, Timer
*, void)
1004 void ScHeaderControl::ShowDragHelp()
1006 aShowHelpTimer
.Stop();
1007 if (!Help::IsQuickHelpEnabled())
1010 tools::Long nScrPos
= GetScrPos( nDragNo
);
1011 bool bLayoutRTL
= IsLayoutRTL();
1012 tools::Long nVal
= bLayoutRTL
? ( nScrPos
- nDragPos
+ 1 )
1013 : ( nDragPos
+ 2 - nScrPos
);
1015 OUString aHelpStr
= GetDragHelp( nVal
);
1016 Point aPos
= OutputToScreenPixel( Point(0,0) );
1017 Size aSize
= GetSizePixel();
1019 Point aMousePos
= OutputToScreenPixel(GetPointerPosPixel());
1021 tools::Rectangle aRect
;
1022 QuickHelpFlags nAlign
;
1026 aRect
.SetLeft( aMousePos
.X() );
1027 aRect
.SetTop( aPos
.Y() - 4 );
1028 nAlign
= QuickHelpFlags::Bottom
|QuickHelpFlags::Center
;
1033 aRect
.SetLeft( aPos
.X() + aSize
.Width() + 8 );
1034 aRect
.SetTop( aMousePos
.Y() - 2 );
1035 nAlign
= QuickHelpFlags::Left
|QuickHelpFlags::Bottom
;
1038 aRect
.SetRight( aRect
.Left() );
1039 aRect
.SetBottom( aRect
.Top() );
1042 Help::HidePopover(this, nTipVisible
);
1043 nTipVisible
= Help::ShowPopover(this, aRect
, aHelpStr
, nAlign
);
1046 void ScHeaderControl::HideDragHelp()
1048 aShowHelpTimer
.Stop();
1051 Help::HidePopover(this, nTipVisible
);
1052 nTipVisible
= nullptr;
1056 void ScHeaderControl::RequestHelp( const HelpEvent
& rHEvt
)
1058 // If the own QuickHelp is displayed, don't let RequestHelp remove it
1060 bool bOwn
= bDragging
&& Help::IsQuickHelpEnabled();
1062 Window::RequestHelp(rHEvt
);
1065 // dummies for virtual methods
1067 SCCOLROW
ScHeaderControl::GetHiddenCount( SCCOLROW nEntryNo
) const
1069 SCCOLROW nHidden
= 0;
1070 while ( nEntryNo
< nSize
&& GetEntrySize( nEntryNo
) == 0 )
1078 bool ScHeaderControl::IsLayoutRTL() const
1083 bool ScHeaderControl::IsMirrored() const
1088 bool ScHeaderControl::IsDisabled() const
1093 bool ScHeaderControl::ResizeAllowed() const
1098 void ScHeaderControl::SelectWindow()
1102 void ScHeaderControl::DrawInvert( tools::Long
/* nDragPos */ )
1106 OUString
ScHeaderControl::GetDragHelp( tools::Long
/* nVal */ )
1111 void ScHeaderControl::SetMarking( bool /* bSet */ )
1115 void ScHeaderControl::GetMarkRange(SCCOLROW
& rStart
, SCCOLROW
& rEnd
) const
1117 rStart
= nMarkStart
;
1121 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */