/* -*- 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 .
*/
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 constint 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;
// 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 if (!bIsLOKMobilePhone)
{
SetHelpId (SID_INPUT_SUM, HID_INSWIN_SUMME);
SetHelpId (SID_INPUT_EQUAL, HID_INSWIN_FUNC);
SetHelpId (SID_INPUT_CANCEL, HID_INSWIN_CANCEL);
SetHelpId (SID_INPUT_OK, HID_INSWIN_OK);
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);
} elseif (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
} elseif (pViewSh)
{ // Don't stop editing in LOK a remote user might be editing. constbool bStopEditing = !comphelper::LibreOfficeKit::isActive();
pViewSh->UpdateInputHandler(true, bStopEditing); // Absolutely necessary update
}
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 );
}
}
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();
}
} elseif (curItemId == SID_INPUT_CANCEL)
{
pScMod->InputCancelHandler();
SetSumAssignMode();
} elseif (curItemId == SID_INPUT_OK)
{
pScMod->InputEnterHandler();
SetSumAssignMode();
mxTextWindow->Invalidate(); // Or else the Selection remains
} elseif (curItemId == SID_INPUT_SUM)
{ bool bRangeFinder = false; bool bSubTotal = false;
AutoSum(bRangeFinder, bSubTotal, ocSum);
} elseif (curItemId == SID_INPUT_EQUAL)
{
StartFormula();
}
}
void ScInputWindow::StartFormula()
{
ScModule* pScMod = ScModule::get();
mxTextWindow->StartEditEngine(); if ( pScMod->IsEditMode() ) // Isn't if e.g. protected
{
mxTextWindow->StartEditEngine();
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);
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();
ScModule* pScMod = ScModule::get(); if ( !pScMod->IsEditMode() ) return;
if ( bDoEdit )
mxTextWindow->TextGrabFocus();
mxTextWindow->SetTextString(rString, true);
EditView* pView = mxTextWindow->GetEditView(); if (!pView) return;
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 ) );
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 ) );
void ScInputWindow::SwitchToTextWin()
{ // used for shift-ctrl-F2
mxTextWindow->StartEditEngine(); 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();
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 elseif (GetOutputSizePixel().Height() - aPosPixel.Y() < -nResizeThreshold)
{
pGroupBar->IncrementVerticalSize();
bResetPointerPos = true;
} // or shrinking up elseif ((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;
}
}
}
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);
// disable the multiline toggle on the mobile phones const SfxViewShell* pViewShell = SfxViewShell::Current(); if (!comphelper::LibreOfficeKit::isActive() || !(pViewShell && pViewShell->isLOKMobilePhone()))
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());
}
// 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();
}
// 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 (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);
}
// 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);
}
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...
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()
{ // 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()));
SfxViewFrame* pViewFrm = SfxViewFrame::Current(); if (pViewFrm)
pViewFrm->GetBindings().Invalidate( SID_ATTR_INSERT );
}
// 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 );
}
{
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
// we get cursor, selection etc. messages from the VCL/window layer // otherwise these are injected into the document causing confusion.
m_xEditView->SuppressLOKMessages(true);
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 );
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();
}
}
// 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
}
}
} elseif ( nCommand == CommandEventId::EndExtTextInput )
{
ScModule* mod = ScModule::get(); if (bFormulaMode)
{
ScInputHandler* pHdl = mod->GetInputHdl(); if (pHdl)
pHdl->InputCommand(rCEvt);
}
mod->InputChanged(m_xEditView.get());
} elseif ( nCommand == CommandEventId::CursorPos )
{ // don't call InputChanged for CommandEventId::CursorPos
} elseif ( 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.
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();
TextGrabFocus();
if (!m_xEditView) returntrue;
ScModule* mod = ScModule::get(); // if we focus input after "Accept Formula" command, we need to notify to get it working
mod->InputChanged(m_xEditView.get());
// information about paragraph is in additional data // information about position in a paragraph in a Mouse Pos // see vcl/jsdialog/executor.cxx "textselection" event const Point* pParaPoint = static_cast<const Point*>(rCEvt.GetEventData());
Point aSelectionStartEnd = rCEvt.GetMousePosPixel();
void ScTextWnd::GetFocus()
{
ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell(); if ( pViewSh )
pViewSh->SetFormShellAtTop( false ); // focus in input line -> FormShell no longer on top
WeldEditView::GetFocus();
}
// 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
}
}
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);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.