1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <rangelst.hxx>
21 #include <scitems.hxx>
22 
23 #include <editeng/editview.hxx>
24 #include <svx/fmshell.hxx>
25 #include <svx/sdr/overlay/overlaymanager.hxx>
26 #include <svx/svdoole2.hxx>
27 #include <sfx2/bindings.hxx>
28 #include <sfx2/lokhelper.hxx>
29 #include <sfx2/viewfrm.hxx>
30 #include <vcl/cursor.hxx>
31 #include <vcl/uitest/logger.hxx>
32 #include <vcl/uitest/eventdescription.hxx>
33 #include <sal/log.hxx>
34 #include <osl/diagnose.h>
35 
36 #include <IAnyRefDialog.hxx>
37 #include <tabview.hxx>
38 #include <tabvwsh.hxx>
39 #include <docsh.hxx>
40 #include <gridwin.hxx>
41 #include <olinewin.hxx>
42 #include <overlayobject.hxx>
43 #include <colrowba.hxx>
44 #include <tabcont.hxx>
45 #include <scmod.hxx>
46 #include <sc.hrc>
47 #include <viewutil.hxx>
48 #include <editutil.hxx>
49 #include <inputhdl.hxx>
50 #include <inputwin.hxx>
51 #include <validat.hxx>
52 #include <inputopt.hxx>
53 #include <rfindlst.hxx>
54 #include <hiranges.hxx>
55 #include <viewuno.hxx>
56 #include <dpobject.hxx>
57 #include <seltrans.hxx>
58 #include <fillinfo.hxx>
59 #include <rangeutl.hxx>
60 #include <client.hxx>
61 #include <tabprotection.hxx>
62 #include <spellcheckcontext.hxx>
63 #include <markdata.hxx>
64 #include <formula/FormulaCompiler.hxx>
65 #include <comphelper/lok.hxx>
66 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
67 #include <output.hxx>
68 
69 #include <utility>
70 
71 #include <com/sun/star/chart2/data/HighlightedRange.hpp>
72 
73 namespace
74 {
75 
lcl_getSubRangeByIndex(const ScRange & rRange,sal_Int32 nIndex)76 ScRange lcl_getSubRangeByIndex( const ScRange& rRange, sal_Int32 nIndex )
77 {
78     ScAddress aResult( rRange.aStart );
79 
80     SCCOL nWidth = rRange.aEnd.Col() - rRange.aStart.Col() + 1;
81     SCROW nHeight = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
82     SCTAB nDepth = rRange.aEnd.Tab() - rRange.aStart.Tab() + 1;
83     if( (nWidth > 0) && (nHeight > 0) && (nDepth > 0) )
84     {
85         // row by row from first to last sheet
86         sal_Int32 nArea = nWidth * nHeight;
87         aResult.IncCol( static_cast< SCCOL >( nIndex % nWidth ) );
88         aResult.IncRow( static_cast< SCROW >( (nIndex % nArea) / nWidth ) );
89         aResult.IncTab( static_cast< SCTAB >( nIndex / nArea ) );
90         if( !rRange.In( aResult ) )
91             aResult = rRange.aStart;
92     }
93 
94     return ScRange( aResult );
95 }
96 
97 } // anonymous namespace
98 
99 using namespace com::sun::star;
100 
~ScExtraEditViewManager()101 ScExtraEditViewManager::~ScExtraEditViewManager()
102 {
103     DBG_ASSERT(nTotalWindows == 0, "ScExtraEditViewManager dtor: some out window has not yet been removed!");
104 }
105 
Add(SfxViewShell * pViewShell,ScSplitPos eWhich)106 inline void ScExtraEditViewManager::Add(SfxViewShell* pViewShell, ScSplitPos eWhich)
107 {
108     Apply<Adder>(pViewShell, eWhich);
109 }
110 
Remove(SfxViewShell * pViewShell,ScSplitPos eWhich)111 inline void ScExtraEditViewManager::Remove(SfxViewShell* pViewShell, ScSplitPos eWhich)
112 {
113     Apply<Remover>(pViewShell, eWhich);
114 }
115 
116 
117 template<ScExtraEditViewManager::ModifierTagType ModifierTag>
Apply(SfxViewShell * pViewShell,ScSplitPos eWhich)118 void ScExtraEditViewManager::Apply(SfxViewShell* pViewShell, ScSplitPos eWhich)
119 {
120     ScTabViewShell* pOtherViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
121     if (pOtherViewShell == nullptr || pOtherViewShell == mpThisViewShell)
122         return;
123 
124     mpOtherEditView = pOtherViewShell->GetViewData().GetEditView(eWhich);
125     if (mpOtherEditView != nullptr)
126     {
127         DBG_ASSERT(mpOtherEditView->GetEditEngine(), "Edit view has no valid engine.");
128         for (int i = 0; i < 4; ++i)
129         {
130             ScGridWindow* pWin = mpGridWin[i].get();
131             if (pWin != nullptr)
132             {
133                 Modifier<ModifierTag>(pWin);
134             }
135         }
136     }
137 }
138 
139 template<ScExtraEditViewManager::ModifierTagType ModifierTag>
Modifier(ScGridWindow *)140 void ScExtraEditViewManager::Modifier(ScGridWindow* /*pWin*/)
141 {
142     (void)this;
143     SAL_WARN("sc", "ScExtraEditViewManager::Modifier<ModifierTag>: non-specialized version should not be invoked.");
144 }
145 
146 template<>
Modifier(ScGridWindow * pWin)147 void ScExtraEditViewManager::Modifier<ScExtraEditViewManager::Adder>(ScGridWindow* pWin)
148 {
149     if (mpOtherEditView->AddOtherViewWindow(pWin))
150         ++nTotalWindows;
151 }
152 
153 template<>
Modifier(ScGridWindow * pWin)154 void ScExtraEditViewManager::Modifier<ScExtraEditViewManager::Remover>(ScGridWindow* pWin)
155 {
156     if (mpOtherEditView->RemoveOtherViewWindow(pWin))
157         --nTotalWindows;
158 }
159 
160 // ---  public functions
161 
ClickCursor(SCCOL nPosX,SCROW nPosY,bool bControl)162 void ScTabView::ClickCursor( SCCOL nPosX, SCROW nPosY, bool bControl )
163 {
164     ScDocument& rDoc = aViewData.GetDocument();
165     SCTAB nTab = aViewData.GetTabNo();
166     rDoc.SkipOverlapped(nPosX, nPosY, nTab);
167 
168     bool bRefMode = SC_MOD()->IsFormulaMode();
169 
170     if ( bRefMode )
171     {
172         DoneRefMode();
173 
174         if (bControl)
175             SC_MOD()->AddRefEntry();
176 
177         InitRefMode( nPosX, nPosY, nTab, SC_REFTYPE_REF );
178     }
179     else
180     {
181         DoneBlockMode( bControl );
182         aViewData.ResetOldCursor();
183         SetCursor( nPosX, nPosY );
184     }
185 }
186 
UpdateAutoFillMark(bool bFromPaste)187 void ScTabView::UpdateAutoFillMark(bool bFromPaste)
188 {
189     // single selection or cursor
190     ScRange aMarkRange;
191     ScMarkType eMarkType = aViewData.GetSimpleArea(aMarkRange);
192     bool bMarked = eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED;
193 
194     for (sal_uInt16 i = 0; i < 4; i++)
195     {
196         if (pGridWin[i] && pGridWin[i]->IsVisible())
197             pGridWin[i]->UpdateAutoFillMark( bMarked, aMarkRange );
198     }
199 
200     for (sal_uInt16 i = 0; i < 2; i++)
201     {
202         if (pColBar[i] && pColBar[i]->IsVisible())
203             pColBar[i]->SetMark( bMarked, aMarkRange.aStart.Col(), aMarkRange.aEnd.Col() );
204         if (pRowBar[i] && pRowBar[i]->IsVisible())
205             pRowBar[i]->SetMark( bMarked, aMarkRange.aStart.Row(), aMarkRange.aEnd.Row() );
206     }
207 
208     //  selection transfer object is checked together with AutoFill marks,
209     //  because it has the same requirement of a single continuous block.
210     if (!bFromPaste)
211         CheckSelectionTransfer();   // update selection transfer object
212 }
213 
FakeButtonUp(ScSplitPos eWhich)214 void ScTabView::FakeButtonUp( ScSplitPos eWhich )
215 {
216     if (pGridWin[eWhich])
217         pGridWin[eWhich]->FakeButtonUp();
218 }
219 
HideAllCursors()220 void ScTabView::HideAllCursors()
221 {
222     for (VclPtr<ScGridWindow> & pWin : pGridWin)
223     {
224         if (pWin && pWin->IsVisible())
225         {
226             vcl::Cursor* pCur = pWin->GetCursor();
227             if (pCur && pCur->IsVisible())
228                 pCur->Hide();
229             pWin->HideCursor();
230         }
231     }
232 }
233 
ShowAllCursors()234 void ScTabView::ShowAllCursors()
235 {
236     for (VclPtr<ScGridWindow> & pWin : pGridWin)
237     {
238         if (pWin && pWin->IsVisible())
239         {
240             pWin->ShowCursor();
241             pWin->CursorChanged();
242         }
243     }
244 }
245 
ShowCursor()246 void ScTabView::ShowCursor()
247 {
248     pGridWin[aViewData.GetActivePart()]->ShowCursor();
249     pGridWin[aViewData.GetActivePart()]->CursorChanged();
250 }
251 
InvalidateAttribs()252 void ScTabView::InvalidateAttribs()
253 {
254     SfxBindings& rBindings = aViewData.GetBindings();
255 
256     rBindings.Invalidate( SID_STYLE_APPLY );
257     rBindings.Invalidate( SID_STYLE_FAMILY2 );
258     // StarCalc knows only paragraph- or cell format templates
259 
260     rBindings.Invalidate( SID_ATTR_CHAR_FONT );
261     rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
262     rBindings.Invalidate( SID_ATTR_CHAR_COLOR );
263 
264     rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT );
265     rBindings.Invalidate( SID_ATTR_CHAR_POSTURE );
266     rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
267     rBindings.Invalidate( SID_ULINE_VAL_NONE );
268     rBindings.Invalidate( SID_ULINE_VAL_SINGLE );
269     rBindings.Invalidate( SID_ULINE_VAL_DOUBLE );
270     rBindings.Invalidate( SID_ULINE_VAL_DOTTED );
271 
272     rBindings.Invalidate( SID_ATTR_CHAR_OVERLINE );
273 
274     rBindings.Invalidate( SID_ATTR_CHAR_KERNING );
275     rBindings.Invalidate( SID_SET_SUPER_SCRIPT );
276     rBindings.Invalidate( SID_SET_SUB_SCRIPT );
277     rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT );
278     rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED );
279 
280     rBindings.Invalidate( SID_ATTR_PARA_ADJUST_LEFT );
281     rBindings.Invalidate( SID_ATTR_PARA_ADJUST_RIGHT );
282     rBindings.Invalidate( SID_ATTR_PARA_ADJUST_BLOCK );
283     rBindings.Invalidate( SID_ATTR_PARA_ADJUST_CENTER);
284     rBindings.Invalidate( SID_NUMBER_TYPE_FORMAT);
285 
286     rBindings.Invalidate( SID_ALIGNLEFT );
287     rBindings.Invalidate( SID_ALIGNRIGHT );
288     rBindings.Invalidate( SID_ALIGNBLOCK );
289     rBindings.Invalidate( SID_ALIGNCENTERHOR );
290 
291     rBindings.Invalidate( SID_ALIGNTOP );
292     rBindings.Invalidate( SID_ALIGNBOTTOM );
293     rBindings.Invalidate( SID_ALIGNCENTERVER );
294 
295     rBindings.Invalidate( SID_SCATTR_CELLPROTECTION );
296 
297     // stuff for sidebar panels
298     {
299         rBindings.Invalidate( SID_H_ALIGNCELL );
300         rBindings.Invalidate( SID_V_ALIGNCELL );
301         rBindings.Invalidate( SID_ATTR_ALIGN_INDENT );
302         rBindings.Invalidate( SID_FRAME_LINECOLOR );
303         rBindings.Invalidate( SID_FRAME_LINESTYLE );
304         rBindings.Invalidate( SID_ATTR_BORDER_OUTER );
305         rBindings.Invalidate( SID_ATTR_BORDER_INNER );
306         rBindings.Invalidate( SID_ATTR_BORDER_DIAG_TLBR );
307         rBindings.Invalidate( SID_ATTR_BORDER_DIAG_BLTR );
308         rBindings.Invalidate( SID_NUMBER_TYPE_FORMAT );
309     }
310 
311     rBindings.Invalidate( SID_BACKGROUND_COLOR );
312 
313     rBindings.Invalidate( SID_ATTR_ALIGN_LINEBREAK );
314     rBindings.Invalidate( SID_NUMBER_FORMAT );
315 
316     rBindings.Invalidate( SID_TEXTDIRECTION_LEFT_TO_RIGHT );
317     rBindings.Invalidate( SID_TEXTDIRECTION_TOP_TO_BOTTOM );
318     rBindings.Invalidate( SID_ATTR_PARA_LEFT_TO_RIGHT );
319     rBindings.Invalidate( SID_ATTR_PARA_RIGHT_TO_LEFT );
320 
321     // pseudo slots for Format menu
322     rBindings.Invalidate( SID_ALIGN_ANY_HDEFAULT );
323     rBindings.Invalidate( SID_ALIGN_ANY_LEFT );
324     rBindings.Invalidate( SID_ALIGN_ANY_HCENTER );
325     rBindings.Invalidate( SID_ALIGN_ANY_RIGHT );
326     rBindings.Invalidate( SID_ALIGN_ANY_JUSTIFIED );
327     rBindings.Invalidate( SID_ALIGN_ANY_VDEFAULT );
328     rBindings.Invalidate( SID_ALIGN_ANY_TOP );
329     rBindings.Invalidate( SID_ALIGN_ANY_VCENTER );
330     rBindings.Invalidate( SID_ALIGN_ANY_BOTTOM );
331 
332     rBindings.Invalidate( SID_NUMBER_CURRENCY );
333     rBindings.Invalidate( SID_NUMBER_SCIENTIFIC );
334     rBindings.Invalidate( SID_NUMBER_DATE );
335     rBindings.Invalidate( SID_NUMBER_CURRENCY );
336     rBindings.Invalidate( SID_NUMBER_PERCENT );
337     rBindings.Invalidate( SID_NUMBER_TWODEC );
338     rBindings.Invalidate( SID_NUMBER_TIME );
339     rBindings.Invalidate( SID_NUMBER_STANDARD );
340     rBindings.Invalidate( SID_NUMBER_THOUSANDS );
341 }
342 
343 namespace {
344 
collectUIInformation(const std::map<OUString,OUString> & aParameters)345 void collectUIInformation(const std::map<OUString, OUString>& aParameters)
346 {
347     EventDescription aDescription;
348     aDescription.aID = "grid_window";
349     aDescription.aAction = "SELECT";
350     aDescription.aParameters = aParameters;
351     aDescription.aParent = "MainWindow";
352     aDescription.aKeyWord = "ScGridWinUIObject";
353 
354     UITestLogger::getInstance().logEvent(aDescription);
355 }
356 
357 }
358 
359 // SetCursor - Cursor, set, draw, update InputWin
360 // or send reference
361 // Optimising breaks the functionality
362 
SetCursor(SCCOL nPosX,SCROW nPosY,bool bNew)363 void ScTabView::SetCursor( SCCOL nPosX, SCROW nPosY, bool bNew )
364 {
365     SCCOL nOldX = aViewData.GetCurX();
366     SCROW nOldY = aViewData.GetCurY();
367 
368     //  DeactivateIP only for MarkListHasChanged
369 
370     // FIXME: this is to limit the number of rows handled in the Online
371     // to 1000; this will be removed again when the performance
372     // bottlenecks are sorted out
373     if (comphelper::LibreOfficeKit::isActive())
374         nPosY = std::min(nPosY, MAXTILEDROW);
375 
376     if ( !(nPosX != nOldX || nPosY != nOldY || bNew) )
377         return;
378 
379     ScTabViewShell* pViewShell = aViewData.GetViewShell();
380     bool bRefMode = pViewShell && pViewShell->IsRefInputMode();
381     if ( aViewData.HasEditView( aViewData.GetActivePart() ) && !bRefMode ) // 23259 or so
382     {
383         UpdateInputLine();
384     }
385 
386     HideAllCursors();
387 
388     aViewData.SetCurX( nPosX );
389     aViewData.SetCurY( nPosY );
390 
391     ShowAllCursors();
392 
393     CursorPosChanged();
394 
395     OUString aCurrAddress = ScAddress(nPosX,nPosY,0).GetColRowString();
396     collectUIInformation({{"CELL", aCurrAddress}});
397 
398     if (!comphelper::LibreOfficeKit::isActive())
399         return;
400 
401     if (nPosX <= aViewData.GetMaxTiledCol() - 10 && nPosY <= aViewData.GetMaxTiledRow() - 25)
402         return;
403 
404     ScDocument& rDoc = aViewData.GetDocument();
405     ScDocShell* pDocSh = aViewData.GetDocShell();
406     ScModelObj* pModelObj = pDocSh ? comphelper::getUnoTunnelImplementation<ScModelObj>( pDocSh->GetModel() ) : nullptr;
407     Size aOldSize(0, 0);
408     if (pModelObj)
409         aOldSize = pModelObj->getDocumentSize();
410 
411     if (nPosX > aViewData.GetMaxTiledCol() - 10)
412         aViewData.SetMaxTiledCol(std::min<SCCOL>(std::max(nPosX, aViewData.GetMaxTiledCol()) + 10, rDoc.MaxCol()));
413 
414     if (nPosY > aViewData.GetMaxTiledRow() - 25)
415         aViewData.SetMaxTiledRow(std::min<SCROW>(std::max(nPosY, aViewData.GetMaxTiledRow()) + 25,  MAXTILEDROW));
416 
417     Size aNewSize(0, 0);
418     if (pModelObj)
419         aNewSize = pModelObj->getDocumentSize();
420 
421     if (!pDocSh)
422         return;
423 
424     // New area extended to the right of the sheet after last column
425     // including overlapping area with aNewRowArea
426     tools::Rectangle aNewColArea(aOldSize.getWidth(), 0, aNewSize.getWidth(), aNewSize.getHeight());
427     // New area extended to the bottom of the sheet after last row
428     // excluding overlapping area with aNewColArea
429     tools::Rectangle aNewRowArea(0, aOldSize.getHeight(), aOldSize.getWidth(), aNewSize.getHeight());
430 
431     // Only invalidate if spreadsheet extended to the right
432     if (aNewColArea.getWidth())
433     {
434         SfxLokHelper::notifyInvalidation(aViewData.GetViewShell(), aNewColArea.toString());
435     }
436 
437     // Only invalidate if spreadsheet extended to the bottom
438     if (aNewRowArea.getHeight())
439     {
440         SfxLokHelper::notifyInvalidation(aViewData.GetViewShell(), aNewRowArea.toString());
441     }
442 
443     // Provide size in the payload, so clients don't have to
444     // call lok::Document::getDocumentSize().
445     std::stringstream ss;
446     ss << aNewSize.Width() << ", " << aNewSize.Height();
447     OString sSize = ss.str().c_str();
448     ScModelObj* pModel = comphelper::getUnoTunnelImplementation<ScModelObj>(aViewData.GetViewShell()->GetCurrentDocument());
449     SfxLokHelper::notifyDocumentSizeChanged(aViewData.GetViewShell(), sSize, pModel, false);
450 }
451 
lcl_IsRefDlgActive(SfxViewFrame * pViewFrm)452 static bool lcl_IsRefDlgActive(SfxViewFrame* pViewFrm)
453 {
454     ScModule* pScMod = SC_MOD();
455     if (!pScMod->IsRefDialogOpen())
456        return false;
457 
458     auto nDlgId = pScMod->GetCurRefDlgId();
459     if (!pViewFrm->HasChildWindow(nDlgId))
460         return false;
461 
462     SfxChildWindow* pChild = pViewFrm->GetChildWindow(nDlgId);
463     if (!pChild)
464         return false;
465 
466     auto xDlgController = pChild->GetController();
467     if (!xDlgController || !xDlgController->getDialog()->get_visible())
468         return false;
469 
470     IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(xDlgController.get());
471     return pRefDlg && pRefDlg->IsRefInputMode();
472 }
473 
CheckSelectionTransfer()474 void ScTabView::CheckSelectionTransfer()
475 {
476     if ( !aViewData.IsActive() )     // only for active view
477         return;
478 
479     ScModule* pScMod = SC_MOD();
480     ScSelectionTransferObj* pOld = pScMod->GetSelectionTransfer();
481     rtl::Reference<ScSelectionTransferObj> pNew = ScSelectionTransferObj::CreateFromView( this );
482     if ( !pNew )
483         return;
484 
485     //  create new selection
486 
487     if (pOld)
488         pOld->ForgetView();
489 
490     pScMod->SetSelectionTransfer( pNew.get() );
491 
492     // tdf#124975/tdf#136242 changing the calc selection can trigger removal of the
493     // selection of an open RefDlg dialog, so don't inform the
494     // desktop clipboard of the changed selection if that dialog is open
495     if (!lcl_IsRefDlgActive(aViewData.GetViewShell()->GetViewFrame()))
496         pNew->CopyToPrimarySelection();                    // may delete pOld
497 
498     // Log the selection change
499     ScMarkData& rMark = aViewData.GetMarkData();
500     if (rMark.IsMarked())
501     {
502         ScRange aMarkRange;
503         rMark.GetMarkArea( aMarkRange );
504         OUString aStartAddress =  aMarkRange.aStart.GetColRowString();
505         OUString aEndAddress = aMarkRange.aEnd.GetColRowString();
506         collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}});
507     }
508 }
509 
510 // update input row / menus
511 // CursorPosChanged calls SelectionChanged
512 // SelectionChanged calls CellContentChanged
513 
CellContentChanged()514 void ScTabView::CellContentChanged()
515 {
516     SfxBindings& rBindings = aViewData.GetBindings();
517 
518     rBindings.Invalidate( SID_ATTR_SIZE );      // -> show error message
519     rBindings.Invalidate( SID_THESAURUS );
520     rBindings.Invalidate( SID_HYPERLINK_GETLINK );
521     rBindings.Invalidate( SID_ROWCOL_SELCOUNT );
522 
523     InvalidateAttribs();                    // attributes updates
524 
525     aViewData.GetViewShell()->UpdateInputHandler();
526 }
527 
SetTabProtectionSymbol(SCTAB nTab,const bool bProtect)528 void ScTabView::SetTabProtectionSymbol( SCTAB nTab, const bool bProtect )
529 {
530     pTabControl->SetProtectionSymbol( static_cast<sal_uInt16>(nTab)+1, bProtect);
531 }
532 
SelectionChanged(bool bFromPaste)533 void ScTabView::SelectionChanged(bool bFromPaste)
534 {
535     SfxViewFrame* pViewFrame = aViewData.GetViewShell()->GetViewFrame();
536     if (pViewFrame)
537     {
538         uno::Reference<frame::XController> xController = pViewFrame->GetFrame().GetController();
539         if (xController.is())
540         {
541             ScTabViewObj* pImp = comphelper::getUnoTunnelImplementation<ScTabViewObj>( xController );
542             if (pImp)
543                 pImp->SelectionChanged();
544         }
545     }
546 
547     UpdateAutoFillMark(bFromPaste);   // also calls CheckSelectionTransfer
548 
549     SfxBindings& rBindings = aViewData.GetBindings();
550 
551     rBindings.Invalidate( SID_CURRENTCELL );    // -> Navigator
552     rBindings.Invalidate( SID_AUTO_FILTER );    // -> Menu
553     rBindings.Invalidate( FID_NOTE_VISIBLE );
554     rBindings.Invalidate( FID_SHOW_NOTE );
555     rBindings.Invalidate( FID_HIDE_NOTE );
556     rBindings.Invalidate( FID_SHOW_ALL_NOTES );
557     rBindings.Invalidate( FID_HIDE_ALL_NOTES );
558     rBindings.Invalidate( SID_TOGGLE_NOTES );
559     rBindings.Invalidate( SID_DELETE_NOTE );
560     rBindings.Invalidate( SID_ROWCOL_SELCOUNT );
561 
562         //  functions than may need to be disabled
563 
564     rBindings.Invalidate( FID_INS_ROWBRK );
565     rBindings.Invalidate( FID_INS_COLBRK );
566     rBindings.Invalidate( FID_DEL_ROWBRK );
567     rBindings.Invalidate( FID_DEL_COLBRK );
568     rBindings.Invalidate( FID_MERGE_ON );
569     rBindings.Invalidate( FID_MERGE_OFF );
570     rBindings.Invalidate( FID_MERGE_TOGGLE );
571     rBindings.Invalidate( SID_AUTOFILTER_HIDE );
572     rBindings.Invalidate( SID_UNFILTER );
573     rBindings.Invalidate( SID_REIMPORT_DATA );
574     rBindings.Invalidate( SID_REFRESH_DBAREA );
575     rBindings.Invalidate( SID_OUTLINE_SHOW );
576     rBindings.Invalidate( SID_OUTLINE_HIDE );
577     rBindings.Invalidate( SID_OUTLINE_REMOVE );
578     rBindings.Invalidate( FID_FILL_TO_BOTTOM );
579     rBindings.Invalidate( FID_FILL_TO_RIGHT );
580     rBindings.Invalidate( FID_FILL_TO_TOP );
581     rBindings.Invalidate( FID_FILL_TO_LEFT );
582     rBindings.Invalidate( FID_FILL_SERIES );
583     rBindings.Invalidate( SID_SCENARIOS );
584     rBindings.Invalidate( SID_AUTOFORMAT );
585     rBindings.Invalidate( SID_OPENDLG_TABOP );
586     rBindings.Invalidate( SID_DATA_SELECT );
587 
588     rBindings.Invalidate( SID_CUT );
589     rBindings.Invalidate( SID_COPY );
590     rBindings.Invalidate( SID_PASTE );
591     rBindings.Invalidate( SID_PASTE_SPECIAL );
592     rBindings.Invalidate( SID_PASTE_UNFORMATTED );
593 
594     rBindings.Invalidate( FID_INS_ROW );
595     rBindings.Invalidate( FID_INS_COLUMN );
596     rBindings.Invalidate( FID_INS_ROWS_BEFORE );
597     rBindings.Invalidate( FID_INS_COLUMNS_BEFORE );
598     rBindings.Invalidate( FID_INS_ROWS_AFTER );
599     rBindings.Invalidate( FID_INS_COLUMNS_AFTER );
600     rBindings.Invalidate( FID_INS_CELL );
601     rBindings.Invalidate( FID_INS_CELLSDOWN );
602     rBindings.Invalidate( FID_INS_CELLSRIGHT );
603 
604     rBindings.Invalidate( FID_CHG_COMMENT );
605 
606         // only due to  protect cell:
607 
608     rBindings.Invalidate( SID_CELL_FORMAT_RESET );
609     rBindings.Invalidate( SID_DELETE );
610     rBindings.Invalidate( SID_DELETE_CONTENTS );
611     rBindings.Invalidate( FID_DELETE_CELL );
612     rBindings.Invalidate( FID_CELL_FORMAT );
613     rBindings.Invalidate( SID_ENABLE_HYPHENATION );
614     rBindings.Invalidate( SID_INSERT_POSTIT );
615     rBindings.Invalidate( SID_CHARMAP );
616     rBindings.Invalidate( SID_OPENDLG_FUNCTION );
617     rBindings.Invalidate( FID_VALIDATION );
618     rBindings.Invalidate( SID_EXTERNAL_SOURCE );
619     rBindings.Invalidate( SID_TEXT_TO_COLUMNS );
620     rBindings.Invalidate( SID_SORT_ASCENDING );
621     rBindings.Invalidate( SID_SORT_DESCENDING );
622     rBindings.Invalidate( SID_SELECT_UNPROTECTED_CELLS );
623 
624     if (aViewData.GetViewShell()->HasAccessibilityObjects())
625         aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccCursorChanged));
626 
627     CellContentChanged();
628 }
629 
CursorPosChanged()630 void ScTabView::CursorPosChanged()
631 {
632     bool bRefMode = SC_MOD()->IsFormulaMode();
633     if ( !bRefMode ) // check that RefMode works when switching sheets
634         aViewData.GetDocShell()->Broadcast( SfxHint( SfxHintId::ScKillEditView ) );
635 
636     //  Broadcast, so that other Views of the document also switch
637 
638     ScDocument& rDoc = aViewData.GetDocument();
639     bool bDP = nullptr != rDoc.GetDPAtCursor(
640         aViewData.GetCurX(), aViewData.GetCurY(), aViewData.GetTabNo() );
641     aViewData.GetViewShell()->SetPivotShell(bDP);
642 
643     //  UpdateInputHandler now in CellContentChanged
644 
645     SelectionChanged();
646 
647     aViewData.SetTabStartCol( SC_TABSTART_NONE );
648 }
649 
650 namespace {
651 
calcHintWindowPosition(const Point & rCellPos,const Size & rCellSize,const Size & rFrameWndSize,const Size & rHintWndSize)652 Point calcHintWindowPosition(
653     const Point& rCellPos, const Size& rCellSize, const Size& rFrameWndSize, const Size& rHintWndSize)
654 {
655     const tools::Long nMargin = 20;
656 
657     tools::Long nMLeft = rCellPos.X();
658     tools::Long nMRight = rFrameWndSize.Width() - rCellPos.X() - rCellSize.Width();
659     tools::Long nMTop = rCellPos.Y();
660     tools::Long nMBottom = rFrameWndSize.Height() - rCellPos.Y() - rCellSize.Height();
661 
662     // First, see if we can fit the entire hint window in the visible region.
663 
664     if (nMRight - nMargin >= rHintWndSize.Width())
665     {
666         // Right margin is wide enough.
667         if (rFrameWndSize.Height() >= rHintWndSize.Height())
668         {
669             // The frame has enough height.  Take it.
670             Point aPos = rCellPos;
671             aPos.AdjustX(rCellSize.Width() + nMargin );
672             if (aPos.Y() + rHintWndSize.Height() > rFrameWndSize.Height())
673             {
674                 // Push the hint window up a bit to make it fit.
675                 aPos.setY( rFrameWndSize.Height() - rHintWndSize.Height() );
676             }
677             return aPos;
678         }
679     }
680 
681     if (nMBottom - nMargin >= rHintWndSize.Height())
682     {
683         // Bottom margin is high enough.
684         if (rFrameWndSize.Width() >= rHintWndSize.Width())
685         {
686             // The frame has enough width.  Take it.
687             Point aPos = rCellPos;
688             aPos.AdjustY(rCellSize.Height() + nMargin );
689             if (aPos.X() + rHintWndSize.Width() > rFrameWndSize.Width())
690             {
691                 // Move the hint window to the left to make it fit.
692                 aPos.setX( rFrameWndSize.Width() - rHintWndSize.Width() );
693             }
694             return aPos;
695         }
696     }
697 
698     if (nMLeft - nMargin >= rHintWndSize.Width())
699     {
700         // Left margin is wide enough.
701         if (rFrameWndSize.Height() >= rHintWndSize.Height())
702         {
703             // The frame is high enough.  Take it.
704             Point aPos = rCellPos;
705             aPos.AdjustX( -(rHintWndSize.Width() + nMargin) );
706             if (aPos.Y() + rHintWndSize.Height() > rFrameWndSize.Height())
707             {
708                 // Push the hint window up a bit to make it fit.
709                 aPos.setY( rFrameWndSize.Height() - rHintWndSize.Height() );
710             }
711             return aPos;
712         }
713     }
714 
715     if (nMTop - nMargin >= rHintWndSize.Height())
716     {
717         // Top margin is high enough.
718         if (rFrameWndSize.Width() >= rHintWndSize.Width())
719         {
720             // The frame is wide enough.  Take it.
721             Point aPos = rCellPos;
722             aPos.AdjustY( -(rHintWndSize.Height() + nMargin) );
723             if (aPos.X() + rHintWndSize.Width() > rFrameWndSize.Width())
724             {
725                 // Move the hint window to the left to make it fit.
726                 aPos.setX( rFrameWndSize.Width() - rHintWndSize.Width() );
727             }
728             return aPos;
729         }
730     }
731 
732     // The popup doesn't fit in any direction in its entirety.  Do our best.
733 
734     if (nMRight - nMargin >= rHintWndSize.Width())
735     {
736         // Right margin is good enough.
737         Point aPos = rCellPos;
738         aPos.AdjustX(nMargin + rCellSize.Width() );
739         aPos.setY( 0 );
740         return aPos;
741     }
742 
743     if (nMBottom - nMargin >= rHintWndSize.Height())
744     {
745         // Bottom margin is good enough.
746         Point aPos = rCellPos;
747         aPos.AdjustY(nMargin + rCellSize.Height() );
748         aPos.setX( 0 );
749         return aPos;
750     }
751 
752     if (nMLeft - nMargin >= rHintWndSize.Width())
753     {
754         // Left margin is good enough.
755         Point aPos = rCellPos;
756         aPos.AdjustX( -(rHintWndSize.Width() + nMargin) );
757         aPos.setY( 0 );
758         return aPos;
759     }
760 
761     if (nMTop - nMargin >= rHintWndSize.Height())
762     {
763         // Top margin is good enough.
764         Point aPos = rCellPos;
765         aPos.AdjustY( -(rHintWndSize.Height() + nMargin) );
766         aPos.setX( 0 );
767         return aPos;
768     }
769 
770     // None of the above.  Hopeless.  At least try not to cover the current
771     // cell.
772     Point aPos = rCellPos;
773     aPos.AdjustX(rCellSize.Width() );
774     return aPos;
775 }
776 
777 }
778 
TestHintWindow()779 void ScTabView::TestHintWindow()
780 {
781     //  show input help window and list drop-down button for validity
782 
783     mxInputHintOO.reset();
784 
785     bool bListValButton = false;
786     ScAddress aListValPos;
787 
788     ScDocument& rDoc = aViewData.GetDocument();
789     const SfxUInt32Item* pItem = rDoc.GetAttr( aViewData.GetCurX(),
790                                                aViewData.GetCurY(),
791                                                aViewData.GetTabNo(),
792                                                ATTR_VALIDDATA );
793     if ( pItem->GetValue() )
794     {
795         const ScValidationData* pData = rDoc.GetValidationEntry( pItem->GetValue() );
796         OSL_ENSURE(pData,"ValidationData not found");
797         OUString aTitle, aMessage;
798 
799         if ( pData && pData->GetInput( aTitle, aMessage ) && !aMessage.isEmpty() )
800         {
801             ScSplitPos eWhich = aViewData.GetActivePart();
802             ScGridWindow* pWin = pGridWin[eWhich].get();
803             SCCOL nCol = aViewData.GetCurX();
804             SCROW nRow = aViewData.GetCurY();
805             Point aPos = aViewData.GetScrPos( nCol, nRow, eWhich );
806             Size aWinSize = pWin->GetOutputSizePixel();
807             // cursor visible?
808             if ( nCol >= aViewData.GetPosX(WhichH(eWhich)) &&
809                  nRow >= aViewData.GetPosY(WhichV(eWhich)) &&
810                  aPos.X() < aWinSize.Width() && aPos.Y() < aWinSize.Height() )
811             {
812                 const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
813                 Color aCommentColor = rColorCfg.GetColorValue(svtools::CALCNOTESBACKGROUND).nColor;
814                 // create HintWindow, determines its size by itself
815                 ScOverlayHint* pOverlay = new ScOverlayHint(aTitle, aMessage, aCommentColor, pFrameWin->GetFont());
816 
817                 mxInputHintOO.reset(new sdr::overlay::OverlayObjectList);
818                 mxInputHintOO->append(std::unique_ptr<sdr::overlay::OverlayObject>(pOverlay));
819 
820                 Size aHintWndSize = pOverlay->GetSizePixel();
821                 tools::Long nCellSizeX = 0;
822                 tools::Long nCellSizeY = 0;
823                 aViewData.GetMergeSizePixel(nCol, nRow, nCellSizeX, nCellSizeY);
824 
825                 Point aHintPos = calcHintWindowPosition(
826                     aPos, Size(nCellSizeX,nCellSizeY), aWinSize, aHintWndSize);
827 
828                 pOverlay->SetPos(pWin->PixelToLogic(aHintPos, pWin->GetDrawMapMode()), pWin->GetDrawMapMode());
829                 for (VclPtr<ScGridWindow> & pWindow : pGridWin)
830                 {
831                     if (!pWindow)
832                         continue;
833                     if (!pWindow->IsVisible())
834                         continue;
835                     rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = pWindow->getOverlayManager();
836                     if (!xOverlayManager.is())
837                         continue;
838                     if (pWindow == pWin)
839                     {
840                         xOverlayManager->add(*pOverlay);
841                         pWindow->updateLOKInputHelp(aTitle, aMessage);
842                     }
843                     else
844                     {
845                         //tdf#92530 if the help tip doesn't fit into its allocated area in a split window
846                         //scenario, then because here we place it into the other split windows as well the
847                         //missing portions will be displayed in the other split windows to form an apparent
848                         //single tip, albeit "under" the split lines
849                         Point aOtherPos(pWindow->ScreenToOutputPixel(pWin->OutputToScreenPixel(aHintPos)));
850                         std::unique_ptr<ScOverlayHint> pOtherOverlay(new ScOverlayHint(aTitle, aMessage, aCommentColor, pFrameWin->GetFont()));
851                         Point aFooPos(pWindow->PixelToLogic(aOtherPos, pWindow->GetDrawMapMode()));
852                         pOtherOverlay->SetPos(aFooPos, pWindow->GetDrawMapMode());
853                         xOverlayManager->add(*pOtherOverlay);
854                         mxInputHintOO->append(std::move(pOtherOverlay));
855                     }
856                 }
857             }
858         }
859 
860         // list drop-down button
861         if ( pData && pData->HasSelectionList() )
862         {
863             aListValPos.Set( aViewData.GetCurX(), aViewData.GetCurY(), aViewData.GetTabNo() );
864             bListValButton = true;
865         }
866     }
867 
868     for (VclPtr<ScGridWindow> const & pWin : pGridWin)
869     {
870         if (pWin && pWin->IsVisible())
871             pWin->UpdateListValPos(bListValButton, aListValPos);
872     }
873 }
874 
HasHintWindow() const875 bool ScTabView::HasHintWindow() const { return mxInputHintOO != nullptr; }
876 
RemoveHintWindow()877 void ScTabView::RemoveHintWindow()
878 {
879     mxInputHintOO.reset();
880 }
881 
882 // find window that should not be over the cursor
lcl_GetCareWin(SfxViewFrame * pViewFrm)883 static weld::Window* lcl_GetCareWin(SfxViewFrame* pViewFrm)
884 {
885     //! also spelling ??? (then set the member variables when calling)
886 
887     // search & replace
888     if (pViewFrm->HasChildWindow(SID_SEARCH_DLG))
889     {
890         SfxChildWindow* pChild = pViewFrm->GetChildWindow(SID_SEARCH_DLG);
891         if (pChild)
892         {
893             auto xDlgController = pChild->GetController();
894             if (xDlgController && xDlgController->getDialog()->get_visible())
895                 return xDlgController->getDialog();
896         }
897     }
898 
899     // apply changes
900     if ( pViewFrm->HasChildWindow(FID_CHG_ACCEPT) )
901     {
902         SfxChildWindow* pChild = pViewFrm->GetChildWindow(FID_CHG_ACCEPT);
903         if (pChild)
904         {
905             auto xDlgController = pChild->GetController();
906             if (xDlgController && xDlgController->getDialog()->get_visible())
907                 return xDlgController->getDialog();
908         }
909     }
910 
911     return nullptr;
912 }
913 
914     // adjust screen with respect to cursor position
915 
AlignToCursor(SCCOL nCurX,SCROW nCurY,ScFollowMode eMode,const ScSplitPos * pWhich)916 void ScTabView::AlignToCursor( SCCOL nCurX, SCROW nCurY, ScFollowMode eMode,
917                                 const ScSplitPos* pWhich )
918 {
919     // now switch active part here
920 
921     ScSplitPos eActive = aViewData.GetActivePart();
922     ScHSplitPos eActiveX = WhichH(eActive);
923     ScVSplitPos eActiveY = WhichV(eActive);
924     bool bHFix = (aViewData.GetHSplitMode() == SC_SPLIT_FIX);
925     bool bVFix = (aViewData.GetVSplitMode() == SC_SPLIT_FIX);
926     if (bHFix && eActiveX == SC_SPLIT_LEFT && nCurX >= aViewData.GetFixPosX())
927     {
928         ActivatePart( (eActiveY==SC_SPLIT_TOP) ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT );
929         eActiveX = SC_SPLIT_RIGHT;
930     }
931     if (bVFix && eActiveY == SC_SPLIT_TOP && nCurY >= aViewData.GetFixPosY())
932     {
933         ActivatePart( (eActiveX==SC_SPLIT_LEFT) ? SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT );
934         eActiveY = SC_SPLIT_BOTTOM;
935     }
936 
937     // actual align
938 
939     if ( eMode != SC_FOLLOW_NONE )
940     {
941         ScSplitPos eAlign;
942         if (pWhich)
943             eAlign = *pWhich;
944         else
945             eAlign = aViewData.GetActivePart();
946         ScHSplitPos eAlignX = WhichH(eAlign);
947         ScVSplitPos eAlignY = WhichV(eAlign);
948 
949         SCCOL nDeltaX = aViewData.GetPosX(eAlignX);
950         SCROW nDeltaY = aViewData.GetPosY(eAlignY);
951         SCCOL nSizeX = aViewData.VisibleCellsX(eAlignX);
952         SCROW nSizeY = aViewData.VisibleCellsY(eAlignY);
953 
954         tools::Long nCellSizeX;
955         tools::Long nCellSizeY;
956         if ( nCurX >= 0 && nCurY >= 0 )
957             aViewData.GetMergeSizePixel( nCurX, nCurY, nCellSizeX, nCellSizeY );
958         else
959             nCellSizeX = nCellSizeY = 0;
960         Size aScrSize = aViewData.GetScrSize();
961         tools::Long nSpaceX = ( aScrSize.Width()  - nCellSizeX ) / 2;
962         tools::Long nSpaceY = ( aScrSize.Height() - nCellSizeY ) / 2;
963         //  nSpaceY: desired start position of cell for FOLLOW_JUMP, modified if dialog interferes
964 
965         bool bForceNew = false;     // force new calculation of JUMP position (vertical only)
966 
967         // VisibleCellsY == CellsAtY( GetPosY( eWhichY ), 1, eWhichY )
968 
969         // when for instance a search dialog is open, don't put the cursor behind the dialog
970         // if possible, put the row with the cursor above or below the dialog
971         //! not if already completely visible
972 
973         if ( eMode == SC_FOLLOW_JUMP )
974         {
975             weld::Window* pCare = lcl_GetCareWin( aViewData.GetViewShell()->GetViewFrame() );
976             if (pCare)
977             {
978                 bool bLimit = false;
979                 tools::Rectangle aDlgPixel;
980                 Size aWinSize;
981                 vcl::Window* pWin = GetActiveWin();
982                 weld::Window* pFrame = pWin ? pWin->GetFrameWeld() : nullptr;
983                 int x, y, width, height;
984                 if (pFrame && pCare->get_extents_relative_to(*pFrame, x, y, width, height))
985                 {
986                     aDlgPixel = tools::Rectangle(Point(x, y), Size(width, height));
987                     aWinSize = pWin->GetOutputSizePixel();
988                     // dos the dialog cover the GridWin?
989                     if ( aDlgPixel.Right() >= 0 && aDlgPixel.Left() < aWinSize.Width() )
990                     {
991                         if ( nCurX < nDeltaX || nCurX >= nDeltaX+nSizeX ||
992                              nCurY < nDeltaY || nCurY >= nDeltaY+nSizeY )
993                             bLimit = true;          // scroll anyway
994                         else
995                         {
996                             // cursor is on the screen
997                             Point aStart = aViewData.GetScrPos( nCurX, nCurY, eAlign );
998                             tools::Long nCSX, nCSY;
999                             aViewData.GetMergeSizePixel( nCurX, nCurY, nCSX, nCSY );
1000                             tools::Rectangle aCursor( aStart, Size( nCSX, nCSY ) );
1001                             if ( aCursor.IsOver( aDlgPixel ) )
1002                                 bLimit = true;      // cell is covered by the dialog
1003                         }
1004                     }
1005                 }
1006 
1007                 if (bLimit)
1008                 {
1009                     bool bBottom = false;
1010                     tools::Long nTopSpace = aDlgPixel.Top();
1011                     tools::Long nBotSpace = aWinSize.Height() - aDlgPixel.Bottom();
1012                     if ( nBotSpace > 0 && nBotSpace > nTopSpace )
1013                     {
1014                         tools::Long nDlgBot = aDlgPixel.Bottom();
1015                         SCCOL nWPosX;
1016                         SCROW nWPosY;
1017                         aViewData.GetPosFromPixel( 0,nDlgBot, eAlign, nWPosX, nWPosY );
1018                         ++nWPosY;   // below the last affected cell
1019 
1020                         SCROW nDiff = nWPosY - nDeltaY;
1021                         if ( nCurY >= nDiff )           // position can not be negative
1022                         {
1023                             nSpaceY = nDlgBot + ( nBotSpace - nCellSizeY ) / 2;
1024                             bBottom = true;
1025                             bForceNew = true;
1026                         }
1027                     }
1028                     if ( !bBottom && nTopSpace > 0 )
1029                     {
1030                         nSpaceY = ( nTopSpace - nCellSizeY ) / 2;
1031                         bForceNew = true;
1032                     }
1033                 }
1034             }
1035         }
1036 
1037         SCCOL nNewDeltaX = nDeltaX;
1038         SCROW nNewDeltaY = nDeltaY;
1039         bool bDoLine = false;
1040 
1041         switch (eMode)
1042         {
1043             case SC_FOLLOW_JUMP:
1044                 if ( nCurX < nDeltaX || nCurX >= nDeltaX+nSizeX )
1045                 {
1046                     nNewDeltaX = nCurX - aViewData.CellsAtX( nCurX, -1, eAlignX, static_cast<sal_uInt16>(nSpaceX) );
1047                     if (nNewDeltaX < 0)
1048                         nNewDeltaX = 0;
1049                     nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
1050                 }
1051                 if ( nCurY < nDeltaY || nCurY >= nDeltaY+nSizeY || bForceNew )
1052                 {
1053                     nNewDeltaY = nCurY - aViewData.CellsAtY( nCurY, -1, eAlignY, static_cast<sal_uInt16>(nSpaceY) );
1054                     if (nNewDeltaY < 0)
1055                         nNewDeltaY = 0;
1056                     nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
1057                 }
1058                 bDoLine = true;
1059                 break;
1060 
1061             case SC_FOLLOW_LINE:
1062                 bDoLine = true;
1063                 break;
1064 
1065             case SC_FOLLOW_FIX:
1066                 if ( nCurX < nDeltaX || nCurX >= nDeltaX+nSizeX )
1067                 {
1068                     nNewDeltaX = nDeltaX + nCurX - aViewData.GetCurX();
1069                     if (nNewDeltaX < 0)
1070                         nNewDeltaX = 0;
1071                     nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
1072                 }
1073                 if ( nCurY < nDeltaY || nCurY >= nDeltaY+nSizeY )
1074                 {
1075                     nNewDeltaY = nDeltaY + nCurY - aViewData.GetCurY();
1076                     if (nNewDeltaY < 0)
1077                         nNewDeltaY = 0;
1078                     nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
1079                 }
1080 
1081                 //  like old version of SC_FOLLOW_JUMP:
1082 
1083                 if ( nCurX < nNewDeltaX || nCurX >= nNewDeltaX+nSizeX )
1084                 {
1085                     nNewDeltaX = nCurX - (nSizeX / 2);
1086                     if (nNewDeltaX < 0)
1087                         nNewDeltaX = 0;
1088                     nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
1089                 }
1090                 if ( nCurY < nNewDeltaY || nCurY >= nNewDeltaY+nSizeY )
1091                 {
1092                     nNewDeltaY = nCurY - (nSizeY / 2);
1093                     if (nNewDeltaY < 0)
1094                         nNewDeltaY = 0;
1095                     nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
1096                 }
1097 
1098                 bDoLine = true;
1099                 break;
1100 
1101             case SC_FOLLOW_NONE:
1102                 break;
1103             default:
1104                 OSL_FAIL("Wrong cursor mode");
1105                 break;
1106         }
1107 
1108         ScDocument& rDoc = aViewData.GetDocument();
1109         if (bDoLine)
1110         {
1111             while ( nCurX >= nNewDeltaX+nSizeX )
1112             {
1113                 nNewDeltaX = nCurX-nSizeX+1;
1114                 SCTAB nTab = aViewData.GetTabNo();
1115                 while ( nNewDeltaX < rDoc.MaxCol() && !rDoc.GetColWidth( nNewDeltaX, nTab ) )
1116                     ++nNewDeltaX;
1117                 nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
1118             }
1119             while ( nCurY >= nNewDeltaY+nSizeY )
1120             {
1121                 nNewDeltaY = nCurY-nSizeY+1;
1122                 SCTAB nTab = aViewData.GetTabNo();
1123                 while ( nNewDeltaY < rDoc.MaxRow() && !rDoc.GetRowHeight( nNewDeltaY, nTab ) )
1124                     ++nNewDeltaY;
1125                 nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
1126             }
1127             if ( nCurX < nNewDeltaX )
1128                 nNewDeltaX = nCurX;
1129             if ( nCurY < nNewDeltaY )
1130                 nNewDeltaY = nCurY;
1131         }
1132 
1133         if ( nNewDeltaX != nDeltaX )
1134             nSizeX = aViewData.CellsAtX( nNewDeltaX, 1, eAlignX );
1135         if (nNewDeltaX+nSizeX-1 > rDoc.MaxCol())
1136             nNewDeltaX = rDoc.MaxCol()-nSizeX+1;
1137         if (nNewDeltaX < 0)
1138             nNewDeltaX = 0;
1139 
1140         if ( nNewDeltaY != nDeltaY )
1141             nSizeY = aViewData.CellsAtY( nNewDeltaY, 1, eAlignY );
1142         if (nNewDeltaY+nSizeY-1 > rDoc.MaxRow())
1143             nNewDeltaY = rDoc.MaxRow()-nSizeY+1;
1144         if (nNewDeltaY < 0)
1145             nNewDeltaY = 0;
1146 
1147         if ( nNewDeltaX != nDeltaX )
1148             ScrollX( nNewDeltaX - nDeltaX, eAlignX );
1149         if ( nNewDeltaY != nDeltaY )
1150             ScrollY( nNewDeltaY - nDeltaY, eAlignY );
1151     }
1152 
1153     // switch active part again
1154 
1155     if (bHFix)
1156         if (eActiveX == SC_SPLIT_RIGHT && nCurX < aViewData.GetFixPosX())
1157         {
1158             ActivatePart( (eActiveY==SC_SPLIT_TOP) ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT );
1159             eActiveX = SC_SPLIT_LEFT;
1160         }
1161     if (bVFix)
1162         if (eActiveY == SC_SPLIT_BOTTOM && nCurY < aViewData.GetFixPosY())
1163         {
1164             ActivatePart( (eActiveX==SC_SPLIT_LEFT) ? SC_SPLIT_TOPLEFT : SC_SPLIT_TOPRIGHT );
1165         }
1166 }
1167 
SelMouseButtonDown(const MouseEvent & rMEvt)1168 bool ScTabView::SelMouseButtonDown( const MouseEvent& rMEvt )
1169 {
1170     bool bRet = false;
1171 
1172     // #i3875# *Hack*
1173     bool bMod1Locked = (aViewData.GetViewShell()->GetLockedModifiers() & KEY_MOD1) != 0;
1174     aViewData.SetSelCtrlMouseClick( rMEvt.IsMod1() || bMod1Locked );
1175 
1176     if ( pSelEngine )
1177     {
1178         bMoveIsShift = rMEvt.IsShift();
1179         bRet = pSelEngine->SelMouseButtonDown( rMEvt );
1180         bMoveIsShift = false;
1181     }
1182 
1183     aViewData.SetSelCtrlMouseClick( false ); // #i3875# *Hack*
1184 
1185     return bRet;
1186 }
1187 
1188     //  MoveCursor - with adjustment of the view section
1189 
MoveCursorAbs(SCCOL nCurX,SCROW nCurY,ScFollowMode eMode,bool bShift,bool bControl,bool bKeepOld,bool bKeepSel)1190 void ScTabView::MoveCursorAbs( SCCOL nCurX, SCROW nCurY, ScFollowMode eMode,
1191                                bool bShift, bool bControl, bool bKeepOld, bool bKeepSel )
1192 {
1193     if (!bKeepOld)
1194         aViewData.ResetOldCursor();
1195 
1196     ScDocument& rDoc = aViewData.GetDocument();
1197     // #i123629#
1198     if( aViewData.GetViewShell()->GetForceFocusOnCurCell() )
1199         aViewData.GetViewShell()->SetForceFocusOnCurCell( !rDoc.ValidColRow(nCurX, nCurY) );
1200 
1201     if (nCurX < 0) nCurX = 0;
1202     if (nCurY < 0) nCurY = 0;
1203     if (nCurX > rDoc.MaxCol()) nCurX = rDoc.MaxCol();
1204     if (nCurY > rDoc.MaxRow()) nCurY = rDoc.MaxRow();
1205 
1206     // FIXME: this is to limit the number of rows handled in the Online
1207     // to 1000; this will be removed again when the performance
1208     // bottlenecks are sorted out
1209     if (comphelper::LibreOfficeKit::isActive())
1210         nCurY = std::min(nCurY, MAXTILEDROW);
1211 
1212     HideAllCursors();
1213 
1214     // switch of active now in AlignToCursor
1215 
1216     AlignToCursor( nCurX, nCurY, eMode );
1217 
1218     if (bKeepSel)
1219     {
1220         SetCursor( nCurX, nCurY );      // keep selection
1221 
1222         // If the cursor is in existing selection, it's a cursor movement by
1223         // ENTER or TAB.  If not, then it's a new selection during ADD
1224         // selection mode.
1225 
1226         const ScMarkData& rMark = aViewData.GetMarkData();
1227         ScRangeList aSelList;
1228         rMark.FillRangeListWithMarks(&aSelList, false);
1229         if (!aSelList.In(ScRange(nCurX, nCurY, aViewData.GetTabNo())))
1230             // Cursor not in existing selection.  Start a new selection.
1231             DoneBlockMode(true);
1232     }
1233     else
1234     {
1235         if (!bShift)
1236         {
1237             // Remove all marked data on cursor movement unless the Shift is locked.
1238             ScMarkData& rMark = aViewData.GetMarkData();
1239             bool bMarked = rMark.IsMarked() || rMark.IsMultiMarked();
1240             if (bMarked)
1241             {
1242                 rMark.ResetMark();
1243                 DoneBlockMode();
1244                 InitOwnBlockMode();
1245                 MarkDataChanged();
1246             }
1247         }
1248 
1249         bool bSame = ( nCurX == aViewData.GetCurX() && nCurY == aViewData.GetCurY() );
1250         bMoveIsShift = bShift;
1251         pSelEngine->CursorPosChanging( bShift, bControl );
1252         bMoveIsShift = false;
1253         aFunctionSet.SetCursorAtCell( nCurX, nCurY, false );
1254 
1255         // If the cursor has not been moved, the SelectionChanged for canceling the
1256         // selection has to happen here individually:
1257         if (bSame)
1258             SelectionChanged();
1259     }
1260 
1261     ShowAllCursors();
1262     TestHintWindow();
1263 }
1264 
MoveCursorRel(SCCOL nMovX,SCROW nMovY,ScFollowMode eMode,bool bShift,bool bKeepSel)1265 void ScTabView::MoveCursorRel( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode,
1266                                bool bShift, bool bKeepSel )
1267 {
1268     ScDocument& rDoc = aViewData.GetDocument();
1269     SCTAB nTab = aViewData.GetTabNo();
1270 
1271     bool bSkipProtected = false, bSkipUnprotected = false;
1272     const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
1273     if ( pProtect && pProtect->isProtected() )
1274     {
1275         bSkipProtected   = !pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS);
1276         bSkipUnprotected = !pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS);
1277     }
1278 
1279     if ( bSkipProtected && bSkipUnprotected )
1280         return;
1281 
1282     SCCOL nOldX;
1283     SCROW nOldY;
1284     SCCOL nCurX;
1285     SCROW nCurY;
1286     if ( aViewData.IsRefMode() )
1287     {
1288         nOldX = aViewData.GetRefEndX();
1289         nOldY = aViewData.GetRefEndY();
1290         nCurX = nOldX + nMovX;
1291         nCurY = nOldY + nMovY;
1292     }
1293     else
1294     {
1295         nOldX = aViewData.GetCurX();
1296         nOldY = aViewData.GetCurY();
1297         nCurX = (nMovX != 0) ? nOldX+nMovX : aViewData.GetOldCurX();
1298         nCurY = (nMovY != 0) ? nOldY+nMovY : aViewData.GetOldCurY();
1299     }
1300 
1301     if (nMovX < 0 && nOldX == 0)
1302     { // trying to go left from 1st column
1303         if (nMovY == 0) // done, because no vertical move is requested
1304             return;
1305     }
1306     if (nMovY < 0 && nOldY == 0)
1307     { // trying to go up from 1st row
1308         if (nMovX == 0) // done, because no horizontal move is requested
1309             return;
1310     }
1311 
1312     aViewData.ResetOldCursor();
1313 
1314     if (nMovX != 0 && rDoc.ValidColRow(nCurX,nCurY))
1315         SkipCursorHorizontal(nCurX, nCurY, nOldX, nMovX);
1316 
1317     if (nMovY != 0 && rDoc.ValidColRow(nCurX,nCurY))
1318         SkipCursorVertical(nCurX, nCurY, nOldY, nMovY);
1319 
1320     MoveCursorAbs( nCurX, nCurY, eMode, bShift, false, true, bKeepSel );
1321 }
1322 
MoveCursorPage(SCCOL nMovX,SCROW nMovY,ScFollowMode eMode,bool bShift,bool bKeepSel)1323 void ScTabView::MoveCursorPage( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, bool bShift, bool bKeepSel )
1324 {
1325     SCCOL nPageX;
1326     SCROW nPageY;
1327     GetPageMoveEndPosition(nMovX, nMovY, nPageX, nPageY);
1328     MoveCursorRel( nPageX, nPageY, eMode, bShift, bKeepSel );
1329 }
1330 
MoveCursorArea(SCCOL nMovX,SCROW nMovY,ScFollowMode eMode,bool bShift,bool bKeepSel)1331 void ScTabView::MoveCursorArea( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, bool bShift, bool bKeepSel )
1332 {
1333     SCCOL nNewX;
1334     SCROW nNewY;
1335     GetAreaMoveEndPosition(nMovX, nMovY, eMode, nNewX, nNewY, eMode);
1336     MoveCursorRel(nNewX, nNewY, eMode, bShift, bKeepSel);
1337 }
1338 
MoveCursorEnd(SCCOL nMovX,SCROW nMovY,ScFollowMode eMode,bool bShift,bool bKeepSel)1339 void ScTabView::MoveCursorEnd( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, bool bShift, bool bKeepSel )
1340 {
1341     ScDocument& rDoc = aViewData.GetDocument();
1342     SCTAB nTab = aViewData.GetTabNo();
1343 
1344     SCCOL nCurX;
1345     SCROW nCurY;
1346     aViewData.GetMoveCursor( nCurX,nCurY );
1347     SCCOL nNewX = nCurX;
1348     SCROW nNewY = nCurY;
1349 
1350     SCCOL nUsedX = 0;
1351     SCROW nUsedY = 0;
1352     if ( nMovX > 0 || nMovY > 0 )
1353         rDoc.GetPrintArea( nTab, nUsedX, nUsedY );     // get end
1354 
1355     if (nMovX<0)
1356         nNewX=0;
1357     else if (nMovX>0)
1358         nNewX=nUsedX;                                   // last used range
1359 
1360     if (nMovY<0)
1361         nNewY=0;
1362     else if (nMovY>0)
1363         nNewY=nUsedY;
1364 
1365     aViewData.ResetOldCursor();
1366     MoveCursorRel( nNewX-nCurX, nNewY-nCurY, eMode, bShift, bKeepSel );
1367 }
1368 
MoveCursorScreen(SCCOL nMovX,SCROW nMovY,ScFollowMode eMode,bool bShift)1369 void ScTabView::MoveCursorScreen( SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, bool bShift )
1370 {
1371     ScDocument& rDoc = aViewData.GetDocument();
1372     SCTAB nTab = aViewData.GetTabNo();
1373 
1374     SCCOL nCurX;
1375     SCROW nCurY;
1376     aViewData.GetMoveCursor( nCurX,nCurY );
1377     SCCOL nNewX = nCurX;
1378     SCROW nNewY = nCurY;
1379 
1380     ScSplitPos eWhich = aViewData.GetActivePart();
1381     SCCOL nPosX = aViewData.GetPosX( WhichH(eWhich) );
1382     SCROW nPosY = aViewData.GetPosY( WhichV(eWhich) );
1383 
1384     SCCOL nAddX = aViewData.VisibleCellsX( WhichH(eWhich) );
1385     if (nAddX != 0)
1386         --nAddX;
1387     SCROW nAddY = aViewData.VisibleCellsY( WhichV(eWhich) );
1388     if (nAddY != 0)
1389         --nAddY;
1390 
1391     if (nMovX<0)
1392         nNewX=nPosX;
1393     else if (nMovX>0)
1394         nNewX=nPosX+nAddX;
1395 
1396     if (nMovY<0)
1397         nNewY=nPosY;
1398     else if (nMovY>0)
1399         nNewY=nPosY+nAddY;
1400 
1401     aViewData.SetOldCursor( nNewX,nNewY );
1402     rDoc.SkipOverlapped(nNewX, nNewY, nTab);
1403     MoveCursorAbs( nNewX, nNewY, eMode, bShift, false, true );
1404 }
1405 
MoveCursorEnter(bool bShift)1406 void ScTabView::MoveCursorEnter( bool bShift )          // bShift -> up/down
1407 {
1408     const ScInputOptions& rOpt = SC_MOD()->GetInputOptions();
1409     if (!rOpt.GetMoveSelection())
1410     {
1411         aViewData.UpdateInputHandler(true);
1412         return;
1413     }
1414 
1415     SCCOL nMoveX = 0;
1416     SCROW nMoveY = 0;
1417     switch (static_cast<ScDirection>(rOpt.GetMoveDir()))
1418     {
1419         case DIR_BOTTOM:
1420             nMoveY = bShift ? -1 : 1;
1421             break;
1422         case DIR_RIGHT:
1423             nMoveX = bShift ? -1 : 1;
1424             break;
1425         case DIR_TOP:
1426             nMoveY = bShift ? 1 : -1;
1427             break;
1428         case DIR_LEFT:
1429             nMoveX = bShift ? 1 : -1;
1430             break;
1431     }
1432 
1433     SCCOL nCurX;
1434     SCROW nCurY;
1435     aViewData.GetMoveCursor( nCurX,nCurY );
1436     SCCOL nNewX = nCurX;
1437     SCROW nNewY = nCurY;
1438     SCTAB nTab  = aViewData.GetTabNo();
1439 
1440     ScMarkData& rMark = aViewData.GetMarkData();
1441     ScDocument& rDoc  = aViewData.GetDocument();
1442 
1443     if (rMark.IsMarked() || rMark.IsMultiMarked())
1444     {
1445         rDoc.GetNextPos( nNewX, nNewY, nTab, nMoveX, nMoveY, true, false, rMark );
1446 
1447         MoveCursorRel( nNewX - nCurX, nNewY - nCurY, SC_FOLLOW_LINE, false, true );
1448 
1449         //  update input line even if cursor was not moved
1450         if ( nNewX == nCurX && nNewY == nCurY )
1451             aViewData.UpdateInputHandler(true);
1452     }
1453     else
1454     {
1455         // After Tab and Enter back to the starting column again.
1456         const SCCOL nTabStartCol = ((nMoveY != 0 && !nMoveX) ? aViewData.GetTabStartCol() : SC_TABSTART_NONE);
1457         rDoc.GetNextPos( nNewX, nNewY, nTab, nMoveX, nMoveY, false, true, rMark, nTabStartCol );
1458 
1459         MoveCursorRel( nNewX - nCurX, nNewY - nCurY, SC_FOLLOW_LINE, false);
1460     }
1461 }
1462 
MoveCursorKeyInput(const KeyEvent & rKeyEvent)1463 bool ScTabView::MoveCursorKeyInput( const KeyEvent& rKeyEvent )
1464 {
1465     const vcl::KeyCode& rKCode = rKeyEvent.GetKeyCode();
1466 
1467     enum { MOD_NONE, MOD_CTRL, MOD_ALT, MOD_BOTH } eModifier =
1468         rKCode.IsMod1() ?
1469             (rKCode.IsMod2() ? MOD_BOTH : MOD_CTRL) :
1470             (rKCode.IsMod2() ? MOD_ALT : MOD_NONE);
1471 
1472     bool bSel = rKCode.IsShift();
1473     sal_uInt16 nCode = rKCode.GetCode();
1474 
1475     // CURSOR keys
1476     SCCOL nDX = 0;
1477     SCROW nDY = 0;
1478     switch( nCode )
1479     {
1480         case KEY_LEFT:  nDX = -1;   break;
1481         case KEY_RIGHT: nDX = 1;    break;
1482         case KEY_UP:    nDY = -1;   break;
1483         case KEY_DOWN:  nDY = 1;    break;
1484     }
1485     if( nDX != 0 || nDY != 0 )
1486     {
1487         switch( eModifier )
1488         {
1489             case MOD_NONE:  MoveCursorRel( nDX, nDY, SC_FOLLOW_LINE, bSel );    break;
1490             case MOD_CTRL:  MoveCursorArea( nDX, nDY, SC_FOLLOW_JUMP, bSel );   break;
1491             default:
1492             {
1493                 // added to avoid warnings
1494             }
1495         }
1496         // always true to suppress changes of col/row size (ALT+CURSOR)
1497         return true;
1498     }
1499 
1500     // PAGEUP/PAGEDOWN
1501     if( (nCode == KEY_PAGEUP) || (nCode == KEY_PAGEDOWN) )
1502     {
1503         nDX = (nCode == KEY_PAGEUP) ? -1 : 1;
1504         switch( eModifier )
1505         {
1506             case MOD_NONE:  MoveCursorPage( 0, static_cast<SCCOLROW>(nDX), SC_FOLLOW_FIX, bSel );  break;
1507             case MOD_ALT:   MoveCursorPage( nDX, 0, SC_FOLLOW_FIX, bSel );  break;
1508             case MOD_CTRL:  SelectNextTab( nDX, false );                    break;
1509             default:
1510             {
1511                 // added to avoid warnings
1512             }
1513         }
1514         return true;
1515     }
1516 
1517     // HOME/END
1518     if( (nCode == KEY_HOME) || (nCode == KEY_END) )
1519     {
1520         nDX = (nCode == KEY_HOME) ? -1 : 1;
1521         ScFollowMode eMode = (nCode == KEY_HOME) ? SC_FOLLOW_LINE : SC_FOLLOW_JUMP;
1522         switch( eModifier )
1523         {
1524             case MOD_NONE:  MoveCursorEnd( nDX, 0, eMode, bSel );   break;
1525             case MOD_CTRL:  MoveCursorEnd( nDX, static_cast<SCCOLROW>(nDX), eMode, bSel ); break;
1526             default:
1527             {
1528                 // added to avoid warnings
1529             }
1530         }
1531         return true;
1532     }
1533 
1534     return false;
1535 }
1536 
1537         // next/previous unprotected cell
FindNextUnprot(bool bShift,bool bInSelection)1538 void ScTabView::FindNextUnprot( bool bShift, bool bInSelection )
1539 {
1540     short nMove = bShift ? -1 : 1;
1541 
1542     ScMarkData& rMark = aViewData.GetMarkData();
1543     bool bMarked = bInSelection && (rMark.IsMarked() || rMark.IsMultiMarked());
1544 
1545     SCCOL nCurX;
1546     SCROW nCurY;
1547     aViewData.GetMoveCursor( nCurX,nCurY );
1548     SCCOL nNewX = nCurX;
1549     SCROW nNewY = nCurY;
1550     SCTAB nTab = aViewData.GetTabNo();
1551 
1552     ScDocument& rDoc = aViewData.GetDocument();
1553     rDoc.GetNextPos( nNewX,nNewY, nTab, nMove,0, bMarked, true, rMark );
1554 
1555     SCCOL nTabCol = aViewData.GetTabStartCol();
1556     if ( nTabCol == SC_TABSTART_NONE )
1557         nTabCol = nCurX;                    // back to this column after Enter
1558 
1559     MoveCursorRel( nNewX-nCurX, nNewY-nCurY, SC_FOLLOW_LINE, false, true );
1560 
1561     // TabCol is reset in MoveCursorRel...
1562     aViewData.SetTabStartCol( nTabCol );
1563 }
1564 
MarkColumns()1565 void ScTabView::MarkColumns()
1566 {
1567     SCCOL nStartCol;
1568     SCCOL nEndCol;
1569 
1570     ScMarkData& rMark = aViewData.GetMarkData();
1571     if (rMark.IsMarked())
1572     {
1573         ScRange aMarkRange;
1574         rMark.GetMarkArea( aMarkRange );
1575         nStartCol = aMarkRange.aStart.Col();
1576         nEndCol = aMarkRange.aEnd.Col();
1577     }
1578     else
1579     {
1580         SCROW nDummy;
1581         aViewData.GetMoveCursor( nStartCol, nDummy );
1582         nEndCol=nStartCol;
1583     }
1584 
1585     SCTAB nTab = aViewData.GetTabNo();
1586     ScDocument& rDoc = aViewData.GetDocument();
1587     DoneBlockMode();
1588     InitBlockMode( nStartCol,0, nTab );
1589     MarkCursor( nEndCol, rDoc.MaxRow(), nTab );
1590     SelectionChanged();
1591 }
1592 
MarkRows()1593 void ScTabView::MarkRows()
1594 {
1595     SCROW nStartRow;
1596     SCROW nEndRow;
1597 
1598     ScMarkData& rMark = aViewData.GetMarkData();
1599     if (rMark.IsMarked())
1600     {
1601         ScRange aMarkRange;
1602         rMark.GetMarkArea( aMarkRange );
1603         nStartRow = aMarkRange.aStart.Row();
1604         nEndRow = aMarkRange.aEnd.Row();
1605     }
1606     else
1607     {
1608         SCCOL nDummy;
1609         aViewData.GetMoveCursor( nDummy, nStartRow );
1610         nEndRow=nStartRow;
1611     }
1612 
1613     SCTAB nTab = aViewData.GetTabNo();
1614     ScDocument& rDoc = aViewData.GetDocument();
1615     DoneBlockMode();
1616     InitBlockMode( 0,nStartRow, nTab );
1617     MarkCursor( rDoc.MaxCol(), nEndRow, nTab );
1618     SelectionChanged();
1619 }
1620 
1621 
MarkColumns(SCCOL nCol,sal_Int16 nModifier)1622 void ScTabView::MarkColumns(SCCOL nCol, sal_Int16 nModifier)
1623 {
1624     ScDocument& rDoc = aViewData.GetDocument();
1625     SCCOL nStartCol = nCol;
1626     SCTAB nTab = aViewData.GetTabNo();
1627 
1628     if ((nModifier & KEY_SHIFT) == KEY_SHIFT)
1629         bMoveIsShift = true;
1630 
1631     DoneBlockMode( nModifier != 0 );
1632     InitBlockMode( nStartCol, 0, nTab, true, true);
1633     MarkCursor( nCol, rDoc.MaxRow(), nTab );
1634     bMoveIsShift = false;
1635     SetCursor( nCol, 0 );
1636     SelectionChanged();
1637 }
1638 
MarkRows(SCROW nRow,sal_Int16 nModifier)1639 void ScTabView::MarkRows(SCROW nRow, sal_Int16 nModifier)
1640 {
1641     ScDocument& rDoc = aViewData.GetDocument();
1642     SCROW nStartRow = nRow;
1643     SCTAB nTab = aViewData.GetTabNo();
1644 
1645     if ((nModifier & KEY_SHIFT) == KEY_SHIFT)
1646         bMoveIsShift = true;
1647 
1648     DoneBlockMode( nModifier != 0 );
1649     InitBlockMode( 0, nStartRow, nTab, true, false, true );
1650     MarkCursor( rDoc.MaxCol(), nRow, nTab );
1651     bMoveIsShift = false;
1652     SetCursor( 0, nRow );
1653     SelectionChanged();
1654 }
1655 
MarkDataArea(bool bIncludeCursor)1656 void ScTabView::MarkDataArea( bool bIncludeCursor )
1657 {
1658     ScDocument& rDoc = aViewData.GetDocument();
1659     SCTAB nTab = aViewData.GetTabNo();
1660     SCCOL nStartCol = aViewData.GetCurX();
1661     SCROW nStartRow = aViewData.GetCurY();
1662     SCCOL nEndCol = nStartCol;
1663     SCROW nEndRow = nStartRow;
1664 
1665     rDoc.GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow, bIncludeCursor, false );
1666 
1667     HideAllCursors();
1668     DoneBlockMode();
1669     InitBlockMode( nStartCol, nStartRow, nTab );
1670     MarkCursor( nEndCol, nEndRow, nTab );
1671     ShowAllCursors();
1672 
1673     SelectionChanged();
1674 }
1675 
MarkMatrixFormula()1676 void ScTabView::MarkMatrixFormula()
1677 {
1678     ScDocument& rDoc = aViewData.GetDocument();
1679     ScAddress aCursor( aViewData.GetCurX(), aViewData.GetCurY(), aViewData.GetTabNo() );
1680     ScRange aMatrix;
1681     if ( rDoc.GetMatrixFormulaRange( aCursor, aMatrix ) )
1682     {
1683         MarkRange( aMatrix, false );        // cursor is already within the range
1684     }
1685 }
1686 
MarkRange(const ScRange & rRange,bool bSetCursor,bool bContinue)1687 void ScTabView::MarkRange( const ScRange& rRange, bool bSetCursor, bool bContinue )
1688 {
1689     ScDocument& rDoc = aViewData.GetDocument();
1690     SCTAB nTab = rRange.aStart.Tab();
1691     SetTabNo( nTab );
1692 
1693     HideAllCursors();
1694     DoneBlockMode( bContinue ); // bContinue==true -> clear old mark
1695     if (bSetCursor)             // if Cursor is set, also always align
1696     {
1697         SCCOL nAlignX = rRange.aStart.Col();
1698         SCROW nAlignY = rRange.aStart.Row();
1699         bool bCol = ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == rDoc.MaxCol() ) && !aViewData.GetDocument().IsInVBAMode();
1700         bool bRow = ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == rDoc.MaxRow() );
1701         if ( bCol )
1702             nAlignX = aViewData.GetPosX(WhichH(aViewData.GetActivePart()));
1703         if ( bRow )
1704             nAlignY = aViewData.GetPosY(WhichV(aViewData.GetActivePart()));
1705         AlignToCursor( nAlignX, nAlignY, SC_FOLLOW_JUMP );
1706     }
1707     InitBlockMode( rRange.aStart.Col(), rRange.aStart.Row(), nTab );
1708     MarkCursor( rRange.aEnd.Col(), rRange.aEnd.Row(), nTab );
1709     if (bSetCursor)
1710     {
1711         SCCOL nPosX = rRange.aStart.Col();
1712         SCROW nPosY = rRange.aStart.Row();
1713         rDoc.SkipOverlapped(nPosX, nPosY, nTab);
1714 
1715         aViewData.ResetOldCursor();
1716         SetCursor( nPosX, nPosY );
1717     }
1718     ShowAllCursors();
1719 
1720     SelectionChanged();
1721 }
1722 
Unmark()1723 void ScTabView::Unmark()
1724 {
1725     ScMarkData& rMark = aViewData.GetMarkData();
1726     if ( rMark.IsMarked() || rMark.IsMultiMarked() )
1727     {
1728         SCCOL nCurX;
1729         SCROW nCurY;
1730         aViewData.GetMoveCursor( nCurX,nCurY );
1731         MoveCursorAbs( nCurX, nCurY, SC_FOLLOW_NONE, false, false );
1732 
1733         SelectionChanged();
1734     }
1735 }
1736 
SetMarkData(const ScMarkData & rNew)1737 void ScTabView::SetMarkData( const ScMarkData& rNew )
1738 {
1739     DoneBlockMode();
1740     InitOwnBlockMode();
1741     aViewData.GetMarkData() = rNew;
1742 
1743     MarkDataChanged();
1744 }
1745 
MarkDataChanged()1746 void ScTabView::MarkDataChanged()
1747 {
1748     // has to be called after making direct changes to mark data (not via MarkCursor etc)
1749 
1750     UpdateSelectionOverlay();
1751 }
1752 
SelectNextTab(short nDir,bool bExtendSelection)1753 void ScTabView::SelectNextTab( short nDir, bool bExtendSelection )
1754 {
1755     if (!nDir)
1756         return;
1757     OSL_ENSURE( nDir==-1 || nDir==1, "SelectNextTab: invalid value");
1758 
1759     ScDocument& rDoc = aViewData.GetDocument();
1760     SCTAB nTab = aViewData.GetTabNo();
1761     if (nDir<0)
1762     {
1763         if (!nTab)
1764             return;
1765         --nTab;
1766         while (!rDoc.IsVisible(nTab))
1767         {
1768             if (!nTab)
1769                 return;
1770             --nTab;
1771         }
1772     }
1773     else
1774     {
1775         SCTAB nCount = rDoc.GetTableCount();
1776         ++nTab;
1777         if (nTab >= nCount)
1778             return;
1779         while (!rDoc.IsVisible(nTab))
1780         {
1781             ++nTab;
1782             if (nTab >= nCount)
1783                 return;
1784         }
1785     }
1786 
1787     SetTabNo( nTab, false, bExtendSelection );
1788     PaintExtras();
1789 }
1790 
SelectTabPage(const sal_uInt16 nTab)1791 void ScTabView::SelectTabPage( const sal_uInt16 nTab )
1792 {
1793     pTabControl->SwitchToPageId( nTab );
1794 }
1795 
1796 //  SetTabNo - set the displayed sheet
1797 
SetTabNo(SCTAB nTab,bool bNew,bool bExtendSelection,bool bSameTabButMoved)1798 void ScTabView::SetTabNo( SCTAB nTab, bool bNew, bool bExtendSelection, bool bSameTabButMoved )
1799 {
1800     if ( !ValidTab(nTab) )
1801     {
1802         OSL_FAIL("SetTabNo: invalid sheet");
1803         return;
1804     }
1805 
1806     if ( !(nTab != aViewData.GetTabNo() || bNew) )
1807         return;
1808 
1809     // FormShell would like to be informed before the switch
1810     FmFormShell* pFormSh = aViewData.GetViewShell()->GetFormShell();
1811     if (pFormSh)
1812     {
1813         bool bAllowed = pFormSh->PrepareClose();
1814         if (!bAllowed)
1815         {
1816             //! error message? or does FormShell do it?
1817             //! return error flag and cancel actions
1818 
1819             return;     // FormShell says that it can not be switched
1820         }
1821     }
1822 
1823                                     // not InputEnterHandler due to reference input
1824 
1825     ScDocument& rDoc = aViewData.GetDocument();
1826 
1827     rDoc.MakeTable( nTab );
1828 
1829     // Update pending row heights before switching the sheet, so Reschedule from the progress bar
1830     // doesn't paint the new sheet with old heights
1831     aViewData.GetDocShell()->UpdatePendingRowHeights( nTab );
1832 
1833     SCTAB nTabCount = rDoc.GetTableCount();
1834     SCTAB nOldPos = nTab;
1835     while (!rDoc.IsVisible(nTab))              // search for next visible
1836     {
1837         bool bUp = (nTab>=nOldPos);
1838         if (bUp)
1839         {
1840             ++nTab;
1841             if (nTab>=nTabCount)
1842             {
1843                 nTab = nOldPos;
1844                 bUp = false;
1845             }
1846         }
1847 
1848         if (!bUp)
1849         {
1850             if (nTab != 0)
1851                 --nTab;
1852             else
1853             {
1854                 OSL_FAIL("no visible sheets");
1855                 rDoc.SetVisible( 0, true );
1856             }
1857         }
1858     }
1859 
1860     // #i71490# Deselect drawing objects before changing the sheet number in view data,
1861     // so the handling of notes still has the sheet selected on which the notes are.
1862     DrawDeselectAll();
1863 
1864     ScModule* pScMod = SC_MOD();
1865     bool bRefMode = pScMod->IsFormulaMode();
1866     if ( !bRefMode ) // query, so that RefMode works when switching sheet
1867     {
1868         DoneBlockMode();
1869         pSelEngine->Reset();                // reset all flags, including locked modifiers
1870         aViewData.SetRefTabNo( nTab );
1871     }
1872 
1873     ScSplitPos eOldActive = aViewData.GetActivePart();      // before switching
1874     bool bFocus = pGridWin[eOldActive] && pGridWin[eOldActive]->HasFocus();
1875 
1876     aViewData.SetTabNo( nTab );
1877     if (mpSpellCheckCxt)
1878         mpSpellCheckCxt->setTabNo( nTab );
1879     // UpdateShow before SetCursor, so that UpdateAutoFillMark finds the correct
1880     // window  (is called from SetCursor)
1881     UpdateShow();
1882 
1883     SfxBindings& rBindings = aViewData.GetBindings();
1884     ScMarkData& rMark = aViewData.GetMarkData();
1885 
1886     bool bAllSelected = true;
1887     for (SCTAB nSelTab = 0; nSelTab < nTabCount; ++nSelTab)
1888     {
1889         if (!rDoc.IsVisible(nSelTab) || rMark.GetTableSelect(nSelTab))
1890         {
1891             if (nTab == nSelTab)
1892                 // This tab is already in selection.  Keep the current
1893                 // selection.
1894                 bExtendSelection = true;
1895         }
1896         else
1897         {
1898             bAllSelected = false;
1899             if (bExtendSelection)
1900                 // We got what we need.  No need to stay in the loop.
1901                 break;
1902         }
1903     }
1904     if (bAllSelected && !bNew)
1905         // #i6327# if all tables are selected, a selection event (#i6330#) will deselect all
1906         // (not if called with bNew to update settings)
1907         bExtendSelection = false;
1908 
1909     if (bExtendSelection)
1910         rMark.SelectTable( nTab, true );
1911     else
1912     {
1913         rMark.SelectOneTable( nTab );
1914         rBindings.Invalidate( FID_FILL_TAB );
1915         rBindings.Invalidate( FID_TAB_DESELECTALL );
1916     }
1917 
1918     bool bUnoRefDialog = pScMod->IsRefDialogOpen() && pScMod->GetCurRefDlgId() == WID_SIMPLE_REF;
1919 
1920     // recalc zoom-dependent values (before TabChanged, before UpdateEditViewPos)
1921     RefreshZoom();
1922     UpdateVarZoom();
1923 
1924     if ( bRefMode )     // hide EditView if necessary (after aViewData.SetTabNo !)
1925     {
1926         for (VclPtr<ScGridWindow> & pWin : pGridWin)
1927         {
1928             if (pWin && pWin->IsVisible())
1929                 pWin->UpdateEditViewPos();
1930         }
1931     }
1932 
1933     TabChanged(bSameTabButMoved);                                       // DrawView
1934     collectUIInformation({{"TABLE", OUString::number(nTab)}});
1935     UpdateVisibleRange();
1936 
1937     aViewData.GetViewShell()->WindowChanged();          // if the active window has changed
1938     aViewData.ResetOldCursor();
1939     SetCursor( aViewData.GetCurX(), aViewData.GetCurY(), true );
1940 
1941     if ( !bUnoRefDialog )
1942         aViewData.GetViewShell()->DisconnectAllClients();   // important for floating frames
1943     else
1944     {
1945         // hide / show inplace client
1946         ScClient* pClient = static_cast<ScClient*>(aViewData.GetViewShell()->GetIPClient());
1947         if ( pClient && pClient->IsObjectInPlaceActive() )
1948         {
1949             tools::Rectangle aObjArea = pClient->GetObjArea();
1950             if ( nTab == aViewData.GetRefTabNo() )
1951             {
1952                 // move to its original position
1953 
1954                 SdrOle2Obj* pDrawObj = pClient->GetDrawObj();
1955                 if ( pDrawObj )
1956                 {
1957                     tools::Rectangle aRect = pDrawObj->GetLogicRect();
1958                     MapMode aMapMode( MapUnit::Map100thMM );
1959                     Size aOleSize = pDrawObj->GetOrigObjSize( &aMapMode );
1960                     aRect.SetSize( aOleSize );
1961                     aObjArea = aRect;
1962                 }
1963             }
1964             else
1965             {
1966                 // move to an invisible position
1967 
1968                 aObjArea.SetPos( Point( 0, -2*aObjArea.GetHeight() ) );
1969             }
1970             pClient->SetObjArea( aObjArea );
1971         }
1972     }
1973 
1974     if ( bFocus && aViewData.GetActivePart() != eOldActive && !bRefMode )
1975         ActiveGrabFocus();      // grab focus to the pane that's active now
1976 
1977         // freeze
1978 
1979     bool bResize = false;
1980     if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX )
1981         if (aViewData.UpdateFixX())
1982             bResize = true;
1983     if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX )
1984         if (aViewData.UpdateFixY())
1985             bResize = true;
1986     if (bResize)
1987         RepeatResize();
1988     InvalidateSplit();
1989 
1990     if ( aViewData.IsPagebreakMode() )
1991         UpdatePageBreakData();              //! asynchronously ??
1992 
1993     // Form Layer must know the visible area of the new sheet
1994     // that is why MapMode must already be correct here
1995     for (VclPtr<ScGridWindow> & pWin : pGridWin)
1996     {
1997         if (pWin)
1998             pWin->SetMapMode(pWin->GetDrawMapMode());
1999     }
2000     SetNewVisArea();
2001 
2002     PaintGrid();
2003     PaintTop();
2004     PaintLeft();
2005     PaintExtras();
2006 
2007     DoResize( aBorderPos, aFrameSize );
2008     rBindings.Invalidate( SID_DELETE_PRINTAREA );   // Menu
2009     rBindings.Invalidate( FID_DEL_MANUALBREAKS );
2010     rBindings.Invalidate( FID_RESET_PRINTZOOM );
2011     rBindings.Invalidate( SID_STATUS_DOCPOS );      // Status bar
2012     rBindings.Invalidate( SID_ROWCOL_SELCOUNT );    // Status bar
2013     rBindings.Invalidate( SID_STATUS_PAGESTYLE );   // Status bar
2014     rBindings.Invalidate( SID_CURRENTTAB );         // Navigator
2015     rBindings.Invalidate( SID_STYLE_FAMILY2 );      // Designer
2016     rBindings.Invalidate( SID_STYLE_FAMILY4 );      // Designer
2017     rBindings.Invalidate( SID_TABLES_COUNT );
2018 
2019     if (pScMod->IsRefDialogOpen())
2020     {
2021         sal_uInt16 nCurRefDlgId=pScMod->GetCurRefDlgId();
2022         SfxViewFrame* pViewFrm = aViewData.GetViewShell()->GetViewFrame();
2023         SfxChildWindow* pChildWnd = pViewFrm->GetChildWindow( nCurRefDlgId );
2024         if (pChildWnd)
2025         {
2026             if (pChildWnd->GetController())
2027             {
2028                 IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pChildWnd->GetController().get());
2029                 if (pRefDlg)
2030                     pRefDlg->ViewShellChanged();
2031             }
2032         }
2033     }
2034 
2035     OnLibreOfficeKitTabChanged();
2036 }
2037 
AddWindowToForeignEditView(SfxViewShell * pViewShell,ScSplitPos eWhich)2038 void ScTabView::AddWindowToForeignEditView(SfxViewShell* pViewShell, ScSplitPos eWhich)
2039 {
2040     aExtraEditViewManager.Add(pViewShell, eWhich);
2041 }
2042 
RemoveWindowFromForeignEditView(SfxViewShell * pViewShell,ScSplitPos eWhich)2043 void ScTabView::RemoveWindowFromForeignEditView(SfxViewShell* pViewShell, ScSplitPos eWhich)
2044 {
2045     aExtraEditViewManager.Remove(pViewShell, eWhich);
2046 }
2047 
OnLibreOfficeKitTabChanged()2048 void ScTabView::OnLibreOfficeKitTabChanged()
2049 {
2050     if (!comphelper::LibreOfficeKit::isActive())
2051         return;
2052 
2053     ScTabViewShell* pThisViewShell = aViewData.GetViewShell();
2054     SCTAB nThisTabNo = pThisViewShell->GetViewData().GetTabNo();
2055     auto lTabSwitch = [pThisViewShell, nThisTabNo] (ScTabViewShell* pOtherViewShell)
2056     {
2057         ScViewData& rOtherViewData = pOtherViewShell->GetViewData();
2058         SCTAB nOtherTabNo = rOtherViewData.GetTabNo();
2059         if (nThisTabNo == nOtherTabNo)
2060         {
2061             for (int i = 0; i < 4; ++i)
2062             {
2063                 if (rOtherViewData.HasEditView(ScSplitPos(i)))
2064                 {
2065                     pThisViewShell->AddWindowToForeignEditView(pOtherViewShell, ScSplitPos(i));
2066                 }
2067             }
2068         }
2069         else
2070         {
2071             for (int i = 0; i < 4; ++i)
2072             {
2073                 if (rOtherViewData.HasEditView(ScSplitPos(i)))
2074                 {
2075                     pThisViewShell->RemoveWindowFromForeignEditView(pOtherViewShell, ScSplitPos(i));
2076                 }
2077             }
2078         }
2079     };
2080 
2081     SfxLokHelper::forEachOtherView(pThisViewShell, lTabSwitch);
2082 
2083     pThisViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_HEADER, "all");
2084 
2085     if (pThisViewShell->GetInputHandler())
2086         pThisViewShell->GetInputHandler()->UpdateLokReferenceMarks();
2087 }
2088 
2089 // paint functions - only for this View
2090 
MakeEditView(ScEditEngineDefaulter * pEngine,SCCOL nCol,SCROW nRow)2091 void ScTabView::MakeEditView( ScEditEngineDefaulter* pEngine, SCCOL nCol, SCROW nRow )
2092 {
2093     DrawDeselectAll();
2094 
2095     if (pDrawView)
2096         DrawEnableAnim( false );
2097 
2098     EditView* pSpellingView = aViewData.GetSpellingView();
2099 
2100     for (sal_uInt16 i = 0; i < 4; i++)
2101     {
2102         if (pGridWin[i] && pGridWin[i]->IsVisible() && !aViewData.HasEditView(ScSplitPos(i)))
2103         {
2104             ScHSplitPos eHWhich = WhichH( static_cast<ScSplitPos>(i) );
2105             ScVSplitPos eVWhich = WhichV( static_cast<ScSplitPos>(i) );
2106             SCCOL nScrX = aViewData.GetPosX( eHWhich );
2107             SCROW nScrY = aViewData.GetPosY( eVWhich );
2108 
2109             bool bPosVisible =
2110                  ( nCol >= nScrX && nCol <= nScrX + aViewData.VisibleCellsX(eHWhich) - 1 &&
2111                    nRow >= nScrY && nRow <= nScrY + aViewData.VisibleCellsY(eVWhich) - 1 );
2112 
2113             //  for the active part, create edit view even if outside the visible area,
2114             //  so input isn't lost (and the edit view may be scrolled into the visible area)
2115 
2116             //  #i26433# during spelling, the spelling view must be active
2117             if ( bPosVisible || aViewData.GetActivePart() == static_cast<ScSplitPos>(i) ||
2118                  ( pSpellingView && aViewData.GetEditView(static_cast<ScSplitPos>(i)) == pSpellingView ) )
2119             {
2120                 pGridWin[i]->HideCursor();
2121 
2122                 pGridWin[i]->DeleteCursorOverlay();
2123                 pGridWin[i]->DeleteAutoFillOverlay();
2124                 pGridWin[i]->DeleteCopySourceOverlay();
2125 
2126                 // flush OverlayManager before changing MapMode to text edit
2127                 pGridWin[i]->flushOverlayManager();
2128 
2129                 // MapMode must be set after HideCursor
2130                 pGridWin[i]->SetMapMode(aViewData.GetLogicMode());
2131 
2132                 aViewData.SetEditEngine( static_cast<ScSplitPos>(i), pEngine, pGridWin[i], nCol, nRow );
2133 
2134                 if ( !bPosVisible )
2135                 {
2136                     //  move the edit view area to the real (possibly negative) position,
2137                     //  or hide if completely above or left of the window
2138                     pGridWin[i]->UpdateEditViewPos();
2139                 }
2140             }
2141         }
2142     }
2143 
2144     if (aViewData.GetViewShell()->HasAccessibilityObjects())
2145         aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccEnterEditMode));
2146 }
2147 
UpdateEditView()2148 void ScTabView::UpdateEditView()
2149 {
2150     ScSplitPos eActive = aViewData.GetActivePart();
2151     for (sal_uInt16 i = 0; i < 4; i++)
2152     {
2153         ScSplitPos eCurrent = ScSplitPos(i);
2154         if (aViewData.HasEditView(eCurrent))
2155         {
2156             EditView* pEditView = aViewData.GetEditView(eCurrent);
2157 
2158             tools::Long nRefTabNo = GetViewData().GetRefTabNo();
2159             tools::Long nX = GetViewData().GetCurXForTab(nRefTabNo);
2160             tools::Long nY = GetViewData().GetCurYForTab(nRefTabNo);
2161 
2162             aViewData.SetEditEngine(eCurrent,
2163                 static_cast<ScEditEngineDefaulter*>(pEditView->GetEditEngine()),
2164                 pGridWin[i], nX, nY );
2165             if (eCurrent == eActive)
2166                 pEditView->ShowCursor( false );
2167         }
2168     }
2169 }
2170 
KillEditView(bool bNoPaint)2171 void ScTabView::KillEditView( bool bNoPaint )
2172 {
2173     SCCOL nCol1 = aViewData.GetEditStartCol();
2174     SCROW nRow1 = aViewData.GetEditStartRow();
2175     SCCOL nCol2 = aViewData.GetEditEndCol();
2176     SCROW nRow2 = aViewData.GetEditEndRow();
2177     bool bPaint[4];
2178     bool bNotifyAcc = false;
2179     tools::Rectangle aRectangle[4];
2180 
2181     bool bExtended = nRow1 != nRow2;                    // column is painted to the end anyway
2182 
2183     bool bAtCursor = nCol1 <= aViewData.GetCurX() &&
2184                      nCol2 >= aViewData.GetCurX() &&
2185                      nRow1 == aViewData.GetCurY();
2186     for (sal_uInt16 i = 0; i < 4; i++)
2187     {
2188         bPaint[i] = aViewData.HasEditView( static_cast<ScSplitPos>(i) );
2189         if (bPaint[i])
2190         {
2191             bNotifyAcc = true;
2192 
2193             EditView* pView = aViewData.GetEditView( static_cast<ScSplitPos>(i) );
2194             aRectangle[i] = pView->GetInvalidateRect();
2195         }
2196     }
2197 
2198     // notify accessibility before all things happen
2199     if (bNotifyAcc && aViewData.GetViewShell()->HasAccessibilityObjects())
2200         aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccLeaveEditMode));
2201 
2202     aViewData.ResetEditView();
2203     for (sal_uInt16 i = 0; i < 4; i++)
2204     {
2205         if (pGridWin[i] && bPaint[i] && pGridWin[i]->IsVisible())
2206         {
2207             pGridWin[i]->ShowCursor();
2208 
2209             pGridWin[i]->SetMapMode(pGridWin[i]->GetDrawMapMode());
2210 
2211             if (comphelper::LibreOfficeKit::isActive())
2212             {
2213                 const tools::Rectangle& rInvRect = aRectangle[i];
2214                 pGridWin[i]->Invalidate(rInvRect);
2215 
2216                 // invalidate other views
2217                 auto lInvalidateWindows =
2218                         [&rInvRect] (ScTabView* pTabView)
2219                         {
2220                             for (VclPtr<ScGridWindow> const & pWin: pTabView->pGridWin)
2221                             {
2222                                 if (pWin)
2223                                     pWin->Invalidate(rInvRect);
2224                             }
2225                         };
2226 
2227                 SfxLokHelper::forEachOtherView(GetViewData().GetViewShell(), lInvalidateWindows);
2228             }
2229             // #i73567# the cell still has to be repainted
2230             else if (bExtended || ( bAtCursor && !bNoPaint ))
2231             {
2232                 pGridWin[i]->Draw( nCol1, nRow1, nCol2, nRow2, ScUpdateMode::All );
2233                 pGridWin[i]->UpdateSelectionOverlay();
2234             }
2235         }
2236     }
2237 
2238     if (pDrawView)
2239         DrawEnableAnim( true );
2240 
2241         // GrabFocus always when this View is active and
2242         // when the input row has the focus
2243 
2244     bool bGrabFocus = false;
2245     if (aViewData.IsActive())
2246     {
2247         ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl();
2248         if ( pInputHdl )
2249         {
2250             ScInputWindow* pInputWin = pInputHdl->GetInputWindow();
2251             if (pInputWin && pInputWin->IsInputActive())
2252                 bGrabFocus = true;
2253         }
2254     }
2255 
2256     if (bGrabFocus)
2257     {
2258 //      should be done like this, so that Sfx notice it, but it does not work:
2259 //!     aViewData.GetViewShell()->GetViewFrame()->GetWindow().GrabFocus();
2260 //      therefore first like this:
2261         GetActiveWin()->GrabFocus();
2262     }
2263 
2264     // cursor query only after GrabFocus
2265 
2266     for (sal_uInt16 i = 0; i < 4; i++)
2267     {
2268         if (pGridWin[i] && pGridWin[i]->IsVisible())
2269         {
2270             vcl::Cursor* pCur = pGridWin[i]->GetCursor();
2271             if (pCur && pCur->IsVisible())
2272                 pCur->Hide();
2273 
2274             if (bPaint[i])
2275             {
2276                 pGridWin[i]->UpdateCursorOverlay();
2277                 pGridWin[i]->UpdateAutoFillOverlay();
2278             }
2279         }
2280     }
2281 }
2282 
UpdateFormulas(SCCOL nStartCol,SCROW nStartRow,SCCOL nEndCol,SCROW nEndRow)2283 void ScTabView::UpdateFormulas(SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow)
2284 {
2285     if ( aViewData.GetDocument().IsAutoCalcShellDisabled() )
2286         return;
2287 
2288     for (sal_uInt16 i = 0; i < 4; i++)
2289     {
2290         if (pGridWin[i] && pGridWin[i]->IsVisible())
2291             pGridWin[i]->UpdateFormulas(nStartCol, nStartRow, nEndCol, nEndRow);
2292     }
2293 
2294     if ( aViewData.IsPagebreakMode() )
2295         UpdatePageBreakData();              //! asynchronous
2296 
2297     UpdateHeaderWidth();
2298 
2299     //  if in edit mode, adjust edit view area because widths/heights may have changed
2300     if ( aViewData.HasEditView( aViewData.GetActivePart() ) )
2301         UpdateEditView();
2302 }
2303 
2304 //  PaintArea - repaint block
2305 
PaintArea(SCCOL nStartCol,SCROW nStartRow,SCCOL nEndCol,SCROW nEndRow,ScUpdateMode eMode)2306 void ScTabView::PaintArea( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
2307                             ScUpdateMode eMode )
2308 {
2309     SCCOL nCol1;
2310     SCROW nRow1;
2311     SCCOL nCol2;
2312     SCROW nRow2;
2313     bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive();
2314     ScDocument& rDoc = aViewData.GetDocument();
2315 
2316     PutInOrder( nStartCol, nEndCol );
2317     PutInOrder( nStartRow, nEndRow );
2318 
2319     for (size_t i = 0; i < 4; ++i)
2320     {
2321         if (!pGridWin[i] || !pGridWin[i]->IsVisible())
2322             continue;
2323 
2324         ScHSplitPos eHWhich = WhichH( static_cast<ScSplitPos>(i) );
2325         ScVSplitPos eVWhich = WhichV( static_cast<ScSplitPos>(i) );
2326         bool bOut = false;
2327 
2328         nCol1 = nStartCol;
2329         nRow1 = nStartRow;
2330         nCol2 = nEndCol;
2331         nRow2 = nEndRow;
2332 
2333         SCCOL nLastX = 0;
2334         SCROW nLastY = 0;
2335 
2336         if (bIsTiledRendering)
2337         {
2338             nLastX = aViewData.GetMaxTiledCol();
2339             nLastY = aViewData.GetMaxTiledRow();
2340         }
2341         else
2342         {
2343 
2344             SCCOL nScrX = aViewData.GetPosX( eHWhich );
2345             SCROW nScrY = aViewData.GetPosY( eVWhich );
2346 
2347             if (nCol1 < nScrX)
2348                 nCol1 = nScrX;
2349             if (nCol2 < nScrX)
2350             {
2351                 if ( eMode == ScUpdateMode::All )   // for UPDATE_ALL, paint anyway
2352                     nCol2 = nScrX;              // (because of extending strings to the right)
2353                 else
2354                     bOut = true;                // completely outside the window
2355             }
2356             if (nRow1 < nScrY)
2357                 nRow1 = nScrY;
2358             if (nRow2 < nScrY)
2359                 bOut = true;
2360 
2361             nLastX = nScrX + aViewData.VisibleCellsX( eHWhich ) + 1;
2362             nLastY = nScrY + aViewData.VisibleCellsY( eVWhich ) + 1;
2363         }
2364 
2365         if (nCol1 > nLastX)
2366             bOut = true;
2367         if (nCol2 > nLastX)
2368             nCol2 = nLastX;
2369         if (nRow1 > nLastY)
2370             bOut = true;
2371         if (nRow2 > nLastY)
2372             nRow2 = nLastY;
2373 
2374         if (bOut)
2375             continue;
2376 
2377         bool bLayoutRTL = aViewData.GetDocument().IsLayoutRTL( aViewData.GetTabNo() );
2378         tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
2379 
2380         Point aStart = aViewData.GetScrPos( nCol1, nRow1, static_cast<ScSplitPos>(i) );
2381         Point aEnd   = aViewData.GetScrPos( nCol2+1, nRow2+1, static_cast<ScSplitPos>(i) );
2382         if ( eMode == ScUpdateMode::All )
2383         {
2384             if (bIsTiledRendering)
2385             {
2386                 // When a cell content is deleted we have no clue about
2387                 // the width of the embedded text.
2388                 // Anyway, clients will ask only for tiles that overlaps
2389                 // the visible area.
2390                 // Remember that wsd expects int and that aEnd.X() is
2391                 // in pixels and will be converted in twips, before performing
2392                 // the lok callback, so we need to avoid that an overflow occurs.
2393                 aEnd.setX( bLayoutRTL ? 0 : std::numeric_limits<int>::max() / 1000 );
2394             }
2395             else
2396             {
2397                 aEnd.setX( bLayoutRTL ? 0 : pGridWin[i]->GetOutputSizePixel().Width() );
2398             }
2399         }
2400         aEnd.AdjustX( -nLayoutSign );
2401         aEnd.AdjustY( -1 );
2402 
2403         // #i85232# include area below cells (could be done in GetScrPos?)
2404         if ( eMode == ScUpdateMode::All && nRow2 >= rDoc.MaxRow() && !bIsTiledRendering )
2405             aEnd.setY( pGridWin[i]->GetOutputSizePixel().Height() );
2406 
2407         aStart.AdjustX( -nLayoutSign );      // include change marks
2408         aStart.AdjustY( -1 );
2409 
2410         bool bMarkClipped = aViewData.GetOptions().GetOption( VOPT_CLIPMARKS );
2411         if (bMarkClipped)
2412         {
2413             // ScColumn::IsEmptyBlock has to be optimized for this
2414             //  (switch to Search() )
2415             //!if ( nCol1 > 0 && !aViewData.GetDocument()->IsBlockEmpty(
2416             //!                     aViewData.GetTabNo(),
2417             //!                     0, nRow1, nCol1-1, nRow2 ) )
2418             tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * aViewData.GetPPTX() );
2419             aStart.AdjustX( -(nMarkPixel * nLayoutSign) );
2420         }
2421 
2422         pGridWin[i]->Invalidate( pGridWin[i]->PixelToLogic( tools::Rectangle( aStart,aEnd ) ) );
2423     }
2424 
2425     // #i79909# Calling UpdateAllOverlays here isn't necessary and would lead to overlay calls from a timer,
2426     // with a wrong MapMode if editing in a cell (reference input).
2427     // #i80499# Overlays need updates in a lot of cases, e.g. changing row/column size,
2428     // or showing/hiding outlines. TODO: selections in inactive windows are vanishing.
2429     // #i84689# With relative conditional formats, PaintArea may be called often (for each changed cell),
2430     // so UpdateAllOverlays was moved to ScTabViewShell::Notify and is called only if PaintPartFlags::Left/PaintPartFlags::Top
2431     // is set (width or height changed).
2432 }
2433 
PaintRangeFinderEntry(const ScRangeFindData * pData,const SCTAB nTab)2434 void ScTabView::PaintRangeFinderEntry (const ScRangeFindData* pData, const SCTAB nTab)
2435 {
2436     ScRange aRef = pData->aRef;
2437     aRef.PutInOrder();                 // PutInOrder for the queries below
2438 
2439     if ( aRef.aStart == aRef.aEnd )     //! ignore sheet?
2440         aViewData.GetDocument().ExtendMerge(aRef);
2441 
2442     if (aRef.aStart.Tab() < nTab || aRef.aEnd.Tab() > nTab)
2443         return;
2444 
2445     SCCOL nCol1 = aRef.aStart.Col();
2446     SCROW nRow1 = aRef.aStart.Row();
2447     SCCOL nCol2 = aRef.aEnd.Col();
2448     SCROW nRow2 = aRef.aEnd.Row();
2449 
2450     //  remove -> repaint
2451     //  ScUpdateMode::Marks: Invalidate, nothing until end of row
2452 
2453     bool bHiddenEdge = false;
2454     SCROW nTmp;
2455     ScDocument& rDoc = aViewData.GetDocument();
2456     while ( nCol1 > 0 && rDoc.ColHidden(nCol1, nTab) )
2457     {
2458         --nCol1;
2459         bHiddenEdge = true;
2460     }
2461     while ( nCol2 < rDoc.MaxCol() && rDoc.ColHidden(nCol2, nTab) )
2462     {
2463         ++nCol2;
2464         bHiddenEdge = true;
2465     }
2466     nTmp = rDoc.LastVisibleRow(0, nRow1, nTab);
2467     if (!rDoc.ValidRow(nTmp))
2468         nTmp = 0;
2469     if (nTmp < nRow1)
2470     {
2471         nRow1 = nTmp;
2472         bHiddenEdge = true;
2473     }
2474     nTmp = rDoc.FirstVisibleRow(nRow2, rDoc.MaxRow(), nTab);
2475     if (!rDoc.ValidRow(nTmp))
2476         nTmp = rDoc.MaxRow();
2477     if (nTmp > nRow2)
2478     {
2479         nRow2 = nTmp;
2480         bHiddenEdge = true;
2481     }
2482 
2483     if ( nCol2 - nCol1 > 1 && nRow2 - nRow1 > 1 && !bHiddenEdge )
2484     {
2485         // only along the edges
2486         PaintArea( nCol1, nRow1, nCol2, nRow1, ScUpdateMode::Marks );
2487         PaintArea( nCol1, nRow1+1, nCol1, nRow2-1, ScUpdateMode::Marks );
2488         PaintArea( nCol2, nRow1+1, nCol2, nRow2-1, ScUpdateMode::Marks );
2489         PaintArea( nCol1, nRow2, nCol2, nRow2, ScUpdateMode::Marks );
2490     }
2491     else    // all in one
2492         PaintArea( nCol1, nRow1, nCol2, nRow2, ScUpdateMode::Marks );
2493 }
2494 
PaintRangeFinder(tools::Long nNumber)2495 void ScTabView::PaintRangeFinder( tools::Long nNumber )
2496 {
2497     ScInputHandler* pHdl = SC_MOD()->GetInputHdl( aViewData.GetViewShell() );
2498     if (!pHdl)
2499         return;
2500 
2501     ScRangeFindList* pRangeFinder = pHdl->GetRangeFindList();
2502     if ( !(pRangeFinder && pRangeFinder->GetDocName() == aViewData.GetDocShell()->GetTitle()) )
2503         return;
2504 
2505     SCTAB nTab = aViewData.GetTabNo();
2506     sal_uInt16 nCount = static_cast<sal_uInt16>(pRangeFinder->Count());
2507 
2508     if (nNumber < 0)
2509     {
2510         for (sal_uInt16 i=0; i<nCount; i++)
2511             PaintRangeFinderEntry(&pRangeFinder->GetObject(i),nTab);
2512     }
2513     else
2514     {
2515         sal_uInt16 idx = nNumber;
2516         if (idx < nCount)
2517             PaintRangeFinderEntry(&pRangeFinder->GetObject(idx),nTab);
2518     }
2519 }
2520 
2521 // for chart data selection
2522 
AddHighlightRange(const ScRange & rRange,const Color & rColor)2523 void ScTabView::AddHighlightRange( const ScRange& rRange, const Color& rColor )
2524 {
2525     maHighlightRanges.emplace_back( rRange, rColor );
2526 
2527     SCTAB nTab = aViewData.GetTabNo();
2528     if ( nTab >= rRange.aStart.Tab() && nTab <= rRange.aEnd.Tab() )
2529         PaintArea( rRange.aStart.Col(), rRange.aStart.Row(),
2530                     rRange.aEnd.Col(), rRange.aEnd.Row(), ScUpdateMode::Marks );
2531 }
2532 
ClearHighlightRanges()2533 void ScTabView::ClearHighlightRanges()
2534 {
2535     SCTAB nTab = aViewData.GetTabNo();
2536     for (ScHighlightEntry const & rEntry : maHighlightRanges)
2537     {
2538         ScRange aRange = rEntry.aRef;
2539         if ( nTab >= aRange.aStart.Tab() && nTab <= aRange.aEnd.Tab() )
2540             PaintArea( aRange.aStart.Col(), aRange.aStart.Row(),
2541                        aRange.aEnd.Col(), aRange.aEnd.Row(), ScUpdateMode::Marks );
2542     }
2543 
2544     maHighlightRanges.clear();
2545 }
2546 
DoChartSelection(const uno::Sequence<chart2::data::HighlightedRange> & rHilightRanges)2547 void ScTabView::DoChartSelection(
2548     const uno::Sequence< chart2::data::HighlightedRange > & rHilightRanges )
2549 {
2550     ClearHighlightRanges();
2551     const sal_Unicode sep = ::formula::FormulaCompiler::GetNativeSymbolChar(ocSep);
2552     size_t nSize = 0;
2553     size_t nIndex = 0;
2554     std::vector<ReferenceMark> aReferenceMarks( nSize );
2555 
2556     for (chart2::data::HighlightedRange const & rHighlightedRange : rHilightRanges)
2557     {
2558         Color aSelColor(ColorTransparency, rHighlightedRange.PreferredColor);
2559         ScRangeList aRangeList;
2560         ScDocument& rDoc = aViewData.GetDocShell()->GetDocument();
2561         if( ScRangeStringConverter::GetRangeListFromString(
2562                 aRangeList, rHighlightedRange.RangeRepresentation, rDoc, rDoc.GetAddressConvention(), sep ))
2563         {
2564             size_t nListSize = aRangeList.size();
2565             nSize += nListSize;
2566             aReferenceMarks.resize(nSize);
2567 
2568             for ( size_t j = 0; j < nListSize; ++j )
2569             {
2570                 ScRange& p = aRangeList[j];
2571                 ScRange aTargetRange;
2572                 if( rHighlightedRange.Index == - 1 )
2573                 {
2574                     aTargetRange = p;
2575                     AddHighlightRange( aTargetRange, aSelColor );
2576                 }
2577                 else
2578                 {
2579                     aTargetRange = lcl_getSubRangeByIndex( p, rHighlightedRange.Index );
2580                     AddHighlightRange( aTargetRange, aSelColor );
2581                 }
2582 
2583                 if ( comphelper::LibreOfficeKit::isActive() && aViewData.GetViewShell() )
2584                 {
2585                     aTargetRange.PutInOrder();
2586 
2587                     tools::Long nX1 = aTargetRange.aStart.Col();
2588                     tools::Long nX2 = aTargetRange.aEnd.Col();
2589                     tools::Long nY1 = aTargetRange.aStart.Row();
2590                     tools::Long nY2 = aTargetRange.aEnd.Row();
2591                     tools::Long nTab = aTargetRange.aStart.Tab();
2592 
2593                     aReferenceMarks[nIndex++] = ScInputHandler::GetReferenceMark( aViewData, aViewData.GetDocShell(),
2594                                                                             nX1, nX2, nY1, nY2,
2595                                                                             nTab, aSelColor );
2596                 }
2597             }
2598         }
2599     }
2600 
2601     if ( comphelper::LibreOfficeKit::isActive() && aViewData.GetViewShell() )
2602         ScInputHandler::SendReferenceMarks( aViewData.GetViewShell(), aReferenceMarks );
2603 }
2604 
DoDPFieldPopup(std::u16string_view rPivotTableName,sal_Int32 nDimensionIndex,Point aPoint,Size aSize)2605 void ScTabView::DoDPFieldPopup(std::u16string_view rPivotTableName, sal_Int32 nDimensionIndex, Point aPoint, Size aSize)
2606 {
2607     ScDocument& rDocument = aViewData.GetDocShell()->GetDocument();
2608     ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get();
2609 
2610     if (!pWin)
2611         return;
2612 
2613     ScDPCollection* pDPCollection = rDocument.GetDPCollection();
2614     ScDPObject* pDPObject = pDPCollection->GetByName(rPivotTableName);
2615     if (!pDPObject)
2616         return;
2617 
2618     pDPObject->BuildAllDimensionMembers();
2619 
2620     Point aScreenPoint = pWin->OutputToScreenPixel(pWin->LogicToPixel(aPoint));
2621     Size aScreenSize = pWin->LogicToPixel(aSize);
2622 
2623     pWin->DPLaunchFieldPopupMenu(aScreenPoint, aScreenSize, nDimensionIndex, pDPObject);
2624 }
2625 
2626 //  PaintGrid - repaint data range
2627 
PaintGrid()2628 void ScTabView::PaintGrid()
2629 {
2630     for (sal_uInt16 i = 0; i < 4; i++)
2631     {
2632         if (pGridWin[i] && pGridWin[i]->IsVisible())
2633             pGridWin[i]->Invalidate();
2634     }
2635 }
2636 
2637 //  PaintTop - repaint top control elements
2638 
PaintTop()2639 void ScTabView::PaintTop()
2640 {
2641     for (sal_uInt16 i = 0; i < 2; i++)
2642     {
2643         if (pColBar[i])
2644             pColBar[i]->Invalidate();
2645         if (pColOutline[i])
2646             pColOutline[i]->Invalidate();
2647     }
2648 }
2649 
CreateAnchorHandles(SdrHdlList & rHdl,const ScAddress & rAddress)2650 void ScTabView::CreateAnchorHandles(SdrHdlList& rHdl, const ScAddress& rAddress)
2651 {
2652     for (sal_uInt16 i = 0; i < 4; i++)
2653     {
2654         if(pGridWin[i] && pGridWin[i]->IsVisible())
2655             pGridWin[i]->CreateAnchorHandle(rHdl, rAddress);
2656     }
2657 }
2658 
PaintTopArea(SCCOL nStartCol,SCCOL nEndCol)2659 void ScTabView::PaintTopArea( SCCOL nStartCol, SCCOL nEndCol )
2660 {
2661         // pixel position of the left edge
2662 
2663     if ( nStartCol < aViewData.GetPosX(SC_SPLIT_LEFT) ||
2664          nStartCol < aViewData.GetPosX(SC_SPLIT_RIGHT) )
2665         aViewData.RecalcPixPos();
2666 
2667         // adjust freeze (UpdateFixX resets HSplitPos)
2668 
2669     if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX && nStartCol < aViewData.GetFixPosX() )
2670         if (aViewData.UpdateFixX())
2671             RepeatResize();
2672 
2673         // paint
2674 
2675     if (nStartCol>0)
2676         --nStartCol;                //! general ?
2677 
2678     ScDocument& rDoc = aViewData.GetDocument();
2679     bool bLayoutRTL = rDoc.IsLayoutRTL( aViewData.GetTabNo() );
2680     tools::Long nLayoutSign = bLayoutRTL ? -1 : 1;
2681 
2682     for (sal_uInt16 i = 0; i < 2; i++)
2683     {
2684         ScHSplitPos eWhich = ScHSplitPos(i);
2685         if (pColBar[eWhich])
2686         {
2687             Size aWinSize = pColBar[eWhich]->GetSizePixel();
2688             tools::Long nStartX = aViewData.GetScrPos( nStartCol, 0, eWhich ).X();
2689             tools::Long nEndX;
2690             if (nEndCol >= rDoc.MaxCol())
2691                 nEndX = bLayoutRTL ? 0 : ( aWinSize.Width()-1 );
2692             else
2693                 nEndX = aViewData.GetScrPos( nEndCol+1, 0, eWhich ).X() - nLayoutSign;
2694             if (nStartX > nEndX)
2695                 std::swap(nStartX, nEndX);
2696             pColBar[eWhich]->Invalidate(
2697                     tools::Rectangle( nStartX, 0, nEndX, aWinSize.Height()-1 ) );
2698         }
2699         if (pColOutline[eWhich])
2700             pColOutline[eWhich]->Invalidate();
2701     }
2702 }
2703 
2704 //  PaintLeft - repaint left control elements
2705 
PaintLeft()2706 void ScTabView::PaintLeft()
2707 {
2708     for (sal_uInt16 i = 0; i < 2; i++)
2709     {
2710         if (pRowBar[i])
2711             pRowBar[i]->Invalidate();
2712         if (pRowOutline[i])
2713             pRowOutline[i]->Invalidate();
2714     }
2715 }
2716 
PaintLeftArea(SCROW nStartRow,SCROW nEndRow)2717 void ScTabView::PaintLeftArea( SCROW nStartRow, SCROW nEndRow )
2718 {
2719         // pixel position of the upper edge
2720 
2721     if ( nStartRow < aViewData.GetPosY(SC_SPLIT_TOP) ||
2722          nStartRow < aViewData.GetPosY(SC_SPLIT_BOTTOM) )
2723         aViewData.RecalcPixPos();
2724 
2725         // adjust freeze (UpdateFixY reset VSplitPos)
2726 
2727     if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX && nStartRow < aViewData.GetFixPosY() )
2728         if (aViewData.UpdateFixY())
2729             RepeatResize();
2730 
2731         // paint
2732 
2733     if (nStartRow>0)
2734         --nStartRow;
2735 
2736     ScDocument& rDoc = aViewData.GetDocument();
2737     for (sal_uInt16 i = 0; i < 2; i++)
2738     {
2739         ScVSplitPos eWhich = ScVSplitPos(i);
2740         if (pRowBar[eWhich])
2741         {
2742             Size aWinSize = pRowBar[eWhich]->GetSizePixel();
2743             tools::Long nStartY = aViewData.GetScrPos( 0, nStartRow, eWhich ).Y();
2744             tools::Long nEndY;
2745             if (nEndRow >= rDoc.MaxRow())
2746                 nEndY = aWinSize.Height() - 1;
2747             else
2748                 nEndY = aViewData.GetScrPos( 0, nEndRow+1, eWhich ).Y() - 1;
2749             if (nStartY > nEndY)
2750                 std::swap(nStartY, nEndY);
2751             pRowBar[eWhich]->Invalidate(
2752                     tools::Rectangle( 0, nStartY, aWinSize.Width()-1, nEndY ) );
2753         }
2754         if (pRowOutline[eWhich])
2755             pRowOutline[eWhich]->Invalidate();
2756     }
2757 }
2758 
PaintExtras()2759 bool ScTabView::PaintExtras()
2760 {
2761     bool bRet = false;
2762     ScDocument& rDoc = aViewData.GetDocument();
2763     SCTAB nTab = aViewData.GetTabNo();
2764     if (!rDoc.HasTable(nTab))                  // sheet is deleted?
2765     {
2766         SCTAB nCount = rDoc.GetTableCount();
2767         aViewData.SetTabNo(nCount-1);
2768         bRet = true;
2769     }
2770     pTabControl->UpdateStatus();                        // true = active
2771     return bRet;
2772 }
2773 
RecalcPPT()2774 void ScTabView::RecalcPPT()
2775 {
2776     //  called after changes that require the PPT values to be recalculated
2777     //  (currently from detective operations)
2778 
2779     double nOldX = aViewData.GetPPTX();
2780     double nOldY = aViewData.GetPPTY();
2781 
2782     aViewData.RefreshZoom();                            // pre-calculate new PPT values
2783 
2784     bool bChangedX = ( aViewData.GetPPTX() != nOldX );
2785     bool bChangedY = ( aViewData.GetPPTY() != nOldY );
2786     if ( !(bChangedX || bChangedY) )
2787         return;
2788 
2789     //  call view SetZoom (including draw scale, split update etc)
2790     //  and paint only if values changed
2791 
2792     Fraction aZoomX = aViewData.GetZoomX();
2793     Fraction aZoomY = aViewData.GetZoomY();
2794     SetZoom( aZoomX, aZoomY, false );
2795 
2796     PaintGrid();
2797     if (bChangedX)
2798         PaintTop();
2799     if (bChangedY)
2800         PaintLeft();
2801 }
2802 
ActivateView(bool bActivate,bool bFirst)2803 void ScTabView::ActivateView( bool bActivate, bool bFirst )
2804 {
2805     if ( bActivate == aViewData.IsActive() && !bFirst )
2806     {
2807         // no assertion anymore - occurs when previously in Drag&Drop switching over
2808         // to another document
2809         return;
2810     }
2811 
2812     // is only called for MDI-(De)Activate
2813     // aViewData.Activate behind due to cursor show for KillEditView
2814     // don't delete selection - if Activate(false) is set in ViewData,
2815     // then the selection is not displayed
2816 
2817     if (!bActivate)
2818     {
2819         ScModule* pScMod = SC_MOD();
2820         bool bRefMode = pScMod->IsFormulaMode();
2821 
2822             // don't cancel reference input, to allow reference
2823             // to other document
2824 
2825         if (!bRefMode)
2826         {
2827             // pass view to GetInputHdl, this view may not be current anymore
2828             ScInputHandler* pHdl = SC_MOD()->GetInputHdl(aViewData.GetViewShell());
2829             if (pHdl)
2830                 pHdl->EnterHandler();
2831         }
2832     }
2833 
2834     PaintExtras();
2835 
2836     aViewData.Activate(bActivate);
2837 
2838     PaintBlock(false);                  // repaint, selection after active status
2839 
2840     if (!bActivate)
2841         HideAllCursors();               // Cursor
2842     else if (!bFirst)
2843         ShowAllCursors();
2844 
2845     if (bActivate)
2846     {
2847         if ( bFirst )
2848         {
2849             ScSplitPos eWin = aViewData.GetActivePart();
2850             OSL_ENSURE( pGridWin[eWin], "Corrupted document, not all SplitPos in GridWin" );
2851             if ( !pGridWin[eWin] )
2852             {
2853                 eWin = SC_SPLIT_BOTTOMLEFT;
2854                 if ( !pGridWin[eWin] )
2855                 {
2856                     short i;
2857                     for ( i=0; i<4; i++ )
2858                     {
2859                         if ( pGridWin[i] )
2860                         {
2861                             eWin = static_cast<ScSplitPos>(i);
2862                             break;  // for
2863                         }
2864                     }
2865                     OSL_ENSURE( i<4, "and BOOM" );
2866                 }
2867                 aViewData.SetActivePart( eWin );
2868             }
2869         }
2870         // do not call GrabFocus from here!
2871         // if the document is processed, then Sfx calls GrabFocus in the window of the shell.
2872         // if it is a mail body for instance, then it can't get the focus
2873         UpdateInputContext();
2874     }
2875     else
2876         pGridWin[aViewData.GetActivePart()]->ClickExtern();
2877 }
2878 
ActivatePart(ScSplitPos eWhich)2879 void ScTabView::ActivatePart( ScSplitPos eWhich )
2880 {
2881     ScSplitPos eOld = aViewData.GetActivePart();
2882     if ( eOld == eWhich )
2883         return;
2884 
2885     bInActivatePart = true;
2886 
2887     bool bRefMode = SC_MOD()->IsFormulaMode();
2888 
2889     //  the HasEditView call during SetCursor would fail otherwise
2890     if ( aViewData.HasEditView(eOld) && !bRefMode )
2891         UpdateInputLine();
2892 
2893     ScHSplitPos eOldH = WhichH(eOld);
2894     ScVSplitPos eOldV = WhichV(eOld);
2895     ScHSplitPos eNewH = WhichH(eWhich);
2896     ScVSplitPos eNewV = WhichV(eWhich);
2897     bool bTopCap  = pColBar[eOldH] && pColBar[eOldH]->IsMouseCaptured();
2898     bool bLeftCap = pRowBar[eOldV] && pRowBar[eOldV]->IsMouseCaptured();
2899 
2900     bool bFocus = pGridWin[eOld]->HasFocus();
2901     bool bCapture = pGridWin[eOld]->IsMouseCaptured();
2902     if (bCapture)
2903         pGridWin[eOld]->ReleaseMouse();
2904     pGridWin[eOld]->ClickExtern();
2905     pGridWin[eOld]->HideCursor();
2906     pGridWin[eWhich]->HideCursor();
2907     aViewData.SetActivePart( eWhich );
2908 
2909     ScTabViewShell* pShell = aViewData.GetViewShell();
2910     pShell->WindowChanged();
2911 
2912     pSelEngine->SetWindow(pGridWin[eWhich]);
2913     pSelEngine->SetWhich(eWhich);
2914     pSelEngine->SetVisibleArea( tools::Rectangle(Point(), pGridWin[eWhich]->GetOutputSizePixel()) );
2915 
2916     pGridWin[eOld]->MoveMouseStatus(*pGridWin[eWhich]);
2917 
2918     if ( bCapture || pGridWin[eWhich]->IsMouseCaptured() )
2919     {
2920         // tracking instead of CaptureMouse, so it can be cancelled cleanly
2921         // (SelectionEngine calls CaptureMouse for SetWindow)
2922         //! someday SelectionEngine itself should call StartTracking!?!
2923         pGridWin[eWhich]->ReleaseMouse();
2924         pGridWin[eWhich]->StartTracking();
2925     }
2926 
2927     if ( bTopCap && pColBar[eNewH] )
2928     {
2929         pColBar[eOldH]->SetIgnoreMove(true);
2930         pColBar[eNewH]->SetIgnoreMove(false);
2931         pHdrSelEng->SetWindow( pColBar[eNewH] );
2932         tools::Long nWidth = pColBar[eNewH]->GetOutputSizePixel().Width();
2933         pHdrSelEng->SetVisibleArea( tools::Rectangle( 0, LONG_MIN, nWidth-1, LONG_MAX ) );
2934         pColBar[eNewH]->CaptureMouse();
2935     }
2936     if ( bLeftCap && pRowBar[eNewV] )
2937     {
2938         pRowBar[eOldV]->SetIgnoreMove(true);
2939         pRowBar[eNewV]->SetIgnoreMove(false);
2940         pHdrSelEng->SetWindow( pRowBar[eNewV] );
2941         tools::Long nHeight = pRowBar[eNewV]->GetOutputSizePixel().Height();
2942         pHdrSelEng->SetVisibleArea( tools::Rectangle( LONG_MIN, 0, LONG_MAX, nHeight-1 ) );
2943         pRowBar[eNewV]->CaptureMouse();
2944     }
2945     aHdrFunc.SetWhich(eWhich);
2946 
2947     pGridWin[eOld]->ShowCursor();
2948     pGridWin[eWhich]->ShowCursor();
2949 
2950     SfxInPlaceClient* pClient = aViewData.GetViewShell()->GetIPClient();
2951     bool bOleActive = ( pClient && pClient->IsObjectInPlaceActive() );
2952 
2953     // don't switch ViewShell's active window during RefInput, because the focus
2954     // might change, and subsequent SetReference calls wouldn't find the right EditView
2955     if ( !bRefMode && !bOleActive )
2956         aViewData.GetViewShell()->SetWindow( pGridWin[eWhich] );
2957 
2958     if ( bFocus && !aViewData.IsAnyFillMode() && !bRefMode )
2959     {
2960         // GrabFocus only if previously the other GridWindow had the focus
2961         // (for instance due to search and replace)
2962         pGridWin[eWhich]->GrabFocus();
2963     }
2964 
2965     bInActivatePart = false;
2966 }
2967 
HideListBox()2968 void ScTabView::HideListBox()
2969 {
2970     for (VclPtr<ScGridWindow> & pWin : pGridWin)
2971     {
2972         if (pWin)
2973             pWin->ClickExtern();
2974     }
2975 }
2976 
UpdateInputContext()2977 void ScTabView::UpdateInputContext()
2978 {
2979     ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get();
2980     if (pWin)
2981         pWin->UpdateInputContext();
2982 
2983     if (pTabControl)
2984         pTabControl->UpdateInputContext();
2985 }
2986 
2987 // GetGridWidth - width of an output range (for ViewData)
2988 
GetGridWidth(ScHSplitPos eWhich)2989 tools::Long ScTabView::GetGridWidth( ScHSplitPos eWhich )
2990 {
2991     // at present only the size of the current pane is synchronized with
2992     // the size of the visible area in Online;
2993     // as a workaround we use the same width for all panes independently
2994     // from the eWhich value
2995     if (comphelper::LibreOfficeKit::isActive())
2996     {
2997         ScGridWindow* pGridWindow = aViewData.GetActiveWin();
2998         if (pGridWindow)
2999             return pGridWindow->GetSizePixel().Width();
3000     }
3001 
3002     ScSplitPos eGridWhich = ( eWhich == SC_SPLIT_LEFT ) ? SC_SPLIT_BOTTOMLEFT : SC_SPLIT_BOTTOMRIGHT;
3003     if (pGridWin[eGridWhich])
3004         return pGridWin[eGridWhich]->GetSizePixel().Width();
3005     else
3006         return 0;
3007 }
3008 
3009 // GetGridHeight - height of an output range (for ViewData)
3010 
GetGridHeight(ScVSplitPos eWhich)3011 tools::Long ScTabView::GetGridHeight( ScVSplitPos eWhich )
3012 {
3013     // at present only the size of the current pane is synchronized with
3014     // the size of the visible area in Online;
3015     // as a workaround we use the same height for all panes independently
3016     // from the eWhich value
3017     if (comphelper::LibreOfficeKit::isActive())
3018     {
3019         ScGridWindow* pGridWindow = aViewData.GetActiveWin();
3020         if (pGridWindow)
3021             return pGridWindow->GetSizePixel().Height();
3022     }
3023 
3024     ScSplitPos eGridWhich = ( eWhich == SC_SPLIT_TOP ) ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT;
3025     if (pGridWin[eGridWhich])
3026         return pGridWin[eGridWhich]->GetSizePixel().Height();
3027     else
3028         return 0;
3029 }
3030 
UpdateInputLine()3031 void ScTabView::UpdateInputLine()
3032 {
3033     SC_MOD()->InputEnterHandler();
3034 }
3035 
ZoomChanged()3036 void ScTabView::ZoomChanged()
3037 {
3038     ScInputHandler* pHdl = SC_MOD()->GetInputHdl(aViewData.GetViewShell());
3039     if (pHdl)
3040         pHdl->SetRefScale( aViewData.GetZoomX(), aViewData.GetZoomY() );
3041 
3042     UpdateFixPos();
3043 
3044     UpdateScrollBars();
3045 
3046     // VisArea...
3047     // AW: Discussed with NN if there is a reason that new map mode was only set for one window,
3048     // but is not. Setting only on one window causes the first repaint to have the old mapMode
3049     // in three of four views, so the overlay will save the wrong content e.g. when zooming out.
3050     // Changing to setting map mode at all windows.
3051 
3052     for (sal_uInt32 i = 0; i < 4; i++)
3053     {
3054         if (pGridWin[i])
3055             pGridWin[i]->SetMapMode(pGridWin[i]->GetDrawMapMode());
3056     }
3057 
3058     SetNewVisArea();
3059 
3060     InterpretVisible();     // have everything calculated before painting
3061 
3062     SfxBindings& rBindings = aViewData.GetBindings();
3063     rBindings.Invalidate( SID_ATTR_ZOOM );
3064     rBindings.Invalidate( SID_ATTR_ZOOMSLIDER );
3065     rBindings.Invalidate(SID_ZOOM_IN);
3066     rBindings.Invalidate(SID_ZOOM_OUT);
3067 
3068     HideNoteMarker();
3069 
3070     // To not change too much, use pWin here
3071     ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get();
3072 
3073     if ( pWin && aViewData.HasEditView( aViewData.GetActivePart() ) )
3074     {
3075         // flush OverlayManager before changing the MapMode
3076         pWin->flushOverlayManager();
3077 
3078         // make sure the EditView's position and size are updated
3079         // with the right (logic, not drawing) MapMode
3080         pWin->SetMapMode( aViewData.GetLogicMode() );
3081         UpdateEditView();
3082     }
3083 }
3084 
CheckNeedsRepaint()3085 void ScTabView::CheckNeedsRepaint()
3086 {
3087     for (sal_uInt16 i = 0; i < 4; i++)
3088     {
3089         if (pGridWin[i] && pGridWin[i]->IsVisible())
3090             pGridWin[i]->CheckNeedsRepaint();
3091     }
3092 }
3093 
NeedsRepaint()3094 bool ScTabView::NeedsRepaint()
3095 {
3096     for (VclPtr<ScGridWindow> & pWin : pGridWin)
3097     {
3098         if (pWin && pWin->IsVisible() && pWin->NeedsRepaint())
3099             return true;
3100     }
3101     return false;
3102 }
3103 
3104 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3105