tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / view / hdrcont.cxx
blobee8a90f92d70f205aaf667e61226edf4bbbf2219
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
29 #include <dbdata.hxx>
30 #include <scmod.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>
40 #define SC_DRAG_MIN 2
42 // passes in paint
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 ) :
53 Window ( pParent ),
54 pSelEngine ( pSelectionEngine ),
55 aShowHelpTimer("sc HeaderControl Popover Timer"),
56 bVertical ( bNewVertical ),
57 nSize ( nNewSize ),
58 nMarkStart ( 0 ),
59 nMarkEnd ( 0 ),
60 bMarkRange ( false ),
61 bDragging ( false ),
62 nDragNo ( 0 ),
63 nDragStart ( 0 ),
64 nDragPos ( 0 ),
65 nTipVisible ( nullptr ),
66 bDragMoved ( false ),
67 bIgnoreMove ( false ),
68 bInRefMode ( false ),
69 pTabView ( pTab )
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.
76 EnableRTL( false );
78 aNormFont = GetFont();
79 aNormFont.SetTransparent( true ); //! hard-set WEIGHT_NORMAL ???
80 aBoldFont = aNormFont;
81 aBoldFont.SetWeight( WEIGHT_BOLD );
82 aAutoFilterFont = aNormFont;
84 SetFont(aBoldFont);
85 bBoldSet = true;
86 bAutoFilterSet = false;
88 Size aSize = LogicToPixel( Size(
89 GetTextWidth(u"8888"_ustr),
90 GetTextHeight() ) );
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());
101 SetBackground();
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 );
118 nWidth = nNew;
120 Invalidate();
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() );
134 if ( bVertical )
136 aRect.SetTop( GetScrPos( nStart )-nLayoutSign ); // extra pixel for line at top of selection
137 aRect.SetBottom( GetScrPos( nEnd+1 )-nLayoutSign );
139 else
141 aRect.SetLeft( GetScrPos( nStart )-nLayoutSign ); // extra pixel for line left of selection
142 aRect.SetRight( GetScrPos( nEnd+1 )-nLayoutSign );
144 Invalidate(aRect);
147 void ScHeaderControl::SetMark( bool bNewSet, SCCOLROW nNewStart, SCCOLROW nNewEnd )
149 bool bEnabled = ScModule::get()->GetInputOptions().GetMarkHeader(); //! cache?
150 if (!bEnabled)
151 bNewSet = false;
153 bool bOldSet = bMarkRange;
154 SCCOLROW nOldStart = nMarkStart;
155 SCCOLROW nOldEnd = nMarkEnd;
156 PutInOrder( nNewStart, nNewEnd );
157 bMarkRange = bNewSet;
158 nMarkStart = nNewStart;
159 nMarkEnd = nNewEnd;
161 // Paint
163 if ( bNewSet )
165 if ( bOldSet )
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 )
176 // two areas
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 ) );
183 else
184 DoPaint( nNewStart, nNewEnd ); // completely new selection
186 else if ( bOldSet )
187 DoPaint( nOldStart, nOldEnd ); // cancel selection
190 tools::Long ScHeaderControl::GetScrPos( SCCOLROW nEntryNo ) const
192 tools::Long nScrPos;
194 tools::Long nMax = ( bVertical ? GetOutputSizePixel().Height() : GetOutputSizePixel().Width() ) + 1;
195 if (nEntryNo >= nSize)
196 nScrPos = nMax;
197 else
199 nScrPos = 0;
200 for (SCCOLROW i=GetPos(); i<nEntryNo && nScrPos<nMax; i++)
202 sal_uInt16 nAdd = GetEntrySize(i);
203 if (nAdd)
204 nScrPos += nAdd;
205 else
207 SCCOLROW nHidden = GetHiddenCount(i);
208 if (nHidden > 0)
209 i += nHidden - 1;
214 if ( IsLayoutRTL() )
215 nScrPos = nMax - nScrPos - 2;
217 return nScrPos;
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
223 // grouped together
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);
236 if ( bHighContrast )
237 aBoldFont.SetColor( aTextColor );
238 else
239 aBoldFont.SetColor( aSelTextColor );
241 if (bAutoFilterSet)
242 SetTextColor(aAFilterTextColor);
243 else
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();
254 OUString aString;
255 sal_uInt16 nBarSize;
256 Point aScrPos;
257 Size aTextSize;
259 if (bVertical)
260 nBarSize = static_cast<sal_uInt16>(GetSizePixel().Width());
261 else
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;
273 if ( bLayoutRTL )
275 std::swap(nPStart, nPEnd);
276 std::swap(nTransStart, nTransEnd);
277 if ( bVertical ) // start loops from the end
278 nInitScrPos = GetSizePixel().Height() - 1;
279 else
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 );
291 if (nSizePix)
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 )
306 nLineEnd = nPEnd;
307 break;
310 else
312 SCCOLROW nHidden = GetHiddenCount(i);
313 if (nHidden > 0)
314 i += nHidden - 1;
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());
326 if (bDark)
327 aFaceColor.IncreaseLuminance(20);
328 else
329 aFaceColor.DecreaseLuminance(20);
330 GetOutDev()->SetFillColor( aFaceColor );
331 if ( bVertical )
332 aFillRect = tools::Rectangle( 0, nInitScrPos, nBarSize-1, nLineEnd );
333 else
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 );
341 if ( bVertical )
342 aFillRect = tools::Rectangle( 0, nLineEnd+nLayoutSign, nBarSize-1, nPEnd );
343 else
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 )
352 if (bVertical)
353 aFillRect = tools::Rectangle( 0, nTransStart, nBarSize-1, nTransEnd );
354 else
355 aFillRect = tools::Rectangle( nTransStart, 0, nTransEnd, nBarSize-1 );
357 if ( bHighContrast )
359 if ( bDark )
361 // solid grey background for dark face color is drawn before lines
362 GetOutDev()->SetLineColor();
363 GetOutDev()->SetFillColor( COL_LIGHTGRAY );
364 GetOutDev()->DrawRect( aFillRect );
367 else
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)
375 #ifdef MACOSX
376 aColor.Merge( rStyleSettings.GetFaceColor(), 80 );
377 #endif
378 GetOutDev()->SetFillColor( aColor );
379 GetOutDev()->DrawRect( aFillRect );
383 GetOutDev()->SetLineColor( rStyleSettings.GetShadowColor() );
384 if (bVertical)
386 GetOutDev()->DrawLine( Point( 0, nPStart ), Point( 0, nLineEnd ) ); //left
387 GetOutDev()->DrawLine( Point( nBarSize-1, nPStart ), Point( nBarSize-1, nLineEnd ) ); //right
389 else
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;
407 SCSIZE nTotal = 0;
408 pDBData->GetFilterSelCount(nSelected, nTotal);
409 if (nTotal > nSelected)
411 ScRange aRange;
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())
416 nStartRow++;
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;
430 SCSIZE nTotal = 0;
431 rxDB->GetFilterSelCount(nSelected, nTotal);
432 if (nTotal > nSelected)
434 ScRange aRange;
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())
439 nStartRow++;
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
459 switch ( nPass )
461 case SC_HDRPAINT_SEL_BOTTOM:
462 // same as non-selected for high contrast
463 GetOutDev()->SetLineColor( bHighContrast ? rStyleSettings.GetShadowColor() : aSelLineColor );
464 break;
465 case SC_HDRPAINT_BOTTOM:
466 GetOutDev()->SetLineColor( rStyleSettings.GetShadowColor() );
467 break;
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;
481 if (bVertical)
482 aTransRect = tools::Rectangle( 0, nTransStart, nBarSize-1, nTransEnd );
483 else
484 aTransRect = tools::Rectangle( nTransStart, 0, nTransEnd, nBarSize-1 );
485 SetBackground( rStyleSettings.GetFaceColor() );
486 DrawSelectionBackground( aTransRect, 0, true, false );
487 SetBackground();
489 break;
492 SCCOLROW nCount=0;
493 tools::Long nScrPos=nInitScrPos;
496 if (bVertical)
497 aScrPos = Point( 0, nScrPos );
498 else
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
504 else
506 sal_uInt16 nSizePix = GetEntrySize( nEntryNo );
508 if (nSizePix == 0)
510 SCCOLROW nHidden = GetHiddenCount(nEntryNo);
511 if (nHidden > 0)
512 nCount += nHidden - 1;
514 else if ((nScrPos+nSizePix*nLayoutSign)*nLayoutSign >= nPStart*nLayoutSign)
516 Point aEndPos(aScrPos);
517 if (bVertical)
518 aEndPos = Point( aScrPos.X()+nBarSize-1, aScrPos.Y()+(nSizePix-1)*nLayoutSign );
519 else
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;
525 switch ( nPass )
527 case SC_HDRPAINT_SEL_BOTTOM:
528 case SC_HDRPAINT_BOTTOM:
529 if ( nPass == ( bNextToMark ? SC_HDRPAINT_SEL_BOTTOM : SC_HDRPAINT_BOTTOM ) )
531 if (bVertical)
532 aGrid.AddHorLine(/* here we work in pixels */ true, aScrPos.X(), aEndPos.X(), aEndPos.Y());
533 else
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 )
541 if (bVertical)
542 GetOutDev()->DrawLine( Point(aScrPos.X(),aEndPos.Y()-nLayoutSign),
543 Point(aEndPos.X(),aEndPos.Y()-nLayoutSign) );
544 else
545 GetOutDev()->DrawLine( Point(aEndPos.X()-nLayoutSign,aScrPos.Y()),
546 Point(aEndPos.X()-nLayoutSign,aEndPos.Y()) );
549 break;
551 case SC_HDRPAINT_TEXT:
552 if ( nSizePix > 1 ) // minimal check for small columns/rows
554 if (bVertical)
556 bool bAutoFilterPos = false;
557 for (const auto& rSpan : aSpans)
559 if (nEntryNo >= rSpan.mnStart && nEntryNo <= rSpan.mnEnd)
561 bAutoFilterPos = true;
562 break;
566 if (bMark != bBoldSet || bAutoFilterPos != bAutoFilterSet)
568 if (bMark)
569 SetFont(aBoldFont);
570 else if (bAutoFilterPos)
571 SetFont(aAutoFilterFont);
572 else
573 SetFont(aNormFont);
574 bBoldSet = bMark;
575 bAutoFilterSet = bAutoFilterPos && !bMark;
578 else
580 if (bMark != bBoldSet)
582 if (bMark)
583 SetFont(aBoldFont);
584 else
585 SetFont(aNormFont);
586 bBoldSet = bMark;
590 aString = GetEntryText( nEntryNo );
591 aTextSize.setWidth( GetTextWidth( aString ) );
592 aTextSize.setHeight( GetTextHeight() );
594 Point aTxtPos(aScrPos);
595 if (bVertical)
597 aTxtPos.AdjustX((nBarSize-aTextSize.Width())/2 );
598 aTxtPos.AdjustY((nSizePix*nLayoutSign-aTextSize.Height())/2 );
599 if ( bMirrored )
600 aTxtPos.AdjustX(1 ); // dark border is left instead of right
602 else
604 aTxtPos.AdjustX((nSizePix*nLayoutSign-aTextSize.Width()+1)/2 );
605 aTxtPos.AdjustY((nBarSize-aTextSize.Height())/2 );
607 GetOutDev()->DrawText( aTxtPos, aString );
609 break;
612 // when selecting the complete row/column:
613 // InvertRect( Rectangle( aScrPos, aEndPos ) );
615 nScrPos += nSizePix * nLayoutSign; // also if before the visible area
617 ++nCount;
619 while ( nScrPos * nLayoutSign <= nPEnd * nLayoutSign );
621 aGrid.Flush();
625 SCCOLROW ScHeaderControl::GetMousePos(const Point& rPos, bool& rBorder) const
627 bool bFound = false;
628 SCCOLROW nPos = GetPos();
629 SCCOLROW nHitNo = nPos;
630 SCCOLROW nEntryNo = 1 + nPos;
631 tools::Long nScrPos;
632 tools::Long nMousePos = bVertical ? rPos.Y() : rPos.X();
633 tools::Long nDif;
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;
646 else
647 nScrPos += GetEntrySize( nEntryNo - 1 ) * nLayoutSign; //! GetHiddenCount() ??
649 nDif = nMousePos - nScrPos;
650 if (nDif >= -5 && nDif <= 5)
652 bFound = true;
653 nHitNo=nEntryNo-1;
655 else if (nDif * nLayoutSign >= 0 && nEntryNo < nSize)
656 nHitNo = nEntryNo;
657 ++nEntryNo;
659 while ( nScrPos * nLayoutSign < nEndPos * nLayoutSign && nDif * nLayoutSign > 0 );
661 rBorder = bFound;
662 return nHitNo;
665 bool ScHeaderControl::IsSelectionAllowed(SCCOLROW nPos) const
667 ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
668 if (!pViewSh)
669 return false;
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;
680 if (bVertical)
682 // row header
683 SCROW nRPos = static_cast<SCROW>(nPos);
684 bCellsProtected = rDoc.HasAttrib(0, nRPos, nTab, rDoc.MaxCol(), nRPos, nTab, HasAttrFlags::Protected);
686 else
688 // column header
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);
696 if (bCellsProtected)
697 bSelectAllowed = bSelProtected;
698 else
699 bSelectAllowed = bSelUnprotected;
701 return bSelectAllowed;
704 void ScHeaderControl::MouseButtonDown( const MouseEvent& rMEvt )
706 if (IsDisabled())
707 return;
709 bIgnoreMove = false;
710 SelectWindow();
712 bool bIsBorder;
713 SCCOLROW nHitNo = GetMousePos(rMEvt.GetPosPixel(), bIsBorder);
714 if (!IsSelectionAllowed(nHitNo))
715 return;
716 if ( ! rMEvt.IsLeft() )
717 return;
718 if (ScModule::get()->IsFormulaMode())
720 if( !pTabView )
721 return;
722 SCTAB nTab = pTabView->GetViewData().GetTabNo();
723 if( !rMEvt.IsShift() )
724 pTabView->DoneRefMode( rMEvt.IsMod1() );
725 ScDocument& rDoc = pTabView->GetViewData().GetDocument();
726 if( !bVertical )
728 pTabView->InitRefMode( nHitNo, 0, nTab, SC_REFTYPE_REF );
729 pTabView->UpdateRef( nHitNo, rDoc.MaxRow(), nTab );
731 else
733 pTabView->InitRefMode( 0, nHitNo, nTab, SC_REFTYPE_REF );
734 pTabView->UpdateRef( rDoc.MaxCol(), nHitNo, nTab );
736 bInRefMode = true;
737 return;
739 if ( bIsBorder && ResizeAllowed() )
741 nDragNo = nHitNo;
742 sal_uInt16 nClicks = rMEvt.GetClicks();
743 if ( nClicks && nClicks%2==0 )
745 SetEntrySize( nDragNo, HDR_SIZE_OPTIMUM );
746 SetPointer( PointerStyle::Arrow );
748 else
750 if (bVertical)
751 nDragStart = rMEvt.GetPosPixel().Y();
752 else
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 );
762 StartTracking();
763 bDragging = true;
764 bDragMoved = false;
767 else
769 pSelEngine->SetWindow( this );
770 tools::Rectangle aVis( Point(), GetOutputSizePixel() );
771 if (bVertical)
773 aVis.SetLeft( LONG_MIN );
774 aVis.SetRight( LONG_MAX );
776 else
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!?!
795 ReleaseMouse();
796 StartTracking();
801 void ScHeaderControl::MouseButtonUp( const MouseEvent& rMEvt )
803 if ( IsDisabled() )
804 return;
806 if (ScModule* mod = ScModule::get(); mod->IsFormulaMode())
808 mod->EndReference();
809 bInRefMode = false;
810 return;
813 SetMarking( false );
814 bIgnoreMove = false;
816 if ( bDragging )
818 DrawInvert( nDragPos );
819 ReleaseMouse();
820 HideDragHelp();
821 bDragging = false;
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) */ )
831 SCCOLROW nStart = 0;
832 SCCOLROW nEnd = nDragNo;
833 while (nNewWidth < 0)
835 nStart = nDragNo;
836 if (nDragNo>0)
838 --nDragNo;
839 nNewWidth += GetEntrySize( nDragNo ); //! GetHiddenCount() ???
841 else
842 nNewWidth = 0;
844 HideEntries( nStart, nEnd );
846 else
848 if (bDragMoved)
849 SetEntrySize( nDragNo, static_cast<sal_uInt16>(nNewWidth) );
852 else
854 pSelEngine->SelMouseButtonUp( rMEvt );
855 ReleaseMouse();
859 void ScHeaderControl::MouseMove( const MouseEvent& rMEvt )
861 if ( IsDisabled() )
863 SetPointer( PointerStyle::Arrow );
864 return;
867 if (bInRefMode && rMEvt.IsLeft() && ScModule::get()->IsFormulaMode())
869 if( !pTabView )
870 return;
871 bool bTmp;
872 SCCOLROW nHitNo = GetMousePos(rMEvt.GetPosPixel(), bTmp);
873 SCTAB nTab = pTabView->GetViewData().GetTabNo();
874 ScDocument& rDoc = pTabView->GetViewData().GetDocument();
875 if( !bVertical )
876 pTabView->UpdateRef( nHitNo, rDoc.MaxRow(), nTab );
877 else
878 pTabView->UpdateRef( rDoc.MaxCol(), nHitNo, nTab );
880 return;
883 if ( bDragging )
885 tools::Long nNewPos = bVertical ? rMEvt.GetPosPixel().Y() : rMEvt.GetPosPixel().X();
886 if ( nNewPos != nDragPos )
888 DrawInvert( nDragPos );
889 nDragPos = nNewPos;
890 ShowDragHelp();
891 DrawInvert( nDragPos );
893 if (nDragPos <= nDragStart-SC_DRAG_MIN || nDragPos >= nDragStart+SC_DRAG_MIN)
894 bDragMoved = true;
897 else
899 bool bIsBorder;
900 (void)GetMousePos(rMEvt.GetPosPixel(), bIsBorder);
902 if ( bIsBorder && rMEvt.GetButtons()==0 && ResizeAllowed() )
903 SetPointer( bVertical ? PointerStyle::VSizeBar : PointerStyle::HSizeBar );
904 else
905 SetPointer( PointerStyle::Arrow );
907 if (!bIgnoreMove)
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() )
918 StopMarking();
919 else if ( rTEvt.IsTrackingEnded() )
920 MouseButtonUp( rTEvt.GetMouseEvent() );
921 else
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() );
935 if ( pViewSh )
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
946 bool bBorder;
947 SCCOLROW nPos = GetMousePos(rCEvt.GetMousePosPixel(), bBorder );
948 if (!IsSelectionAllowed(nPos))
949 // Selecting this cell is not allowed, neither is context menu.
950 return;
952 SCTAB nTab = rViewData.GetTabNo();
953 ScDocument& rDoc = pViewSh->GetViewData().GetDocument();
954 ScRange aNewRange;
955 if ( bVertical )
956 aNewRange = ScRange( 0, sal::static_int_cast<SCROW>(nPos), nTab,
957 rDoc.MaxCol(), sal::static_int_cast<SCROW>(nPos), nTab );
958 else
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
963 ScRangeList aRanges;
964 rViewData.GetMarkData().FillRangeListWithMarks( &aRanges, false );
965 bool bSelected = aRanges.Intersects(aNewRange);
967 // select the range if no part of it was selected
968 if ( !bSelected )
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()
983 if ( bDragging )
985 DrawInvert( nDragPos );
986 HideDragHelp();
987 bDragging = false;
990 SetMarking( false );
991 bIgnoreMove = true;
993 // don't call pSelEngine->Reset, so selection across the parts of
994 // a split/frozen view is possible
995 if (IsMouseCaptured())
996 ReleaseMouse();
999 IMPL_LINK_NOARG(ScHeaderControl, ShowDragHelpHdl, Timer*, void)
1001 ShowDragHelp();
1004 void ScHeaderControl::ShowDragHelp()
1006 aShowHelpTimer.Stop();
1007 if (!Help::IsQuickHelpEnabled())
1008 return;
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;
1023 if (!bVertical)
1025 // above
1026 aRect.SetLeft( aMousePos.X() );
1027 aRect.SetTop( aPos.Y() - 4 );
1028 nAlign = QuickHelpFlags::Bottom|QuickHelpFlags::Center;
1030 else
1032 // top right
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() );
1041 if (nTipVisible)
1042 Help::HidePopover(this, nTipVisible);
1043 nTipVisible = Help::ShowPopover(this, aRect, aHelpStr, nAlign);
1046 void ScHeaderControl::HideDragHelp()
1048 aShowHelpTimer.Stop();
1049 if (nTipVisible)
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();
1061 if (!bOwn)
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 )
1072 ++nEntryNo;
1073 ++nHidden;
1075 return nHidden;
1078 bool ScHeaderControl::IsLayoutRTL() const
1080 return false;
1083 bool ScHeaderControl::IsMirrored() const
1085 return false;
1088 bool ScHeaderControl::IsDisabled() const
1090 return false;
1093 bool ScHeaderControl::ResizeAllowed() const
1095 return true;
1098 void ScHeaderControl::SelectWindow()
1102 void ScHeaderControl::DrawInvert( tools::Long /* nDragPos */ )
1106 OUString ScHeaderControl::GetDragHelp( tools::Long /* nVal */ )
1108 return OUString();
1111 void ScHeaderControl::SetMarking( bool /* bSet */ )
1115 void ScHeaderControl::GetMarkRange(SCCOLROW& rStart, SCCOLROW& rEnd) const
1117 rStart = nMarkStart;
1118 rEnd = nMarkEnd;
1121 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */