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 21 #include <fmprop.hxx> 22 #include <fmtextcontroldialogs.hxx> 23 #include <fmtextcontrolfeature.hxx> 24 #include <fmtextcontrolshell.hxx> 25 #include <editeng/crossedoutitem.hxx> 26 #include <editeng/editeng.hxx> 27 #include <editeng/eeitem.hxx> 28 #include <svx/fmglob.hxx> 29 #include <editeng/scriptspaceitem.hxx> 30 #include <svx/svxids.hrc> 31 #include <editeng/udlnitem.hxx> 32 33 #include <com/sun/star/beans/XPropertySet.hpp> 34 #include <com/sun/star/awt/FontDescriptor.hpp> 35 #include <com/sun/star/frame/XDispatchProvider.hpp> 36 #include <com/sun/star/form/XForm.hpp> 37 #include <com/sun/star/container/XChild.hpp> 38 #include <com/sun/star/awt/XFocusListener.hpp> 39 #include <com/sun/star/awt/XMouseListener.hpp> 40 #include <com/sun/star/awt/XWindow.hpp> 41 #include <com/sun/star/util/URLTransformer.hpp> 42 43 #include <comphelper/processfactory.hxx> 44 #include <cppuhelper/implbase.hxx> 45 #include <sfx2/app.hxx> 46 #include <sfx2/bindings.hxx> 47 #include <sfx2/dispatch.hxx> 48 #include <sfx2/msgpool.hxx> 49 #include <sfx2/msg.hxx> 50 #include <sfx2/objsh.hxx> 51 #include <sfx2/request.hxx> 52 #include <sfx2/sfxuno.hxx> 53 #include <sfx2/viewfrm.hxx> 54 #include <svl/eitem.hxx> 55 #include <svl/intitem.hxx> 56 #include <svl/itempool.hxx> 57 #include <svl/languageoptions.hxx> 58 #include <svtools/stringtransfer.hxx> 59 #include <svl/whiter.hxx> 60 #include <toolkit/helper/vclunohelper.hxx> 61 #include <tools/diagnose_ex.h> 62 #include <sal/log.hxx> 63 #include <vcl/outdev.hxx> 64 #include <vcl/svapp.hxx> 65 66 #include <memory> 67 68 69 namespace svx 70 { 71 72 73 using namespace ::com::sun::star; 74 using namespace ::com::sun::star::uno; 75 using namespace ::com::sun::star::form; 76 using namespace ::com::sun::star::form::runtime; 77 using namespace ::com::sun::star::lang; 78 using namespace ::com::sun::star::frame; 79 using namespace ::com::sun::star::util; 80 using namespace ::com::sun::star::beans; 81 using namespace ::com::sun::star::container; 82 83 84 typedef sal_uInt16 WhichId; 85 86 87 static SfxSlotId pTextControlSlots[] = 88 { 89 SID_CLIPBOARD_FORMAT_ITEMS, 90 SID_CUT, 91 SID_COPY, 92 SID_PASTE, 93 SID_SELECTALL, 94 // SID_ATTR_TABSTOP, /* 2 */ 95 SID_ATTR_CHAR_FONT, 96 SID_ATTR_CHAR_POSTURE, 97 SID_ATTR_CHAR_WEIGHT, 98 SID_ATTR_CHAR_SHADOWED, 99 SID_ATTR_CHAR_WORDLINEMODE, 100 SID_ATTR_CHAR_CONTOUR, 101 SID_ATTR_CHAR_STRIKEOUT, 102 SID_ATTR_CHAR_UNDERLINE, 103 SID_ATTR_CHAR_FONTHEIGHT, 104 SID_ATTR_CHAR_COLOR, 105 SID_ATTR_CHAR_KERNING, 106 SID_ATTR_CHAR_LANGUAGE, /* 20 */ 107 SID_ATTR_CHAR_ESCAPEMENT, 108 SID_ATTR_PARA_ADJUST, /* 28 */ 109 SID_ATTR_PARA_ADJUST_LEFT, 110 SID_ATTR_PARA_ADJUST_RIGHT, 111 SID_ATTR_PARA_ADJUST_CENTER, 112 SID_ATTR_PARA_ADJUST_BLOCK, 113 SID_ATTR_PARA_LINESPACE, /* 33 */ 114 SID_ATTR_PARA_LINESPACE_10, 115 SID_ATTR_PARA_LINESPACE_15, 116 SID_ATTR_PARA_LINESPACE_20, 117 SID_ATTR_LRSPACE, /* 48 */ 118 SID_ATTR_ULSPACE, /* 49 */ 119 SID_ATTR_CHAR_AUTOKERN, 120 SID_SET_SUPER_SCRIPT, 121 SID_SET_SUB_SCRIPT, 122 SID_CHAR_DLG, 123 SID_PARA_DLG, 124 // SID_TEXTDIRECTION_LEFT_TO_RIGHT, /* 907 */ 125 // SID_TEXTDIRECTION_TOP_TO_BOTTOM, 126 SID_ATTR_CHAR_SCALEWIDTH, /* 911 */ 127 SID_ATTR_CHAR_RELIEF, 128 SID_ATTR_PARA_LEFT_TO_RIGHT, /* 950 */ 129 SID_ATTR_PARA_RIGHT_TO_LEFT, 130 SID_ATTR_CHAR_OVERLINE, 131 0 132 }; 133 134 // slots which we are not responsible for on the SfxShell level, but 135 // need to handle during the "paragraph attributes" and/or "character 136 // attributes" dialogs 137 static SfxSlotId pDialogSlots[] = 138 { 139 SID_ATTR_TABSTOP, 140 SID_ATTR_PARA_HANGPUNCTUATION, 141 SID_ATTR_PARA_FORBIDDEN_RULES, 142 SID_ATTR_PARA_SCRIPTSPACE, 143 SID_ATTR_CHAR_LATIN_LANGUAGE, 144 SID_ATTR_CHAR_CJK_LANGUAGE, 145 SID_ATTR_CHAR_CTL_LANGUAGE, 146 SID_ATTR_CHAR_LATIN_FONT, 147 SID_ATTR_CHAR_CJK_FONT, 148 SID_ATTR_CHAR_CTL_FONT, 149 SID_ATTR_CHAR_LATIN_FONTHEIGHT, 150 SID_ATTR_CHAR_CJK_FONTHEIGHT, 151 SID_ATTR_CHAR_CTL_FONTHEIGHT, 152 SID_ATTR_CHAR_LATIN_WEIGHT, 153 SID_ATTR_CHAR_CJK_WEIGHT, 154 SID_ATTR_CHAR_CTL_WEIGHT, 155 SID_ATTR_CHAR_LATIN_POSTURE, 156 SID_ATTR_CHAR_CJK_POSTURE, 157 SID_ATTR_CHAR_CTL_POSTURE, 158 SID_ATTR_CHAR_EMPHASISMARK, 159 0 160 }; 161 162 typedef ::cppu::WeakImplHelper < css::awt::XFocusListener 163 > FmFocusListenerAdapter_Base; 164 class FmFocusListenerAdapter : public FmFocusListenerAdapter_Base 165 { 166 private: 167 IFocusObserver* m_pObserver; 168 Reference< css::awt::XWindow > m_xWindow; 169 170 public: 171 FmFocusListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IFocusObserver* _pObserver ); 172 173 // clean up the instance 174 void dispose(); 175 176 protected: 177 virtual ~FmFocusListenerAdapter() override; 178 179 protected: 180 virtual void SAL_CALL focusGained( const css::awt::FocusEvent& e ) override; 181 virtual void SAL_CALL focusLost( const css::awt::FocusEvent& e ) override; 182 virtual void SAL_CALL disposing( const EventObject& Source ) override; 183 }; 184 185 FmFocusListenerAdapter(const Reference<css::awt::XControl> & _rxControl,IFocusObserver * _pObserver)186 FmFocusListenerAdapter::FmFocusListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IFocusObserver* _pObserver ) 187 :m_pObserver( _pObserver ) 188 ,m_xWindow( _rxControl, UNO_QUERY ) 189 { 190 191 DBG_ASSERT( m_xWindow.is(), "FmFocusListenerAdapter::FmFocusListenerAdapter: invalid control!" ); 192 osl_atomic_increment( &m_refCount ); 193 { 194 try 195 { 196 if ( m_xWindow.is() ) 197 m_xWindow->addFocusListener( this ); 198 } 199 catch( const Exception& ) 200 { 201 DBG_UNHANDLED_EXCEPTION("svx"); 202 } 203 } 204 osl_atomic_decrement( &m_refCount ); 205 } 206 207 ~FmFocusListenerAdapter()208 FmFocusListenerAdapter::~FmFocusListenerAdapter() 209 { 210 acquire(); 211 dispose(); 212 213 } 214 215 dispose()216 void FmFocusListenerAdapter::dispose() 217 { 218 if ( m_xWindow.is() ) 219 { 220 m_xWindow->removeFocusListener( this ); 221 m_xWindow.clear(); 222 } 223 } 224 225 focusGained(const css::awt::FocusEvent & e)226 void SAL_CALL FmFocusListenerAdapter::focusGained( const css::awt::FocusEvent& e ) 227 { 228 if ( m_pObserver ) 229 m_pObserver->focusGained( e ); 230 } 231 232 focusLost(const css::awt::FocusEvent & e)233 void SAL_CALL FmFocusListenerAdapter::focusLost( const css::awt::FocusEvent& e ) 234 { 235 if ( m_pObserver ) 236 m_pObserver->focusLost( e ); 237 } 238 239 disposing(const EventObject & Source)240 void SAL_CALL FmFocusListenerAdapter::disposing( const EventObject& Source ) 241 { 242 DBG_ASSERT( Source.Source == m_xWindow, "FmFocusListenerAdapter::disposing: where did this come from?" ); 243 m_xWindow.clear(); 244 } 245 246 typedef ::cppu::WeakImplHelper < css::awt::XMouseListener 247 > FmMouseListenerAdapter_Base; 248 class FmMouseListenerAdapter : public FmMouseListenerAdapter_Base 249 { 250 private: 251 IContextRequestObserver* m_pObserver; 252 Reference< css::awt::XWindow > m_xWindow; 253 254 public: 255 FmMouseListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IContextRequestObserver* _pObserver ); 256 257 // clean up the instance 258 void dispose(); 259 260 protected: 261 virtual ~FmMouseListenerAdapter() override; 262 263 protected: 264 virtual void SAL_CALL mousePressed( const css::awt::MouseEvent& e ) override; 265 virtual void SAL_CALL mouseReleased( const css::awt::MouseEvent& e ) override; 266 virtual void SAL_CALL mouseEntered( const css::awt::MouseEvent& e ) override; 267 virtual void SAL_CALL mouseExited( const css::awt::MouseEvent& e ) override; 268 virtual void SAL_CALL disposing( const EventObject& Source ) override; 269 }; 270 FmMouseListenerAdapter(const Reference<css::awt::XControl> & _rxControl,IContextRequestObserver * _pObserver)271 FmMouseListenerAdapter::FmMouseListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IContextRequestObserver* _pObserver ) 272 :m_pObserver( _pObserver ) 273 ,m_xWindow( _rxControl, UNO_QUERY ) 274 { 275 276 DBG_ASSERT( m_xWindow.is(), "FmMouseListenerAdapter::FmMouseListenerAdapter: invalid control!" ); 277 osl_atomic_increment( &m_refCount ); 278 { 279 try 280 { 281 if ( m_xWindow.is() ) 282 m_xWindow->addMouseListener( this ); 283 } 284 catch( const Exception& ) 285 { 286 DBG_UNHANDLED_EXCEPTION("svx"); 287 } 288 } 289 osl_atomic_decrement( &m_refCount ); 290 } 291 292 ~FmMouseListenerAdapter()293 FmMouseListenerAdapter::~FmMouseListenerAdapter() 294 { 295 acquire(); 296 dispose(); 297 298 } 299 300 dispose()301 void FmMouseListenerAdapter::dispose() 302 { 303 if ( m_xWindow.is() ) 304 { 305 m_xWindow->removeMouseListener( this ); 306 m_xWindow.clear(); 307 } 308 } 309 310 mousePressed(const css::awt::MouseEvent & _rEvent)311 void SAL_CALL FmMouseListenerAdapter::mousePressed( const css::awt::MouseEvent& _rEvent ) 312 { 313 SolarMutexGuard aGuard; 314 // is this a request for a context menu? 315 if ( _rEvent.PopupTrigger ) 316 { 317 if ( m_pObserver ) 318 m_pObserver->contextMenuRequested(); 319 } 320 } 321 322 mouseReleased(const css::awt::MouseEvent &)323 void SAL_CALL FmMouseListenerAdapter::mouseReleased( const css::awt::MouseEvent& /*e*/ ) 324 { 325 // not interested in 326 } 327 328 mouseEntered(const css::awt::MouseEvent &)329 void SAL_CALL FmMouseListenerAdapter::mouseEntered( const css::awt::MouseEvent& /*e*/ ) 330 { 331 // not interested in 332 } 333 334 mouseExited(const css::awt::MouseEvent &)335 void SAL_CALL FmMouseListenerAdapter::mouseExited( const css::awt::MouseEvent& /*e*/ ) 336 { 337 // not interested in 338 } 339 340 disposing(const EventObject & Source)341 void SAL_CALL FmMouseListenerAdapter::disposing( const EventObject& Source ) 342 { 343 DBG_ASSERT( Source.Source == m_xWindow, "FmMouseListenerAdapter::disposing: where did this come from?" ); 344 m_xWindow.clear(); 345 } 346 347 348 //= FmTextControlShell 349 350 351 namespace 352 { 353 lcl_translateUnoStateToItem(SfxSlotId _nSlot,const Any & _rUnoState,SfxItemSet & _rSet)354 void lcl_translateUnoStateToItem( SfxSlotId _nSlot, const Any& _rUnoState, SfxItemSet& _rSet ) 355 { 356 WhichId nWhich = _rSet.GetPool()->GetWhich( _nSlot ); 357 if ( !_rUnoState.hasValue() ) 358 { 359 if ( ( _nSlot != SID_CUT ) 360 && ( _nSlot != SID_COPY ) 361 && ( _nSlot != SID_PASTE ) 362 ) 363 { 364 _rSet.InvalidateItem( nWhich ); 365 } 366 } 367 else 368 { 369 switch ( _rUnoState.getValueType().getTypeClass() ) 370 { 371 case TypeClass_BOOLEAN: 372 { 373 bool bState = false; 374 _rUnoState >>= bState; 375 if ( _nSlot == SID_ATTR_PARA_SCRIPTSPACE ) 376 _rSet.Put( SvxScriptSpaceItem( bState, nWhich ) ); 377 else 378 _rSet.Put( SfxBoolItem( nWhich, bState ) ); 379 } 380 break; 381 382 default: 383 { 384 Sequence< PropertyValue > aComplexState; 385 if ( _rUnoState >>= aComplexState ) 386 { 387 if ( !aComplexState.hasElements() ) 388 _rSet.InvalidateItem( nWhich ); 389 else 390 { 391 SfxAllItemSet aAllItems( _rSet ); 392 TransformParameters( _nSlot, aComplexState, aAllItems ); 393 const SfxPoolItem* pTransformed = aAllItems.GetItem( nWhich ); 394 OSL_ENSURE( pTransformed, "lcl_translateUnoStateToItem: non-empty parameter sequence leading to empty item?" ); 395 if ( pTransformed ) 396 _rSet.Put( *pTransformed ); 397 else 398 _rSet.InvalidateItem( nWhich ); 399 } 400 } 401 else 402 { 403 OSL_FAIL( "lcl_translateUnoStateToItem: invalid state!" ); 404 } 405 } 406 } 407 } 408 } 409 410 lcl_getUnoSlotName(SfxSlotId _nSlotId)411 OUString lcl_getUnoSlotName( SfxSlotId _nSlotId ) 412 { 413 OUString sSlotUnoName; 414 415 SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool(); 416 const SfxSlot* pSlot = rSlotPool.GetSlot( _nSlotId ); 417 418 const sal_Char* pAsciiUnoName = nullptr; 419 if ( pSlot ) 420 { 421 pAsciiUnoName = pSlot->GetUnoName(); 422 } 423 else 424 { 425 // some hard-coded slots, which do not have a UNO name at SFX level, but which 426 // we nevertheless need to transport via UNO mechanisms, so we need a name 427 switch ( _nSlotId ) 428 { 429 case SID_ATTR_PARA_HANGPUNCTUATION: pAsciiUnoName = "AllowHangingPunctuation"; break; 430 case SID_ATTR_PARA_FORBIDDEN_RULES: pAsciiUnoName = "ApplyForbiddenCharacterRules"; break; 431 case SID_ATTR_PARA_SCRIPTSPACE: pAsciiUnoName = "UseScriptSpacing"; break; 432 } 433 } 434 435 if ( pAsciiUnoName ) 436 { 437 sSlotUnoName = ".uno:" + OUString::createFromAscii( pAsciiUnoName ); 438 } 439 else 440 { 441 SAL_WARN( "svx", "lcl_getUnoSlotName: invalid slot id, or invalid slot, or no UNO name! " 442 "(slot id: " << _nSlotId << ")"); 443 } 444 return sSlotUnoName; 445 } 446 447 lcl_determineReadOnly(const Reference<css::awt::XControl> & _rxControl)448 bool lcl_determineReadOnly( const Reference< css::awt::XControl >& _rxControl ) 449 { 450 bool bIsReadOnlyModel = true; 451 try 452 { 453 Reference< XPropertySet > xModelProps; 454 if ( _rxControl.is() ) 455 xModelProps.set(_rxControl->getModel(), css::uno::UNO_QUERY); 456 Reference< XPropertySetInfo > xModelPropInfo; 457 if ( xModelProps.is() ) 458 xModelPropInfo = xModelProps->getPropertySetInfo(); 459 460 if ( !xModelPropInfo.is() || !xModelPropInfo->hasPropertyByName( FM_PROP_READONLY ) ) 461 bIsReadOnlyModel = true; 462 else 463 { 464 bool bReadOnly = true; 465 xModelProps->getPropertyValue( FM_PROP_READONLY ) >>= bReadOnly; 466 bIsReadOnlyModel = bReadOnly; 467 } 468 } 469 catch( const Exception& ) 470 { 471 DBG_UNHANDLED_EXCEPTION("svx"); 472 } 473 return bIsReadOnlyModel; 474 } 475 476 lcl_getWindow(const Reference<css::awt::XControl> & _rxControl)477 vcl::Window* lcl_getWindow( const Reference< css::awt::XControl >& _rxControl ) 478 { 479 vcl::Window* pWindow = nullptr; 480 try 481 { 482 Reference< css::awt::XWindowPeer > xControlPeer; 483 if ( _rxControl.is() ) 484 xControlPeer = _rxControl->getPeer(); 485 if ( xControlPeer.is() ) 486 pWindow = VCLUnoHelper::GetWindow( xControlPeer ).get(); 487 } 488 catch( const Exception& ) 489 { 490 DBG_UNHANDLED_EXCEPTION("svx"); 491 } 492 493 return pWindow; 494 } 495 496 lcl_isRichText(const Reference<css::awt::XControl> & _rxControl)497 bool lcl_isRichText( const Reference< css::awt::XControl >& _rxControl ) 498 { 499 if ( !_rxControl.is() ) 500 return false; 501 502 bool bIsRichText = false; 503 try 504 { 505 Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY ); 506 Reference< XPropertySetInfo > xPSI; 507 if ( xModelProps.is() ) 508 xPSI = xModelProps->getPropertySetInfo(); 509 OUString sRichTextPropertyName = "RichText"; 510 if ( xPSI.is() && xPSI->hasPropertyByName( sRichTextPropertyName ) ) 511 { 512 OSL_VERIFY( xModelProps->getPropertyValue( sRichTextPropertyName ) >>= bIsRichText ); 513 } 514 } 515 catch( const Exception& ) 516 { 517 DBG_UNHANDLED_EXCEPTION("svx"); 518 } 519 return bIsRichText; 520 } 521 } 522 523 FmTextControlShell(SfxViewFrame * _pFrame)524 FmTextControlShell::FmTextControlShell( SfxViewFrame* _pFrame ) 525 :m_bActiveControl( false ) 526 ,m_bActiveControlIsReadOnly( true ) 527 ,m_bActiveControlIsRichText( false ) 528 ,m_pViewFrame( _pFrame ) 529 ,m_rBindings( _pFrame->GetBindings() ) 530 ,m_bNeedClipboardInvalidation( true ) 531 { 532 m_aClipboardInvalidation.SetInvokeHandler( LINK( this, FmTextControlShell, OnInvalidateClipboard ) ); 533 m_aClipboardInvalidation.SetTimeout( 200 ); 534 } 535 536 ~FmTextControlShell()537 FmTextControlShell::~FmTextControlShell() 538 { 539 dispose(); 540 } 541 542 IMPL_LINK_NOARG(FmTextControlShell,OnInvalidateClipboard,Timer *,void)543 IMPL_LINK_NOARG( FmTextControlShell, OnInvalidateClipboard, Timer*, void ) 544 { 545 if ( m_bNeedClipboardInvalidation ) 546 { 547 SAL_INFO("svx.form", "invalidating clipboard slots" ); 548 m_rBindings.Invalidate( SID_CUT ); 549 m_rBindings.Invalidate( SID_COPY ); 550 m_rBindings.Invalidate( SID_PASTE ); 551 m_bNeedClipboardInvalidation = false; 552 } 553 } 554 555 transferFeatureStatesToItemSet(ControlFeatures & _rDispatchers,SfxAllItemSet & _rSet,bool _bTranslateLatin)556 void FmTextControlShell::transferFeatureStatesToItemSet( ControlFeatures& _rDispatchers, SfxAllItemSet& _rSet, bool _bTranslateLatin ) 557 { 558 SfxItemPool& rPool = *_rSet.GetPool(); 559 560 for (const auto& rFeature : _rDispatchers) 561 { 562 SfxSlotId nSlotId( rFeature.first ); 563 #if OSL_DEBUG_LEVEL > 0 564 OUString sUnoSlotName; 565 if ( SfxGetpApp() ) 566 sUnoSlotName = lcl_getUnoSlotName( nSlotId ); 567 else 568 sUnoSlotName = "<unknown>"; 569 OString sUnoSlotNameAscii = "\"" + 570 OString( sUnoSlotName.getStr(), sUnoSlotName.getLength(), RTL_TEXTENCODING_ASCII_US ) + 571 "\""; 572 #endif 573 574 if ( _bTranslateLatin ) 575 { 576 // A rich text control offers a dispatcher for the "Font" slot/feature. 577 // Sadly, the semantics of the dispatches is that the feature "Font" depends 578 // on the current cursor position: If it's on latin text, it's the "latin font" 579 // which is set up at the control. If it's on CJK text, it's the "CJK font", and 580 // equivalent for "CTL font". 581 // The same holds for some other font related features/slots. 582 // Thus, we have separate dispatches for "Latin Font", "Latin Font Size", etc, 583 // which are only "virtual", in a sense that there exist no item with this id. 584 // So when we encounter such a dispatcher for, say, "Latin Font", we need to 585 // put an item into the set which has the "Font" id. 586 587 switch ( nSlotId ) 588 { 589 case SID_ATTR_CHAR_LATIN_FONT: nSlotId = SID_ATTR_CHAR_FONT; break; 590 case SID_ATTR_CHAR_LATIN_FONTHEIGHT:nSlotId = SID_ATTR_CHAR_FONTHEIGHT; break; 591 case SID_ATTR_CHAR_LATIN_LANGUAGE: nSlotId = SID_ATTR_CHAR_LANGUAGE; break; 592 case SID_ATTR_CHAR_LATIN_POSTURE: nSlotId = SID_ATTR_CHAR_POSTURE; break; 593 case SID_ATTR_CHAR_LATIN_WEIGHT: nSlotId = SID_ATTR_CHAR_WEIGHT; break; 594 } 595 } 596 597 WhichId nWhich = rPool.GetWhich( nSlotId ); 598 bool bIsInPool = rPool.IsInRange( nWhich ); 599 if ( bIsInPool ) 600 { 601 #if OSL_DEBUG_LEVEL > 0 602 bool bFeatureIsEnabled = rFeature.second->isFeatureEnabled(); 603 OString sMessage = "found a feature state for " + sUnoSlotNameAscii; 604 if ( !bFeatureIsEnabled ) 605 sMessage += " (disabled)"; 606 SAL_INFO("svx.form", sMessage ); 607 #endif 608 609 lcl_translateUnoStateToItem( nSlotId, rFeature.second->getFeatureState(), _rSet ); 610 } 611 #if OSL_DEBUG_LEVEL > 0 612 else 613 { 614 SAL_WARN("svx.form", "found a feature state for " << sUnoSlotNameAscii << ", but could not translate it into an item!" ); 615 } 616 #endif 617 } 618 } 619 620 executeAttributeDialog(AttributeSet _eSet,SfxRequest & rReq)621 void FmTextControlShell::executeAttributeDialog( AttributeSet _eSet, SfxRequest& rReq ) 622 { 623 const SvxFontListItem* pFontList = dynamic_cast<const SvxFontListItem*>( m_pViewFrame->GetObjectShell()->GetItem( SID_ATTR_CHAR_FONTLIST ) ); 624 DBG_ASSERT( pFontList, "FmTextControlShell::executeAttributeDialog: no font list item!" ); 625 if ( !pFontList ) 626 return; 627 628 SfxItemPool* pPool = EditEngine::CreatePool(); 629 pPool->FreezeIdRanges(); 630 std::unique_ptr< SfxItemSet > xPureItems( new SfxItemSet( *pPool ) ); 631 632 // put the current states of the items into the set 633 std::unique_ptr<SfxAllItemSet> xCurrentItems( new SfxAllItemSet( *xPureItems ) ); 634 transferFeatureStatesToItemSet( m_aControlFeatures, *xCurrentItems, false ); 635 636 // additional items, which we are not responsible for at the SfxShell level, 637 // but which need to be forwarded to the dialog, anyway 638 ControlFeatures aAdditionalFestures; 639 fillFeatureDispatchers( m_xActiveControl, pDialogSlots, aAdditionalFestures ); 640 transferFeatureStatesToItemSet( aAdditionalFestures, *xCurrentItems, true ); 641 642 std::unique_ptr<SfxTabDialogController> xDialog; 643 if (_eSet == eCharAttribs) 644 xDialog = std::make_unique<TextControlCharAttribDialog>(rReq.GetFrameWeld(), *xCurrentItems, *pFontList); 645 else 646 xDialog = std::make_unique<TextControlParaAttribDialog>(rReq.GetFrameWeld(), *xCurrentItems); 647 if ( RET_OK == xDialog->run() ) 648 { 649 const SfxItemSet& rModifiedItems = *xDialog->GetOutputItemSet(); 650 for ( WhichId nWhich = pPool->GetFirstWhich(); nWhich <= pPool->GetLastWhich(); ++nWhich ) 651 { 652 if ( rModifiedItems.GetItemState( nWhich ) == SfxItemState::SET ) 653 { 654 SfxSlotId nSlotForItemSet = pPool->GetSlotId( nWhich ); 655 const SfxPoolItem* pModifiedItem = rModifiedItems.GetItem( nWhich ); 656 657 658 SfxSlotId nSlotForDispatcher = nSlotForItemSet; 659 switch ( nSlotForDispatcher ) 660 { 661 case SID_ATTR_CHAR_FONT: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_FONT; break; 662 case SID_ATTR_CHAR_FONTHEIGHT:nSlotForDispatcher = SID_ATTR_CHAR_LATIN_FONTHEIGHT; break; 663 case SID_ATTR_CHAR_LANGUAGE: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_LANGUAGE; break; 664 case SID_ATTR_CHAR_POSTURE: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_POSTURE; break; 665 case SID_ATTR_CHAR_WEIGHT: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_WEIGHT; break; 666 } 667 668 // do we already have a dispatcher for this slot/feature? 669 ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlotForDispatcher ); 670 bool bFound = aFeaturePos != m_aControlFeatures.end( ); 671 672 if ( !bFound ) 673 { 674 aFeaturePos = aAdditionalFestures.find( nSlotForDispatcher ); 675 bFound = aFeaturePos != aAdditionalFestures.end( ); 676 } 677 678 if ( bFound ) 679 { 680 Sequence< PropertyValue > aArgs; 681 // temporarily put the modified item into a "clean" set, 682 // and let TransformItems calc the respective UNO parameters 683 xPureItems->Put( *pModifiedItem ); 684 TransformItems( nSlotForItemSet, *xPureItems, aArgs ); 685 xPureItems->ClearItem( nWhich ); 686 687 if ( ( nSlotForItemSet == SID_ATTR_PARA_HANGPUNCTUATION ) 688 || ( nSlotForItemSet == SID_ATTR_PARA_FORBIDDEN_RULES ) 689 || ( nSlotForItemSet == SID_ATTR_PARA_SCRIPTSPACE ) 690 ) 691 { 692 // these are no UNO slots, they need special handling since TransformItems cannot 693 // handle them 694 DBG_ASSERT( !aArgs.hasElements(), "FmTextControlShell::executeAttributeDialog: these are no UNO slots - are they?" ); 695 696 const SfxBoolItem* pBoolItem = dynamic_cast<const SfxBoolItem*>( pModifiedItem ); 697 DBG_ASSERT( pBoolItem, "FmTextControlShell::executeAttributeDialog: no bool item?!" ); 698 if ( pBoolItem ) 699 { 700 aArgs.realloc( 1 ); 701 aArgs[ 0 ].Name = "Enable"; 702 aArgs[ 0 ].Value <<= pBoolItem->GetValue(); 703 } 704 } 705 706 // dispatch this 707 aFeaturePos->second->dispatch( aArgs ); 708 } 709 #if OSL_DEBUG_LEVEL > 0 710 else 711 { 712 OUString sUnoSlotName = lcl_getUnoSlotName( nSlotForItemSet ); 713 if ( sUnoSlotName.isEmpty() ) 714 sUnoSlotName = "unknown (no SfxSlot)"; 715 SAL_WARN( "svx", "FmTextControShell::executeAttributeDialog: Could not handle the following item:" 716 "\n SlotID: " << nSlotForItemSet 717 << "\n WhichID: " << nWhich 718 << "\n UNO name: " << sUnoSlotName ); 719 } 720 #endif 721 } 722 } 723 rReq.Done( rModifiedItems ); 724 } 725 726 xDialog.reset(); 727 xCurrentItems.reset(); 728 xPureItems.reset(); 729 SfxItemPool::Free(pPool); 730 } 731 732 executeSelectAll()733 void FmTextControlShell::executeSelectAll( ) 734 { 735 try 736 { 737 if ( m_xActiveTextComponent.is() ) 738 { 739 sal_Int32 nTextLen = m_xActiveTextComponent->getText().getLength(); 740 m_xActiveTextComponent->setSelection( css::awt::Selection( 0, nTextLen ) ); 741 } 742 } 743 catch( const Exception& ) 744 { 745 DBG_UNHANDLED_EXCEPTION("svx"); 746 } 747 } 748 749 executeClipboardSlot(SfxSlotId _nSlot)750 void FmTextControlShell::executeClipboardSlot( SfxSlotId _nSlot ) 751 { 752 try 753 { 754 if ( m_xActiveTextComponent.is() ) 755 { 756 switch ( _nSlot ) 757 { 758 case SID_COPY: 759 case SID_CUT: 760 { 761 OUString sSelectedText( m_xActiveTextComponent->getSelectedText() ); 762 ::svt::OStringTransfer::CopyString( sSelectedText, lcl_getWindow( m_xActiveControl ) ); 763 if ( SID_CUT == _nSlot ) 764 { 765 css::awt::Selection aSelection( m_xActiveTextComponent->getSelection() ); 766 m_xActiveTextComponent->insertText( aSelection, OUString() ); 767 } 768 } 769 break; 770 case SID_PASTE: 771 { 772 OUString sClipboardContent; 773 OSL_VERIFY( ::svt::OStringTransfer::PasteString( sClipboardContent, lcl_getWindow( m_xActiveControl ) ) ); 774 css::awt::Selection aSelection( m_xActiveTextComponent->getSelection() ); 775 m_xActiveTextComponent->insertText( aSelection, sClipboardContent ); 776 } 777 break; 778 default: 779 OSL_FAIL( "FmTextControlShell::executeClipboardSlot: invalid slot!" ); 780 } 781 } 782 } 783 catch( const Exception& ) 784 { 785 DBG_UNHANDLED_EXCEPTION("svx"); 786 } 787 } 788 789 ExecuteTextAttribute(SfxRequest & _rReq)790 void FmTextControlShell::ExecuteTextAttribute( SfxRequest& _rReq ) 791 { 792 SfxSlotId nSlot = _rReq.GetSlot(); 793 794 ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlot ); 795 if ( aFeaturePos == m_aControlFeatures.end() ) 796 { 797 // special slots 798 switch ( nSlot ) 799 { 800 case SID_CHAR_DLG: 801 executeAttributeDialog( eCharAttribs, _rReq ); 802 break; 803 804 case SID_PARA_DLG: 805 executeAttributeDialog( eParaAttribs, _rReq ); 806 break; 807 808 case SID_SELECTALL: 809 executeSelectAll(); 810 break; 811 812 case SID_CUT: 813 case SID_COPY: 814 case SID_PASTE: 815 executeClipboardSlot( nSlot ); 816 break; 817 818 default: 819 DBG_ASSERT( aFeaturePos != m_aControlFeatures.end(), "FmTextControShell::ExecuteTextAttribute: I have no such dispatcher, and cannot handle it at all!" ); 820 return; 821 } 822 } 823 else 824 { 825 // slots which are dispatched to the control 826 827 switch ( nSlot ) 828 { 829 case SID_ATTR_CHAR_STRIKEOUT: 830 case SID_ATTR_CHAR_UNDERLINE: 831 case SID_ATTR_CHAR_OVERLINE: 832 { 833 SfxItemSet aToggled( *_rReq.GetArgs() ); 834 835 lcl_translateUnoStateToItem( nSlot, aFeaturePos->second->getFeatureState(), aToggled ); 836 WhichId nWhich = aToggled.GetPool()->GetWhich( nSlot ); 837 const SfxPoolItem* pItem = aToggled.GetItem( nWhich ); 838 if ( ( SID_ATTR_CHAR_UNDERLINE == nSlot ) || ( SID_ATTR_CHAR_OVERLINE == nSlot ) ) 839 { 840 const SvxTextLineItem* pTextLine = dynamic_cast<const SvxTextLineItem*>( pItem ); 841 DBG_ASSERT( pTextLine, "FmTextControlShell::ExecuteTextAttribute: ooops - no underline/overline item!" ); 842 if ( pTextLine ) 843 { 844 FontLineStyle eTL = pTextLine->GetLineStyle(); 845 if ( SID_ATTR_CHAR_UNDERLINE == nSlot ) { 846 aToggled.Put( SvxUnderlineItem( eTL == LINESTYLE_SINGLE ? LINESTYLE_NONE : LINESTYLE_SINGLE, nWhich ) ); 847 } else { 848 aToggled.Put( SvxOverlineItem( eTL == LINESTYLE_SINGLE ? LINESTYLE_NONE : LINESTYLE_SINGLE, nWhich ) ); 849 } 850 } 851 } 852 else 853 { 854 const SvxCrossedOutItem* pCrossedOut = dynamic_cast<const SvxCrossedOutItem*>( pItem ); 855 DBG_ASSERT( pCrossedOut, "FmTextControlShell::ExecuteTextAttribute: ooops - no CrossedOut item!" ); 856 if ( pCrossedOut ) 857 { 858 FontStrikeout eFS = pCrossedOut->GetStrikeout(); 859 aToggled.Put( SvxCrossedOutItem( eFS == STRIKEOUT_SINGLE ? STRIKEOUT_NONE : STRIKEOUT_SINGLE, nWhich ) ); 860 } 861 } 862 863 Sequence< PropertyValue > aArguments; 864 TransformItems( nSlot, aToggled, aArguments ); 865 aFeaturePos->second->dispatch( aArguments ); 866 } 867 break; 868 869 case SID_ATTR_CHAR_FONTHEIGHT: 870 case SID_ATTR_CHAR_FONT: 871 case SID_ATTR_CHAR_POSTURE: 872 case SID_ATTR_CHAR_WEIGHT: 873 case SID_ATTR_CHAR_SHADOWED: 874 case SID_ATTR_CHAR_CONTOUR: 875 case SID_SET_SUPER_SCRIPT: 876 case SID_SET_SUB_SCRIPT: 877 { 878 const SfxItemSet* pArgs = _rReq.GetArgs(); 879 Sequence< PropertyValue > aArgs; 880 if ( pArgs ) 881 TransformItems( nSlot, *pArgs, aArgs ); 882 aFeaturePos->second->dispatch( aArgs ); 883 } 884 break; 885 886 default: 887 if ( aFeaturePos->second->isFeatureEnabled() ) 888 aFeaturePos->second->dispatch(); 889 break; 890 } 891 } 892 _rReq.Done(); 893 } 894 895 GetTextAttributeState(SfxItemSet & _rSet)896 void FmTextControlShell::GetTextAttributeState( SfxItemSet& _rSet ) 897 { 898 SfxWhichIter aIter( _rSet ); 899 sal_uInt16 nSlot = aIter.FirstWhich(); 900 while ( nSlot ) 901 { 902 if ( ( nSlot == SID_ATTR_PARA_LEFT_TO_RIGHT ) 903 || ( nSlot == SID_ATTR_PARA_RIGHT_TO_LEFT ) 904 ) 905 { 906 if ( !SvtLanguageOptions().IsCTLFontEnabled() ) 907 { 908 _rSet.DisableItem( nSlot ); 909 nSlot = aIter.NextWhich(); 910 continue; 911 } 912 } 913 914 ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlot ); 915 if ( aFeaturePos != m_aControlFeatures.end() ) 916 { 917 if ( aFeaturePos->second->isFeatureEnabled() ) 918 lcl_translateUnoStateToItem( nSlot, aFeaturePos->second->getFeatureState(), _rSet ); 919 else 920 _rSet.DisableItem( nSlot ); 921 } 922 else 923 { 924 bool bDisable = false; 925 926 bool bNeedWriteableControl = false; 927 bool bNeedTextComponent = false; 928 bool bNeedSelection = false; 929 930 switch ( nSlot ) 931 { 932 case SID_CHAR_DLG: 933 case SID_PARA_DLG: 934 bDisable |= m_aControlFeatures.empty(); 935 bNeedWriteableControl = true; 936 break; 937 938 case SID_CUT: 939 bNeedSelection = true; 940 bNeedTextComponent = true; 941 bNeedWriteableControl = true; 942 SAL_INFO("svx.form", "need to invalidate again" ); 943 m_bNeedClipboardInvalidation = true; 944 break; 945 946 case SID_PASTE: 947 { 948 vcl::Window* pActiveControlVCLWindow = lcl_getWindow( m_xActiveControl ); 949 if ( pActiveControlVCLWindow ) 950 { 951 TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pActiveControlVCLWindow) ); 952 bDisable |= !aDataHelper.HasFormat( SotClipboardFormatId::STRING ); 953 } 954 else 955 bDisable = true; 956 957 bNeedTextComponent = true; 958 bNeedWriteableControl = true; 959 } 960 break; 961 962 case SID_COPY: 963 bNeedTextComponent = true; 964 bNeedSelection = true; 965 break; 966 967 case SID_SELECTALL: 968 bNeedTextComponent = true; 969 break; 970 971 default: 972 // slot is unknown at all 973 bDisable = true; 974 break; 975 } 976 SAL_WARN_IF( bNeedSelection && !bNeedTextComponent, "svx.form", "FmTextControlShell::GetTextAttributeState: bNeedSelection should imply bNeedTextComponent!" ); 977 978 if ( !bDisable && bNeedWriteableControl ) 979 bDisable |= !IsActiveControl( ) || m_bActiveControlIsReadOnly; 980 981 if ( !bDisable && bNeedTextComponent ) 982 bDisable |= !m_xActiveTextComponent.is(); 983 984 if ( !bDisable && bNeedSelection ) 985 { 986 css::awt::Selection aSelection = m_xActiveTextComponent->getSelection(); 987 bDisable |= aSelection.Min == aSelection.Max; 988 } 989 990 if ( bDisable ) 991 _rSet.DisableItem( nSlot ); 992 } 993 994 nSlot = aIter.NextWhich(); 995 } 996 } 997 998 IsActiveControl(bool _bCountRichTextOnly) const999 bool FmTextControlShell::IsActiveControl( bool _bCountRichTextOnly ) const 1000 { 1001 if ( _bCountRichTextOnly && !m_bActiveControlIsRichText ) 1002 return false; 1003 1004 return m_bActiveControl; 1005 } 1006 1007 dispose()1008 void FmTextControlShell::dispose() 1009 { 1010 if ( IsActiveControl() ) 1011 controlDeactivated(); 1012 if ( isControllerListening() ) 1013 stopControllerListening(); 1014 } 1015 1016 designModeChanged()1017 void FmTextControlShell::designModeChanged() 1018 { 1019 m_rBindings.Invalidate( pTextControlSlots ); 1020 } 1021 1022 formActivated(const Reference<runtime::XFormController> & _rxController)1023 void FmTextControlShell::formActivated( const Reference< runtime::XFormController >& _rxController ) 1024 { 1025 #if OSL_DEBUG_LEVEL > 0 1026 SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(_rxController.get()), 16 )); 1027 #endif 1028 1029 DBG_ASSERT( _rxController.is(), "FmTextControlShell::formActivated: invalid controller!" ); 1030 if ( !_rxController.is() ) 1031 return; 1032 1033 // sometimes, a form controller notifies activations, even if it's already activated 1034 if ( m_xActiveController == _rxController ) 1035 return; 1036 1037 try 1038 { 1039 startControllerListening( _rxController ); 1040 controlActivated( _rxController->getCurrentControl() ); 1041 } 1042 catch( const Exception& ) 1043 { 1044 DBG_UNHANDLED_EXCEPTION("svx"); 1045 } 1046 } 1047 1048 formDeactivated(const Reference<runtime::XFormController> & _rxController)1049 void FmTextControlShell::formDeactivated( const Reference< runtime::XFormController >& _rxController ) 1050 { 1051 SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(_rxController.get()), 16 )); 1052 1053 if ( IsActiveControl() ) 1054 controlDeactivated(); 1055 if ( isControllerListening() ) 1056 stopControllerListening(); 1057 } 1058 1059 startControllerListening(const Reference<runtime::XFormController> & _rxController)1060 void FmTextControlShell::startControllerListening( const Reference< runtime::XFormController >& _rxController ) 1061 { 1062 OSL_PRECOND( _rxController.is(), "FmTextControlShell::startControllerListening: invalid controller!" ); 1063 if ( !_rxController.is() ) 1064 return; 1065 1066 OSL_PRECOND( !isControllerListening(), "FmTextControlShell::startControllerListening: already listening!" ); 1067 if ( isControllerListening() ) 1068 stopControllerListening( ); 1069 DBG_ASSERT( !isControllerListening(), "FmTextControlShell::startControllerListening: inconsistence!" ); 1070 1071 try 1072 { 1073 Sequence< Reference< css::awt::XControl > > aControls( _rxController->getControls() ); 1074 m_aControlObservers.resize( 0 ); 1075 m_aControlObservers.reserve( aControls.getLength() ); 1076 1077 std::transform(aControls.begin(), aControls.end(), std::back_inserter(m_aControlObservers), 1078 [this](const Reference< css::awt::XControl >& rControl) -> FocusListenerAdapter { 1079 return FocusListenerAdapter( new FmFocusListenerAdapter( rControl, this ) ); }); 1080 } 1081 catch( const Exception& ) 1082 { 1083 DBG_UNHANDLED_EXCEPTION("svx"); 1084 } 1085 1086 m_xActiveController = _rxController; 1087 } 1088 1089 stopControllerListening()1090 void FmTextControlShell::stopControllerListening( ) 1091 { 1092 OSL_PRECOND( isControllerListening(), "FmTextControlShell::stopControllerListening: inconsistence!" ); 1093 1094 // dispose all listeners associated with the controls of the active controller 1095 for (auto& rpObserver : m_aControlObservers) 1096 { 1097 rpObserver->dispose(); 1098 } 1099 1100 FocusListenerAdapters aEmpty; 1101 m_aControlObservers.swap( aEmpty ); 1102 1103 m_xActiveController.clear(); 1104 } 1105 1106 implClearActiveControlRef()1107 void FmTextControlShell::implClearActiveControlRef() 1108 { 1109 // no more features for this control 1110 for (auto& rFeature : m_aControlFeatures) 1111 { 1112 rFeature.second->dispose(); 1113 } 1114 1115 ControlFeatures aEmpty; 1116 m_aControlFeatures.swap( aEmpty ); 1117 1118 if ( m_aContextMenuObserver.get() ) 1119 { 1120 m_aContextMenuObserver->dispose(); 1121 m_aContextMenuObserver = MouseListenerAdapter(); 1122 } 1123 1124 if ( m_xActiveTextComponent.is() ) 1125 { 1126 SAL_INFO("svx.form", "stopping timer for clipboard invalidation" ); 1127 m_aClipboardInvalidation.Stop(); 1128 } 1129 // no more active control 1130 m_xActiveControl.clear(); 1131 m_xActiveTextComponent.clear(); 1132 m_bActiveControlIsReadOnly = true; 1133 m_bActiveControlIsRichText = false; 1134 m_bActiveControl = false; 1135 } 1136 1137 controlDeactivated()1138 void FmTextControlShell::controlDeactivated( ) 1139 { 1140 DBG_ASSERT( IsActiveControl(), "FmTextControlShell::controlDeactivated: no active control!" ); 1141 1142 m_bActiveControl = false; 1143 1144 m_rBindings.Invalidate( pTextControlSlots ); 1145 } 1146 1147 controlActivated(const Reference<css::awt::XControl> & _rxControl)1148 void FmTextControlShell::controlActivated( const Reference< css::awt::XControl >& _rxControl ) 1149 { 1150 // ensure that all knittings with the previously active control are lost 1151 if ( m_xActiveControl.is() ) 1152 implClearActiveControlRef(); 1153 DBG_ASSERT( m_aControlFeatures.empty(), "FmTextControlShell::controlActivated: should have no dispatchers when I'm here!" ); 1154 1155 #if OSL_DEBUG_LEVEL > 0 1156 { 1157 Sequence< Reference< css::awt::XControl > > aActiveControls; 1158 if ( m_xActiveController.is() ) 1159 aActiveControls = m_xActiveController->getControls(); 1160 1161 bool bFoundThisControl = false; 1162 1163 const Reference< css::awt::XControl >* pControls = aActiveControls.getConstArray(); 1164 const Reference< css::awt::XControl >* pControlsEnd = pControls + aActiveControls.getLength(); 1165 for ( ; ( pControls != pControlsEnd ) && !bFoundThisControl; ++pControls ) 1166 { 1167 if ( *pControls == _rxControl ) 1168 bFoundThisControl = true; 1169 } 1170 DBG_ASSERT( bFoundThisControl, "FmTextControlShell::controlActivated: only controls which belong to the active controller can be activated!" ); 1171 } 1172 #endif 1173 // ask the control for dispatchers for our text-related slots 1174 fillFeatureDispatchers( _rxControl, pTextControlSlots, m_aControlFeatures ); 1175 1176 // remember this control 1177 m_xActiveControl = _rxControl; 1178 m_xActiveTextComponent.set(_rxControl, css::uno::UNO_QUERY); 1179 m_bActiveControlIsReadOnly = lcl_determineReadOnly( m_xActiveControl ); 1180 m_bActiveControlIsRichText = lcl_isRichText( m_xActiveControl ); 1181 1182 // if we found a rich text control, we need context menu support 1183 if ( m_bActiveControlIsRichText ) 1184 { 1185 DBG_ASSERT( nullptr == m_aContextMenuObserver.get(), "FmTextControlShell::controlActivated: already have an observer!" ); 1186 m_aContextMenuObserver = MouseListenerAdapter( new FmMouseListenerAdapter( _rxControl, this ) ); 1187 } 1188 1189 if ( m_xActiveTextComponent.is() ) 1190 { 1191 SAL_INFO("svx.form", "starting timer for clipboard invalidation" ); 1192 m_aClipboardInvalidation.Start(); 1193 } 1194 1195 m_bActiveControl = true; 1196 1197 m_rBindings.Invalidate( pTextControlSlots ); 1198 1199 if ( m_pViewFrame ) 1200 m_pViewFrame->UIFeatureChanged(); 1201 1202 // don't call the activation handler if we don't have any slots we can serve 1203 // The activation handler is used to put the shell on the top of the dispatcher stack, 1204 // so it's preferred when slots are distributed. 1205 // Note that this is a slight hack, to prevent that we grab slots from the SfxDispatcher 1206 // which should be served by other shells (e.g. Cut/Copy/Paste). 1207 // A real solution would be a forwarding-mechanism for slots: We should be on the top 1208 // if we're active, but if we cannot handle the slot, then we need to tell the dispatcher 1209 // to skip our shell, and pass the slot to the next one. However, this mechanism is not 1210 // not in place in SFX. 1211 // Another possibility would be to have dedicated shells for the slots which we might 1212 // or might not be able to serve. However, this could probably increase the number of 1213 // shells too much (In theory, nearly every slot could have an own shell then). 1214 1215 // #i51621# / 2005-08-19 / frank.schoenheit@sun.com 1216 // bool bHaveAnyServeableSlots = m_xActiveTextComponent.is() || !m_aControlFeatures.empty(); 1217 // LEM: not calling m_aControlActivatonHandler causes fdo#63695, so disable this hack for now. 1218 m_aControlActivationHandler.Call( nullptr ); 1219 1220 m_bNeedClipboardInvalidation = true; 1221 } 1222 1223 fillFeatureDispatchers(const Reference<css::awt::XControl> & _rxControl,SfxSlotId * _pZeroTerminatedSlots,ControlFeatures & _rDispatchers)1224 void FmTextControlShell::fillFeatureDispatchers(const Reference< css::awt::XControl >& _rxControl, SfxSlotId* _pZeroTerminatedSlots, 1225 ControlFeatures& _rDispatchers) 1226 { 1227 Reference< XDispatchProvider > xProvider( _rxControl, UNO_QUERY ); 1228 SfxApplication* pApplication = SfxGetpApp(); 1229 DBG_ASSERT( pApplication, "FmTextControlShell::fillFeatureDispatchers: no SfxApplication!" ); 1230 if ( xProvider.is() && pApplication ) 1231 { 1232 SfxSlotId* pSlots = _pZeroTerminatedSlots; 1233 while ( *pSlots ) 1234 { 1235 FmTextControlFeature* pDispatcher = implGetFeatureDispatcher( xProvider, pApplication, *pSlots ); 1236 if ( pDispatcher ) 1237 _rDispatchers.emplace( *pSlots, ControlFeature( pDispatcher ) ); 1238 1239 ++pSlots; 1240 } 1241 } 1242 } 1243 1244 implGetFeatureDispatcher(const Reference<XDispatchProvider> & _rxProvider,SfxApplication const * _pApplication,SfxSlotId _nSlot)1245 FmTextControlFeature* FmTextControlShell::implGetFeatureDispatcher( const Reference< XDispatchProvider >& _rxProvider, SfxApplication const * _pApplication, SfxSlotId _nSlot ) 1246 { 1247 OSL_PRECOND( _rxProvider.is() && _pApplication, "FmTextControlShell::implGetFeatureDispatcher: invalid arg(s)!" ); 1248 URL aFeatureURL; 1249 aFeatureURL.Complete = lcl_getUnoSlotName( _nSlot ); 1250 try 1251 { 1252 if ( !m_xURLTransformer.is() ) 1253 { 1254 m_xURLTransformer = util::URLTransformer::create( ::comphelper::getProcessComponentContext() ); 1255 } 1256 if ( m_xURLTransformer.is() ) 1257 m_xURLTransformer->parseStrict( aFeatureURL ); 1258 } 1259 catch( const Exception& ) 1260 { 1261 DBG_UNHANDLED_EXCEPTION("svx"); 1262 } 1263 Reference< XDispatch > xDispatcher = _rxProvider->queryDispatch( aFeatureURL, OUString(), 0xFF ); 1264 if ( xDispatcher.is() ) 1265 return new FmTextControlFeature( xDispatcher, aFeatureURL, _nSlot, this ); 1266 return nullptr; 1267 } 1268 1269 Invalidate(SfxSlotId _nSlot)1270 void FmTextControlShell::Invalidate( SfxSlotId _nSlot ) 1271 { 1272 m_rBindings.Invalidate( _nSlot ); 1273 // despite this method being called "Invalidate", we also update here - this gives more immediate 1274 // feedback in the UI 1275 m_rBindings.Update( _nSlot ); 1276 } 1277 1278 focusGained(const css::awt::FocusEvent & _rEvent)1279 void FmTextControlShell::focusGained( const css::awt::FocusEvent& _rEvent ) 1280 { 1281 Reference< css::awt::XControl > xControl( _rEvent.Source, UNO_QUERY ); 1282 1283 #if OSL_DEBUG_LEVEL > 0 1284 SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(xControl.get()), 16 )); 1285 #endif 1286 1287 DBG_ASSERT( xControl.is(), "FmTextControlShell::focusGained: suspicious focus event!" ); 1288 if ( xControl.is() ) 1289 controlActivated( xControl ); 1290 } 1291 1292 focusLost(const css::awt::FocusEvent & _rEvent)1293 void FmTextControlShell::focusLost( const css::awt::FocusEvent& _rEvent ) 1294 { 1295 Reference< css::awt::XControl > xControl( _rEvent.Source, UNO_QUERY ); 1296 1297 #if OSL_DEBUG_LEVEL > 0 1298 SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(xControl.get()), 16 )); 1299 #endif 1300 1301 m_bActiveControl = false; 1302 } 1303 1304 ForgetActiveControl()1305 void FmTextControlShell::ForgetActiveControl() 1306 { 1307 implClearActiveControlRef(); 1308 } 1309 1310 contextMenuRequested()1311 void FmTextControlShell::contextMenuRequested() 1312 { 1313 m_rBindings.GetDispatcher()->ExecutePopup( "formrichtext" ); 1314 } 1315 1316 1317 } 1318 1319 1320 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1321