Files
odfstudio/sc/source/ui/app/inputwin.cxx
T
Michael Meeks e1b35f0bb1 sc: forward declare ScDrawView in tabview.hxx.
tabview.hxx included drawview.hxx for a std::unique_ptr<ScDrawView> member
and an accessor that returns the raw pointer. Neither needs the complete
type, and the destructor of ScTabView is already out of line. The include
dragged svx/fmview.hxx, svx/view3d.hxx, svx/svdview.hxx and the rest of the
svx view class chain into the 200 translation units that include the view
shell headers.

tabvwsh.hxx and tabview.hxx now include the small headers they used through
that chain, and the files that call into the draw view include drawview.hxx
themselves.

clang -ftime-trace says around 2% of the module build time saved here.

Signed-off-by: Michael Meeks <michael.meeks@collabora.com>
Change-Id: I198b19e74f6ad56090b987880a063dc9ee677cda
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/12683
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
(cherry picked from commit a7fd69666ea5fb4d26dfad0d286f6c1f3bcd3144)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211884
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
2026-10-07 12:18:06 +02:00

2797 lines
88 KiB
C++

/* -*- 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 <memory>
#include <algorithm>
#include <string_view>
#include <editeng/eeitem.hxx>
#include <editeng/colritem.hxx>
#include <vcl/ptrstyle.hxx>
#include <sfx2/app.hxx>
#include <sfx2/chalign.hxx>
#include <editeng/adjustitem.hxx>
#include <editeng/editview.hxx>
#include <editeng/editstat.hxx>
#include <editeng/lspcitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/wghtitem.hxx>
#include <editeng/postitem.hxx>
#include <editeng/langitem.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/event.hxx>
#include <editeng/scriptspaceitem.hxx>
#include <vcl/commandevent.hxx>
#include <vcl/cursor.hxx>
#include <vcl/help.hxx>
#include <vcl/image.hxx>
#include <vcl/ptrstyle.hxx>
#include <vcl/settings.hxx>
#include <svl/stritem.hxx>
#include <vcl/svapp.hxx>
#include <vcl/weld/Menu.hxx>
#include <vcl/weld/Window.hxx>
#include <vcl/weld/weldutils.hxx>
#include <unotools/charclass.hxx>
#include <inputwin.hxx>
#include <scmod.hxx>
#include <global.hxx>
#include <scresid.hxx>
#include <strings.hrc>
#include <globstr.hrc>
#include <bitmaps.hlst>
#include <reffact.hxx>
#include <editutil.hxx>
#include <inputhdl.hxx>
#include <tabvwsh.hxx>
#include <document.hxx>
#include <docsh.hxx>
#include <appoptio.hxx>
#include <rangenam.hxx>
#include <rangeutl.hxx>
#include <docfunc.hxx>
#include <funcdesc.hxx>
#include <editeng/fontitem.hxx>
#include <AccessibleEditObject.hxx>
#include <AccessibleText.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/string.hxx>
#include <com/sun/star/frame/XLayoutManager.hpp>
#include <helpids.h>
#include <output.hxx>
namespace com::sun::star::accessibility { class XAccessible; }
const tools::Long THESIZE = 1000000; // Should be more than enough!
const tools::Long INPUTLINE_INSET_MARGIN = 2; // Space between border and interior widgets of input line
const tools::Long LEFT_OFFSET = 5; // Left offset of input line
//TODO const long BUTTON_OFFSET = 2; // Space between input line and button to expand/collapse
const tools::Long INPUTWIN_MULTILINES = 6; // Initial number of lines within multiline dropdown
const tools::Long TOOLBOX_WINDOW_HEIGHT = 22; // Height of toolbox window in pixels - TODO: The same on all systems?
const tools::Long POSITION_COMBOBOX_WIDTH = 18; // Width of position combobox in characters
const int RESIZE_HOTSPOT_HEIGHT = 4;
using com::sun::star::uno::Reference;
using com::sun::star::uno::UNO_QUERY;
using com::sun::star::frame::XLayoutManager;
using com::sun::star::beans::XPropertySet;
namespace {
constexpr ToolBoxItemId SID_INPUT_FUNCTION (SC_VIEW_START + 47);
constexpr ToolBoxItemId SID_INPUT_SUM (SC_VIEW_START + 48);
constexpr ToolBoxItemId SID_INPUT_EQUAL (SC_VIEW_START + 49);
constexpr ToolBoxItemId SID_INPUT_CANCEL (SC_VIEW_START + 50);
constexpr ToolBoxItemId SID_INPUT_OK (SC_VIEW_START + 51);
enum ScNameInputType
{
SC_NAME_INPUT_CELL,
SC_NAME_INPUT_RANGE,
SC_NAME_INPUT_NAMEDRANGE_LOCAL,
SC_NAME_INPUT_NAMEDRANGE_GLOBAL,
SC_NAME_INPUT_DATABASE,
SC_NAME_INPUT_ROW,
SC_NAME_INPUT_SHEET,
SC_NAME_INPUT_DEFINE,
SC_NAME_INPUT_BAD_NAME,
SC_NAME_INPUT_BAD_SELECTION,
SC_MANAGE_NAMES
};
}
SFX_IMPL_CHILDWINDOW_WITHID(ScInputWindowWrapper,FID_INPUTLINE_STATUS)
ScInputWindowWrapper::ScInputWindowWrapper(vcl::Window* pParentP, sal_uInt16 nId,
SfxBindings& rBindings, SfxChildWinInfo&)
: SfxChildWindow(pParentP, nId)
{
VclPtr<ScInputWindow> pWin = VclPtr<ScInputWindow>::Create(pParentP, &rBindings);
SetWindow( pWin );
pWin->Show();
pWin->SetSizePixel( pWin->CalcWindowSizePixel() );
SetAlignment(SfxChildAlignment::LOWESTTOP);
rBindings.Invalidate(FID_TOGGLEINPUTLINE);
}
/**
* GetInfo is disposed of if there's a SFX_IMPL_TOOLBOX!
*/
SfxChildWinInfo ScInputWindowWrapper::GetInfo() const
{
SfxChildWinInfo aInfo = SfxChildWindow::GetInfo();
return aInfo;
}
static ScTabViewShell* lcl_chooseRuntimeImpl( const SfxBindings* pBind )
{
ScTabViewShell* pViewSh = nullptr;
SfxDispatcher* pDisp = pBind->GetDispatcher();
if ( pDisp )
{
SfxViewFrame* pViewFrm = pDisp->GetFrame();
if ( pViewFrm )
pViewSh = dynamic_cast<ScTabViewShell*>( pViewFrm->GetViewShell() );
}
return pViewSh;
}
ScInputWindow::ScInputWindow( vcl::Window* pParent, const SfxBindings* pBind ) :
// With WB_CLIPCHILDREN otherwise we get flickering
ToolBox ( pParent, WinBits(WB_CLIPCHILDREN | WB_BORDER | WB_NOSHADOW) ),
aWndPos(VclPtr<ScPosWnd>::Create(this)),
mxTextWindow ( VclPtr<ScInputBarGroup>::Create( this, lcl_chooseRuntimeImpl(pBind)) ),
pInputHdl ( nullptr ),
mpViewShell ( nullptr ),
mnMaxY (0),
mnStandardItemHeight(0),
bIsOkCancelMode ( false ),
bInResize ( false )
{
// #i73615# don't rely on SfxViewShell::Current while constructing the input line
// (also for GetInputHdl below)
ScTabViewShell* pViewSh = nullptr;
SfxDispatcher* pDisp = pBind->GetDispatcher();
if ( pDisp )
{
SfxViewFrame* pViewFrm = pDisp->GetFrame();
if ( pViewFrm )
pViewSh = dynamic_cast<ScTabViewShell*>( pViewFrm->GetViewShell() );
}
OSL_ENSURE( pViewSh, "no view shell for input window" );
mpViewShell = pViewSh;
// Position window, 3 buttons, input window
if (!comphelper::LibreOfficeKit::isActive())
{
InsertWindow (ToolBoxItemId(1), aWndPos.get(), ToolBoxItemBits::NONE, 0);
InsertSeparator (1);
InsertItem (SID_INPUT_FUNCTION, Image(StockImage::Yes, RID_BMP_INPUT_FUNCTION), ToolBoxItemBits::NONE, 2);
}
// sigma and equal buttons
{
InsertItem (SID_INPUT_SUM, Image(StockImage::Yes, RID_BMP_INPUT_SUM), ToolBoxItemBits::DROPDOWN, 3);
InsertItem (SID_INPUT_EQUAL, Image(StockImage::Yes, RID_BMP_INPUT_EQUAL), ToolBoxItemBits::NONE, 4);
InsertItem (SID_INPUT_CANCEL, Image(StockImage::Yes, RID_BMP_INPUT_CANCEL), ToolBoxItemBits::NONE, 5);
InsertItem (SID_INPUT_OK, Image(StockImage::Yes, RID_BMP_INPUT_OK), ToolBoxItemBits::NONE, 6);
}
InsertWindow (ToolBoxItemId(7), mxTextWindow.get(), ToolBoxItemBits::NONE, 7);
SetDropdownClickHdl( LINK( this, ScInputWindow, DropdownClickHdl ));
if (!comphelper::LibreOfficeKit::isActive())
{
aWndPos ->SetQuickHelpText(ScResId(SCSTR_QHELP_POSWND));
aWndPos ->SetHelpId (HID_INSWIN_POS);
mxTextWindow->SetQuickHelpText(ScResId(SCSTR_QHELP_INPUTWND));
mxTextWindow->SetHelpId (HID_INSWIN_INPUT);
// No SetHelpText: the helptexts come from the Help
SetItemText (SID_INPUT_FUNCTION, ScResId(SCSTR_QHELP_BTNCALC));
SetHelpId (SID_INPUT_FUNCTION, HID_INSWIN_CALC);
}
// sigma and equal buttons
{
SetHelpId (SID_INPUT_SUM, HID_INSWIN_SUM);
SetHelpId (SID_INPUT_EQUAL, HID_INSWIN_FUNC);
SetHelpId (SID_INPUT_CANCEL, HID_INSWIN_CANCEL);
SetHelpId (SID_INPUT_OK, HID_INSWIN_OK);
if (!comphelper::LibreOfficeKit::isActive())
{
SetItemText ( SID_INPUT_SUM, ScResId( SCSTR_QHELP_BTNSUM ) );
SetItemText ( SID_INPUT_EQUAL, ScResId( SCSTR_QHELP_BTNEQUAL ) );
SetItemText ( SID_INPUT_CANCEL, ScResId( SCSTR_QHELP_BTNCANCEL ) );
SetItemText ( SID_INPUT_OK, ScResId( SCSTR_QHELP_BTNOK ) );
}
EnableItem( SID_INPUT_CANCEL, false );
EnableItem( SID_INPUT_OK, false );
HideItem( SID_INPUT_CANCEL );
HideItem( SID_INPUT_OK );
mnStandardItemHeight = GetItemRect(SID_INPUT_SUM).GetHeight();
}
SetHelpId( HID_SC_INPUTWIN ); // For the whole input row
if (!comphelper::LibreOfficeKit::isActive())
aWndPos ->Show();
mxTextWindow->Show();
pInputHdl = ScModule::get()->GetInputHdl( pViewSh, false ); // use own handler even if ref-handler is set
if (pInputHdl)
pInputHdl->SetInputWindow( this );
if (pInputHdl && !pInputHdl->GetFormString().isEmpty())
{
// Switch over while the Function AutoPilot is active
// -> show content of the Function AutoPilot again
// Also show selection (remember at the InputHdl)
mxTextWindow->SetTextString(pInputHdl->GetFormString(), true);
}
else if (pInputHdl && pInputHdl->IsInputMode())
{
// If the input row was hidden while editing (e.g. when editing a formula
// and then switching to another document or the help), display the text
// we just edited from the InputHandler
mxTextWindow->SetTextString(pInputHdl->GetEditString(), true); // Display text
if ( pInputHdl->IsTopMode() )
pInputHdl->SetMode( SC_INPUT_TABLE ); // Focus ends up at the bottom anyways
}
else if (pViewSh)
{
// Don't stop editing in LOK a remote user might be editing.
const bool bStopEditing = !comphelper::LibreOfficeKit::isActive();
pViewSh->UpdateInputHandler(true, bStopEditing); // Absolutely necessary update
}
SetToolbarLayoutMode( ToolBoxLayoutMode::Locked );
SetAccessibleName(ScResId(STR_ACC_TOOLBAR_FORMULA));
}
ScInputWindow::~ScInputWindow()
{
disposeOnce();
}
void ScInputWindow::dispose()
{
bool bDown = !ScGlobal::oSysLocale; // after Clear?
// if any view's input handler has a pointer to this input window, reset it
// (may be several ones, #74522#)
// member pInputHdl is not used here
if ( !bDown )
{
SfxViewShell* pSh = SfxViewShell::GetFirst( true, checkSfxViewShell<ScTabViewShell> );
while ( pSh )
{
ScInputHandler* pHdl = static_cast<ScTabViewShell*>(pSh)->GetInputHandler();
if ( pHdl && pHdl->GetInputWindow() == this )
{
pHdl->SetInputWindow( nullptr );
pHdl->StopInputWinEngine( false ); // reset pTopView pointer
}
pSh = SfxViewShell::GetNext( *pSh, true, checkSfxViewShell<ScTabViewShell> );
}
}
if (comphelper::LibreOfficeKit::isActive())
{
if (GetLOKNotifier())
ReleaseLOKNotifier();
}
mxTextWindow.disposeAndClear();
aWndPos.disposeAndClear();
ToolBox::dispose();
}
void ScInputWindow::SetInputHandler( ScInputHandler* pNew )
{
// Is called in the Activate of the View ...
if ( pNew != pInputHdl )
{
// On Reload (last version) the pInputHdl is the InputHandler of the old, deleted
// ViewShell: so don't touch it here!
pInputHdl = pNew;
if (pInputHdl)
pInputHdl->SetInputWindow( this );
}
}
void ScInputWindow::Select()
{
ScModule* pScMod = ScModule::get();
ToolBox::Select();
ToolBoxItemId curItemId = GetCurItemId();
if (curItemId == SID_INPUT_FUNCTION)
{
//! new method at ScModule to query if function autopilot is open
SfxViewFrame* pViewFrm = SfxViewFrame::Current();
if ( pViewFrm && ( comphelper::LibreOfficeKit::isActive() || !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) ) )
{
pViewFrm->GetDispatcher()->Execute( SID_OPENDLG_FUNCTION,
SfxCallMode::SYNCHRON | SfxCallMode::RECORD );
// The Toolbox will be disabled anyways, so we don't need to switch here,
// regardless whether it succeeded or not!
// SetOkCancelMode();
}
}
else if (curItemId == SID_INPUT_CANCEL)
{
pScMod->InputCancelHandler();
SetSumAssignMode();
}
else if (curItemId == SID_INPUT_OK)
{
pScMod->InputEnterHandler();
SetSumAssignMode();
mxTextWindow->Invalidate(); // Or else the Selection remains
}
else if (curItemId == SID_INPUT_SUM)
{
bool bRangeFinder = false;
bool bSubTotal = false;
AutoSum(bRangeFinder, bSubTotal, ocSum);
}
else if (curItemId == SID_INPUT_EQUAL)
{
StartFormula();
}
}
void ScInputWindow::StartFormula()
{
ScModule* pScMod = ScModule::get();
mxTextWindow->StartEditEngine(ScInputHandler::ErrorMessage);
if ( pScMod->IsEditMode() ) // Isn't if e.g. protected
{
mxTextWindow->StartEditEngine(ScInputHandler::ErrorMessage);
sal_Int32 nStartPos = 1;
sal_Int32 nEndPos = 1;
ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
if ( pViewSh )
{
const OUString& rString = mxTextWindow->GetTextString();
const sal_Int32 nLen = rString.getLength();
ScDocument& rDoc = pViewSh->GetViewData().GetDocument();
CellType eCellType = rDoc.GetCellType( pViewSh->GetViewData().GetCurPos() );
switch ( eCellType )
{
case CELLTYPE_VALUE:
{
nEndPos = nLen + 1;
mxTextWindow->SetTextString("=" + rString, true);
break;
}
case CELLTYPE_STRING:
case CELLTYPE_EDIT:
nStartPos = 0;
nEndPos = nLen;
break;
case CELLTYPE_FORMULA:
nEndPos = nLen;
break;
default:
mxTextWindow->SetTextString(u"="_ustr, true);
break;
}
}
EditView* pView = mxTextWindow->GetEditView();
if (pView)
{
sal_Int32 nStartPara = 0, nEndPara = 0;
if (comphelper::LibreOfficeKit::isActive())
{
TextGrabFocus();
}
pView->SetSelection(ESelection(nStartPara, nStartPos, nEndPara, nEndPos));
pScMod->InputChanged(pView);
SetOkCancelMode();
pView->SetEditEngineUpdateLayout(true);
}
}
}
void ScInputWindow::PixelInvalidate(const tools::Rectangle* pRectangle)
{
if (comphelper::LibreOfficeKit::isDialogPainting() || !comphelper::LibreOfficeKit::isActive())
return;
if (pRectangle)
{
tools::Rectangle aRect(*pRectangle);
aRect.Move(-GetDeviceOriginX(), -GetDeviceOriginY());
Window::PixelInvalidate(&aRect);
}
else
{
Window::PixelInvalidate(nullptr);
}
}
void ScInputWindow::SetSizePixel( const Size& rNewSize )
{
const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier();
if (pNotifier)
{
if (vcl::Window* pFrameWindowImpl = GetParent())
{
if (vcl::Window* pWorkWindow = pFrameWindowImpl->GetParent())
{
if (vcl::Window* pImplBorderWindow = pWorkWindow->GetParent())
{
Size aSize = pImplBorderWindow->GetSizePixel();
aSize.setWidth(rNewSize.getWidth());
pImplBorderWindow->SetSizePixel(aSize);
}
}
}
}
ToolBox::SetSizePixel(rNewSize);
}
void ScInputWindow::Resize()
{
ToolBox::Resize();
Size aStartSize = GetSizePixel();
Size aSize = aStartSize;
auto nLines = mxTextWindow->GetNumLines();
//(-10) to allow margin between sidebar and formulabar
tools::Long margin = (comphelper::LibreOfficeKit::isActive()) ? 10 : 0;
Size aTextWindowSize(aSize.Width() - mxTextWindow->GetPosPixel().X() - LEFT_OFFSET - margin,
mxTextWindow->GetPixelHeightForLines(nLines));
mxTextWindow->SetSizePixel(aTextWindowSize);
int nTopOffset = 0;
if (nLines > 1)
{
// Initially there is 1 line and the edit is vertically centered in the toolbar
// Later, if expanded then the vertical position of the edit will remain at
// that initial position, so when calculating the overall size of the expanded
// toolbar we have to include that initial offset in order to not make
// the edit overlap the RESIZE_HOTSPOT_HEIGHT area so that dragging to resize
// is still possible.
int nNormalHeight = mxTextWindow->GetPixelHeightForLines(1);
int nInitialTopMargin = (mnStandardItemHeight - nNormalHeight) / 2;
if (nInitialTopMargin > 0)
nTopOffset = nInitialTopMargin;
}
// add empty space of RESIZE_HOTSPOT_HEIGHT so resize is possible when hovering there
aSize.setHeight(CalcWindowSizePixel().Height() + RESIZE_HOTSPOT_HEIGHT + nTopOffset);
if (aStartSize != aSize)
SetSizePixel(aSize);
Invalidate();
}
void ScInputWindow::NotifyLOKClient()
{
if (comphelper::LibreOfficeKit::isActive() && !GetLOKNotifier() && mpViewShell)
SetLOKNotifier(mpViewShell);
}
void ScInputWindow::SetFuncString( const OUString& rString, bool bDoEdit )
{
//! new method at ScModule to query if function autopilot is open
SfxViewFrame* pViewFrm = SfxViewFrame::Current();
EnableButtons( pViewFrm && !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) );
mxTextWindow->StartEditEngine(ScInputHandler::ErrorMessage);
ScModule* pScMod = ScModule::get();
if ( !pScMod->IsEditMode() )
return;
if ( bDoEdit )
mxTextWindow->TextGrabFocus();
mxTextWindow->SetTextString(rString, true);
EditView* pView = mxTextWindow->GetEditView();
if (!pView)
return;
sal_Int32 nLen = rString.getLength();
if ( nLen > 0 )
{
nLen--;
pView->SetSelection(ESelection(0, nLen));
}
pScMod->InputChanged(pView);
if ( bDoEdit )
SetOkCancelMode(); // Not the case if immediately followed by Enter/Cancel
pView->SetEditEngineUpdateLayout(true);
}
void ScInputWindow::SetPosString( const OUString& rStr )
{
aWndPos->SetPos( rStr );
}
void ScInputWindow::SetTextString( const OUString& rString, bool bKitUpdate )
{
if (rString.getLength() <= 32767)
mxTextWindow->SetTextString(rString, bKitUpdate);
else
mxTextWindow->SetTextString(rString.copy(0, 32767), bKitUpdate);
}
void ScInputWindow::SetOkCancelMode()
{
//! new method at ScModule to query if function autopilot is open
SfxViewFrame* pViewFrm = SfxViewFrame::Current();
EnableButtons( pViewFrm && !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) );
if (bIsOkCancelMode)
return;
EnableItem ( SID_INPUT_SUM, false );
EnableItem ( SID_INPUT_EQUAL, false );
HideItem ( SID_INPUT_SUM );
HideItem ( SID_INPUT_EQUAL );
ShowItem ( SID_INPUT_CANCEL, true );
ShowItem ( SID_INPUT_OK, true );
EnableItem ( SID_INPUT_CANCEL, true );
EnableItem ( SID_INPUT_OK, true );
bIsOkCancelMode = true;
}
void ScInputWindow::SetSumAssignMode()
{
//! new method at ScModule to query if function autopilot is open
SfxViewFrame* pViewFrm = SfxViewFrame::Current();
EnableButtons( pViewFrm && !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) );
if (!bIsOkCancelMode)
return;
EnableItem ( SID_INPUT_CANCEL, false );
EnableItem ( SID_INPUT_OK, false );
HideItem ( SID_INPUT_CANCEL );
HideItem ( SID_INPUT_OK );
ShowItem ( SID_INPUT_SUM, true );
ShowItem ( SID_INPUT_EQUAL, true );
EnableItem ( SID_INPUT_SUM, true );
EnableItem ( SID_INPUT_EQUAL, true );
bIsOkCancelMode = false;
SetFormulaMode(false); // No editing -> no formula
}
void ScInputWindow::SetFormulaMode( bool bSet )
{
if (!comphelper::LibreOfficeKit::isActive())
aWndPos->SetFormulaMode(bSet);
mxTextWindow->SetFormulaMode(bSet);
}
bool ScInputWindow::IsInputActive()
{
return mxTextWindow->IsInputActive();
}
EditView* ScInputWindow::GetEditView()
{
return mxTextWindow->GetEditView();
}
vcl::Window* ScInputWindow::GetEditWindow()
{
return mxTextWindow;
}
Point ScInputWindow::GetCursorScreenPixelPos(bool bBelow)
{
return mxTextWindow->GetCursorScreenPixelPos(bBelow);
}
void ScInputWindow::MakeDialogEditView()
{
mxTextWindow->MakeDialogEditView();
}
void ScInputWindow::StopEditEngine( bool bAll )
{
mxTextWindow->StopEditEngine( bAll );
}
void ScInputWindow::TextGrabFocus()
{
mxTextWindow->TextGrabFocus();
}
void ScInputWindow::TextInvalidate()
{
mxTextWindow->Invalidate();
}
void ScInputWindow::SwitchToTextWin()
{
// used for shift-ctrl-F2
mxTextWindow->StartEditEngine(ScInputHandler::ErrorMessage);
if (ScModule::get()->IsEditMode())
{
mxTextWindow->TextGrabFocus();
EditView* pView = mxTextWindow->GetEditView();
if (pView)
{
pView->SetSelection(ESelection::AtEnd()); // set cursor to end of text
}
}
}
void ScInputWindow::PosGrabFocus()
{
if (!comphelper::LibreOfficeKit::isActive())
aWndPos->GrabFocus();
}
void ScInputWindow::EnableButtons( bool bEnable )
{
// when enabling buttons, always also enable the input window itself
if ( bEnable && !IsEnabled() )
Enable();
EnableItem( SID_INPUT_FUNCTION, bEnable );
EnableItem( bIsOkCancelMode ? SID_INPUT_CANCEL : SID_INPUT_SUM, bEnable );
EnableItem( bIsOkCancelMode ? SID_INPUT_OK : SID_INPUT_EQUAL, bEnable );
// Invalidate();
}
void ScInputWindow::NumLinesChanged()
{
mxTextWindow->NumLinesChanged();
}
void ScInputWindow::StateChanged( StateChangedType nType )
{
ToolBox::StateChanged( nType );
if ( nType == StateChangedType::InitShow ) Resize();
}
void ScInputWindow::DataChanged( const DataChangedEvent& rDCEvt )
{
if ( rDCEvt.GetType() == DataChangedEventType::SETTINGS && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
{
// update item images
SetItemImage(SID_INPUT_FUNCTION, Image(StockImage::Yes, RID_BMP_INPUT_FUNCTION));
if ( bIsOkCancelMode )
{
SetItemImage(SID_INPUT_CANCEL, Image(StockImage::Yes, RID_BMP_INPUT_CANCEL));
SetItemImage(SID_INPUT_OK, Image(StockImage::Yes, RID_BMP_INPUT_OK));
}
else
{
SetItemImage(SID_INPUT_SUM, Image(StockImage::Yes, RID_BMP_INPUT_SUM));
SetItemImage(SID_INPUT_EQUAL, Image(StockImage::Yes, RID_BMP_INPUT_EQUAL));
}
}
ToolBox::DataChanged( rDCEvt );
}
bool ScInputWindow::IsPointerAtResizePos()
{
return GetOutputSizePixel().Height() - GetPointerPosPixel().Y() <= RESIZE_HOTSPOT_HEIGHT;
}
void ScInputWindow::MouseMove( const MouseEvent& rMEvt )
{
Point aPosPixel = GetPointerPosPixel();
ScInputBarGroup* pGroupBar = mxTextWindow.get();
if (bInResize || IsPointerAtResizePos())
SetPointer(PointerStyle::WindowSSize);
else
SetPointer(PointerStyle::Arrow);
if (bInResize)
{
// detect direction
tools::Long nResizeThreshold = tools::Long(TOOLBOX_WINDOW_HEIGHT * 0.7);
bool bResetPointerPos = false;
// Detect attempt to expand toolbar too much
if (aPosPixel.Y() >= mnMaxY)
{
bResetPointerPos = true;
aPosPixel.setY( mnMaxY );
} // or expanding down
else if (GetOutputSizePixel().Height() - aPosPixel.Y() < -nResizeThreshold)
{
pGroupBar->IncrementVerticalSize();
bResetPointerPos = true;
} // or shrinking up
else if ((GetOutputSizePixel().Height() - aPosPixel.Y()) > nResizeThreshold)
{
bResetPointerPos = true;
pGroupBar->DecrementVerticalSize();
}
if (bResetPointerPos)
{
aPosPixel.setY( GetOutputSizePixel().Height() );
SetPointerPosPixel(aPosPixel);
}
}
ToolBox::MouseMove(rMEvt);
}
void ScInputWindow::MouseButtonDown( const MouseEvent& rMEvt )
{
if (rMEvt.IsLeft())
{
if (IsPointerAtResizePos())
{
// Don't leave the mouse pointer leave *this* window
CaptureMouse();
bInResize = true;
// find the height of the gridwin, we don't want to be
// able to expand the toolbar too far so we need to
// calculate an upper limit
// I'd prefer to leave at least a single column header and a
// row but I don't know how to get that value in pixels.
// Use TOOLBOX_WINDOW_HEIGHT for the moment
if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
{
mnMaxY = GetOutputSizePixel().Height() + (pViewSh->GetGridHeight(SC_SPLIT_TOP)
+ pViewSh->GetGridHeight(SC_SPLIT_BOTTOM)) - TOOLBOX_WINDOW_HEIGHT;
}
}
}
ToolBox::MouseButtonDown( rMEvt );
}
void ScInputWindow::MouseButtonUp( const MouseEvent& rMEvt )
{
ReleaseMouse();
if ( rMEvt.IsLeft() )
{
bInResize = false;
mnMaxY = 0;
}
ToolBox::MouseButtonUp( rMEvt );
}
void ScInputWindow::AutoSum( bool& bRangeFinder, bool& bSubTotal, OpCode eCode )
{
ScModule* pScMod = ScModule::get();
ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
if ( !pViewSh )
return;
const OUString aFormula = pViewSh->DoAutoSum(bRangeFinder, bSubTotal, eCode);
if ( aFormula.isEmpty() )
return;
SetFuncString( aFormula );
const sal_Int32 aOpen = aFormula.indexOf('(');
const sal_Int32 aLen = aFormula.getLength();
if (!(bRangeFinder && pScMod->IsEditMode()))
return;
ScInputHandler* pHdl = pScMod->GetInputHdl( pViewSh );
if ( !pHdl )
return;
pHdl->InitRangeFinder( aFormula );
//! SetSelection at the InputHandler?
//! Set bSelIsRef?
if ( aOpen != -1 && aLen > aOpen )
{
ESelection aSel( 0, aOpen + (bSubTotal ? 3 : 1), 0, aLen-1 );
EditView* pTableView = pHdl->GetTableView();
if ( pTableView )
pTableView->SetSelection( aSel );
EditView* pTopView = pHdl->GetTopView();
if ( pTopView )
pTopView->SetSelection( aSel );
}
}
ScInputBarGroup::ScInputBarGroup(vcl::Window* pParent, ScTabViewShell* pViewSh)
: InterimItemWindow(pParent, u"modules/scalc/ui/inputbar.ui"_ustr, u"InputBar"_ustr, true)
, mxBackground(m_xBuilder->weld_container(u"background"_ustr))
, mxTextWndGroup(new ScTextWndGroup(*this, pViewSh))
, mxButtonUp(m_xBuilder->weld_button(u"up"_ustr))
, mxButtonDown(m_xBuilder->weld_button(u"down"_ustr))
{
InitControlBase(m_xContainer.get());
SetPaintTransparent(false);
SetBackgrounds();
mxButtonUp->connect_clicked(LINK(this, ScInputBarGroup, ClickHdl));
mxButtonDown->connect_clicked(LINK(this, ScInputBarGroup, ClickHdl));
if (!comphelper::LibreOfficeKit::isActive())
{
mxButtonUp->set_tooltip_text(ScResId( SCSTR_QHELP_COLLAPSE_FORMULA));
mxButtonDown->set_tooltip_text(ScResId(SCSTR_QHELP_EXPAND_FORMULA));
}
int nHeight = mxTextWndGroup->GetPixelHeightForLines(1);
mxButtonUp->set_size_request(-1, nHeight);
mxButtonDown->set_size_request(-1, nHeight);
mxButtonDown->show();
// tdf#154042 Use an initial height of one row so the Toolbar positions
// this in the same place regardless of how many rows it eventually shows
Size aSize(GetSizePixel().Width(), nHeight);
SetSizePixel(aSize);
}
void ScInputBarGroup::SetBackgrounds()
{
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
SetBackground(rStyleSettings.GetFaceColor());
// match to bg used in ScTextWnd::SetDrawingArea to the margin area is drawn with the
// same desired bg
mxBackground->set_background(rStyleSettings.GetFieldColor());
}
void ScInputBarGroup::DataChanged(const DataChangedEvent& rDCEvt)
{
InterimItemWindow::DataChanged(rDCEvt);
if ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
{
SetBackgrounds();
Invalidate();
}
}
Point ScInputBarGroup::GetCursorScreenPixelPos(bool bBelow)
{
return mxTextWndGroup->GetCursorScreenPixelPos(bBelow);
}
ScInputBarGroup::~ScInputBarGroup()
{
disposeOnce();
}
void ScInputBarGroup::dispose()
{
mxTextWndGroup.reset();
mxButtonUp.reset();
mxButtonDown.reset();
mxBackground.reset();
InterimItemWindow::dispose();
}
void ScInputBarGroup::InsertAccessibleTextData( ScAccessibleEditLineTextData& rTextData )
{
mxTextWndGroup->InsertAccessibleTextData(rTextData);
}
void ScInputBarGroup::RemoveAccessibleTextData( ScAccessibleEditLineTextData& rTextData )
{
mxTextWndGroup->RemoveAccessibleTextData(rTextData);
}
const OUString& ScInputBarGroup::GetTextString() const
{
return mxTextWndGroup->GetTextString();
}
void ScInputBarGroup::SetTextString(const OUString& rString, bool bKitUpdate)
{
mxTextWndGroup->SetTextString(rString, bKitUpdate);
}
void ScInputBarGroup::Resize()
{
mxTextWndGroup->SetScrollPolicy();
InterimItemWindow::Resize();
}
void ScInputBarGroup::StopEditEngine(bool bAll)
{
mxTextWndGroup->StopEditEngine(bAll);
}
void ScInputBarGroup::StartEditEngine(const ErrorHdl& errorHdl)
{
mxTextWndGroup->StartEditEngine(errorHdl);
}
void ScInputBarGroup::MakeDialogEditView()
{
mxTextWndGroup->MakeDialogEditView();
}
EditView* ScInputBarGroup::GetEditView() const
{
return mxTextWndGroup->GetEditView();
}
bool ScInputBarGroup::HasEditView() const
{
return mxTextWndGroup->HasEditView();
}
bool ScInputBarGroup::IsInputActive()
{
return mxTextWndGroup->IsInputActive();
}
void ScInputBarGroup::SetFormulaMode(bool bSet)
{
mxTextWndGroup->SetFormulaMode(bSet);
}
void ScInputBarGroup::IncrementVerticalSize()
{
mxTextWndGroup->SetNumLines(mxTextWndGroup->GetNumLines() + 1);
TriggerToolboxLayout();
}
void ScInputBarGroup::DecrementVerticalSize()
{
if (mxTextWndGroup->GetNumLines() > 1)
{
mxTextWndGroup->SetNumLines(mxTextWndGroup->GetNumLines() - 1);
TriggerToolboxLayout();
}
}
void ScInputWindow::MenuHdl(std::u16string_view command)
{
if (command.empty())
return;
bool bSubTotal = false;
bool bRangeFinder = false;
OpCode eCode = ocSum;
if ( command == u"sum" )
{
eCode = ocSum;
}
else if ( command == u"average" )
{
eCode = ocAverage;
}
else if ( command == u"max" )
{
eCode = ocMax;
}
else if ( command == u"min" )
{
eCode = ocMin;
}
else if ( command == u"count" )
{
eCode = ocCount;
}
else if ( command == u"counta" )
{
eCode = ocCount2;
}
else if ( command == u"product" )
{
eCode = ocProduct;
}
else if (command == u"stdev")
{
eCode = ocStDev;
}
else if (command == u"stdevp")
{
eCode = ocStDevP;
}
else if (command == u"var")
{
eCode = ocVar;
}
else if (command == u"varp")
{
eCode = ocVarP;
}
AutoSum( bRangeFinder, bSubTotal, eCode );
}
IMPL_LINK_NOARG(ScInputWindow, DropdownClickHdl, ToolBox *, void)
{
ToolBoxItemId nCurID = GetCurItemId();
EndSelection();
if (nCurID == SID_INPUT_SUM)
{
tools::Rectangle aRect(GetItemRect(SID_INPUT_SUM));
weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, u"modules/scalc/ui/autosum.ui"_ustr));
std::unique_ptr<weld::Menu> xPopMenu(xBuilder->weld_menu(u"menu"_ustr));
MenuHdl(xPopMenu->popup_at_rect(pPopupParent, aRect));
}
}
IMPL_LINK_NOARG(ScInputBarGroup, ClickHdl, weld::Button&, void)
{
if (mxTextWndGroup->GetNumLines() > 1)
mxTextWndGroup->SetNumLines(1);
else
mxTextWndGroup->SetNumLines(mxTextWndGroup->GetLastNumExpandedLines());
NumLinesChanged();
}
void ScInputBarGroup::NumLinesChanged()
{
if (mxTextWndGroup->GetNumLines() > 1)
{
mxButtonDown->hide();
mxButtonUp->show();
mxTextWndGroup->SetLastNumExpandedLines(mxTextWndGroup->GetNumLines());
}
else
{
mxButtonUp->hide();
mxButtonDown->show();
}
TriggerToolboxLayout();
// Restore focus to input line(s) if necessary
ScInputHandler* pHdl = ScModule::get()->GetInputHdl();
if ( pHdl && pHdl->IsTopMode() )
mxTextWndGroup->TextGrabFocus();
}
void ScInputBarGroup::TriggerToolboxLayout()
{
// layout changes are expensive and un-necessary.
if (comphelper::LibreOfficeKit::isActive())
return;
vcl::Window *w=GetParent();
ScInputWindow &rParent = dynamic_cast<ScInputWindow&>(*w);
SfxViewFrame* pViewFrm = SfxViewFrame::Current();
if ( !pViewFrm )
return;
Reference< css::beans::XPropertySet > xPropSet( pViewFrm->GetFrame().GetFrameInterface(), UNO_QUERY );
Reference< css::frame::XLayoutManager > xLayoutManager;
if ( xPropSet.is() )
{
css::uno::Any aValue = xPropSet->getPropertyValue(u"LayoutManager"_ustr);
aValue >>= xLayoutManager;
}
if ( !xLayoutManager.is() )
return;
xLayoutManager->lock();
DataChangedEvent aFakeUpdate( DataChangedEventType::SETTINGS, nullptr, AllSettingsFlags::STYLE );
// this basically will trigger the repositioning of the
// items in the toolbar from ImplFormat ( which is controlled by
// mnWinHeight ) which in turn is updated in ImplCalcItem which is
// controlled by mbCalc. Additionally the ImplFormat above is
// controlled via mbFormat. It seems the easiest way to get these
// booleans set is to send in the fake event below.
rParent.DataChanged( aFakeUpdate);
// highest item in toolbar will have been calculated via the
// event above. Call resize on InputBar to pick up the height
// change
rParent.Resize();
// unlock relayouts the toolbars in the 4 quadrants
xLayoutManager->unlock();
}
void ScInputBarGroup::TextGrabFocus()
{
mxTextWndGroup->TextGrabFocus();
}
constexpr tools::Long gnBorderWidth = (INPUTLINE_INSET_MARGIN + 1) * 2;
constexpr tools::Long gnBorderHeight = INPUTLINE_INSET_MARGIN + 1;
ScTextWndGroup::ScTextWndGroup(ScInputBarGroup& rParent, ScTabViewShell* pViewSh)
: mxTextWnd(new ScTextWnd(*this, pViewSh))
, mxScrollWin(rParent.GetBuilder().weld_scrolled_window(u"scrolledwindow"_ustr, true))
, mxTextWndWin(new weld::CustomWeld(rParent.GetBuilder(), u"sc_input_window"_ustr, *mxTextWnd))
, mrParent(rParent)
{
mxScrollWin->connect_vadjustment_value_changed(LINK(this, ScTextWndGroup, Impl_ScrollHdl));
if (ScTabViewShell* pActiveViewShell = comphelper::LibreOfficeKit::isActive() ?
dynamic_cast<ScTabViewShell*>(SfxViewShell::Current()) : nullptr)
{
pActiveViewShell->LOKSendFormulabarUpdate(nullptr, u""_ustr, ESelection());
}
}
Point ScTextWndGroup::GetCursorScreenPixelPos(bool bBelow)
{
Point aPos;
if (!HasEditView())
return aPos;
EditView* pEditView = GetEditView();
vcl::Cursor* pCur = pEditView->GetCursor();
if (!pCur)
return aPos;
Point aLogicPos = pCur->GetPos();
if (bBelow)
aLogicPos.AdjustY(pCur->GetHeight());
aPos = GetEditViewDevice().LogicToPixel(aLogicPos);
bool bRTL = mrParent.IsRTLEnabled();
if (bRTL)
aPos.setX(mxTextWnd->GetOutputSizePixel().Width() - aPos.X() + gnBorderWidth);
else
aPos.AdjustX(gnBorderWidth + 1);
return mrParent.OutputToScreenPixel(aPos);
}
ScTextWndGroup::~ScTextWndGroup()
{
}
void ScTextWndGroup::InsertAccessibleTextData(ScAccessibleEditLineTextData& rTextData)
{
mxTextWnd->InsertAccessibleTextData(rTextData);
}
EditView* ScTextWndGroup::GetEditView() const
{
return mxTextWnd->GetEditView();
}
const OutputDevice& ScTextWndGroup::GetEditViewDevice() const
{
return mxTextWnd->GetEditViewDevice();
}
tools::Long ScTextWndGroup::GetLastNumExpandedLines() const
{
return mxTextWnd->GetLastNumExpandedLines();
}
void ScTextWndGroup::SetLastNumExpandedLines(tools::Long nLastExpandedLines)
{
mxTextWnd->SetLastNumExpandedLines(nLastExpandedLines);
}
tools::Long ScTextWndGroup::GetNumLines() const
{
return mxTextWnd->GetNumLines();
}
int ScTextWndGroup::GetPixelHeightForLines(tools::Long nLines)
{
return mxTextWnd->GetPixelHeightForLines(nLines) + 2 * gnBorderHeight;
}
weld::ScrolledWindow& ScTextWndGroup::GetScrollWin()
{
return *mxScrollWin;
}
const OUString& ScTextWndGroup::GetTextString() const
{
return mxTextWnd->GetTextString();
}
bool ScTextWndGroup::HasEditView() const
{
return mxTextWnd->HasEditView();
}
bool ScTextWndGroup::IsInputActive()
{
return mxTextWnd->IsInputActive();
}
void ScTextWndGroup::MakeDialogEditView()
{
mxTextWnd->MakeDialogEditView();
}
void ScTextWndGroup::RemoveAccessibleTextData(ScAccessibleEditLineTextData& rTextData)
{
mxTextWnd->RemoveAccessibleTextData(rTextData);
}
void ScTextWndGroup::SetScrollPolicy()
{
if (mxTextWnd->GetNumLines() > 2)
mxScrollWin->set_vpolicy(VclPolicyType::ALWAYS);
else
mxScrollWin->set_vpolicy(VclPolicyType::NEVER);
}
void ScTextWndGroup::SetNumLines(tools::Long nLines)
{
mxTextWnd->SetNumLines(nLines);
}
void ScTextWndGroup::SetFormulaMode(bool bSet)
{
mxTextWnd->SetFormulaMode(bSet);
}
void ScTextWndGroup::SetTextString(const OUString& rString, bool bKitUpdate)
{
mxTextWnd->SetTextString(rString, bKitUpdate);
}
void ScTextWndGroup::StartEditEngine(const ErrorHdl& errorHdl)
{
mxTextWnd->StartEditEngine(errorHdl);
}
void ScTextWndGroup::StopEditEngine(bool bAll)
{
mxTextWnd->StopEditEngine( bAll );
}
void ScTextWndGroup::TextGrabFocus()
{
mxTextWnd->TextGrabFocus();
}
IMPL_LINK_NOARG(ScTextWndGroup, Impl_ScrollHdl, weld::ScrolledWindow&, void)
{
mxTextWnd->DoScroll();
}
void ScTextWnd::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect )
{
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
Color aBgColor = rStyleSettings.GetFieldColor();
rRenderContext.SetBackground(aBgColor);
// tdf#137713 we rely on GetEditView creating it if it doesn't already exist so
// GetEditView() must be called unconditionally
if (EditView* pView = GetEditView())
{
if (mbInvalidate)
{
pView->Invalidate();
mbInvalidate = false;
}
}
if (m_xEditEngine)
{
// tdf#97354 - use disabled text color while a modeless ref dialog owns reference input
Color aTxtColor = ScModule::get()->IsRefDialogOpen() ? rStyleSettings.GetDisableColor()
: rStyleSettings.GetWindowTextColor();
if (aTextFont.GetColor() != aTxtColor)
{
aTextFont.SetColor(aTxtColor);
SfxItemSet aColorSet(m_xEditEngine->GetEmptyItemSet());
aColorSet.Put(SvxColorItem(aTxtColor, EE_CHAR_COLOR));
const bool bPrevInputMode = bInputMode;
bInputMode = true;
m_xEditEngine->QuickSetAttribs(aColorSet, ESelection::All());
bInputMode = bPrevInputMode;
}
}
if (comphelper::LibreOfficeKit::isActive() && m_xEditEngine)
{
// EditEngine/EditView works in twips logical coordinates, so set the device map-mode to twips before painting
// and use twips version of the painting area 'rRect'.
// Document zoom should not be included in this conversion.
tools::Rectangle aLogicRect = OutputDevice::LogicToLogic(rRect, MapMode(MapUnit::MapPixel), MapMode(MapUnit::MapTwip));
MapMode aOriginalMode = rRenderContext.GetMapMode();
rRenderContext.SetMapMode(MapMode(MapUnit::MapTwip));
WeldEditView::Paint(rRenderContext, aLogicRect);
rRenderContext.SetMapMode(aOriginalMode);
}
else
WeldEditView::Paint(rRenderContext, rRect);
}
EditView* ScTextWnd::GetEditView() const
{
if ( !m_xEditView )
const_cast<ScTextWnd&>(*this).InitEditEngine();
return m_xEditView.get();
}
bool ScTextWnd::HasEditView() const { return m_xEditView != nullptr; }
const OutputDevice& ScTextWnd::GetEditViewDevice() const
{
return EditViewOutputDevice();
}
int ScTextWnd::GetPixelHeightForLines(tools::Long nLines)
{
OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
return rDevice.LogicToPixel(Size(0, nLines * rDevice.GetTextHeight())).Height() + 1;
}
tools::Long ScTextWnd::GetNumLines() const
{
ScViewData& rViewData = mpViewShell->GetViewData();
return rViewData.GetFormulaBarLines();
}
void ScTextWnd::SetNumLines(tools::Long nLines)
{
ScViewData& rViewData = mpViewShell->GetViewData();
rViewData.SetFormulaBarLines(nLines);
if ( nLines > 1 )
{
// SetFormulaBarLines sanitizes the height, so get the sanitized value
mnLastExpandedLines = rViewData.GetFormulaBarLines();
Resize();
}
}
void ScTextWnd::Resize()
{
if (m_xEditView)
{
Size aOutputSize = GetOutputSizePixel();
OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
tools::Rectangle aOutputArea = rDevice.PixelToLogic( tools::Rectangle( Point(), aOutputSize ));
m_xEditView->SetOutputArea( aOutputArea );
// Don't leave an empty area at the bottom if we can move the text down.
tools::Long nMaxVisAreaTop = m_xEditEngine->GetTextHeight() - aOutputArea.GetHeight();
if (m_xEditView->GetVisArea().Top() > nMaxVisAreaTop)
{
m_xEditView->Scroll(0, m_xEditView->GetVisArea().Top() - nMaxVisAreaTop);
}
m_xEditEngine->SetPaperSize( rDevice.PixelToLogic( Size( aOutputSize.Width(), 10000 ) ) );
}
// skip WeldEditView's Resize();
weld::CustomWidgetController::Resize();
SetScrollBarRange();
}
int ScTextWnd::GetEditEngTxtHeight() const
{
return m_xEditView ? m_xEditView->getEditEngine().GetTextHeight() : 0;
}
void ScTextWnd::SetScrollBarRange()
{
if (!m_xEditView)
return;
OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
Size aOutputSize = rDevice.GetOutputSize();
int nUpper = GetEditEngTxtHeight();
int nCurrentDocPos = m_xEditView->GetVisArea().Top();
int nStepIncrement = GetTextHeight();
int nPageIncrement = aOutputSize.Height();
int nPageSize = aOutputSize.Height();
/* limit the page size to below nUpper because gtk's gtk_scrolled_window_start_deceleration has
effectively...
lower = gtk_adjustment_get_lower
upper = gtk_adjustment_get_upper - gtk_adjustment_get_page_size
and requires that upper > lower or the deceleration animation never ends
*/
nPageSize = std::min(nPageSize, nUpper);
weld::ScrolledWindow& rVBar = mrGroupBar.GetScrollWin();
rVBar.vadjustment_configure(nCurrentDocPos, nUpper, nStepIncrement, nPageIncrement, nPageSize);
}
void ScTextWnd::DoScroll()
{
if (m_xEditView)
{
weld::ScrolledWindow& rVBar = mrGroupBar.GetScrollWin();
auto currentDocPos = m_xEditView->GetVisArea().Top();
auto nDiff = currentDocPos - rVBar.vadjustment_get_value();
// we expect SetScrollBarRange callback to be triggered by Scroll
// to set where we ended up
m_xEditView->Scroll(0, nDiff);
}
}
void ScTextWnd::StartEditEngine(const ErrorHdl& errorHdl)
{
// Don't activate if we're a modal dialog ourselves (Doc-modal dialog)
SfxObjectShell* pObjSh = SfxObjectShell::Current();
if ( pObjSh && pObjSh->IsInModalMode() )
return;
if ( !m_xEditView || !m_xEditEngine )
{
InitEditEngine();
}
ScInputHandler* pHdl = mpViewShell->GetInputHandler();
if (pHdl)
{
pHdl->SetMode(SC_INPUT_TOP, nullptr, static_cast<ScEditEngineDefaulter*>(m_xEditEngine.get()),
errorHdl);
}
SfxViewFrame* pViewFrm = SfxViewFrame::Current();
if (pViewFrm)
pViewFrm->GetBindings().Invalidate( SID_ATTR_INSERT );
}
static void lcl_ExtendEditFontAttribs( SfxItemSet& rSet )
{
const SfxPoolItem& rFontItem = rSet.Get( EE_CHAR_FONTINFO );
std::unique_ptr<SfxPoolItem> pNewItem(rFontItem.Clone());
pNewItem->SetWhich(EE_CHAR_FONTINFO_CJK);
rSet.Put( *pNewItem );
pNewItem->SetWhich(EE_CHAR_FONTINFO_CTL);
rSet.Put( *pNewItem );
const SfxPoolItem& rHeightItem = rSet.Get( EE_CHAR_FONTHEIGHT );
pNewItem.reset(rHeightItem.Clone());
pNewItem->SetWhich(EE_CHAR_FONTHEIGHT_CJK);
rSet.Put( *pNewItem );
pNewItem->SetWhich(EE_CHAR_FONTHEIGHT_CTL);
rSet.Put( *pNewItem );
const SfxPoolItem& rWeightItem = rSet.Get( EE_CHAR_WEIGHT );
pNewItem.reset(rWeightItem.Clone());
pNewItem->SetWhich(EE_CHAR_WEIGHT_CJK);
rSet.Put( *pNewItem );
pNewItem->SetWhich(EE_CHAR_WEIGHT_CTL);
rSet.Put( *pNewItem );
const SfxPoolItem& rItalicItem = rSet.Get( EE_CHAR_ITALIC );
pNewItem.reset(rItalicItem.Clone());
pNewItem->SetWhich(EE_CHAR_ITALIC_CJK);
rSet.Put( *pNewItem );
pNewItem->SetWhich(EE_CHAR_ITALIC_CTL);
rSet.Put( *pNewItem );
const SfxPoolItem& rLangItem = rSet.Get( EE_CHAR_LANGUAGE );
pNewItem.reset(rLangItem.Clone());
pNewItem->SetWhich(EE_CHAR_LANGUAGE_CJK);
rSet.Put( *pNewItem );
pNewItem->SetWhich(EE_CHAR_LANGUAGE_CTL);
rSet.Put( *pNewItem );
}
static void lcl_ModifyRTLDefaults( SfxItemSet& rSet )
{
rSet.Put( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) );
// always using rtl writing direction would break formulas
//rSet.Put( SvxFrameDirectionItem( SvxFrameDirection::Horizontal_RL_TB, EE_PARA_WRITINGDIR ) );
// PaperSize width is limited to USHRT_MAX in RTL mode (because of EditEngine's
// sal_uInt16 values in EditLine), so the text may be wrapped and line spacing must be
// increased to not see the beginning of the next line.
SvxLineSpacingItem aItem( LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL );
aItem.SetPropLineSpace( 200 );
rSet.Put( aItem );
}
static void lcl_ModifyRTLVisArea( EditView* pEditView )
{
tools::Rectangle aVisArea = pEditView->GetVisArea();
Size aPaper = pEditView->getEditEngine().GetPaperSize();
tools::Long nDiff = aPaper.Width() - aVisArea.Right();
aVisArea.AdjustLeft(nDiff );
aVisArea.AdjustRight(nDiff );
pEditView->SetVisArea(aVisArea);
}
void ScTextWnd::InitEditEngine()
{
std::unique_ptr<ScFieldEditEngine> pNew;
ScDocShell* pDocSh = nullptr;
if ( mpViewShell )
{
pDocSh = mpViewShell->GetViewData().GetDocShell();
ScDocument& rDoc = mpViewShell->GetViewData().GetDocument();
pNew = std::make_unique<ScFieldEditEngine>(&rDoc, rDoc.GetEditEnginePool());
}
else
pNew = std::make_unique<ScFieldEditEngine>(nullptr, EditEngine::CreatePool().get(), true);
pNew->SetExecuteURL( false );
m_xEditEngine = std::move(pNew);
Size barSize = GetOutputSizePixel();
m_xEditEngine->SetUpdateLayout( false );
m_xEditEngine->SetPaperSize( GetDrawingArea()->get_ref_device().PixelToLogic(Size(barSize.Width(),10000)) );
m_xEditEngine->SetWordDelimiters(
ScEditUtil::ModifyDelimiters( m_xEditEngine->GetWordDelimiters() ) );
m_xEditEngine->SetReplaceLeadingSingleQuotationMark( false );
UpdateAutoCorrFlag();
{
SfxItemSet aSet( m_xEditEngine->GetEmptyItemSet() );
EditEngine::SetFontInfoInItemSet( aSet, aTextFont );
lcl_ExtendEditFontAttribs( aSet );
// turn off script spacing to match DrawText output
aSet.Put( SvxScriptSpaceItem( false, EE_PARA_ASIANCJKSPACING ) );
if ( bIsRTL )
lcl_ModifyRTLDefaults( aSet );
static_cast<ScEditEngineDefaulter*>(m_xEditEngine.get())->SetDefaults( std::move(aSet) );
}
// If the Cell contains URLFields, they need to be taken over into the entry row,
// or else the position is not correct anymore
bool bFilled = false;
ScInputHandler* pHdl = ScModule::get()->GetInputHdl();
if ( pHdl ) //! Test if it's the right InputHdl?
bFilled = pHdl->GetTextAndFields(static_cast<ScEditEngineDefaulter&>(*m_xEditEngine));
m_xEditEngine->SetUpdateLayout( true );
// aString is the truth ...
if (bFilled && m_xEditEngine->GetText() == aString)
Invalidate(); // Repaint for (filled) Field
else
static_cast<ScEditEngineDefaulter*>(m_xEditEngine.get())->SetTextCurrentDefaults(aString); // At least the right text then
m_xEditView = std::make_unique<EditView>(*m_xEditEngine, nullptr);
// we get cursor, selection etc. messages from the VCL/window layer
// otherwise these are injected into the document causing confusion.
m_xEditView->SuppressLOKMessages(true);
m_xEditView->setEditViewCallbacks(this);
m_xEditView->SetInsertMode(bIsInsertMode);
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
Color aBgColor = rStyleSettings.GetFieldColor();
m_xEditView->SetBackgroundColor(aBgColor);
if (pAcc)
{
pAcc->InitAcc(nullptr, m_xEditView.get(),
ScResId(STR_ACC_EDITLINE_NAME),
ScResId(STR_ACC_EDITLINE_DESCR));
}
if (comphelper::LibreOfficeKit::isActive())
m_xEditView->RegisterViewShell(mpViewShell);
// Text from Clipboard is taken over as ASCII in a single row
EVControlBits n = m_xEditView->GetControlWord();
m_xEditView->SetControlWord( n | EVControlBits::SINGLELINEPASTE );
m_xEditEngine->InsertView( m_xEditView.get(), EE_APPEND );
Resize();
if ( bIsRTL )
lcl_ModifyRTLVisArea( m_xEditView.get() );
m_xEditEngine->SetModifyHdl(LINK(this, ScTextWnd, ModifyHdl));
m_xEditEngine->SetStatusEventHdl(LINK(this, ScTextWnd, EditStatusHdl));
if (!maAccTextDatas.empty())
maAccTextDatas.back()->StartEdit();
// as long as EditEngine and DrawText sometimes differ for CTL text,
// repaint now to have the EditEngine's version visible
if (pDocSh)
{
ScDocument& rDoc = pDocSh->GetDocument(); // any document
SvtScriptType nScript = rDoc.GetStringScriptType( aString );
if ( nScript & SvtScriptType::COMPLEX )
Invalidate();
}
}
ScTextWnd::ScTextWnd(ScTextWndGroup& rParent, ScTabViewShell* pViewSh) :
bIsRTL(AllSettings::GetLayoutRTL()),
bIsInsertMode(true),
bFormulaMode (false),
bInputMode (false),
mpViewShell(pViewSh),
mrGroupBar(rParent),
mnLastExpandedLines(INPUTWIN_MULTILINES),
mbInvalidate(false)
{
}
ScTextWnd::~ScTextWnd()
{
while (!maAccTextDatas.empty()) {
maAccTextDatas.back()->Dispose();
}
}
bool ScTextWnd::MouseMove( const MouseEvent& rMEvt )
{
return m_xEditView && m_xEditView->MouseMove(rMEvt);
}
bool ScTextWnd::CanFocus() const
{
return ScModule::get()->IsEditMode();
}
void ScTextWnd::UpdateFocus(const ErrorHdl& errorHdl)
{
if (!HasFocus())
{
StartEditEngine(errorHdl);
if (CanFocus())
TextGrabFocus();
}
}
bool ScTextWnd::MouseButtonDown( const MouseEvent& rMEvt )
{
// tdf#97354 - prevent editable input line while a modeless ref dialog owns reference input
if (ScModule::get()->IsRefDialogOpen())
return false;
// tdf#169351 If trying to set focus to the formula bar triggers an error
// dialog, intercept the launch of that dialog to detect this case, and in
// that case abandon the mouse handler.
bool bCanceled(false);
auto errorHdl = [&bCanceled](ScTabViewShell* pActiveViewShell, TranslateId messageId) {
ScInputHandler::ErrorMessage(pActiveViewShell, messageId);
bCanceled = true;
};
UpdateFocus(errorHdl);
if (bCanceled)
return true;
bool bClickOnSelection = false;
if (m_xEditView)
{
m_xEditView->SetEditEngineUpdateLayout( true );
bClickOnSelection = m_xEditView->IsSelectionAtPoint(rMEvt.GetPosPixel());
}
if (!bClickOnSelection)
{
rtl::Reference<TransferDataContainer> xTransferable(new TransferDataContainer);
GetDrawingArea()->enable_drag_source(xTransferable, DND_ACTION_NONE);
}
else
{
rtl::Reference<TransferDataContainer> xTransferable(m_xHelper);
GetDrawingArea()->enable_drag_source(xTransferable, DND_ACTION_COPY);
}
return WeldEditView::MouseButtonDown(rMEvt);
}
bool ScTextWnd::MouseButtonUp( const MouseEvent& rMEvt )
{
bool bRet = WeldEditView::MouseButtonUp(rMEvt);
if (bRet)
{
if ( rMEvt.IsMiddle() &&
Application::GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection )
{
// EditView may have pasted from selection
ScModule::get()->InputChanged(m_xEditView.get());
}
else
ScModule::get()->InputSelection(m_xEditView.get());
}
return bRet;
}
bool ScTextWnd::Command( const CommandEvent& rCEvt )
{
if (!m_xEditView)
return false;
bool bConsumed = false;
bInputMode = true;
CommandEventId nCommand = rCEvt.GetCommand();
ScModule* pScMod = ScModule::get();
bool bExtInput = nCommand == CommandEventId::StartExtTextInput ||
nCommand == CommandEventId::EndExtTextInput ||
nCommand == CommandEventId::ExtTextInput ||
nCommand == CommandEventId::CursorPos ||
nCommand == CommandEventId::QueryCharPosition;
if (bExtInput)
{
if (ScInputHandler* pInputHdl = pScMod->GetInputHdl())
{
// Similar to ScGridWindow::Command (and ScTextWnd::KeyInput)
// forward the CommandEvent to both EditEngines, the inputbar one
// and the main document one.
pInputHdl->InputCommand(rCEvt);
bConsumed = true;
}
}
else
{
ScTabViewShell* pStartViewSh = ScTabViewShell::GetActiveViewShell();
// don't modify the font defaults here - the right defaults are
// already set in StartEditEngine when the EditEngine is created
// Prevent that the EditView is lost when switching between Views
pScMod->SetInEditCommand( true );
m_xEditView->Command( rCEvt );
pScMod->SetInEditCommand( false );
// CommandEventId::StartDrag does not mean by far that the content was actually changed,
// so don't trigger an InputChanged.
//! Detect if dragged with Move or forbid Drag&Move somehow
if ( nCommand == CommandEventId::StartDrag )
{
// Is dragged onto another View?
ScTabViewShell* pEndViewSh = ScTabViewShell::GetActiveViewShell();
if ( pEndViewSh != pStartViewSh && pStartViewSh != nullptr )
{
ScViewData& rViewData = pStartViewSh->GetViewData();
ScInputHandler* pHdl = pScMod->GetInputHdl( pStartViewSh );
if ( pHdl && rViewData.HasEditView( rViewData.GetActivePart() ) )
{
pHdl->CancelHandler();
rViewData.GetView()->ShowCursor(); // Missing for KillEditView, due to being inactive
}
}
}
else if ( nCommand == CommandEventId::InputLanguageChange )
{
// #i55929# Font and font size state depends on input language if nothing is selected,
// so the slots have to be invalidated when the input language is changed.
SfxViewFrame* pViewFrm = SfxViewFrame::Current();
if (pViewFrm)
{
SfxBindings& rBindings = pViewFrm->GetBindings();
rBindings.Invalidate( SID_ATTR_CHAR_FONT );
rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
}
}
else if ( nCommand == CommandEventId::ContextMenu )
{
bConsumed = true;
SfxViewFrame* pViewFrm = SfxViewFrame::Current();
if (pViewFrm)
{
Point aPos = rCEvt.GetMousePosPixel();
if (!rCEvt.IsMouseEvent())
{
Size aSize = GetOutputSizePixel();
aPos = Point(aSize.Width() / 2, aSize.Height() / 2);
}
if (IsMouseCaptured())
ReleaseMouse();
UpdateFocus(ScInputHandler::ErrorMessage);
pViewFrm->GetDispatcher()->ExecutePopup(u"formulabar"_ustr, &mrGroupBar.GetVclParent(), &aPos);
}
}
else if ( nCommand == CommandEventId::Wheel )
{
//don't call InputChanged for CommandEventId::Wheel
}
else if ( nCommand == CommandEventId::GestureSwipe )
{
//don't call InputChanged for CommandEventId::GestureSwipe
}
else if ( nCommand == CommandEventId::GestureLongPress )
{
//don't call InputChanged for CommandEventId::GestureLongPress
}
else if ( nCommand == CommandEventId::ModKeyChange )
{
//pass alt press/release to parent impl
}
else
{
// I suspect this path doesn't get call anymore or it's called
// and shouldn't be
SAL_WARN("sc.core", "Likely we lost input bar formatting");
pScMod->InputChanged(m_xEditView.get());
}
}
if ( comphelper::LibreOfficeKit::isActive() && nCommand == CommandEventId::CursorPos )
{
// LOK uses this to setup caret position because drawingarea is replaced
// with text input field, it sends logical caret position (start, end) not pixels
StartEditEngine(ScInputHandler::ErrorMessage);
TextGrabFocus();
Point aSelectionStartEnd = rCEvt.GetMousePosPixel();
sal_Int32 nPosStart, nPosEnd;
nPosStart = m_xEditView->GetPosNoField(0, aSelectionStartEnd.X());
nPosEnd = m_xEditView->GetPosNoField(0, aSelectionStartEnd.Y());
m_xEditView->SetSelection(ESelection(0, nPosStart, 0, nPosEnd));
pScMod->InputSelection(m_xEditView.get());
bConsumed = true;
}
bInputMode = false;
return bConsumed;
}
bool ScTextWnd::StartDrag()
{
// tdf#145248 don't start a drag if actively selecting
if (m_xEditView && !m_xEditEngine->IsInSelectionMode())
{
OUString sSelection = m_xEditView->GetSelected();
m_xHelper->SetData(sSelection);
return sSelection.isEmpty();
}
return true;
}
bool ScTextWnd::KeyInput(const KeyEvent& rKEvt)
{
bool bUsed = true;
bInputMode = true;
if (!ScModule::get()->InputKeyEvent(rKEvt))
{
bUsed = false;
ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
if ( pViewSh )
bUsed = pViewSh->SfxKeyInput(rKEvt); // Only accelerators, no input
}
bInputMode = false;
return bUsed;
}
void ScTextWnd::GetFocus()
{
ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
if ( pViewSh )
pViewSh->SetFormShellAtTop( false ); // focus in input line -> FormShell no longer on top
WeldEditView::GetFocus();
}
void ScTextWnd::SetFormulaMode( bool bSet )
{
if ( bSet != bFormulaMode )
{
bFormulaMode = bSet;
UpdateAutoCorrFlag();
}
}
void ScTextWnd::UpdateAutoCorrFlag()
{
if (m_xEditEngine)
{
EEControlBits nControl = m_xEditEngine->GetControlWord();
EEControlBits nOld = nControl;
if ( bFormulaMode )
nControl &= ~EEControlBits::AUTOCORRECT; // No AutoCorrect in Formulas
else
nControl |= EEControlBits::AUTOCORRECT; // Else do enable it
if ( nControl != nOld )
m_xEditEngine->SetControlWord( nControl );
}
}
void ScTextWnd::EditViewScrollStateChange()
{
// editengine height has changed or editview scroll pos has changed
SetScrollBarRange();
}
IMPL_LINK_NOARG(ScTextWnd, ModifyHdl, LinkParamNone*, void)
{
if (m_xEditView && !bInputMode)
{
ScInputHandler* pHdl = ScModule::get()->GetInputHdl();
// Use the InputHandler's InOwnChange flag to prevent calling InputChanged
// while an InputHandler method is modifying the EditEngine content
if ( pHdl && !pHdl->IsInOwnChange() )
pHdl->InputChanged( m_xEditView.get(), true ); // #i20282# InputChanged must know if called from modify handler
}
}
IMPL_LINK_NOARG(ScTextWnd, EditStatusHdl, EditStatus&, void)
{
SetScrollBarRange();
DoScroll();
Invalidate();
}
void ScTextWnd::StopEditEngine( bool bAll )
{
if (!m_xEditEngine)
return;
if (m_xEditView)
{
if (!maAccTextDatas.empty())
maAccTextDatas.back()->EndEdit();
ScModule* pScMod = ScModule::get();
if (!bAll)
pScMod->InputSelection( m_xEditView.get() );
aString = m_xEditEngine->GetText();
bIsInsertMode = m_xEditView->IsInsertMode();
bool bSelection = m_xEditView->HasSelection();
m_xEditEngine->SetStatusEventHdl(Link<EditStatus&, void>());
m_xEditEngine->SetModifyHdl(Link<LinkParamNone*,void>());
m_xEditView.reset();
m_xEditEngine.reset();
ScInputHandler* pHdl = mpViewShell->GetInputHandler();
if (pHdl && pHdl->IsEditMode() && !bAll)
pHdl->SetMode(SC_INPUT_TABLE);
SfxViewFrame* pViewFrm = SfxViewFrame::Current();
if (pViewFrm)
pViewFrm->GetBindings().Invalidate( SID_ATTR_INSERT );
if (bSelection)
Invalidate(); // So that the Selection is not left there
}
if (comphelper::LibreOfficeKit::isActive())
{
// Clear
std::vector<ReferenceMark> aReferenceMarks;
ScInputHandler::SendReferenceMarks( mpViewShell, aReferenceMarks );
}
}
static sal_Int32 findFirstNonMatchingChar(const OUString& rStr1, const OUString& rStr2)
{
// Search the string for unmatching chars
const sal_Unicode* pStr1 = rStr1.getStr();
const sal_Unicode* pStr2 = rStr2.getStr();
sal_Int32 i = 0;
while ( i < rStr1.getLength() )
{
// Abort on the first unmatching char
if ( *pStr1 != *pStr2 )
return i;
++pStr1;
++pStr2;
++i;
}
return i;
}
void ScTextWnd::SetTextString( const OUString& rNewString, bool bKitUpdate )
{
// Ideally it would be best to create on demand the EditEngine/EditView here, but... for
// the initialisation scenario where a cell is first clicked on we end up with the text in the
// inputbar window scrolled to the bottom if we do that here ( because the tableview and topview
// are synced I guess ).
// should fix that I suppose :-/ need to look a bit further into that
mbInvalidate = true; // ensure next Paint ( that uses editengine ) call will call Invalidate first
if ( rNewString != aString )
{
bInputMode = true;
// Find position of the change, only paint the rest
if (!m_xEditEngine)
{
bool bPaintAll = GetNumLines() > 1 || bIsRTL;
if (!bPaintAll)
{
// test if CTL script type is involved
SvtScriptType nOldScript = SvtScriptType::NONE;
SvtScriptType nNewScript = SvtScriptType::NONE;
SfxObjectShell* pObjSh = SfxObjectShell::Current();
if ( auto pDocShell = dynamic_cast<ScDocShell*>( pObjSh) )
{
// any document can be used (used only for its break iterator)
ScDocument& rDoc = pDocShell->GetDocument();
nOldScript = rDoc.GetStringScriptType( aString );
nNewScript = rDoc.GetStringScriptType( rNewString );
}
bPaintAll = ( nOldScript & SvtScriptType::COMPLEX ) || ( nNewScript & SvtScriptType::COMPLEX );
}
if ( bPaintAll )
{
// In multiline mode, or if CTL is involved, the whole text has to be redrawn
Invalidate();
}
else
{
tools::Long nTextSize = 0;
sal_Int32 nDifPos;
if (rNewString.getLength() > aString.getLength())
nDifPos = findFirstNonMatchingChar(rNewString, aString);
else
nDifPos = findFirstNonMatchingChar(aString, rNewString);
tools::Long nSize1 = GetTextWidth(aString);
tools::Long nSize2 = GetTextWidth(rNewString);
if ( nSize1>0 && nSize2>0 )
nTextSize = std::max( nSize1, nSize2 );
else
nTextSize = GetOutputSizePixel().Width(); // Overflow
Point aLogicStart = GetDrawingArea()->get_ref_device().PixelToLogic(Point(0,0));
tools::Long nStartPos = aLogicStart.X();
tools::Long nInvPos = nStartPos;
if (nDifPos)
nInvPos += GetTextWidth(aString.copy(0,nDifPos));
Invalidate(tools::Rectangle(nInvPos, 0, nStartPos+nTextSize, GetOutputSizePixel().Height() - 1));
}
}
else
{
static_cast<ScEditEngineDefaulter*>(m_xEditEngine.get())->SetTextCurrentDefaults(rNewString);
}
aString = rNewString;
if (!maAccTextDatas.empty())
maAccTextDatas.back()->TextChanged();
bInputMode = false;
}
if (ScTabViewShell* pActiveViewShell = bKitUpdate && comphelper::LibreOfficeKit::isActive() ?
dynamic_cast<ScTabViewShell*>(SfxViewShell::Current()) : nullptr)
{
ESelection aSel = m_xEditView ? m_xEditView->GetSelection() : ESelection();
pActiveViewShell->LOKSendFormulabarUpdate(m_xEditView.get(), rNewString, aSel);
}
SetScrollBarRange();
DoScroll();
}
const OUString& ScTextWnd::GetTextString() const
{
return aString;
}
bool ScTextWnd::IsInputActive()
{
return HasFocus();
}
void ScTextWnd::MakeDialogEditView()
{
if ( m_xEditView ) return;
std::unique_ptr<ScFieldEditEngine> pNew;
ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
if ( pViewSh )
{
ScDocument& rDoc = pViewSh->GetViewData().GetDocument();
pNew = std::make_unique<ScFieldEditEngine>(&rDoc, rDoc.GetEditEnginePool());
}
else
pNew = std::make_unique<ScFieldEditEngine>(nullptr, EditEngine::CreatePool().get(), true);
pNew->SetExecuteURL( false );
m_xEditEngine = std::move(pNew);
const bool bPrevUpdateLayout = m_xEditEngine->SetUpdateLayout( false );
m_xEditEngine->SetWordDelimiters( m_xEditEngine->GetWordDelimiters() + "=" );
m_xEditEngine->SetPaperSize( Size( bIsRTL ? USHRT_MAX : THESIZE, 300 ) );
SfxItemSet aSet( m_xEditEngine->GetEmptyItemSet() );
EditEngine::SetFontInfoInItemSet( aSet, aTextFont );
lcl_ExtendEditFontAttribs( aSet );
if ( bIsRTL )
lcl_ModifyRTLDefaults( aSet );
static_cast<ScEditEngineDefaulter*>(m_xEditEngine.get())->SetDefaults( std::move(aSet) );
m_xEditEngine->SetUpdateLayout( bPrevUpdateLayout );
m_xEditView = std::make_unique<EditView>(*m_xEditEngine, nullptr);
m_xEditView->setEditViewCallbacks(this);
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
Color aBgColor = rStyleSettings.GetFieldColor();
m_xEditView->SetBackgroundColor(aBgColor);
if (pAcc)
{
pAcc->InitAcc(nullptr, m_xEditView.get(),
ScResId(STR_ACC_EDITLINE_NAME),
ScResId(STR_ACC_EDITLINE_DESCR));
}
if (comphelper::LibreOfficeKit::isActive())
m_xEditView->RegisterViewShell(mpViewShell);
m_xEditEngine->InsertView( m_xEditView.get(), EE_APPEND );
Resize();
if ( bIsRTL )
lcl_ModifyRTLVisArea( m_xEditView.get() );
if (!maAccTextDatas.empty())
maAccTextDatas.back()->StartEdit();
}
void ScTextWnd::ImplInitSettings()
{
bIsRTL = AllSettings::GetLayoutRTL();
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
Color aBgColor= rStyleSettings.GetFieldColor();
Color aTxtColor= rStyleSettings.GetWindowTextColor();
aTextFont.SetFillColor ( aBgColor );
aTextFont.SetColor (aTxtColor);
Invalidate();
}
void ScTextWnd::SetDrawingArea(weld::DrawingArea* pDrawingArea)
{
// bypass WeldEditView::SetDrawingArea
weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
// set cursor
pDrawingArea->set_cursor(PointerStyle::Text);
// initialize dnd, deliberately just a simple string so
// we don't transfer the happenstance formatting in
// the input line
m_xHelper.set(new svt::OStringTransferable(OUString()));
rtl::Reference<TransferDataContainer> xHelper(m_xHelper);
SetDragDataTransferable(xHelper, DND_ACTION_COPY);
OutputDevice& rDevice = pDrawingArea->get_ref_device();
pDrawingArea->set_margin_start(gnBorderWidth);
pDrawingArea->set_margin_end(gnBorderWidth);
// leave 1 for the width of the scrolledwindow border
pDrawingArea->set_margin_top(gnBorderHeight - 1);
pDrawingArea->set_margin_bottom(gnBorderHeight - 1);
// always use application font, so a font with cjk chars can be installed
vcl::Font aAppFont = Application::GetSettings().GetStyleSettings().GetAppFont();
weld::SetPointFont(rDevice, aAppFont);
aTextFont = rDevice.GetFont();
Size aFontSize = aTextFont.GetFontSize();
aTextFont.SetFontSize(rDevice.PixelToLogic(aFontSize, MapMode(MapUnit::MapTwip)));
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
Color aBgColor = rStyleSettings.GetFieldColor();
Color aTxtColor = rStyleSettings.GetWindowTextColor();
aTextFont.SetTransparent(true);
aTextFont.SetFillColor(aBgColor);
aTextFont.SetColor(aTxtColor);
aTextFont.SetWeight(WEIGHT_NORMAL);
Size aSize(1, GetPixelHeightForLines(1));
pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
rDevice.SetBackground(aBgColor);
rDevice.SetLineColor(COL_BLACK);
rDevice.SetMapMode(MapMode(MapUnit::MapTwip));
rDevice.SetFont(aTextFont);
EnableRTL(false); // EditEngine can't be used with VCL EnableRTL
}
rtl::Reference<comphelper::OAccessible> ScTextWnd::CreateAccessible()
{
pAcc = new ScAccessibleEditLineObject(this);
return pAcc;
}
void ScTextWnd::InsertAccessibleTextData( ScAccessibleEditLineTextData& rTextData )
{
OSL_ENSURE( ::std::find( maAccTextDatas.begin(), maAccTextDatas.end(), &rTextData ) == maAccTextDatas.end(),
"ScTextWnd::InsertAccessibleTextData - passed object already registered" );
maAccTextDatas.push_back( &rTextData );
}
void ScTextWnd::RemoveAccessibleTextData( ScAccessibleEditLineTextData& rTextData )
{
AccTextDataVector::iterator aEnd = maAccTextDatas.end();
AccTextDataVector::iterator aIt = ::std::find( maAccTextDatas.begin(), aEnd, &rTextData );
OSL_ENSURE( aIt != aEnd, "ScTextWnd::RemoveAccessibleTextData - passed object not registered" );
if( aIt != aEnd )
maAccTextDatas.erase( aIt );
}
void ScTextWnd::StyleUpdated()
{
ImplInitSettings();
CustomWidgetController::Invalidate();
}
void ScTextWnd::TextGrabFocus()
{
GrabFocus();
}
// Position window
ScPosWnd::ScPosWnd(vcl::Window* pParent)
: InterimItemWindow(pParent, u"modules/scalc/ui/posbox.ui"_ustr, u"PosBox"_ustr, true)
, m_xWidget(m_xBuilder->weld_combo_box(u"pos_window"_ustr))
, m_nAsyncGetFocusId(nullptr)
, nTipVisible(nullptr)
, bFormulaMode(false)
{
InitControlBase(m_xWidget.get());
// Use calculation according to tdf#132338 to align combobox width to width of fontname combobox within formatting toolbar;
// formatting toolbar is placed above formulabar when using multiple toolbars typically
m_xWidget->set_entry_width_chars(1);
Size aSize(LogicToPixel(Size(POSITION_COMBOBOX_WIDTH * 4, 0), MapMode(MapUnit::MapAppFont)));
m_xWidget->set_size_request(aSize.Width(), -1);
SetSizePixel(m_xContainer->get_preferred_size());
FillRangeNames(true);
StartListening( *SfxGetpApp() ); // For Navigator rangename updates
m_xWidget->connect_key_press(LINK(this, ScPosWnd, KeyInputHdl));
m_xWidget->connect_entry_activate(LINK(this, ScPosWnd, ActivateHdl));
m_xWidget->connect_changed(LINK(this, ScPosWnd, ModifyHdl));
m_xWidget->connect_focus_in(LINK(this, ScPosWnd, FocusInHdl));
m_xWidget->connect_focus_out(LINK(this, ScPosWnd, FocusOutHdl));
}
ScPosWnd::~ScPosWnd()
{
disposeOnce();
}
void ScPosWnd::dispose()
{
EndListening( *SfxGetpApp() );
HideTip();
if (m_nAsyncGetFocusId)
{
Application::RemoveUserEvent(m_nAsyncGetFocusId);
m_nAsyncGetFocusId = nullptr;
}
m_xWidget.reset();
InterimItemWindow::dispose();
}
void ScPosWnd::SetFormulaMode( bool bSet )
{
if ( bSet != bFormulaMode )
{
if ( bSet )
FillFunctions();
else
FillRangeNames();
bFormulaMode = bSet;
HideTip();
}
}
void ScPosWnd::SetPos( const OUString& rPosStr )
{
if ( aPosStr != rPosStr )
{
aPosStr = rPosStr;
m_xWidget->set_entry_text(aPosStr);
}
}
// static
OUString ScPosWnd::createLocalRangeName(std::u16string_view rName, std::u16string_view rTableName)
{
return OUString::Concat(rName) + " (" + rTableName + ")";
}
void ScPosWnd::FillRangeNames(bool initialize)
{
std::set<OUString> aSet;
SfxObjectShell* pObjSh = SfxObjectShell::Current();
if (auto pDocShell = dynamic_cast<ScDocShell*>(pObjSh))
{
ScDocument& rDoc = pDocShell->GetDocument();
ScRange aDummy;
ScRangeName& rRangeNames = rDoc.GetRangeName();
for (const auto& rEntry : rRangeNames)
{
if (rEntry.second->IsValidReference(aDummy))
aSet.insert(rEntry.second->GetName());
}
for (SCTAB i = 0; i < rDoc.GetTableCount(); ++i)
{
ScRangeName* pLocalRangeName = rDoc.GetRangeName(i);
if (pLocalRangeName && !pLocalRangeName->empty())
{
OUString aTableName;
rDoc.GetName(i, aTableName);
for (const auto& rEntry : *pLocalRangeName)
{
if (rEntry.second->IsValidReference(aDummy))
aSet.insert(createLocalRangeName(rEntry.second->GetName(), aTableName));
}
}
}
}
if (!bFormulaMode && !initialize && aSet == aRangeNames)
return;
aRangeNames = aSet;
m_xWidget->clear();
m_xWidget->freeze();
m_xWidget->append_text(ScResId(STR_MANAGE_NAMES));
m_xWidget->append_separator(u"separator"_ustr);
for (const auto& rItem : aSet)
{
m_xWidget->append_text(rItem);
}
m_xWidget->thaw();
if (!aPosStr.isEmpty())
m_xWidget->set_entry_text(aPosStr);
}
void ScPosWnd::FillFunctions()
{
m_xWidget->clear();
m_xWidget->freeze();
OUString aFirstName;
const ScAppOptions& rOpt = ScModule::get()->GetAppOptions();
sal_uInt16 nMRUCount = rOpt.GetLRUFuncListCount();
const sal_uInt16* pMRUList = rOpt.GetLRUFuncList();
if (pMRUList)
{
const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
sal_uInt32 nListCount = pFuncList->GetCount();
for (sal_uInt16 i=0; i<nMRUCount; i++)
{
sal_uInt16 nId = pMRUList[i];
for (sal_uInt32 j=0; j<nListCount; j++)
{
const ScFuncDesc* pDesc = pFuncList->GetFunction( j );
if ( pDesc->nFIndex == nId && pDesc->mxFuncName )
{
m_xWidget->append_text(*pDesc->mxFuncName);
if (aFirstName.isEmpty())
aFirstName = *pDesc->mxFuncName;
break; // Stop searching
}
}
}
}
//! Re-add entry "Other..." for Function AutoPilot if it can work with text that
// has been entered so far
// m_xWidget->append_text(ScResId(STR_FUNCTIONLIST_MORE));
m_xWidget->thaw();
m_xWidget->set_entry_text(aFirstName);
}
void ScPosWnd::Notify( SfxBroadcaster&, const SfxHint& rHint )
{
if ( bFormulaMode )
return;
const SfxHintId nHintId = rHint.GetId();
// Does the list of range names need updating?
if (nHintId == SfxHintId::ThisIsAnSfxEventHint)
{
SfxEventHintId nEventId = static_cast<const SfxEventHint&>(rHint).GetEventId();
if ( nEventId == SfxEventHintId::ActivateDoc )
FillRangeNames();
}
else
{
if (nHintId == SfxHintId::ScAreasChanged || nHintId == SfxHintId::ScNavigatorUpdateAll)
FillRangeNames();
}
}
void ScPosWnd::HideTip()
{
if (nTipVisible)
{
Help::HidePopover(this, nTipVisible);
nTipVisible = nullptr;
}
}
static ScNameInputType lcl_GetInputType( const OUString& rText )
{
ScNameInputType eRet = SC_NAME_INPUT_BAD_NAME; // the more general error
ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
if ( pViewSh )
{
ScViewData& rViewData = pViewSh->GetViewData();
ScDocument& rDoc = rViewData.GetDocument();
SCTAB nTab = rViewData.CurrentTabForData();
ScAddress::Details aDetails( rDoc.GetAddressConvention());
// test in same order as in SID_CURRENTCELL execute
ScRange aRange;
ScAddress aAddress;
SCTAB nNameTab;
sal_Int32 nNumeric;
// From the context we know that when testing for a range name
// sheet-local scope names have " (sheetname)" appended and global
// names don't and can't contain ')', so we can force one or the other.
const RutlNameScope eNameScope =
((!rText.isEmpty() && rText[rText.getLength()-1] == ')') ? RUTL_NAMES_LOCAL : RUTL_NAMES_GLOBAL);
if (rText == ScResId(STR_MANAGE_NAMES))
eRet = SC_MANAGE_NAMES;
else if ( aRange.Parse( rText, rDoc, aDetails ) & ScRefFlags::VALID )
eRet = SC_NAME_INPUT_RANGE;
else if ( aAddress.Parse( rText, rDoc, aDetails ) & ScRefFlags::VALID )
eRet = SC_NAME_INPUT_CELL;
else if ( ScRangeUtil::MakeRangeFromName( rText, rDoc, nTab, aRange, eNameScope, aDetails ) )
{
eRet = ((eNameScope == RUTL_NAMES_LOCAL) ? SC_NAME_INPUT_NAMEDRANGE_LOCAL :
SC_NAME_INPUT_NAMEDRANGE_GLOBAL);
}
else if ( ScRangeUtil::MakeRangeFromName( rText, rDoc, nTab, aRange, RUTL_DBASE, aDetails ) )
eRet = SC_NAME_INPUT_DATABASE;
else if ( comphelper::string::isdigitAsciiString( rText ) &&
( nNumeric = rText.toInt32() ) > 0 && nNumeric <= rDoc.MaxRow()+1 )
eRet = SC_NAME_INPUT_ROW;
else if ( rDoc.GetTable( rText, nNameTab ) )
eRet = SC_NAME_INPUT_SHEET;
else if (ScRangeData::IsNameValid(rText, rDoc)
== ScRangeData::IsNameValidType::NAME_VALID) // nothing found, create new range?
{
if ( rViewData.GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
eRet = SC_NAME_INPUT_DEFINE;
else
eRet = SC_NAME_INPUT_BAD_SELECTION;
}
else
eRet = SC_NAME_INPUT_BAD_NAME;
}
return eRet;
}
IMPL_LINK_NOARG(ScPosWnd, ModifyHdl, weld::ComboBox&, void)
{
HideTip();
if (m_xWidget->changed_by_direct_pick())
{
DoEnter();
return;
}
if (bFormulaMode)
return;
// determine the action that would be taken for the current input
ScNameInputType eType = lcl_GetInputType(m_xWidget->get_active_text()); // uses current view
TranslateId pStrId;
switch ( eType )
{
case SC_NAME_INPUT_CELL:
pStrId = STR_NAME_INPUT_CELL;
break;
case SC_NAME_INPUT_RANGE:
case SC_NAME_INPUT_NAMEDRANGE_LOCAL:
case SC_NAME_INPUT_NAMEDRANGE_GLOBAL:
pStrId = STR_NAME_INPUT_RANGE; // named range or range reference
break;
case SC_NAME_INPUT_DATABASE:
pStrId = STR_NAME_INPUT_DBRANGE;
break;
case SC_NAME_INPUT_ROW:
pStrId = STR_NAME_INPUT_ROW;
break;
case SC_NAME_INPUT_SHEET:
pStrId = STR_NAME_INPUT_SHEET;
break;
case SC_NAME_INPUT_DEFINE:
pStrId = STR_NAME_INPUT_DEFINE;
break;
default:
// other cases (error): no tip help
break;
}
if (!pStrId)
return;
// show the help tip at the text cursor position
Point aPos;
vcl::Cursor* pCur = GetCursor();
if (pCur)
aPos = LogicToPixel( pCur->GetPos() );
aPos = OutputToScreenPixel( aPos );
tools::Rectangle aRect( aPos, aPos );
OUString aText = ScResId(pStrId);
QuickHelpFlags const nAlign = QuickHelpFlags::Left|QuickHelpFlags::Bottom;
nTipVisible = Help::ShowPopover(this, aRect, aText, nAlign);
}
void ScPosWnd::DoEnter()
{
bool bOpenManageNamesDialog = false;
OUString aText = m_xWidget->get_active_text();
if ( !aText.isEmpty() )
{
if ( bFormulaMode )
{
ScModule* pScMod = ScModule::get();
if ( aText == ScResId(STR_FUNCTIONLIST_MORE) )
{
// Function AutoPilot
//! Continue working with the text entered so far
//! new method at ScModule to query if function autopilot is open
SfxViewFrame* pViewFrm = SfxViewFrame::Current();
if ( pViewFrm && !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) )
pViewFrm->GetDispatcher()->Execute( SID_OPENDLG_FUNCTION,
SfxCallMode::SYNCHRON | SfxCallMode::RECORD );
}
else
{
ScTabViewShell* pViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
ScInputHandler* pHdl = pScMod->GetInputHdl( pViewSh );
if (pHdl)
pHdl->InsertFunction( aText );
}
}
else
{
// depending on the input, select something or create a new named range
ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
if ( pViewSh )
{
ScViewData& rViewData = pViewSh->GetViewData();
ScDocShell* pDocShell = rViewData.GetDocShell();
ScDocument& rDoc = pDocShell->GetDocument();
ScNameInputType eType = lcl_GetInputType( aText );
if ( eType == SC_NAME_INPUT_BAD_NAME || eType == SC_NAME_INPUT_BAD_SELECTION )
{
TranslateId pId = (eType == SC_NAME_INPUT_BAD_NAME) ? STR_NAME_ERROR_NAME : STR_NAME_ERROR_SELECTION;
pViewSh->ErrorMessage(pId);
}
else if ( eType == SC_NAME_INPUT_DEFINE )
{
ScRangeName& rNames = rDoc.GetRangeName();
ScRange aSelection;
if ( !rNames.findByUpperName(ScGlobal::getCharClass().uppercase(aText)) &&
(rViewData.GetSimpleArea( aSelection ) == SC_MARK_SIMPLE) )
{
ScRangeName aNewRanges( rNames );
ScAddress aCursor( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.CurrentTabForData() );
OUString aContent(aSelection.Format(rDoc, ScRefFlags::RANGE_ABS_3D, rDoc.GetAddressConvention()));
std::unique_ptr<ScRangeData> pNew(new ScRangeData( rDoc, aText, aContent, aCursor ));
if ( aNewRanges.insert(std::move(pNew)) )
{
pDocShell->GetDocFunc().ModifyRangeNames( aNewRanges );
pViewSh->UpdateInputHandler(true);
}
}
}
else if (eType == SC_MANAGE_NAMES)
{
// dialog is only set below after calling 'ReleaseFocus_Impl' to ensure it gets focus
bOpenManageNamesDialog = true;
}
else
{
bool bForceGlobalName = false;
// for all selection types, execute the SID_CURRENTCELL slot.
if (eType == SC_NAME_INPUT_CELL || eType == SC_NAME_INPUT_RANGE)
{
// Note that SID_CURRENTCELL always expects address to
// be in Calc A1 format. Convert the text.
ScRange aRange(0,0, rViewData.CurrentTabForData());
aRange.ParseAny(aText, rDoc, rDoc.GetAddressConvention());
aText = aRange.Format(rDoc, ScRefFlags::RANGE_ABS_3D, ::formula::FormulaGrammar::CONV_OOO);
}
else if (eType == SC_NAME_INPUT_NAMEDRANGE_GLOBAL)
{
bForceGlobalName = true;
}
SfxStringItem aPosItem( SID_CURRENTCELL, aText );
SfxBoolItem aUnmarkItem( FN_PARAM_1, true ); // remove existing selection
// FN_PARAM_2 reserved for AlignToCursor
SfxBoolItem aForceGlobalName( FN_PARAM_3, bForceGlobalName );
pViewSh->GetViewData().GetDispatcher().ExecuteList( SID_CURRENTCELL,
SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
{ &aPosItem, &aUnmarkItem, &aForceGlobalName });
}
}
}
}
else
m_xWidget->set_entry_text(aPosStr);
ReleaseFocus_Impl();
if (bOpenManageNamesDialog)
{
const sal_uInt16 nId = ScNameDlgWrapper::GetChildWindowId();
if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
{
SfxViewFrame& rViewFrm = pViewSh->GetViewFrame();
SfxChildWindow* pWnd = rViewFrm.GetChildWindow( nId );
ScModule::get()->SetRefDialog(nId, pWnd == nullptr);
}
}
}
IMPL_LINK_NOARG(ScPosWnd, ActivateHdl, weld::ComboBox&, bool)
{
DoEnter();
return true;
}
IMPL_LINK(ScPosWnd, KeyInputHdl, const KeyEvent&, rKEvt, bool)
{
bool bHandled = true;
switch (rKEvt.GetKeyCode().GetCode())
{
case KEY_RETURN:
bHandled = ActivateHdl(*m_xWidget);
break;
case KEY_ESCAPE:
if (nTipVisible)
{
// escape when the tip help is shown: only hide the tip
HideTip();
}
else
{
if (!bFormulaMode)
m_xWidget->set_entry_text(aPosStr);
ReleaseFocus_Impl();
}
break;
default:
bHandled = false;
break;
}
return bHandled || ChildKeyInput(rKEvt);
}
IMPL_LINK_NOARG(ScPosWnd, OnAsyncGetFocus, void*, void)
{
m_nAsyncGetFocusId = nullptr;
m_xWidget->select_entry_region(0, -1);
}
IMPL_LINK_NOARG(ScPosWnd, FocusInHdl, weld::Widget&, void)
{
if (m_nAsyncGetFocusId)
return;
// do it async to defeat entry in combobox having its own ideas about the focus
m_nAsyncGetFocusId = Application::PostUserEvent(LINK(this, ScPosWnd, OnAsyncGetFocus));
}
IMPL_LINK_NOARG(ScPosWnd, FocusOutHdl, weld::Widget&, void)
{
if (m_nAsyncGetFocusId)
{
Application::RemoveUserEvent(m_nAsyncGetFocusId);
m_nAsyncGetFocusId = nullptr;
}
HideTip();
}
void ScPosWnd::ReleaseFocus_Impl()
{
HideTip();
SfxViewShell* pCurSh = SfxViewShell::Current();
ScInputHandler* pHdl = ScModule::get()->GetInputHdl(dynamic_cast<ScTabViewShell*>(pCurSh));
if ( pHdl && pHdl->IsTopMode() )
{
// Focus back in input row?
ScInputWindow* pInputWin = pHdl->GetInputWindow();
if (pInputWin)
{
pInputWin->TextGrabFocus();
return;
}
}
// Set focus to active View
if ( pCurSh )
{
vcl::Window* pShellWnd = pCurSh->GetWindow();
if ( pShellWnd )
pShellWnd->GrabFocus();
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */