1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <osl/diagnose.h>
21 #include <sfx2/viewfrm.hxx>
22 #include <sfx2/dispatch.hxx>
23 #include <sfx2/docfile.hxx>
24 #include <tools/urlobj.hxx>
25 #include <vcl/commandevent.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/weldutils.hxx>
28 #include <tabcont.hxx>
29 #include <tabvwsh.hxx>
33 #include <globstr.hrc>
34 #include <transobj.hxx>
35 #include <clipparam.hxx>
36 #include <dragdata.hxx>
37 #include <markdata.hxx>
38 #include <gridwin.hxx>
39 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
40 #include <comphelper/lok.hxx>
42 ScTabControl::ScTabControl( vcl::Window
* pParent
, ScViewData
* pData
)
43 : TabBar(pParent
, WB_3DLOOK
| WB_MINSCROLL
| WB_SCROLL
| WB_RANGESELECT
| WB_MULTISELECT
| WB_DRAG
, true)
44 , DropTargetHelper(this)
45 , DragSourceHelper(this)
47 , nMouseClickPageId(TabBar::PAGE_NOT_FOUND
)
48 , nSelPageIdByMouse(TabBar::PAGE_NOT_FOUND
)
51 ScDocument
& rDoc
= pViewData
->GetDocument();
55 SCTAB nCount
= rDoc
.GetTableCount();
56 for (SCTAB i
=0; i
<nCount
; i
++)
58 if (!rDoc
.IsVisible(i
))
61 if (!rDoc
.GetName(i
,aString
))
64 if ( rDoc
.IsScenario(i
) )
65 InsertPage( static_cast<sal_uInt16
>(i
)+1, aString
, TabBarPageBits::Blue
);
67 InsertPage( static_cast<sal_uInt16
>(i
)+1, aString
);
69 if ( rDoc
.IsTabProtected(i
) )
70 SetProtectionSymbol(static_cast<sal_uInt16
>(i
)+1, true);
72 if ( !rDoc
.IsDefaultTabBgColor(i
) )
74 aTabBgColor
= rDoc
.GetTabBgColor(i
);
75 SetTabBgColor( static_cast<sal_uInt16
>(i
)+1, aTabBgColor
);
79 SetCurPageId( static_cast<sal_uInt16
>(pViewData
->GetTabNo()) + 1 );
81 SetSizePixel( Size(SC_TABBAR_DEFWIDTH
, 0) );
83 SetSplitHdl( LINK( pViewData
->GetView(), ScTabView
, TabBarResize
) );
88 SetScrollAlwaysEnabled(false);
90 SetScrollAreaContextHdl( LINK( this, ScTabControl
, ShowPageList
) );
93 IMPL_LINK(ScTabControl
, ShowPageList
, const CommandEvent
&, rEvent
, void)
95 tools::Rectangle
aRect(rEvent
.GetMousePosPixel(), Size(1, 1));
96 weld::Window
* pPopupParent
= weld::GetPopupParent(*this, aRect
);
97 std::unique_ptr
<weld::Builder
> xBuilder(Application::CreateBuilder(pPopupParent
, u
"modules/scalc/ui/pagelistmenu.ui"_ustr
));
98 std::unique_ptr
<weld::Menu
> xPopup(xBuilder
->weld_menu(u
"menu"_ustr
));
100 sal_uInt16 nCurPageId
= GetCurPageId();
102 ScDocument
& rDoc
= pViewData
->GetDocument();
103 SCTAB nCount
= rDoc
.GetTableCount();
104 for (SCTAB i
=0; i
<nCount
; ++i
)
106 if (!rDoc
.IsVisible(i
))
109 if (!rDoc
.GetName(i
, aString
))
111 sal_uInt16 nId
= static_cast<sal_uInt16
>(i
)+1;
112 OUString sId
= OUString::number(nId
);
113 xPopup
->append_radio(sId
, aString
);
114 if (nId
== nCurPageId
)
115 xPopup
->set_active(sId
, true);
118 OUString
sIdent(xPopup
->popup_at_rect(pPopupParent
, aRect
));
119 if (!sIdent
.isEmpty())
120 SwitchToPageId(sIdent
.toUInt32());
123 ScTabControl::~ScTabControl()
128 void ScTabControl::dispose()
130 DragSourceHelper::dispose();
131 DropTargetHelper::dispose();
135 sal_uInt16
ScTabControl::GetMaxId() const
137 sal_uInt16 nVisCnt
= GetPageCount();
139 return GetPageId(nVisCnt
-1);
144 SCTAB
ScTabControl::GetPrivatDropPos(const Point
& rPos
)
146 sal_uInt16 nPos
= ShowDropPos(rPos
);
148 SCTAB nRealPos
= static_cast<SCTAB
>(nPos
);
152 ScDocument
& rDoc
= pViewData
->GetDocument();
154 SCTAB nCount
= rDoc
.GetTableCount();
156 sal_uInt16 nViewPos
=0;
158 for (SCTAB i
=0; i
<nCount
; i
++)
160 if (rDoc
.IsVisible(i
))
166 for (j
=i
+1; j
<nCount
; j
++)
168 if (rDoc
.IsVisible(j
))
182 void ScTabControl::MouseButtonDown( const MouseEvent
& rMEvt
)
184 ScModule
* pScMod
= ScModule::get();
185 if ( !pScMod
->IsModalMode() && !pScMod
->IsFormulaMode() && !IsInEditMode() )
188 pViewData
->GetViewShell()->SetActive(); // Appear and SetViewFrame
189 pViewData
->GetView()->ActiveGrabFocus();
192 if (rMEvt
.IsLeft() && rMEvt
.GetModifier() == 0)
193 nMouseClickPageId
= GetPageId(rMEvt
.GetPosPixel());
195 TabBar::MouseButtonDown( rMEvt
);
198 void ScTabControl::MouseButtonUp( const MouseEvent
& rMEvt
)
200 Point aPos
= PixelToLogic( rMEvt
.GetPosPixel() );
202 // mouse button down and up on same page?
203 if( nMouseClickPageId
!= GetPageId(aPos
))
204 nMouseClickPageId
= TabBar::PAGE_NOT_FOUND
;
206 if ( rMEvt
.GetClicks() == 2 && rMEvt
.IsLeft() && nMouseClickPageId
!= 0 && nMouseClickPageId
!= TabBar::PAGE_NOT_FOUND
)
208 SfxDispatcher
* pDispatcher
= pViewData
->GetViewShell()->GetViewFrame().GetDispatcher();
209 pDispatcher
->Execute( FID_TAB_MENU_RENAME
, SfxCallMode::SYNCHRON
| SfxCallMode::RECORD
);
213 if( nMouseClickPageId
== 0 )
215 // Click in the area next to the existing tabs:
216 SfxDispatcher
* pDispatcher
= pViewData
->GetViewShell()->GetViewFrame().GetDispatcher();
217 pDispatcher
->Execute( FID_TAB_DESELECTALL
, SfxCallMode::SYNCHRON
| SfxCallMode::RECORD
);
218 // forget page ID, to be really sure that the dialog is not called twice
219 nMouseClickPageId
= TabBar::PAGE_NOT_FOUND
;
222 TabBar::MouseButtonUp( rMEvt
);
225 void ScTabControl::AddTabClick()
227 TabBar::AddTabClick();
229 // Insert a new sheet at the right end, with default name.
230 ScDocument
& rDoc
= pViewData
->GetDocument();
231 ScModule
* pScMod
= ScModule::get();
232 if (!rDoc
.IsDocEditable() || pScMod
->IsTableLocked())
235 // auto-accept any in-process input - which would otherwise end up on the new sheet
236 if (!pScMod
->IsFormulaMode())
237 pScMod
->InputEnterHandler();
240 rDoc
.CreateValidTabName(aName
);
241 SCTAB nTabCount
= rDoc
.GetTableCount();
242 pViewData
->GetViewShell()->InsertTable(aName
, nTabCount
);
243 if (!pScMod
->IsModalMode() && !pScMod
->IsFormulaMode() && !IsInEditMode())
244 pViewData
->GetViewShell()->SetActive();
247 void ScTabControl::Select()
249 /* Remember last clicked page ID. */
250 nSelPageIdByMouse
= nMouseClickPageId
;
251 /* Reset nMouseClickPageId, so that next Select() call may invalidate
252 nSelPageIdByMouse (i.e. if called from keyboard). */
253 nMouseClickPageId
= TabBar::PAGE_NOT_FOUND
;
255 ScModule
* pScMod
= ScModule::get();
256 ScDocument
& rDoc
= pViewData
->GetDocument();
257 ScMarkData
& rMark
= pViewData
->GetMarkData();
258 SCTAB nCount
= rDoc
.GetTableCount();
261 if ( pScMod
->IsTableLocked() ) // may not be switched now ?
263 // restore the old state of TabControls
265 for (i
=0; i
<nCount
; i
++)
266 SelectPage( static_cast<sal_uInt16
>(i
)+1, rMark
.GetTableSelect(i
) );
267 SetCurPageId( static_cast<sal_uInt16
>(pViewData
->GetTabNo()) + 1 );
272 sal_uInt16 nCurId
= GetCurPageId();
273 if (!nCurId
) return; // for Excel import it can happen that everything is hidden
274 sal_uInt16 nPage
= nCurId
- 1;
276 // OLE-inplace deactivate
277 if ( nPage
!= static_cast<sal_uInt16
>(pViewData
->GetTabNo()) )
278 pViewData
->GetView()->DrawMarkListHasChanged();
280 // InputEnterHandler onlw when not reference input
282 bool bRefMode
= pScMod
->IsFormulaMode();
284 pScMod
->InputEnterHandler();
286 for (i
=0; i
<nCount
; i
++)
287 rMark
.SelectTable( i
, IsPageSelected(static_cast<sal_uInt16
>(i
)+1) );
289 SfxDispatcher
& rDisp
= pViewData
->GetDispatcher();
290 if (rDisp
.IsLocked())
291 pViewData
->GetView()->SetTabNo( static_cast<SCTAB
>(nPage
) );
294 // sheet for basic is 1-based
295 SfxUInt16Item
aItem( SID_CURRENTTAB
, nPage
+ 1 );
296 rDisp
.ExecuteList(SID_CURRENTTAB
,
297 SfxCallMode::SLOT
| SfxCallMode::RECORD
, { &aItem
});
300 SfxBindings
& rBind
= pViewData
->GetBindings();
301 rBind
.Invalidate( FID_FILL_TAB
);
302 rBind
.Invalidate( FID_TAB_DESELECTALL
);
304 rBind
.Invalidate( FID_INS_TABLE
);
305 rBind
.Invalidate( FID_TAB_APPEND
);
306 rBind
.Invalidate( FID_TAB_MOVE
);
307 rBind
.Invalidate( FID_TAB_DUPLICATE
);
308 rBind
.Invalidate( FID_TAB_RENAME
);
309 rBind
.Invalidate( FID_DELETE_TABLE
);
310 rBind
.Invalidate( FID_TABLE_SHOW
);
311 rBind
.Invalidate( FID_TABLE_HIDE
);
312 rBind
.Invalidate( FID_TAB_SET_TAB_BG_COLOR
);
314 // Recalculate status bar functions.
315 rBind
.Invalidate( SID_TABLE_CELL
);
317 // SetReference onlw when the consolidate dialog is open
318 // (for references over multiple sheets)
319 // for others this is only needed fidgeting
321 if ( bRefMode
&& pViewData
->GetRefType() == SC_REFTYPE_REF
)
322 if ( pViewData
->GetViewShell()->GetViewFrame().HasChildWindow(SID_OPENDLG_CONSOLIDATE
) )
325 pViewData
->GetRefStartX(), pViewData
->GetRefStartY(), pViewData
->GetRefStartZ(),
326 pViewData
->GetRefEndX(), pViewData
->GetRefEndY(), pViewData
->GetRefEndZ() );
327 pScMod
->SetReference( aRange
, rDoc
, &rMark
);
328 pScMod
->EndReference(); // due to Auto-Hide
332 void ScTabControl::UpdateInputContext()
334 ScDocument
& rDoc
= pViewData
->GetDocument();
335 WinBits nStyle
= GetStyle();
336 if (rDoc
.GetDocumentShell()->IsReadOnly())
337 // no insert sheet tab for readonly doc.
338 SetStyle(nStyle
& ~WB_INSERTTAB
);
340 SetStyle(nStyle
| WB_INSERTTAB
);
343 void ScTabControl::UpdateStatus()
345 ScDocument
& rDoc
= pViewData
->GetDocument();
346 ScMarkData
& rMark
= pViewData
->GetMarkData();
347 bool bActive
= pViewData
->IsActive();
349 SCTAB nCount
= rDoc
.GetTableCount();
352 SCTAB nMaxCnt
= std::max( nCount
, static_cast<SCTAB
>(GetMaxId()) );
355 bool bModified
= false; // sheet name
356 for (i
=0; i
<nMaxCnt
&& !bModified
; i
++)
358 if (rDoc
.IsVisible(i
))
360 rDoc
.GetName(i
,aString
);
361 aTabBgColor
= rDoc
.GetTabBgColor(i
);
368 if ( aString
!= GetPageText(static_cast<sal_uInt16
>(i
)+1) || (GetTabBgColor(static_cast<sal_uInt16
>(i
)+1) != aTabBgColor
) )
375 for (i
=0; i
<nCount
; i
++)
377 if (rDoc
.IsVisible(i
))
379 if (rDoc
.GetName(i
,aString
))
381 if ( rDoc
.IsScenario(i
) )
382 InsertPage(static_cast<sal_uInt16
>(i
)+1, aString
, TabBarPageBits::Blue
);
384 InsertPage( static_cast<sal_uInt16
>(i
)+1, aString
);
386 if ( rDoc
.IsTabProtected(i
) )
387 SetProtectionSymbol(static_cast<sal_uInt16
>(i
)+1, true);
389 if ( !rDoc
.IsDefaultTabBgColor(i
) )
391 aTabBgColor
= rDoc
.GetTabBgColor(i
);
392 SetTabBgColor(static_cast<sal_uInt16
>(i
)+1, aTabBgColor
);
398 SetCurPageId( static_cast<sal_uInt16
>(pViewData
->GetTabNo()) + 1 );
402 bModified
= false; // selection
403 for (i
=0; i
<nMaxCnt
&& !bModified
; i
++)
404 if ( rMark
.GetTableSelect(i
) != IsPageSelected(static_cast<sal_uInt16
>(i
)+1) )
408 for (i
=0; i
<nCount
; i
++)
409 SelectPage( static_cast<sal_uInt16
>(i
)+1, rMark
.GetTableSelect(i
) );
413 void ScTabControl::SetSheetLayoutRTL( bool bSheetRTL
)
415 SetEffectiveRTL( bSheetRTL
);
416 nSelPageIdByMouse
= TabBar::PAGE_NOT_FOUND
;
419 void ScTabControl::SwitchToPageId(sal_uInt16 nId
)
424 bool bAlreadySelected
= IsPageSelected( nId
);
425 //make the clicked page the current one
427 //change the selection when the current one is not already
428 //selected or part of a multi selection
432 sal_uInt16 nCount
= GetMaxId();
434 for (sal_uInt16 i
=1; i
<=nCount
; i
++)
435 SelectPage( i
, i
==nId
);
438 if (comphelper::LibreOfficeKit::isActive())
440 // notify LibreOfficeKit about changed page
441 OString aPayload
= OString::number(nId
- 1);
442 pViewData
->GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART
, aPayload
);
446 void ScTabControl::Command( const CommandEvent
& rCEvt
)
448 ScModule
* pScMod
= ScModule::get();
449 ScTabViewShell
* pViewSh
= pViewData
->GetViewShell();
450 bool bDisable
= pScMod
->IsFormulaMode() || pScMod
->IsModalMode();
452 // first activate ViewFrame (Bug 19493):
453 pViewSh
->SetActive();
455 if (rCEvt
.GetCommand() != CommandEventId::ContextMenu
|| bDisable
)
458 // #i18735# select the page that is under the mouse cursor
459 // if multiple tables are selected and the one under the cursor
460 // is not part of them then unselect them
461 sal_uInt16 nId
= GetPageId( rCEvt
.GetMousePosPixel() );
464 // #i52073# OLE inplace editing has to be stopped before showing the sheet tab context menu
465 pViewSh
->DeactivateOle();
468 // get Dispatcher from ViewData (ViewFrame) instead of Shell (Frame), so it can't be null
469 pViewData
->GetDispatcher().ExecutePopup( u
"sheettab"_ustr
);
472 void ScTabControl::StartDrag( sal_Int8
/* nAction */, const Point
& rPosPixel
)
474 ScModule
* pScMod
= ScModule::get();
475 bool bDisable
= pScMod
->IsFormulaMode() || pScMod
->IsModalMode();
479 vcl::Region
aRegion( tools::Rectangle(0,0,0,0) );
480 CommandEvent
aCEvt( rPosPixel
, CommandEventId::StartDrag
, true ); // needed for StartDrag
481 if (TabBar::StartDrag( aCEvt
, aRegion
))
486 void ScTabControl::DoDrag()
488 ScDocShell
* pDocSh
= pViewData
->GetDocShell();
489 ScDocument
& rDoc
= pDocSh
->GetDocument();
491 SCTAB nTab
= pViewData
->GetTabNo();
492 ScRange
aTabRange( 0, 0, nTab
, rDoc
.MaxCol(), rDoc
.MaxRow(), nTab
);
493 ScMarkData aTabMark
= pViewData
->GetMarkData();
494 aTabMark
.ResetMark(); // doesn't change marked table information
495 aTabMark
.SetMarkArea( aTabRange
);
497 ScDocumentUniquePtr
pClipDoc(new ScDocument( SCDOCMODE_CLIP
));
498 ScClipParam
aClipParam(aTabRange
, false);
499 rDoc
.CopyToClip(aClipParam
, pClipDoc
.get(), &aTabMark
, false, false);
501 TransferableObjectDescriptor aObjDesc
;
502 pDocSh
->FillTransferableObjectDescriptor( aObjDesc
);
503 aObjDesc
.maDisplayName
= pDocSh
->GetMedium()->GetURLObject().GetURLNoPass();
504 // maSize is set in ScTransferObj ctor
506 rtl::Reference
<ScTransferObj
> pTransferObj
= new ScTransferObj( std::move(pClipDoc
), std::move(aObjDesc
) );
508 pTransferObj
->SetDragSourceFlags(ScDragSrc::Table
);
510 pTransferObj
->SetDragSource( pDocSh
, aTabMark
);
512 pTransferObj
->SetSourceCursorPos( pViewData
->GetCurX(), pViewData
->GetCurY() );
514 vcl::Window
* pWindow
= pViewData
->GetActiveWin();
515 ScModule::get()->SetDragObject(pTransferObj
.get(), nullptr); // for internal D&D
516 pTransferObj
->StartDrag( pWindow
, DND_ACTION_COPYMOVE
| DND_ACTION_LINK
);
519 static sal_uInt16
lcl_DocShellNr( const ScDocument
& rDoc
)
521 sal_uInt16 nShellCnt
= 0;
522 SfxObjectShell
* pShell
= SfxObjectShell::GetFirst();
525 if ( auto pDocShell
= dynamic_cast<const ScDocShell
*>(pShell
) )
527 if ( &pDocShell
->GetDocument() == &rDoc
)
532 pShell
= SfxObjectShell::GetNext( *pShell
);
535 OSL_FAIL("Document not found");
539 sal_Int8
ScTabControl::ExecuteDrop( const ExecuteDropEvent
& rEvt
)
543 ScDocument
& rDoc
= pViewData
->GetDocument();
544 const ScDragData
& rData
= ScModule::get()->GetDragData();
545 if ( rData
.pCellTransfer
&& (rData
.pCellTransfer
->GetDragSourceFlags() & ScDragSrc::Table
) &&
546 rData
.pCellTransfer
->GetSourceDocument() == &rDoc
)
548 // moving of tables within the document
549 SCTAB nPos
= GetPrivatDropPos( rEvt
.maPosPixel
);
552 if ( nPos
== rData
.pCellTransfer
->GetVisibleTab() && rEvt
.mnAction
== DND_ACTION_MOVE
)
554 // #i83005# do nothing - don't move to the same position
555 // (too easily triggered unintentionally, and might take a long time in large documents)
559 if ( !rDoc
.GetChangeTrack() && rDoc
.IsDocEditable() )
561 //! use table selection from the tab control where dragging was started?
562 pViewData
->GetView()->MoveTable( lcl_DocShellNr(rDoc
), nPos
, rEvt
.mnAction
!= DND_ACTION_MOVE
);
564 rData
.pCellTransfer
->SetDragWasInternal(); // don't delete
565 return DND_ACTION_COPY
;
570 return DND_ACTION_NONE
;
573 sal_Int8
ScTabControl::AcceptDrop( const AcceptDropEvent
& rEvt
)
575 if ( rEvt
.mbLeaving
)
579 return rEvt
.mnAction
;
582 const ScDocument
& rDoc
= pViewData
->GetDocument();
583 const ScDragData
& rData
= ScModule::get()->GetDragData();
584 if ( rData
.pCellTransfer
&& (rData
.pCellTransfer
->GetDragSourceFlags() & ScDragSrc::Table
) &&
585 rData
.pCellTransfer
->GetSourceDocument() == &rDoc
)
587 // moving of tables within the document
588 if ( !rDoc
.GetChangeTrack() && rDoc
.IsDocEditable() )
590 ShowDropPos( rEvt
.maPosPixel
);
591 return rEvt
.mnAction
;
594 else // switch sheets for all formats
596 SwitchPage( rEvt
.maPosPixel
); // switch sheet after timeout
597 return 0; // nothing can be dropped here
603 bool ScTabControl::StartRenaming()
605 return pViewData
->GetDocument().IsDocEditable();
608 TabBarAllowRenamingReturnCode
ScTabControl::AllowRenaming()
610 ScTabViewShell
* pViewSh
= pViewData
->GetViewShell();
611 OSL_ENSURE( pViewSh
, "pViewData->GetViewShell()" );
613 TabBarAllowRenamingReturnCode nRet
= TABBAR_RENAMING_CANCEL
;
614 sal_uInt16 nId
= GetEditPageId();
617 SCTAB nTab
= nId
- 1;
618 OUString aNewName
= GetEditText();
619 bool bDone
= pViewSh
->RenameTable( aNewName
, nTab
);
621 nRet
= TABBAR_RENAMING_YES
;
622 else if ( bErrorShown
)
624 // if the error message from this TabControl is currently visible,
625 // don't end edit mode now, to avoid problems when returning to
626 // the other call (showing the error) - this should not happen
627 OSL_FAIL("ScTabControl::AllowRenaming: nested calls");
628 nRet
= TABBAR_RENAMING_NO
;
630 else if (pViewData
->GetDocShell()->IsInModalMode())
632 // don't show error message above any modal dialog
633 // instead cancel renaming without error message
634 // e.g. start with default Sheet1, add another sheet
635 // alt+left click on Sheet2 tab, edit to say Sheet1
636 // ctrl+S to trigger modal file save dialog
637 nRet
= TABBAR_RENAMING_CANCEL
;
642 pViewSh
->ErrorMessage( STR_INVALIDTABNAME
);
644 nRet
= TABBAR_RENAMING_NO
;
650 void ScTabControl::EndRenaming()
653 pViewData
->GetView()->ActiveGrabFocus();
656 void ScTabControl::Mirror()
659 if( nSelPageIdByMouse
!= TabBar::PAGE_NOT_FOUND
)
661 tools::Rectangle
aRect( GetPageRect( GetCurPageId() ) );
662 if( !aRect
.IsEmpty() )
663 SetPointerPosPixel( aRect
.Center() );
664 nSelPageIdByMouse
= TabBar::PAGE_NOT_FOUND
; // only once after a Select()
668 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */