diff options
Diffstat (limited to 'sw/source/uibase/ribbar/inputwin.cxx')
-rw-r--r-- | sw/source/uibase/ribbar/inputwin.cxx | 623 |
1 files changed, 623 insertions, 0 deletions
diff --git a/sw/source/uibase/ribbar/inputwin.cxx b/sw/source/uibase/ribbar/inputwin.cxx new file mode 100644 index 000000000..8c0666549 --- /dev/null +++ b/sw/source/uibase/ribbar/inputwin.cxx @@ -0,0 +1,623 @@ +/* -*- 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 <sal/config.h> +#include <sal/log.hxx> + +#include <osl/diagnose.h> +#include <comphelper/string.hxx> +#include <o3tl/safeint.hxx> +#include <officecfg/Office/Common.hxx> +#include <tools/gen.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/ruler.hxx> +#include <svl/stritem.hxx> +#include <vcl/event.hxx> +#include <vcl/weldutils.hxx> + +#include <swtypes.hxx> +#include <cmdid.h> +#include <swmodule.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <inputwin.hxx> +#include <fldbas.hxx> +#include <fldmgr.hxx> +#include <frmfmt.hxx> +#include <cellatr.hxx> +#include <edtwin.hxx> +#include <helpids.h> +#include <strings.hrc> +#include <bitmaps.hlst> + +// Only for the UpdateRange: Delete the box in which the stacked cursor is positioned. +#include <pam.hxx> + +#include <swundo.hxx> + +#include <IDocumentContentOperations.hxx> + +constexpr ToolBoxItemId ED_POS(2); +constexpr ToolBoxItemId ED_FORMULA(3); +constexpr ToolBoxItemId FN_FORMULA_CALC(FN_FORMAT + 156); /* select formula */ +constexpr ToolBoxItemId FN_FORMULA_CANCEL(FN_FORMAT + 157); /* don't apply formula */ +constexpr ToolBoxItemId FN_FORMULA_APPLY(FN_FORMAT + 158); /* apply formula */ + +SFX_IMPL_POS_CHILDWINDOW_WITHID( SwInputChild, FN_EDIT_FORMULA, SFX_OBJECTBAR_OBJECT ) + +IMPL_LINK(PosEdit, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +SwInputWindow::SwInputWindow(vcl::Window* pParent, SfxDispatcher const * pDispatcher) + : ToolBox(pParent, WB_3DLOOK|WB_BORDER) + , mxPos(VclPtr<PosEdit>::Create(this)) + , mxEdit(VclPtr<InputEdit>::Create(this)) + , m_pWrtShell(nullptr) + , m_pView(nullptr) + , m_bDoesUndo(true) + , m_bResetUndo(false) + , m_bCallUndo(false) +{ + m_bFirst = true; + m_bIsTable = m_bDelSel = false; + + InsertItem(FN_FORMULA_CALC, Image(StockImage::Yes, RID_BMP_FORMULA_CALC), + SwResId(STR_FORMULA_CALC)); + InsertItem(FN_FORMULA_CANCEL, Image(StockImage::Yes, RID_BMP_FORMULA_CANCEL), + SwResId(STR_FORMULA_CANCEL)); + InsertItem(FN_FORMULA_APPLY, Image(StockImage::Yes, RID_BMP_FORMULA_APPLY), + SwResId(STR_FORMULA_APPLY)); + + SetHelpId(FN_FORMULA_CALC, HID_TBX_FORMULA_CALC); + SetHelpId(FN_FORMULA_CANCEL, HID_TBX_FORMULA_CANCEL); + SetHelpId(FN_FORMULA_APPLY, HID_TBX_FORMULA_APPLY); + + SwView *pDispatcherView = dynamic_cast<SwView*>(pDispatcher ? pDispatcher->GetFrame()->GetViewShell() : nullptr); + SwView* pActiveView = ::GetActiveView(); + if (pDispatcherView == pActiveView) + m_pView = pActiveView; + m_pWrtShell = m_pView ? m_pView->GetWrtShellPtr() : nullptr; + + InsertWindow(ED_POS, mxPos.get(), ToolBoxItemBits::NONE, 0); + SetItemText(ED_POS, SwResId(STR_ACCESS_FORMULA_TYPE)); + mxPos->set_accessible_name(SwResId(STR_ACCESS_FORMULA_TYPE)); + SetAccessibleName(SwResId(STR_ACCESS_FORMULA_TOOLBAR)); + InsertSeparator ( 1 ); + InsertSeparator (); + InsertWindow(ED_FORMULA, mxEdit.get()); + SetItemText(ED_FORMULA, SwResId(STR_ACCESS_FORMULA_TEXT)); + mxEdit->set_accessible_name(SwResId(STR_ACCESS_FORMULA_TEXT)); + SetHelpId(ED_FORMULA, HID_EDIT_FORMULA); + + SetItemBits( FN_FORMULA_CALC, GetItemBits( FN_FORMULA_CALC ) | ToolBoxItemBits::DROPDOWNONLY ); + SetDropdownClickHdl( LINK( this, SwInputWindow, DropdownClickHdl )); + + Size aSizeTbx = CalcWindowSizePixel(); + Size aEditSize = mxEdit->GetSizePixel(); + tools::Rectangle aItemRect( GetItemRect(FN_FORMULA_CALC) ); + tools::Long nMaxHeight = std::max(aEditSize.Height(), aItemRect.GetHeight()); + if( nMaxHeight+2 > aSizeTbx.Height() ) + aSizeTbx.setHeight( nMaxHeight+2 ); + Size aSize = GetSizePixel(); + aSize.setHeight( aSizeTbx.Height() ); + SetSizePixel( aSize ); + + // align edit and item vcentered + Size aPosSize = mxPos->GetSizePixel(); + aPosSize.setHeight( nMaxHeight ); + aEditSize.setHeight( nMaxHeight ); + Point aPosPos = mxPos->GetPosPixel(); + Point aEditPos = mxEdit->GetPosPixel(); + aPosPos.setY( (aSize.Height() - nMaxHeight)/2 + 1 ); + aEditPos.setY( (aSize.Height() - nMaxHeight)/2 + 1 ); + mxPos->SetPosSizePixel( aPosPos, aPosSize ); + mxEdit->SetPosSizePixel( aEditPos, aEditSize ); +} + +SwInputWindow::~SwInputWindow() +{ + disposeOnce(); +} + +void SwInputWindow::dispose() +{ + // wake rulers + if(m_pView) + { + m_pView->GetHRuler().SetActive(); + m_pView->GetVRuler().SetActive(); + } + m_pMgr.reset(); + if(m_pWrtShell) + m_pWrtShell->EndSelTableCells(); + + CleanupUglyHackWithUndo(); + + mxPos.disposeAndClear(); + mxEdit.disposeAndClear(); + ToolBox::dispose(); +} + +void SwInputWindow::CleanupUglyHackWithUndo() +{ + if (!m_bResetUndo) + return; + + if (m_pWrtShell) + { + DelBoxContent(); + m_pWrtShell->DoUndo(m_bDoesUndo); + if (m_bCallUndo) + { + m_pWrtShell->Undo(); + } + } + m_bResetUndo = false; // #i117122# once is enough :) +} + +void SwInputWindow::Resize() +{ + ToolBox::Resize(); + + tools::Long nWidth = GetSizePixel().Width(); + tools::Long nLeft = mxEdit->GetPosPixel().X(); + Size aEditSize = mxEdit->GetSizePixel(); + + aEditSize.setWidth( std::max( static_cast<tools::Long>(nWidth - nLeft - 5), tools::Long(0) ) ); + mxEdit->SetSizePixel( aEditSize ); +} + +void SwInputWindow::ShowWin() +{ + m_bIsTable = false; + // stop rulers + if (m_pView) + { + m_pView->GetHRuler().SetActive( false ); + m_pView->GetVRuler().SetActive( false ); + + OSL_ENSURE(m_pWrtShell, "no WrtShell!"); + // Cursor in table + m_bIsTable = m_pWrtShell->IsCursorInTable(); + + if( m_bFirst ) + m_pWrtShell->SelTableCells( LINK( this, SwInputWindow, + SelTableCellsNotify) ); + if( m_bIsTable ) + { + const OUString& rPos = m_pWrtShell->GetBoxNms(); + sal_Int32 nPos = 0; + short nSrch = -1; + while( (nPos = rPos.indexOf( ':',nPos + 1 ) ) != -1 ) + nSrch = static_cast<short>(nPos); + mxPos->set_text( rPos.copy( ++nSrch ) ); + m_aCurrentTableName = m_pWrtShell->GetTableFormat()->GetName(); + } + else + mxPos->set_text(SwResId(STR_TBL_FORMULA)); + + // Edit current field + OSL_ENSURE(m_pMgr == nullptr, "FieldManager not deleted"); + m_pMgr.reset(new SwFieldMgr); + + // Form should always begin with "=" , so set here + OUString sEdit('='); + if( m_pMgr->GetCurField() && SwFieldTypesEnum::Formel == m_pMgr->GetCurTypeId() ) + { + sEdit += m_pMgr->GetCurFieldPar2(); + } + else if( m_bFirst && m_bIsTable ) + { + m_bResetUndo = true; + SAL_WARN_IF( + officecfg::Office::Common::Undo::Steps::get() <= 0, + "sw", "/org.openoffice.Office.Common/Undo/Steps <= 0"); + + m_bDoesUndo = m_pWrtShell->DoesUndo(); + if( !m_bDoesUndo ) + { + m_pWrtShell->DoUndo(); + } + + if( !m_pWrtShell->SwCursorShell::HasSelection() ) + { + m_pWrtShell->MoveSection( GoCurrSection, fnSectionStart ); + m_pWrtShell->SetMark(); + m_pWrtShell->MoveSection( GoCurrSection, fnSectionEnd ); + } + if( m_pWrtShell->SwCursorShell::HasSelection() ) + { + m_pWrtShell->StartUndo( SwUndoId::DELETE ); + m_pWrtShell->Delete(false); + if( SwUndoId::EMPTY != m_pWrtShell->EndUndo( SwUndoId::DELETE )) + { + m_bCallUndo = true; + } + } + m_pWrtShell->DoUndo(false); + + SfxItemSetFixed<RES_BOXATR_FORMULA, RES_BOXATR_FORMULA> aSet( m_pWrtShell->GetAttrPool() ); + if( m_pWrtShell->GetTableBoxFormulaAttrs( aSet )) + sEdit += aSet.Get( RES_BOXATR_FORMULA ).GetFormula(); + } + + if( m_bFirst ) + { + // Set WrtShell flags correctly + m_pWrtShell->SttSelect(); + m_pWrtShell->EndSelect(); + } + + m_bFirst = false; + + mxEdit->connect_changed( LINK( this, SwInputWindow, ModifyHdl )); + + mxEdit->set_text( sEdit ); + m_sOldFormula = sEdit; + + // For input cut the UserInterface + + m_pView->GetEditWin().LockKeyInput(true); + m_pView->GetViewFrame()->GetDispatcher()->Lock(true); + m_pWrtShell->Push(); + } + + ToolBox::Show(); + + // grab focus after ToolBox is shown so focus isn't potentially lost elsewhere + if (m_pView) + { + int nPos = mxEdit->get_text().getLength(); + mxEdit->select_region(nPos, nPos); + mxEdit->GrabFocus(); + } +} + +void SwInputWindow::MenuHdl(std::string_view command) +{ + if (!command.empty()) + mxEdit->replace_selection(OUString::fromUtf8(command) + " "); +} + +IMPL_LINK_NOARG(SwInputWindow, DropdownClickHdl, ToolBox *, void) +{ + ToolBoxItemId nCurID = GetCurItemId(); + EndSelection(); // reset back CurItemId ! + if (nCurID == FN_FORMULA_CALC) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "modules/swriter/ui/inputwinmenu.ui")); + std::unique_ptr<weld::Menu> xPopMenu(xBuilder->weld_menu("menu")); + tools::Rectangle aRect(GetItemRect(FN_FORMULA_CALC)); + weld::Window* pParent = weld::GetPopupParent(*this, aRect); + MenuHdl(xPopMenu->popup_at_rect(pParent, aRect)); + } +} + +void SwInputWindow::Click( ) +{ + ToolBoxItemId nCurID = GetCurItemId(); + EndSelection(); // reset back CurItemId ! + if ( nCurID == FN_FORMULA_CANCEL ) + { + CancelFormula(); + } + else if (nCurID == FN_FORMULA_APPLY) + { + ApplyFormula(); + } +} + +void SwInputWindow::ApplyFormula() +{ + // in case it was created while loading the document, the active view + // wasn't initialised at that time, so ShowWin() didn't initialise anything + // either - nothing to do + if (!m_pView) + { + // presumably there must be an active view now since the event arrived + if (SwView* pView = GetActiveView()) + { + // this just makes the input window go away, so that the next time it works + pView->GetViewFrame()->GetDispatcher()->Execute(FN_EDIT_FORMULA, SfxCallMode::ASYNCHRON); + } + return; + } + + m_pView->GetViewFrame()->GetDispatcher()->Lock(false); + m_pView->GetEditWin().LockKeyInput(false); + CleanupUglyHackWithUndo(); + m_pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); + + // Form should always begin with "=", so remove it here again + OUString sEdit(comphelper::string::strip(mxEdit->get_text(), ' ')); + if( !sEdit.isEmpty() && '=' == sEdit[0] ) + sEdit = sEdit.copy( 1 ); + SfxStringItem aParam(FN_EDIT_FORMULA, sEdit); + + m_pWrtShell->EndSelTableCells(); + m_pView->GetEditWin().GrabFocus(); + const SfxPoolItem* aArgs[2]; + aArgs[0] = &aParam; + aArgs[1] = nullptr; + m_pView->GetViewFrame()->GetBindings().Execute( FN_EDIT_FORMULA, aArgs, SfxCallMode::ASYNCHRON ); +} + +void SwInputWindow::CancelFormula() +{ + // in case it was created while loading the document, the active view + // wasn't initialised at that time, so ShowWin() didn't initialise anything + // either - nothing to do + if (!m_pView) + { + // presumably there must be an active view now since the event arrived + if (SwView* pActiveView = GetActiveView()) + { + // this just makes the input window go away, so that the next time it works + pActiveView->GetViewFrame()->GetDispatcher()->Execute(FN_EDIT_FORMULA, SfxCallMode::ASYNCHRON); + } + return; + } + + m_pView->GetViewFrame()->GetDispatcher()->Lock( false ); + m_pView->GetEditWin().LockKeyInput(false); + CleanupUglyHackWithUndo(); + m_pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); + + if( m_bDelSel ) + m_pWrtShell->EnterStdMode(); + + m_pWrtShell->EndSelTableCells(); + + m_pView->GetEditWin().GrabFocus(); + + m_pView->GetViewFrame()->GetDispatcher()->Execute( FN_EDIT_FORMULA, SfxCallMode::ASYNCHRON); +} + +const sal_Unicode CH_LRE = 0x202a; +const sal_Unicode CH_PDF = 0x202c; + +IMPL_LINK( SwInputWindow, SelTableCellsNotify, SwWrtShell&, rCaller, void ) +{ + if(m_bIsTable) + { + SwFrameFormat* pTableFormat = rCaller.GetTableFormat(); + OUString sBoxNms( rCaller.GetBoxNms() ); + OUString sTableNm; + if( pTableFormat && m_aCurrentTableName != pTableFormat->GetName() ) + sTableNm = pTableFormat->GetName(); + + mxEdit->UpdateRange( sBoxNms, sTableNm ); + + OUString sNew = OUStringChar(CH_LRE) + mxEdit->get_text() + + OUStringChar(CH_PDF); + + if( sNew != m_sOldFormula ) + { + // The WrtShell is in the table selection, + // then cancel the table selection otherwise, the cursor is + // positioned "in the forest" and the live update does not work! + m_pWrtShell->StartAllAction(); + + SwPaM aPam( *m_pWrtShell->GetStackCursor()->GetPoint() ); + aPam.Move( fnMoveBackward, GoInSection ); + aPam.SetMark(); + aPam.Move( fnMoveForward, GoInSection ); + + IDocumentContentOperations& rIDCO = m_pWrtShell->getIDocumentContentOperations(); + rIDCO.DeleteRange( aPam ); + rIDCO.InsertString( aPam, sNew ); + m_pWrtShell->EndAllAction(); + m_sOldFormula = sNew; + } + } + else + mxEdit->GrabFocus(); +} + +void SwInputWindow::SetFormula( const OUString& rFormula ) +{ + OUString sEdit('='); + if( !rFormula.isEmpty() ) + { + if( '=' == rFormula[0] ) + sEdit = rFormula; + else + sEdit += rFormula; + } + mxEdit->set_text( sEdit ); + mxEdit->select_region(sEdit.getLength(), sEdit.getLength()); + m_bDelSel = true; +} + +IMPL_LINK_NOARG(SwInputWindow, ModifyHdl, weld::Entry&, void) +{ + if (m_bIsTable && m_bResetUndo) + { + m_pWrtShell->StartAllAction(); + DelBoxContent(); + OUString sNew = OUStringChar(CH_LRE) + mxEdit->get_text() + + OUStringChar(CH_PDF); + m_pWrtShell->SwEditShell::Insert2( sNew ); + m_pWrtShell->EndAllAction(); + m_sOldFormula = sNew; + } +} + +void SwInputWindow::DelBoxContent() +{ + if( m_bIsTable ) + { + m_pWrtShell->StartAllAction(); + m_pWrtShell->ClearMark(); + m_pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); + m_pWrtShell->Push(); + m_pWrtShell->MoveSection( GoCurrSection, fnSectionStart ); + m_pWrtShell->SetMark(); + m_pWrtShell->MoveSection( GoCurrSection, fnSectionEnd ); + m_pWrtShell->SwEditShell::Delete(false); + m_pWrtShell->EndAllAction(); + } +} + +IMPL_LINK(InputEdit, KeyInputHdl, const KeyEvent&, rEvent, bool) +{ + bool bHandled = false; + const vcl::KeyCode aCode = rEvent.GetKeyCode(); + if (aCode == KEY_RETURN || aCode == KEY_F2) + { + bHandled = ActivateHdl(*m_xWidget); + } + else if(aCode == KEY_ESCAPE ) + { + static_cast<SwInputWindow*>(GetParent())->CancelFormula(); + bHandled = true; + } + return bHandled || ChildKeyInput(rEvent); +} + +IMPL_LINK_NOARG(InputEdit, ActivateHdl, weld::Entry&, bool) +{ + static_cast<SwInputWindow*>(GetParent())->ApplyFormula(); + return true; +} + +void InputEdit::UpdateRange(std::u16string_view rBoxes, + const OUString& rName ) +{ + if( rBoxes.empty() ) + { + GrabFocus(); + return; + } + const sal_Unicode cOpen = '<', cClose = '>', + cOpenBracket = '('; + OUString aPrefix = rName; + if(!rName.isEmpty()) + aPrefix += "."; + OUString aBoxes = aPrefix + rBoxes; + + int nSelStartPos, nSelEndPos; + m_xWidget->get_selection_bounds(nSelStartPos, nSelEndPos); + + Selection aSelection(nSelStartPos, nSelEndPos); + sal_uInt16 nSel = o3tl::narrowing<sal_uInt16>(aSelection.Len()); + // OS: The following expression ensures that in the overwrite mode, + // the selected closing parenthesis will be not deleted. + if( nSel && ( nSel > 1 || + m_xWidget->get_text()[ o3tl::narrowing<sal_uInt16>(aSelection.Min()) ] != cClose )) + m_xWidget->cut_clipboard(); + else + aSelection.Max() = aSelection.Min(); + OUString aActText(m_xWidget->get_text()); + const sal_uInt16 nLen = aActText.getLength(); + if( !nLen ) + { + OUString aStr = OUStringChar(cOpen) + aBoxes + OUStringChar(cClose); + m_xWidget->set_text(aStr); + sal_Int32 nPos = aStr.indexOf( cClose ); + OSL_ENSURE(nPos != -1, "delimiter not found"); + ++nPos; + m_xWidget->select_region(nPos, nPos); + } + else + { + bool bFound = false; + sal_Unicode cCh; + sal_uInt16 nPos, nEndPos = 0, nStartPos = o3tl::narrowing<sal_uInt16>(aSelection.Min()); + if( nStartPos-- ) + { + do { + if( cOpen == (cCh = aActText[ nStartPos ] ) || + cOpenBracket == cCh ) + { + bFound = cCh == cOpen; + break; + } + } while( nStartPos-- > 0 ); + } + if( bFound ) + { + bFound = false; + nEndPos = nStartPos; + while( nEndPos < nLen ) + { + if( cClose == aActText[ nEndPos ] ) + { + bFound = true; + break; + } + ++nEndPos; + } + // Only if the current position lies in the range or right behind. + if( bFound && ( nStartPos >= o3tl::make_unsigned(aSelection.Max()) || + o3tl::narrowing<sal_uInt16>(aSelection.Max()) > nEndPos + 1 )) + bFound = false; + } + if( bFound ) + { + nPos = ++nStartPos + 1; // We want behind + aActText = aActText.replaceAt( nStartPos, nEndPos - nStartPos, aBoxes ); + nPos = nPos + aBoxes.getLength(); + } + else + { + OUString aTmp = OUStringChar(cOpen) + aBoxes + OUStringChar(cClose); + nPos = o3tl::narrowing<sal_uInt16>(aSelection.Min()); + aActText = aActText.replaceAt( nPos, 0, aTmp ); + nPos = nPos + aTmp.getLength(); + } + if( m_xWidget->get_text() != aActText ) + { + m_xWidget->set_text(aActText); + m_xWidget->select_region(nPos, nPos); + } + } + GrabFocus(); + +} + +SwInputChild::SwInputChild(vcl::Window* _pParent, + sal_uInt16 nId, + SfxBindings const * pBindings, + SfxChildWinInfo* ) : + SfxChildWindow( _pParent, nId ) +{ + m_pDispatch = pBindings->GetDispatcher(); + SetWindow(VclPtr<SwInputWindow>::Create(_pParent, m_pDispatch)); + static_cast<SwInputWindow*>(GetWindow())->ShowWin(); + SetAlignment(SfxChildAlignment::LOWESTTOP); +} + +SwInputChild::~SwInputChild() +{ + if(m_pDispatch) + m_pDispatch->Lock(false); +} + +SfxChildWinInfo SwInputChild::GetInfo() const +{ + SfxChildWinInfo aInfo = SfxChildWindow::GetInfo(); + return aInfo; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |