1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <sal/config.h> 21 #include <sal/log.hxx> 22 23 #include <comphelper/string.hxx> 24 #include <officecfg/Office/Common.hxx> 25 #include <tools/gen.hxx> 26 #include <sfx2/objface.hxx> 27 #include <sfx2/viewfrm.hxx> 28 #include <sfx2/dispatch.hxx> 29 #include <svx/ruler.hxx> 30 #include <svl/zforlist.hxx> 31 #include <svl/stritem.hxx> 32 #include <vcl/settings.hxx> 33 34 #include <swtypes.hxx> 35 #include <cmdid.h> 36 #include <swmodule.hxx> 37 #include <wrtsh.hxx> 38 #include <view.hxx> 39 #include <calc.hxx> 40 #include <inputwin.hxx> 41 #include <fldbas.hxx> 42 #include <fldmgr.hxx> 43 #include <frmfmt.hxx> 44 #include <cellatr.hxx> 45 #include <edtwin.hxx> 46 #include <helpids.h> 47 #include <strings.hrc> 48 #include <bitmaps.hlst> 49 50 // Only for the UpdateRange: Delete the box in which the stacked cursor is positioned. 51 #include <pam.hxx> 52 53 #include <swundo.hxx> 54 #include <dbui.hrc> 55 56 #include <IDocumentContentOperations.hxx> 57 58 #define ED_POS 2 59 #define ED_FORMULA 3 60 61 SFX_IMPL_POS_CHILDWINDOW_WITHID( SwInputChild, FN_EDIT_FORMULA, SFX_OBJECTBAR_OBJECT ) 62 63 SwInputWindow::SwInputWindow(vcl::Window* pParent, SfxDispatcher const * pDispatcher) 64 : ToolBox(pParent, WB_3DLOOK|WB_BORDER) 65 , aPos(VclPtr<Edit>::Create(this, WB_3DLOOK|WB_CENTER|WB_BORDER|WB_READONLY)) 66 , aEdit(VclPtr<InputEdit>::Create(this, WB_3DLOOK|WB_TABSTOP|WB_BORDER|WB_NOHIDESELECTION)) 67 , pWrtShell(nullptr) 68 , pView(nullptr) 69 , m_bDoesUndo(true) 70 , m_bResetUndo(false) 71 , m_bCallUndo(false) 72 { 73 bFirst = true; 74 bIsTable = bDelSel = false; 75 76 aEdit->SetSizePixel(aEdit->CalcMinimumSize()); 77 aPos->SetSizePixel(aPos->LogicToPixel(Size(45, 11), MapMode(MapUnit::MapAppFont))); 78 79 InsertItem(FN_FORMULA_CALC, Image(StockImage::Yes, RID_BMP_FORMULA_CALC), 80 SwResId(STR_FORMULA_CALC)); 81 InsertItem(FN_FORMULA_CANCEL, Image(StockImage::Yes, RID_BMP_FORMULA_CANCEL), 82 SwResId(STR_FORMULA_CANCEL)); 83 InsertItem(FN_FORMULA_APPLY, Image(StockImage::Yes, RID_BMP_FORMULA_APPLY), 84 SwResId(STR_FORMULA_APPLY)); 85 86 SetHelpId(FN_FORMULA_CALC, HID_TBX_FORMULA_CALC); 87 SetHelpId(FN_FORMULA_CANCEL, HID_TBX_FORMULA_CANCEL); 88 SetHelpId(FN_FORMULA_APPLY, HID_TBX_FORMULA_APPLY); 89 90 SwView *pDispatcherView = dynamic_cast<SwView*>(pDispatcher ? pDispatcher->GetFrame()->GetViewShell() : nullptr); 91 SwView* pActiveView = ::GetActiveView(); 92 if (pDispatcherView == pActiveView) 93 pView = pActiveView; 94 pWrtShell = pView ? pView->GetWrtShellPtr() : nullptr; 95 96 InsertWindow(ED_POS, aPos.get(), ToolBoxItemBits::NONE, 0); 97 SetItemText(ED_POS, SwResId(STR_ACCESS_FORMULA_TYPE)); 98 aPos->SetAccessibleName(SwResId(STR_ACCESS_FORMULA_TYPE)); 99 SetAccessibleName(SwResId(STR_ACCESS_FORMULA_TOOLBAR)); 100 InsertSeparator ( 1 ); 101 InsertSeparator (); 102 InsertWindow(ED_FORMULA, aEdit.get()); 103 SetItemText(ED_FORMULA, SwResId(STR_ACCESS_FORMULA_TEXT)); 104 aEdit->SetAccessibleName(SwResId(STR_ACCESS_FORMULA_TEXT)); 105 SetHelpId(ED_FORMULA, HID_EDIT_FORMULA); 106 107 SetItemBits( FN_FORMULA_CALC, GetItemBits( FN_FORMULA_CALC ) | ToolBoxItemBits::DROPDOWNONLY ); 108 SetDropdownClickHdl( LINK( this, SwInputWindow, DropdownClickHdl )); 109 110 Size aSizeTbx = CalcWindowSizePixel(); 111 Size aEditSize = aEdit->GetSizePixel(); 112 tools::Rectangle aItemRect( GetItemRect(FN_FORMULA_CALC) ); 113 long nMaxHeight = std::max(aEditSize.Height(), aItemRect.GetHeight()); 114 if( nMaxHeight+2 > aSizeTbx.Height() ) 115 aSizeTbx.setHeight( nMaxHeight+2 ); 116 Size aSize = GetSizePixel(); 117 aSize.setHeight( aSizeTbx.Height() ); 118 SetSizePixel( aSize ); 119 120 // align edit and item vcentered 121 Size aPosSize = aPos->GetSizePixel(); 122 aPosSize.setHeight( nMaxHeight ); 123 aEditSize.setHeight( nMaxHeight ); 124 Point aPosPos = aPos->GetPosPixel(); 125 Point aEditPos = aEdit->GetPosPixel(); 126 aPosPos.setY( (aSize.Height() - nMaxHeight)/2 + 1 ); 127 aEditPos.setY( (aSize.Height() - nMaxHeight)/2 + 1 ); 128 aPos->SetPosSizePixel( aPosPos, aPosSize ); 129 aEdit->SetPosSizePixel( aEditPos, aEditSize ); 130 } 131 132 SwInputWindow::~SwInputWindow() 133 { 134 disposeOnce(); 135 } 136 137 void SwInputWindow::dispose() 138 { 139 // wake rulers 140 if(pView) 141 { 142 pView->GetHRuler().SetActive(); 143 pView->GetVRuler().SetActive(); 144 } 145 pMgr.reset(); 146 if(pWrtShell) 147 pWrtShell->EndSelTableCells(); 148 149 CleanupUglyHackWithUndo(); 150 151 aPos.disposeAndClear(); 152 aEdit.disposeAndClear(); 153 ToolBox::dispose(); 154 } 155 156 void SwInputWindow::CleanupUglyHackWithUndo() 157 { 158 if (m_bResetUndo) 159 { 160 if (pWrtShell) 161 { 162 DelBoxContent(); 163 pWrtShell->DoUndo(m_bDoesUndo); 164 if (m_bCallUndo) 165 { 166 pWrtShell->Undo(); 167 } 168 } 169 m_bResetUndo = false; // #i117122# once is enough :) 170 } 171 } 172 173 void SwInputWindow::Resize() 174 { 175 ToolBox::Resize(); 176 177 long nWidth = GetSizePixel().Width(); 178 long nLeft = aEdit->GetPosPixel().X(); 179 Size aEditSize = aEdit->GetSizePixel(); 180 181 aEditSize.setWidth( std::max( static_cast<long>(nWidth - nLeft - 5), long(0) ) ); 182 aEdit->SetSizePixel( aEditSize ); 183 aEdit->Invalidate(); 184 } 185 186 void SwInputWindow::ShowWin() 187 { 188 bIsTable = false; 189 // stop rulers 190 if(pView) 191 { 192 pView->GetHRuler().SetActive( false ); 193 pView->GetVRuler().SetActive( false ); 194 195 OSL_ENSURE(pWrtShell, "no WrtShell!"); 196 // Cursor in table 197 bIsTable = pWrtShell->IsCursorInTable(); 198 199 if( bFirst ) 200 pWrtShell->SelTableCells( LINK( this, SwInputWindow, 201 SelTableCellsNotify) ); 202 if( bIsTable ) 203 { 204 const OUString& rPos = pWrtShell->GetBoxNms(); 205 sal_Int32 nPos = 0; 206 short nSrch = -1; 207 while( (nPos = rPos.indexOf( ':',nPos + 1 ) ) != -1 ) 208 nSrch = static_cast<short>(nPos); 209 aPos->SetText( rPos.copy( ++nSrch ) ); 210 aCurrentTableName = pWrtShell->GetTableFormat()->GetName(); 211 } 212 else 213 aPos->SetText(SwResId(STR_TBL_FORMULA)); 214 215 // Edit current field 216 OSL_ENSURE(pMgr == nullptr, "FieldManager not deleted"); 217 pMgr.reset(new SwFieldMgr); 218 219 // Form should always begin with "=" , so set here 220 OUString sEdit('='); 221 if( pMgr->GetCurField() && TYP_FORMELFLD == pMgr->GetCurTypeId() ) 222 { 223 sEdit += pMgr->GetCurFieldPar2(); 224 } 225 else if( bFirst && bIsTable ) 226 { 227 m_bResetUndo = true; 228 SAL_WARN_IF( 229 officecfg::Office::Common::Undo::Steps::get() <= 0, 230 "sw", "/org.openoffice.Office.Common/Undo/Steps <= 0"); 231 232 m_bDoesUndo = pWrtShell->DoesUndo(); 233 if( !m_bDoesUndo ) 234 { 235 pWrtShell->DoUndo(); 236 } 237 238 if( !pWrtShell->SwCursorShell::HasSelection() ) 239 { 240 pWrtShell->MoveSection( GoCurrSection, fnSectionStart ); 241 pWrtShell->SetMark(); 242 pWrtShell->MoveSection( GoCurrSection, fnSectionEnd ); 243 } 244 if( pWrtShell->SwCursorShell::HasSelection() ) 245 { 246 pWrtShell->StartUndo( SwUndoId::DELETE ); 247 pWrtShell->Delete(); 248 if( SwUndoId::EMPTY != pWrtShell->EndUndo( SwUndoId::DELETE )) 249 { 250 m_bCallUndo = true; 251 } 252 } 253 pWrtShell->DoUndo(false); 254 255 SfxItemSet aSet( pWrtShell->GetAttrPool(), svl::Items<RES_BOXATR_FORMULA, RES_BOXATR_FORMULA>{} ); 256 if( pWrtShell->GetTableBoxFormulaAttrs( aSet )) 257 sEdit += aSet.Get( RES_BOXATR_FORMULA ).GetFormula(); 258 } 259 260 if( bFirst ) 261 { 262 // Set WrtShell flags correctly 263 pWrtShell->SttSelect(); 264 pWrtShell->EndSelect(); 265 } 266 267 bFirst = false; 268 269 aEdit->SetModifyHdl( LINK( this, SwInputWindow, ModifyHdl )); 270 271 aEdit->SetText( sEdit ); 272 aEdit->SetSelection( Selection( sEdit.getLength(), sEdit.getLength() ) ); 273 sOldFormula = sEdit; 274 275 aEdit->Invalidate(); 276 aEdit->Update(); 277 aEdit->GrabFocus(); 278 // For input cut the UserInterface 279 280 pView->GetEditWin().LockKeyInput(true); 281 pView->GetViewFrame()->GetDispatcher()->Lock(true); 282 pWrtShell->Push(); 283 } 284 ToolBox::Show(); 285 } 286 287 IMPL_LINK( SwInputWindow, MenuHdl, Menu *, pMenu, bool ) 288 { 289 OString aCommand = pMenu->GetCurItemIdent(); 290 if (!aCommand.isEmpty()) 291 { 292 aCommand += " "; 293 aEdit->ReplaceSelected(OStringToOUString(aCommand, RTL_TEXTENCODING_ASCII_US)); 294 } 295 return false; 296 } 297 298 IMPL_LINK_NOARG(SwInputWindow, DropdownClickHdl, ToolBox *, void) 299 { 300 sal_uInt16 nCurID = GetCurItemId(); 301 EndSelection(); // reset back CurItemId ! 302 if (nCurID == FN_FORMULA_CALC) 303 { 304 VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "modules/swriter/ui/inputwinmenu.ui", ""); 305 VclPtr<PopupMenu> aPopMenu(aBuilder.get_menu("menu")); 306 aPopMenu->SetSelectHdl(LINK(this, SwInputWindow, MenuHdl)); 307 aPopMenu->Execute(this, GetItemRect(FN_FORMULA_CALC), PopupMenuFlags::NoMouseUpClose); 308 } 309 } 310 311 void SwInputWindow::Click( ) 312 { 313 sal_uInt16 nCurID = GetCurItemId(); 314 EndSelection(); // reset back CurItemId ! 315 switch ( nCurID ) 316 { 317 case FN_FORMULA_CANCEL: 318 { 319 CancelFormula(); 320 } 321 break; 322 case FN_FORMULA_APPLY: 323 { 324 ApplyFormula(); 325 } 326 break; 327 } 328 } 329 330 void SwInputWindow::ApplyFormula() 331 { 332 pView->GetViewFrame()->GetDispatcher()->Lock(false); 333 pView->GetEditWin().LockKeyInput(false); 334 CleanupUglyHackWithUndo(); 335 pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); 336 337 // Form should always begin with "=", so remove it here again 338 OUString sEdit(comphelper::string::strip(aEdit->GetText(), ' ')); 339 if( !sEdit.isEmpty() && '=' == sEdit[0] ) 340 sEdit = sEdit.copy( 1 ); 341 SfxStringItem aParam(FN_EDIT_FORMULA, sEdit); 342 343 pWrtShell->EndSelTableCells(); 344 pView->GetEditWin().GrabFocus(); 345 const SfxPoolItem* aArgs[2]; 346 aArgs[0] = &aParam; 347 aArgs[1] = nullptr; 348 pView->GetViewFrame()->GetBindings().Execute( FN_EDIT_FORMULA, aArgs, SfxCallMode::ASYNCHRON ); 349 } 350 351 void SwInputWindow::CancelFormula() 352 { 353 if(pView) 354 { 355 pView->GetViewFrame()->GetDispatcher()->Lock( false ); 356 pView->GetEditWin().LockKeyInput(false); 357 CleanupUglyHackWithUndo(); 358 pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); 359 360 if( bDelSel ) 361 pWrtShell->EnterStdMode(); 362 363 pWrtShell->EndSelTableCells(); 364 365 pView->GetEditWin().GrabFocus(); 366 367 pView->GetViewFrame()->GetDispatcher()->Execute( FN_EDIT_FORMULA, SfxCallMode::ASYNCHRON); 368 } 369 } 370 371 const sal_Unicode CH_LRE = 0x202a; 372 const sal_Unicode CH_PDF = 0x202c; 373 374 IMPL_LINK( SwInputWindow, SelTableCellsNotify, SwWrtShell&, rCaller, void ) 375 { 376 if(bIsTable) 377 { 378 SwFrameFormat* pTableFormat = rCaller.GetTableFormat(); 379 OUString sBoxNms( rCaller.GetBoxNms() ); 380 OUString sTableNm; 381 if( pTableFormat && aCurrentTableName != pTableFormat->GetName() ) 382 sTableNm = pTableFormat->GetName(); 383 384 aEdit->UpdateRange( sBoxNms, sTableNm ); 385 386 OUString sNew = OUStringLiteral1(CH_LRE) + aEdit->GetText() 387 + OUStringLiteral1(CH_PDF); 388 389 if( sNew != sOldFormula ) 390 { 391 // The WrtShell is in the table selection, 392 // then cancel the table selection otherwise, the cursor is 393 // positioned "in the forest" and the live update does not work! 394 pWrtShell->StartAllAction(); 395 396 SwPaM aPam( *pWrtShell->GetStackCursor()->GetPoint() ); 397 aPam.Move( fnMoveBackward, GoInSection ); 398 aPam.SetMark(); 399 aPam.Move( fnMoveForward, GoInSection ); 400 401 IDocumentContentOperations& rIDCO = pWrtShell->getIDocumentContentOperations(); 402 rIDCO.DeleteRange( aPam ); 403 rIDCO.InsertString( aPam, sNew ); 404 pWrtShell->EndAllAction(); 405 sOldFormula = sNew; 406 } 407 } 408 else 409 aEdit->GrabFocus(); 410 } 411 412 void SwInputWindow::SetFormula( const OUString& rFormula ) 413 { 414 OUString sEdit('='); 415 if( !rFormula.isEmpty() ) 416 { 417 if( '=' == rFormula[0] ) 418 sEdit = rFormula; 419 else 420 sEdit += rFormula; 421 } 422 aEdit->SetText( sEdit ); 423 aEdit->SetSelection( Selection( sEdit.getLength(), sEdit.getLength() ) ); 424 aEdit->Invalidate(); 425 bDelSel = true; 426 } 427 428 IMPL_LINK_NOARG(SwInputWindow, ModifyHdl, Edit&, void) 429 { 430 if (bIsTable && m_bResetUndo) 431 { 432 pWrtShell->StartAllAction(); 433 DelBoxContent(); 434 OUString sNew = OUStringLiteral1(CH_LRE) + aEdit->GetText() 435 + OUStringLiteral1(CH_PDF); 436 pWrtShell->SwEditShell::Insert2( sNew ); 437 pWrtShell->EndAllAction(); 438 sOldFormula = sNew; 439 } 440 } 441 442 void SwInputWindow::DelBoxContent() 443 { 444 if( bIsTable ) 445 { 446 pWrtShell->StartAllAction(); 447 pWrtShell->ClearMark(); 448 pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); 449 pWrtShell->Push(); 450 pWrtShell->MoveSection( GoCurrSection, fnSectionStart ); 451 pWrtShell->SetMark(); 452 pWrtShell->MoveSection( GoCurrSection, fnSectionEnd ); 453 pWrtShell->SwEditShell::Delete(); 454 pWrtShell->EndAllAction(); 455 } 456 } 457 458 void InputEdit::KeyInput(const KeyEvent& rEvent) 459 { 460 const vcl::KeyCode aCode = rEvent.GetKeyCode(); 461 if(aCode == KEY_RETURN || aCode == KEY_F2 ) 462 static_cast<SwInputWindow*>(GetParent())->ApplyFormula(); 463 else if(aCode == KEY_ESCAPE ) 464 static_cast<SwInputWindow*>(GetParent())->CancelFormula(); 465 else 466 Edit::KeyInput(rEvent); 467 } 468 469 void InputEdit::UpdateRange(const OUString& rBoxes, 470 const OUString& rName ) 471 { 472 if( rBoxes.isEmpty() ) 473 { 474 GrabFocus(); 475 return; 476 } 477 const sal_Unicode cOpen = '<', cClose = '>', 478 cOpenBracket = '('; 479 OUString aPrefix = rName; 480 if(!rName.isEmpty()) 481 aPrefix += "."; 482 OUString aBoxes = aPrefix; 483 aBoxes += rBoxes; 484 Selection aSelection(GetSelection()); 485 sal_uInt16 nSel = static_cast<sal_uInt16>(aSelection.Len()); 486 // OS: The following expression ensures that in the overwrite mode, 487 // the selected closing parenthesis will be not deleted. 488 if( nSel && ( nSel > 1 || 489 GetText()[ static_cast<sal_uInt16>(aSelection.Min()) ] != cClose )) 490 Cut(); 491 else 492 aSelection.Max() = aSelection.Min(); 493 OUString aActText(GetText()); 494 const sal_uInt16 nLen = aActText.getLength(); 495 if( !nLen ) 496 { 497 OUString aStr = OUStringBuffer(). 498 append(cOpen).append(aBoxes).append(cClose). 499 makeStringAndClear(); 500 SetText(aStr); 501 sal_Int32 nPos = aStr.indexOf( cClose ); 502 OSL_ENSURE(nPos != -1, "delimiter not found"); 503 ++nPos; 504 SetSelection( Selection( nPos, nPos )); 505 } 506 else 507 { 508 bool bFound = false; 509 sal_Unicode cCh; 510 sal_uInt16 nPos, nEndPos = 0, nStartPos = static_cast<sal_uInt16>(aSelection.Min()); 511 if( nStartPos-- ) 512 { 513 do { 514 if( cOpen == (cCh = aActText[ nStartPos ] ) || 515 cOpenBracket == cCh ) 516 { 517 bFound = cCh == cOpen; 518 break; 519 } 520 } while( nStartPos-- > 0 ); 521 } 522 if( bFound ) 523 { 524 bFound = false; 525 nEndPos = nStartPos; 526 while( nEndPos < nLen ) 527 { 528 if( cClose == (cCh = aActText[ nEndPos ])) 529 { 530 bFound = true; 531 break; 532 } 533 ++nEndPos; 534 } 535 // Only if the current position lies in the range or right behind. 536 if( bFound && !( nStartPos < static_cast<sal_uInt16>(aSelection.Max()) && 537 static_cast<sal_uInt16>(aSelection.Max()) <= nEndPos + 1 )) 538 bFound = false; 539 } 540 if( bFound ) 541 { 542 nPos = ++nStartPos + 1; // We want behind 543 aActText = aActText.replaceAt( nStartPos, nEndPos - nStartPos, aBoxes ); 544 nPos = nPos + aBoxes.getLength(); 545 } 546 else 547 { 548 OUString aTmp = OUStringBuffer(). 549 append(cOpen).append(aBoxes).append(cClose). 550 makeStringAndClear(); 551 nPos = static_cast<sal_uInt16>(aSelection.Min()); 552 aActText = aActText.replaceAt( nPos, 0, aTmp ); 553 nPos = nPos + aTmp.getLength(); 554 } 555 if( GetText() != aActText ) 556 { 557 SetText( aActText ); 558 SetSelection( Selection( nPos, nPos ) ); 559 } 560 } 561 GrabFocus(); 562 563 } 564 565 SwInputChild::SwInputChild(vcl::Window* _pParent, 566 sal_uInt16 nId, 567 SfxBindings const * pBindings, 568 SfxChildWinInfo* ) : 569 SfxChildWindow( _pParent, nId ) 570 { 571 pDispatch = pBindings->GetDispatcher(); 572 SetWindow(VclPtr<SwInputWindow>::Create(_pParent, pDispatch)); 573 static_cast<SwInputWindow*>(GetWindow())->ShowWin(); 574 SetAlignment(SfxChildAlignment::LOWESTTOP); 575 } 576 577 SwInputChild::~SwInputChild() 578 { 579 if(pDispatch) 580 pDispatch->Lock(false); 581 } 582 583 SfxChildWinInfo SwInputChild::GetInfo() const 584 { 585 SfxChildWinInfo aInfo = SfxChildWindow::GetInfo(); 586 return aInfo; 587 } 588 589 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 590
