/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ShellClass_ScCellShell #define ShellClass_CellMovement #include SFX_IMPL_INTERFACE(ScCellShell, ScFormatShell) void ScCellShell::InitInterface_Impl() { GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Standard | SfxVisibilityFlags::Server, ToolbarId::Objectbar_Format); GetStaticInterface()->RegisterPopupMenu("cell"); } ScCellShell::ScCellShell(ScViewData& rData, const VclPtr& frameWin) : ScFormatShell(rData), pImpl( new CellShell_Impl() ), bPastePossible(false), pFrameWin(frameWin) { SetName("Cell"); SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Cell)); } ScCellShell::~ScCellShell() { if ( pImpl->m_xClipEvtLstnr.is() ) { pImpl->m_xClipEvtLstnr->RemoveListener( GetViewData().GetActiveWin() ); // The listener may just now be waiting for the SolarMutex and call the link // afterwards, in spite of RemoveListener. So the link has to be reset, too. pImpl->m_xClipEvtLstnr->ClearCallbackLink(); pImpl->m_xClipEvtLstnr.clear(); } pImpl->m_pLinkedDlg.disposeAndClear(); delete pImpl->m_pRequest; } void ScCellShell::GetBlockState( SfxItemSet& rSet ) { ScTabViewShell* pTabViewShell = GetViewData().GetViewShell(); ScRange aMarkRange; ScMarkType eMarkType = GetViewData().GetSimpleArea( aMarkRange ); bool bSimpleArea = (eMarkType == SC_MARK_SIMPLE); bool bOnlyNotBecauseOfMatrix; bool bEditable = pTabViewShell->SelectionEditable( &bOnlyNotBecauseOfMatrix ); ScDocument& rDoc = GetViewData().GetDocument(); ScDocShell* pDocShell = GetViewData().GetDocShell(); ScMarkData& rMark = GetViewData().GetMarkData(); SCCOL nCol1, nCol2; SCROW nRow1, nRow2; nCol1 = aMarkRange.aStart.Col(); nRow1 = aMarkRange.aStart.Row(); nCol2 = aMarkRange.aEnd.Col(); nRow2 = aMarkRange.aEnd.Row(); SCTAB nTab = GetViewData().GetTabNo(); SfxWhichIter aIter(rSet); sal_uInt16 nWhich = aIter.FirstWhich(); while ( nWhich ) { bool bDisable = false; bool bNeedEdit = true; // need selection be editable? switch ( nWhich ) { case FID_FILL_TO_BOTTOM: // fill to top / bottom { bDisable = !bSimpleArea || (nRow1 == 0 && nRow2 == 0); if (!bDisable && GetViewData().SelectionForbidsCellFill()) bDisable = true; if ( !bDisable && bEditable ) { // do not damage matrix bDisable = rDoc.HasSelectedBlockMatrixFragment( nCol1, nRow1, nCol2, nRow1, rMark ); // first row } } break; case FID_FILL_TO_TOP: { bDisable = (!bSimpleArea) || (nRow1 == rDoc.MaxRow() && nRow2 == rDoc.MaxRow()); if (!bDisable && GetViewData().SelectionForbidsCellFill()) bDisable = true; if ( !bDisable && bEditable ) { // do not damage matrix bDisable = rDoc.HasSelectedBlockMatrixFragment( nCol1, nRow2, nCol2, nRow2, rMark ); // last row } } break; case FID_FILL_TO_RIGHT: // fill to left / right { bDisable = !bSimpleArea || (nCol1 == 0 && nCol2 == 0); if (!bDisable && GetViewData().SelectionForbidsCellFill()) bDisable = true; if ( !bDisable && bEditable ) { // do not damage matrix bDisable = rDoc.HasSelectedBlockMatrixFragment( nCol1, nRow1, nCol1, nRow2, rMark ); // first column } } break; case FID_FILL_TO_LEFT: { bDisable = (!bSimpleArea) || (nCol1 == rDoc.MaxCol() && nCol2 == rDoc.MaxCol()); if (!bDisable && GetViewData().SelectionForbidsCellFill()) bDisable = true; if ( !bDisable && bEditable ) { // do not damage matrix bDisable = rDoc.HasSelectedBlockMatrixFragment( nCol2, nRow1, nCol2, nRow2, rMark ); // last column } } break; case SID_RANDOM_NUMBER_GENERATOR_DIALOG: bDisable = !bSimpleArea || GetViewData().SelectionForbidsCellFill(); break; case SID_SAMPLING_DIALOG: case SID_DESCRIPTIVE_STATISTICS_DIALOG: case SID_ANALYSIS_OF_VARIANCE_DIALOG: case SID_CORRELATION_DIALOG: case SID_COVARIANCE_DIALOG: case SID_INSERT_SPARKLINE: { bDisable = !bSimpleArea; } break; case SID_GROUP_SPARKLINES: case SID_UNGROUP_SPARKLINES: { bDisable = !bSimpleArea; } break; case SID_EDIT_SPARKLINE: { bDisable = !rDoc.HasSparkline(GetViewData().GetCurPos()); } break; case SID_DELETE_SPARKLINE: case SID_EDIT_SPARKLINE_GROUP: case SID_DELETE_SPARKLINE_GROUP: { bDisable = !rDoc.HasOneSparklineGroup(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab)); } break; case FID_FILL_SERIES: // fill block case SID_OPENDLG_TABOP: // multiple-cell operations, are at least 2 cells marked? if (rDoc.GetChangeTrack()!=nullptr &&nWhich ==SID_OPENDLG_TABOP) bDisable = true; else bDisable = (!bSimpleArea) || (nCol1 == nCol2 && nRow1 == nRow2); if (!bDisable && GetViewData().SelectionForbidsCellFill()) bDisable = true; if ( !bDisable && bEditable && nWhich == FID_FILL_SERIES ) { // do not damage matrix bDisable = rDoc.HasSelectedBlockMatrixFragment( nCol1, nRow1, nCol2, nRow1, rMark ) // first row || rDoc.HasSelectedBlockMatrixFragment( nCol1, nRow2, nCol2, nRow2, rMark ) // last row || rDoc.HasSelectedBlockMatrixFragment( nCol1, nRow1, nCol1, nRow2, rMark ) // first column || rDoc.HasSelectedBlockMatrixFragment( nCol2, nRow1, nCol2, nRow2, rMark ); // last column } break; case FID_FILL_SINGLE_EDIT: bDisable = false; break; case SID_CUT: // cut bDisable = !bSimpleArea || GetObjectShell()->isContentExtractionLocked(); break; case FID_INS_CELL: // insert cells, just simple selection bDisable = (!bSimpleArea); break; case SID_PASTE: case SID_PASTE_SPECIAL: case SID_PASTE_UNFORMATTED: case SID_PASTE_ONLY_VALUE: case SID_PASTE_ONLY_TEXT: case SID_PASTE_ONLY_FORMULA: case SID_PASTE_TRANSPOSED: case SID_PASTE_AS_LINK: case SID_PASTE_TEXTIMPORT_DIALOG: bDisable = GetViewData().SelectionForbidsPaste(); break; case FID_INS_ROW: case FID_INS_ROWS_BEFORE: // insert rows case FID_INS_ROWS_AFTER: { sc::ColRowEditAction eAction = sc::ColRowEditAction::InsertRowsBefore; if (nWhich == FID_INS_ROWS_AFTER) eAction = sc::ColRowEditAction::InsertRowsAfter; bDisable = (!bSimpleArea) || GetViewData().SimpleColMarked(); if (!bEditable && nCol1 == 0 && nCol2 == rDoc.MaxCol()) { // See if row insertions are allowed. bEditable = rDoc.IsEditActionAllowed(eAction, rMark, nRow1, nRow2); } break; } case FID_INS_CELLSDOWN: bDisable = (!bSimpleArea) || GetViewData().SimpleColMarked(); break; case FID_INS_COLUMN: case FID_INS_COLUMNS_BEFORE: // insert columns case FID_INS_COLUMNS_AFTER: { sc::ColRowEditAction eAction = sc::ColRowEditAction::InsertColumnsBefore; if (nWhich == FID_INS_COLUMNS_AFTER) eAction = sc::ColRowEditAction::InsertColumnsAfter; bDisable = (!bSimpleArea && eMarkType != SC_MARK_SIMPLE_FILTERED) || GetViewData().SimpleRowMarked(); if (!bEditable && nRow1 == 0 && nRow2 == rDoc.MaxRow()) { // See if row insertions are allowed. bEditable = rDoc.IsEditActionAllowed(eAction, rMark, nCol1, nCol2); } break; } case FID_INS_CELLSRIGHT: bDisable = (!bSimpleArea) || GetViewData().SimpleRowMarked(); break; case SID_COPY: // copy // not editable because of matrix only? Do not damage matrix //! is not called, when protected AND matrix, we will have //! to live with this... is caught in Copy-Routine, otherwise //! work is to be done once more if ( bEditable || !bOnlyNotBecauseOfMatrix ) bNeedEdit = false; // allowed when protected/ReadOnly bDisable = GetObjectShell()->isContentExtractionLocked(); break; case SID_AUTOFORMAT: // Autoformat, at least 3x3 selected bDisable = (!bSimpleArea) || ((nCol2 - nCol1) < 2) || ((nRow2 - nRow1) < 2); break; case SID_CELL_FORMAT_RESET : case FID_CELL_FORMAT : case SID_ENABLE_HYPHENATION : // not editable because of matrix only? Attribute ok nonetheless if ( !bEditable && bOnlyNotBecauseOfMatrix ) bNeedEdit = false; break; case FID_VALIDATION: { if ( pDocShell && pDocShell->IsDocShared() ) { bDisable = true; } } break; case SID_TRANSLITERATE_HALFWIDTH: case SID_TRANSLITERATE_FULLWIDTH: case SID_TRANSLITERATE_HIRAGANA: case SID_TRANSLITERATE_KATAKANA: ScViewUtil::HideDisabledSlot( rSet, GetViewData().GetBindings(), nWhich ); break; case SID_CONVERT_FORMULA_TO_VALUE: { // Check and see if the marked range has at least one formula cell. bDisable = !rDoc.HasFormulaCell(aMarkRange); } break; } if (!bDisable && bNeedEdit && !bEditable) bDisable = true; if (bDisable) rSet.DisableItem(nWhich); else if (nWhich == SID_ENABLE_HYPHENATION) { // toggle slots need a bool item rSet.Put( SfxBoolItem( nWhich, false ) ); } nWhich = aIter.NextWhich(); } } // functions, disabled depending on cursor position // Default: // SID_INSERT_POSTIT, SID_CHARMAP, SID_OPENDLG_FUNCTION void ScCellShell::GetCellState( SfxItemSet& rSet ) { ScDocShell* pDocShell = GetViewData().GetDocShell(); ScDocument& rDoc = GetViewData().GetDocShell()->GetDocument(); ScAddress aCursor( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() ); SfxWhichIter aIter(rSet); sal_uInt16 nWhich = aIter.FirstWhich(); while ( nWhich ) { bool bDisable = false; bool bNeedEdit = true; // need cursor position be editable? switch ( nWhich ) { case SID_THESAURUS: { CellType eType = rDoc.GetCellType( aCursor ); bDisable = ( eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT); if (!bDisable) { // test for available languages LanguageType nLang = ScViewUtil::GetEffLanguage( rDoc, aCursor ); bDisable = !ScModule::HasThesaurusLanguage( nLang ); } } break; case SID_OPENDLG_FUNCTION: { ScMarkData aMarkData = GetViewData().GetMarkData(); aMarkData.MarkToSimple(); const ScRange& aRange = aMarkData.GetMarkArea(); if(aMarkData.IsMarked()) { if (!rDoc.IsBlockEditable( aCursor.Tab(), aRange.aStart.Col(),aRange.aStart.Row(), aRange.aEnd.Col(),aRange.aEnd.Row() )) { bDisable = true; } bNeedEdit=false; } } break; case SID_INSERT_POSTIT: { ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() ); if( rDoc.GetNote(aPos) ) { bDisable = true; } else { bDisable = false; if ( pDocShell && pDocShell->IsDocShared() ) { bDisable = true; } } } break; case SID_EDIT_POSTIT: { ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() ); bDisable = rDoc.GetNote(aPos) == nullptr; } break; } if (!bDisable && bNeedEdit) if (!rDoc.IsBlockEditable( aCursor.Tab(), aCursor.Col(),aCursor.Row(), aCursor.Col(),aCursor.Row() )) bDisable = true; if (bDisable) rSet.DisableItem(nWhich); nWhich = aIter.NextWhich(); } } static bool lcl_TestFormat( SvxClipboardFormatItem& rFormats, const TransferableDataHelper& rDataHelper, SotClipboardFormatId nFormatId ) { if ( rDataHelper.HasFormat( nFormatId ) ) { // translated format name strings are no longer inserted here, // handled by "paste special" dialog / toolbox controller instead. // Only the object type name has to be set here: OUString aStrVal; if ( nFormatId == SotClipboardFormatId::EMBED_SOURCE ) { TransferableObjectDescriptor aDesc; if ( const_cast(rDataHelper).GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aDesc ) ) aStrVal = aDesc.maTypeName; } else if ( nFormatId == SotClipboardFormatId::EMBED_SOURCE_OLE || nFormatId == SotClipboardFormatId::EMBEDDED_OBJ_OLE ) { OUString aSource; SvPasteObjectHelper::GetEmbeddedName( rDataHelper, aStrVal, aSource, nFormatId ); } if ( !aStrVal.isEmpty() ) rFormats.AddClipbrdFormat( nFormatId, aStrVal ); else rFormats.AddClipbrdFormat( nFormatId ); return true; } return false; } void ScCellShell::GetPossibleClipboardFormats( SvxClipboardFormatItem& rFormats ) { vcl::Window* pWin = GetViewData().GetActiveWin(); bool bDraw = ScDrawTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(pWin)) != nullptr; TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ) ); lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::DRAWING ); lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::SVXB ); lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::GDIMETAFILE ); lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::PNG ); lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::BITMAP ); lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::EMBED_SOURCE ); if ( !bDraw ) { lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::LINK ); lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::STRING ); lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::STRING_TSVC ); lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::DIF ); lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::RTF ); lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::RICHTEXT ); lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::HTML ); lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::HTML_SIMPLE ); lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::BIFF_8 ); lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::BIFF_5 ); } if ( !lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::EMBED_SOURCE_OLE ) ) lcl_TestFormat( rFormats, aDataHelper, SotClipboardFormatId::EMBEDDED_OBJ_OLE ); } // insert, insert contents static bool lcl_IsCellPastePossible( const TransferableDataHelper& rData ) { bool bPossible = false; css::uno::Reference< css::datatransfer::XTransferable2 > xTransferable(rData.GetXTransferable(), css::uno::UNO_QUERY); if ( ScTransferObj::GetOwnClipboard(xTransferable) || ScDrawTransferObj::GetOwnClipboard(xTransferable) ) bPossible = true; else { if ( rData.HasFormat( SotClipboardFormatId::PNG ) || rData.HasFormat( SotClipboardFormatId::BITMAP ) || rData.HasFormat( SotClipboardFormatId::GDIMETAFILE ) || rData.HasFormat( SotClipboardFormatId::SVXB ) || rData.HasFormat( SotClipboardFormatId::PRIVATE ) || rData.HasFormat( SotClipboardFormatId::RTF ) || rData.HasFormat( SotClipboardFormatId::RICHTEXT ) || rData.HasFormat( SotClipboardFormatId::EMBED_SOURCE ) || rData.HasFormat( SotClipboardFormatId::LINK_SOURCE ) || rData.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE ) || rData.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE ) || rData.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE ) || rData.HasFormat( SotClipboardFormatId::STRING ) || rData.HasFormat( SotClipboardFormatId::STRING_TSVC ) || rData.HasFormat( SotClipboardFormatId::SYLK ) || rData.HasFormat( SotClipboardFormatId::LINK ) || rData.HasFormat( SotClipboardFormatId::HTML ) || rData.HasFormat( SotClipboardFormatId::HTML_SIMPLE ) || rData.HasFormat( SotClipboardFormatId::DIF ) ) { bPossible = true; } } return bPossible; } bool ScCellShell::HasClipboardFormat( SotClipboardFormatId nFormatId ) { vcl::Window* pWin = GetViewData().GetActiveWin(); TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin )); return aDataHelper.HasFormat( nFormatId ); } IMPL_LINK( ScCellShell, ClipboardChanged, TransferableDataHelper*, pDataHelper, void ) { bPastePossible = lcl_IsCellPastePossible( *pDataHelper ); SfxBindings& rBindings = GetViewData().GetBindings(); rBindings.Invalidate( SID_PASTE ); rBindings.Invalidate( SID_PASTE_SPECIAL ); rBindings.Invalidate( SID_PASTE_UNFORMATTED ); rBindings.Invalidate( SID_PASTE_ONLY_VALUE ); rBindings.Invalidate( SID_PASTE_ONLY_TEXT ); rBindings.Invalidate( SID_PASTE_ONLY_FORMULA ); rBindings.Invalidate( SID_PASTE_TRANSPOSED ); rBindings.Invalidate( SID_PASTE_AS_LINK ); rBindings.Invalidate( SID_PASTE_TEXTIMPORT_DIALOG ); rBindings.Invalidate( SID_CLIPBOARD_FORMAT_ITEMS ); } namespace { bool checkDestRanges(ScViewData& rViewData) { ScRange aDummy; ScMarkType eMarkType = rViewData.GetSimpleArea( aDummy); if (eMarkType != SC_MARK_MULTI) { // Single destination range. if (eMarkType != SC_MARK_SIMPLE && eMarkType != SC_MARK_SIMPLE_FILTERED) return false; } // Multiple destination ranges. // Same as ScViewData::SelectionForbidsPaste() in // sc/source/ui/view/viewdata.cxx but different return details. vcl::Window* pWin = rViewData.GetActiveWin(); if (!pWin) return false; const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(pWin)); if (!pOwnClip) // If it's not a Calc document, we won't be picky. return true; ScDocument* pClipDoc = pOwnClip->GetDocument(); if (!pClipDoc) return false; ScRange aSrcRange = pClipDoc->GetClipParam().getWholeRange(); SCROW nRowSize = aSrcRange.aEnd.Row() - aSrcRange.aStart.Row() + 1; SCCOL nColSize = aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1; if (rViewData.SelectionForbidsPaste( nColSize, nRowSize)) return false; ScMarkData aMark = rViewData.GetMarkData(); ScRangeList aRanges; aMark.MarkToSimple(); aMark.FillRangeListWithMarks(&aRanges, false); return ScClipUtil::CheckDestRanges(rViewData.GetDocument(), nColSize, nRowSize, aMark, aRanges); } } void ScCellShell::GetClipState( SfxItemSet& rSet ) { // SID_PASTE // SID_PASTE_SPECIAL // SID_PASTE_UNFORMATTED // SID_CLIPBOARD_FORMAT_ITEMS if ( !pImpl->m_xClipEvtLstnr.is() ) { // create listener pImpl->m_xClipEvtLstnr = new TransferableClipboardListener( LINK( this, ScCellShell, ClipboardChanged ) ); vcl::Window* pWin = GetViewData().GetActiveWin(); pImpl->m_xClipEvtLstnr->AddListener( pWin ); // get initial state TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ) ); bPastePossible = lcl_IsCellPastePossible( aDataHelper ); } bool bDisable = !bPastePossible; // cell protection / multiple selection if (!bDisable) { SCCOL nCol = GetViewData().GetCurX(); SCROW nRow = GetViewData().GetCurY(); SCTAB nTab = GetViewData().GetTabNo(); ScDocument& rDoc = GetViewData().GetDocShell()->GetDocument(); if (!rDoc.IsBlockEditable( nTab, nCol,nRow, nCol,nRow )) bDisable = true; if (!bDisable && !checkDestRanges(GetViewData())) bDisable = true; } if (bDisable) { rSet.DisableItem( SID_PASTE ); rSet.DisableItem( SID_PASTE_SPECIAL ); rSet.DisableItem( SID_PASTE_UNFORMATTED ); rSet.DisableItem( SID_PASTE_ONLY_VALUE ); rSet.DisableItem( SID_PASTE_ONLY_TEXT ); rSet.DisableItem( SID_PASTE_ONLY_FORMULA ); rSet.DisableItem( SID_PASTE_TRANSPOSED ); rSet.DisableItem( SID_PASTE_AS_LINK ); rSet.DisableItem( SID_PASTE_TEXTIMPORT_DIALOG ); rSet.DisableItem( SID_CLIPBOARD_FORMAT_ITEMS ); } else if ( rSet.GetItemState( SID_CLIPBOARD_FORMAT_ITEMS ) != SfxItemState::UNKNOWN ) { SvxClipboardFormatItem aFormats( SID_CLIPBOARD_FORMAT_ITEMS ); GetPossibleClipboardFormats( aFormats ); rSet.Put( aFormats ); } } // only SID_HYPERLINK_GETLINK: void ScCellShell::GetHLinkState( SfxItemSet& rSet ) { // always return an item (or inserting will be disabled) // if the cell at the cursor contains only a link, return that link SvxHyperlinkItem aHLinkItem; if ( !GetViewData().GetView()->HasBookmarkAtCursor( &aHLinkItem ) ) { // tdf#80043 - put selected text into item ScViewData& rData = GetViewData(); ScDocument& rDoc = rData.GetDocument(); SCCOL nPosX = rData.GetCurX(); SCROW nPosY = rData.GetCurY(); SCTAB nTab = rData.GetTabNo(); aHLinkItem.SetName(rDoc.GetString(nPosX, nPosY, nTab)); } rSet.Put(aHLinkItem); } void ScCellShell::GetState(SfxItemSet &rSet) { ScTabViewShell* pTabViewShell = GetViewData().GetViewShell(); ScDocShell* pDocSh = GetViewData().GetDocShell(); ScViewData& rData = GetViewData(); ScDocument& rDoc = rData.GetDocument(); ScMarkData& rMark = rData.GetMarkData(); SCCOL nPosX = rData.GetCurX(); SCROW nPosY = rData.GetCurY(); SCTAB nTab = rData.GetTabNo(); SCTAB nTabCount = rDoc.GetTableCount(); SCTAB nTabSelCount = rMark.GetSelectCount(); SfxWhichIter aIter(rSet); sal_uInt16 nWhich = aIter.FirstWhich(); while ( nWhich ) { switch ( nWhich ) { case SID_DETECTIVE_REFRESH: if (!rDoc.HasDetectiveOperations()) rSet.DisableItem( nWhich ); break; case SID_RANGE_ADDRESS: { ScRange aRange; if ( rData.GetSimpleArea( aRange ) == SC_MARK_SIMPLE ) { OUString aStr(aRange.Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_3D)); rSet.Put( SfxStringItem( nWhich, aStr ) ); } } break; case SID_RANGE_NOTETEXT: { // always take cursor position, do not use top-left cell of selection OUString aNoteText; if ( const ScPostIt* pNote = rDoc.GetNote(nPosX, nPosY, nTab) ) aNoteText = pNote->GetText(); rSet.Put( SfxStringItem( nWhich, aNoteText ) ); } break; case SID_RANGE_ROW: rSet.Put( SfxInt32Item( nWhich, nPosY+1 ) ); break; case SID_RANGE_COL: rSet.Put( SfxInt16Item( nWhich, nPosX+1 ) ); break; case SID_RANGE_TABLE: rSet.Put( SfxInt16Item( nWhich, nTab+1 ) ); break; case SID_RANGE_FORMULA: { OUString aString = rDoc.GetFormula( nPosX, nPosY, nTab ); if( aString.isEmpty() ) { aString = rDoc.GetInputString( nPosX, nPosY, nTab ); } rSet.Put( SfxStringItem( nWhich, aString ) ); } break; case SID_RANGE_TEXTVALUE: { OUString aString = rDoc.GetString(nPosX, nPosY, nTab); rSet.Put( SfxStringItem( nWhich, aString ) ); } break; case SID_STATUS_SELMODE: { /* 0: STD Click cancels Sel * 1: ER Click extends selection * 2: ERG Click defines further selection */ sal_uInt16 nMode = pTabViewShell->GetLockedModifiers(); switch ( nMode ) { case KEY_SHIFT: nMode = 1; break; case KEY_MOD1: nMode = 2; break; // Control-key case 0: default: nMode = 0; } rSet.Put( SfxUInt16Item( nWhich, nMode ) ); } break; case SID_STATUS_DOCPOS: { OUString aStr = ScResId( STR_TABLE_COUNT ); aStr = aStr.replaceFirst("%1", OUString::number( nTab + 1 ) ); aStr = aStr.replaceFirst("%2", OUString::number( nTabCount ) ); rSet.Put( SfxStringItem( nWhich, aStr ) ); } break; case SID_ROWCOL_SELCOUNT: { ScRangeListRef aMarkRanges; GetViewData().GetMultiArea(aMarkRanges); const SCCOL nCol1 = aMarkRanges->front().aStart.Col(); const SCROW nRow1 = aMarkRanges->front().aStart.Row(); const SCCOL nCol2 = aMarkRanges->front().aEnd.Col(); const SCROW nRow2 = aMarkRanges->front().aEnd.Row(); const size_t nRanges = aMarkRanges->size(); if ((nRanges == 1 && (nCol2 != nCol1 || nRow1 != nRow2)) || nRanges > 1) { bool bSameRows = true; bool bSameCols = true; SCROW nRowsSum = 0; SCCOL nColsSum = 0; for (size_t i = 0; i < nRanges; ++i) { const ScRange& rRange = (*aMarkRanges)[i]; const SCCOL nRangeCol1 = rRange.aStart.Col(); const SCROW nRangeRow1 = rRange.aStart.Row(); const SCCOL nRangeCol2 = rRange.aEnd.Col(); const SCROW nRangeRow2 = rRange.aEnd.Row(); bSameRows &= (nRow1 == nRangeRow1 && nRow2 == nRangeRow2); bSameCols &= (nCol1 == nRangeCol1 && nCol2 == nRangeCol2); // Sum rows if the number of cols is the same or // sum columns if the number of rows is the same, // otherwise do not show any count of selected cells. if (bSameRows || bSameCols) { const auto nCols = nRangeCol2 - nRangeCol1 + 1; const auto nRows = (bSameCols || nRowsSum == 0) ? rDoc.CountNonFilteredRows( nRangeRow1, nRangeRow2, rRange.aStart.Tab()) : nRowsSum; if (bSameRows) { nRowsSum = nRows; nColsSum += nCols; } else if (bSameCols) { nRowsSum += nRows; nColsSum = nCols; } } else break; } // Either the rows or columns are the same among selections if (bSameRows || bSameCols) { const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetUILocaleDataWrapper(); OUString aRowArg = ScResId(STR_SELCOUNT_ROWARG, nRowsSum) .replaceAll("$1", rLocaleData.getNum(nRowsSum, 0)); OUString aColArg = ScResId(STR_SELCOUNT_COLARG, nColsSum) .replaceAll("$1", rLocaleData.getNum(nColsSum, 0)); OUString aStr = ScResId(STR_SELCOUNT); aStr = aStr.replaceAll("$1", aRowArg); aStr = aStr.replaceAll("$2", aColArg); rSet.Put(SfxStringItem(nWhich, aStr)); } } else { SCSIZE nSelected, nTotal; rDoc.GetFilterSelCount( nPosX, nPosY, nTab, nSelected, nTotal ); if( nTotal && nSelected != SCSIZE_MAX ) { OUString aStr = ScResId( STR_FILTER_SELCOUNT ); aStr = aStr.replaceAll( "$1", OUString::number( nSelected ) ); aStr = aStr.replaceAll( "$2", OUString::number( nTotal ) ); rSet.Put( SfxStringItem( nWhich, aStr ) ); } } } break; // calculations etc. with date/time/Fail/position&size together // #i34458# The SfxStringItem belongs only into SID_TABLE_CELL. It no longer has to be // duplicated in SID_ATTR_POSITION or SID_ATTR_SIZE for SvxPosSizeStatusBarControl. case SID_TABLE_CELL: { // Test, if error under cursor // (not rDoc.GetErrCode, to avoid erasing circular references) // In interpreter may happen via rescheduled Basic if ( rDoc.IsInInterpreter() ) rSet.Put( SfxStringItem( nWhich, "..." ) ); else { FormulaError nErrCode = FormulaError::NONE; ScFormulaCell* pCell = rDoc.GetFormulaCell(ScAddress(nPosX, nPosY, nTab)); if (pCell && !pCell->IsRunning()) nErrCode = pCell->GetErrCode(); OUString aFuncStr; if ( pTabViewShell->GetFunction( aFuncStr, nErrCode ) ) rSet.Put( SfxStringItem( nWhich, aFuncStr ) ); } } break; case SID_DATA_SELECT: // HasSelectionData includes column content and validity, // page fields have to be checked separately. if ( !rDoc.HasSelectionData( nPosX, nPosY, nTab ) && !pTabViewShell->HasPageFieldDataAtCursor() ) rSet.DisableItem( nWhich ); break; case FID_CURRENTVALIDATION: if ( !rDoc.HasValidationData( nPosX, nPosY, nTab )) rSet.DisableItem( nWhich ); break; case SID_STATUS_SUM: { OUString aFuncStr; if ( pTabViewShell->GetFunction( aFuncStr, FormulaError::NONE ) ) rSet.Put( SfxStringItem( nWhich, aFuncStr ) ); } break; case FID_MERGE_ON: if ( rDoc.GetChangeTrack() || !pTabViewShell->TestMergeCells() ) rSet.DisableItem( nWhich ); break; case FID_MERGE_OFF: if ( rDoc.GetChangeTrack() || !pTabViewShell->TestRemoveMerge() ) rSet.DisableItem( nWhich ); break; case FID_MERGE_TOGGLE: if ( rDoc.GetChangeTrack() ) rSet.DisableItem( nWhich ); else { bool bCanMerge = pTabViewShell->TestMergeCells(); bool bCanSplit = pTabViewShell->TestRemoveMerge(); if( !bCanMerge && !bCanSplit ) rSet.DisableItem( nWhich ); else rSet.Put( SfxBoolItem( nWhich, bCanSplit ) ); } break; case FID_INS_ROWBRK: if ( nPosY==0 || (rDoc.HasRowBreak(nPosY, nTab) & ScBreakType::Manual) || rDoc.IsTabProtected(nTab) ) rSet.DisableItem( nWhich ); break; case FID_INS_COLBRK: if ( nPosX==0 || (rDoc.HasColBreak(nPosX, nTab) & ScBreakType::Manual) || rDoc.IsTabProtected(nTab) ) rSet.DisableItem( nWhich ); break; case FID_DEL_ROWBRK: if ( nPosY==0 || !(rDoc.HasRowBreak(nPosY, nTab) & ScBreakType::Manual) || rDoc.IsTabProtected(nTab) ) rSet.DisableItem( nWhich ); break; case FID_DEL_COLBRK: if ( nPosX==0 || !(rDoc.HasColBreak(nPosX, nTab) & ScBreakType::Manual) || rDoc.IsTabProtected(nTab) ) rSet.DisableItem( nWhich ); break; case FID_FILL_TAB: if ( nTabSelCount < 2 ) rSet.DisableItem( nWhich ); break; case SID_INSERT_CURRENT_DATE: case SID_INSERT_CURRENT_TIME: { if ( rDoc.IsTabProtected(nTab) && rDoc.HasAttrib(nPosX, nPosY, nTab, nPosX, nPosY, nTab, HasAttrFlags::Protected)) rSet.DisableItem( nWhich ); } break; case SID_SELECT_SCENARIO: { std::vector aList; Color aDummyCol; if ( !rDoc.IsScenario(nTab) ) { OUString aStr; ScScenarioFlags nFlags; SCTAB nScTab = nTab + 1; bool bSheetProtected = rDoc.IsTabProtected(nTab); while ( rDoc.IsScenario(nScTab) ) { rDoc.GetName( nScTab, aStr ); aList.push_back(aStr); rDoc.GetScenarioData( nScTab, aStr, aDummyCol, nFlags ); aList.push_back(aStr); // Protection is sal_True if both Sheet and Scenario are protected aList.push_back((bSheetProtected && (nFlags & ScScenarioFlags::Protected)) ? OUString("1") : OUString("0")); ++nScTab; } } else { OUString aComment; ScScenarioFlags nDummyFlags; rDoc.GetScenarioData( nTab, aComment, aDummyCol, nDummyFlags ); OSL_ENSURE( aList.empty(), "List not empty!" ); aList.push_back(aComment); } rSet.Put( SfxStringListItem( nWhich, &aList ) ); } break; case FID_ROW_HIDE: case FID_ROW_SHOW: case FID_COL_HIDE: case FID_COL_SHOW: case FID_COL_OPT_WIDTH: case FID_ROW_OPT_HEIGHT: case FID_DELETE_CELL: if ( rDoc.IsTabProtected(nTab) || pDocSh->IsReadOnly()) rSet.DisableItem( nWhich ); break; case SID_OUTLINE_MAKE: { if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() ) ) { //! test for data pilot operation } else if (rDoc.GetChangeTrack()!=nullptr || GetViewData().IsMultiMarked()) { rSet.DisableItem( nWhich ); } } break; case SID_OUTLINE_SHOW: if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() ) ) { //! test for data pilot operation } else if (!pTabViewShell->OutlinePossible(false)) rSet.DisableItem( nWhich ); break; case SID_OUTLINE_HIDE: if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() ) ) { //! test for data pilot operation } else if (!pTabViewShell->OutlinePossible(true)) rSet.DisableItem( nWhich ); break; case SID_OUTLINE_REMOVE: { if ( GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() ) ) { //! test for data pilot operation } else { bool bCol, bRow; pTabViewShell->TestRemoveOutline( bCol, bRow ); if ( !bCol && !bRow ) rSet.DisableItem( nWhich ); } } break; case FID_COL_WIDTH: { SfxUInt16Item aWidthItem( FID_COL_WIDTH, rDoc.GetColWidth( nPosX , nTab) ); rSet.Put( aWidthItem ); if ( pDocSh->IsReadOnly()) rSet.DisableItem( nWhich ); //XXX disable if not conclusive } break; case FID_ROW_HEIGHT: { SfxUInt16Item aHeightItem( FID_ROW_HEIGHT, rDoc.GetRowHeight( nPosY , nTab) ); rSet.Put( aHeightItem ); //XXX disable if not conclusive if ( pDocSh->IsReadOnly()) rSet.DisableItem( nWhich ); } break; case SID_DETECTIVE_FILLMODE: rSet.Put(SfxBoolItem( nWhich, pTabViewShell->IsAuditShell() )); break; case FID_INPUTLINE_STATUS: OSL_FAIL( "Old update method. Use ScTabViewShell::UpdateInputHandler()." ); break; case SID_SCENARIOS: // scenarios: if (!(rMark.IsMarked() || rMark.IsMultiMarked())) // only, if something selected rSet.DisableItem( nWhich ); break; case FID_NOTE_VISIBLE: { const ScPostIt* pNote = rDoc.GetNote(nPosX, nPosY, nTab); if ( pNote && rDoc.IsBlockEditable( nTab, nPosX,nPosY, nPosX,nPosY ) ) rSet.Put( SfxBoolItem( nWhich, pNote->IsCaptionShown() ) ); else rSet.DisableItem( nWhich ); } break; case FID_HIDE_NOTE: case FID_SHOW_NOTE: { bool bEnable = false; bool bSearchForHidden = nWhich == FID_SHOW_NOTE; if (!rMark.IsMarked() && !rMark.IsMultiMarked()) { // Check current cell const ScPostIt* pNote = rDoc.GetNote(nPosX, nPosY, nTab); if ( pNote && rDoc.IsBlockEditable( nTab, nPosX,nPosY, nPosX,nPosY ) ) if ( pNote->IsCaptionShown() != bSearchForHidden) bEnable = true; } else { // Check selection range ScRangeListRef aRangesRef; rData.GetMultiArea(aRangesRef); ScRangeList aRanges = *aRangesRef; std::vector aNotes; rDoc.GetNotesInRange(aRanges, aNotes); for(const auto& rNote : aNotes) { const ScAddress& rAdr = rNote.maPos; if( rDoc.IsBlockEditable( rAdr.Tab(), rAdr.Col(), rAdr.Row(), rAdr.Col(), rAdr.Row() )) { if (rNote.mpNote->IsCaptionShown() != bSearchForHidden) { bEnable = true; break; } } } } if ( !bEnable ) rSet.DisableItem( nWhich ); } break; case FID_SHOW_ALL_NOTES: case FID_HIDE_ALL_NOTES: case FID_DELETE_ALL_NOTES: { bool bHasNotes = false; for (auto const& rTab : rMark.GetSelectedTabs()) { if (rDoc.HasTabNotes( rTab )) { bHasNotes = true; break; } } if ( !bHasNotes ) rSet.DisableItem( nWhich ); } break; case SID_TOGGLE_NOTES: { bool bHasNotes = false; ScRangeList aRanges; for (auto const& rTab : rMark.GetSelectedTabs()) { if (rDoc.HasTabNotes( rTab )) { bHasNotes = true; aRanges.push_back(ScRange(0,0,rTab,rDoc.MaxCol(),rDoc.MaxRow(),rTab)); } } if ( !bHasNotes ) rSet.DisableItem( nWhich ); else { CommentCaptionState eState = rDoc.GetAllNoteCaptionsState( aRanges ); bool bAllNotesInShown = (eState != ALLHIDDEN && eState != MIXED); rSet.Put( SfxBoolItem( SID_TOGGLE_NOTES, bAllNotesInShown) ); } } break; case SID_DELETE_NOTE: { bool bEnable = false; if ( rMark.IsMarked() || rMark.IsMultiMarked() ) { if ( rDoc.IsSelectionEditable( rMark ) ) { // look for at least one note in selection ScRangeList aRanges; rMark.FillRangeListWithMarks( &aRanges, false ); bEnable = rDoc.ContainsNotesInRange( aRanges ); } } else { bEnable = rDoc.IsBlockEditable( nTab, nPosX,nPosY, nPosX,nPosY ) && rDoc.GetNote(nPosX, nPosY, nTab); } if ( !bEnable ) rSet.DisableItem( nWhich ); } break; case SID_OPENDLG_CONSOLIDATE: case SCITEM_CONSOLIDATEDATA: { if (rDoc.GetChangeTrack()!=nullptr) rSet.DisableItem( nWhich); } break; case SID_CHINESE_CONVERSION: case SID_HANGUL_HANJA_CONVERSION: ScViewUtil::HideDisabledSlot( rSet, rData.GetBindings(), nWhich ); break; case FID_USE_NAME: { if ( pDocSh && pDocSh->IsDocShared() ) rSet.DisableItem( nWhich ); else { ScRange aRange; if ( rData.GetSimpleArea( aRange ) != SC_MARK_SIMPLE ) rSet.DisableItem( nWhich ); } } break; case FID_DEFINE_NAME: case FID_INSERT_NAME: case FID_ADD_NAME: case SID_DEFINE_COLROWNAMERANGES: { if ( pDocSh && pDocSh->IsDocShared() ) { rSet.DisableItem( nWhich ); } } break; case FID_DEFINE_CURRENT_NAME: { ScAddress aCurrentAddress( nPosX, nPosY, nTab ); if ( !rDoc.IsAddressInRangeName( RangeNameScope::GLOBAL, aCurrentAddress ) && !rDoc.IsAddressInRangeName( RangeNameScope::SHEET, aCurrentAddress )) { rSet.DisableItem( nWhich ); } } break; case SID_SPELL_DIALOG: { if (rDoc.IsTabProtected(rData.GetTabNo())) { bool bVisible = false; SfxViewFrame* pViewFrame = ( pTabViewShell ? pTabViewShell->GetViewFrame() : nullptr ); if ( pViewFrame && pViewFrame->HasChildWindow( nWhich ) ) { SfxChildWindow* pChild = pViewFrame->GetChildWindow( nWhich ); std::shared_ptr xController = pChild ? pChild->GetController() : nullptr; if (xController && xController->getDialog()->get_visible()) { bVisible = true; } } if ( !bVisible ) { rSet.DisableItem( nWhich ); } } } break; case SID_OPENDLG_CURRENTCONDFRMT: case SID_OPENDLG_CURRENTCONDFRMT_MANAGER: { const SfxPoolItem* pItem = rDoc.GetAttr( nPosX, nPosY, nTab, ATTR_CONDITIONAL ); const ScCondFormatItem* pCondFormatItem = static_cast(pItem); if ( pCondFormatItem->GetCondFormatData().empty() ) rSet.DisableItem( nWhich ); else if ( pCondFormatItem->GetCondFormatData().size() == 1 ) rSet.DisableItem( SID_OPENDLG_CURRENTCONDFRMT_MANAGER ); else if ( pCondFormatItem->GetCondFormatData().size() > 1 ) rSet.DisableItem( SID_OPENDLG_CURRENTCONDFRMT ); } break; } // switch ( nWitch ) nWhich = aIter.NextWhich(); } // while ( nWitch ) } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */