1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_sw.hxx" 26 27 28 #include <com/sun/star/accessibility/AccessibleStateType.hpp> 29 #include <com/sun/star/accessibility/AccessibleEventId.hpp> 30 #include <unotools/accessiblestatesethelper.hxx> 31 #include <vos/mutex.hxx> 32 #include <vcl/svapp.hxx> 33 #include <vcl/window.hxx> 34 #include <frmfmt.hxx> 35 #include <ndnotxt.hxx> 36 #include <flyfrm.hxx> 37 #include <cntfrm.hxx> 38 #include <fmtcntnt.hxx> 39 #include <ndindex.hxx> 40 #include "fesh.hxx" 41 #include <hints.hxx> 42 #include "accmap.hxx" 43 #include "accframebase.hxx" 44 45 #ifndef _CRSRSH_HXX 46 #include <crsrsh.hxx> 47 #endif 48 #ifndef _FESH_HXX 49 #include "fesh.hxx" 50 #endif 51 #ifndef _TXTFRM_HXX 52 #include <txtfrm.hxx> 53 #endif 54 #ifndef _NDTXT_HXX 55 #include <ndtxt.hxx> 56 #endif 57 #ifndef _DCONTACT_HXX 58 #include <dcontact.hxx> 59 #endif 60 #ifndef _FMTANCHR_HXX 61 #include <fmtanchr.hxx> 62 #endif 63 using namespace ::com::sun::star; 64 using namespace ::com::sun::star::accessibility; 65 using ::rtl::OUString; 66 67 sal_Bool SwAccessibleFrameBase::IsSelected() 68 { 69 sal_Bool bRet = sal_False; 70 71 DBG_ASSERT( GetMap(), "no map?" ); 72 const ViewShell *pVSh = GetMap()->GetShell(); 73 DBG_ASSERT( pVSh, "no shell?" ); 74 if( pVSh->ISA( SwFEShell ) ) 75 { 76 const SwFEShell *pFESh = static_cast< const SwFEShell * >( pVSh ); 77 const SwFrm *pFlyFrm = pFESh->GetCurrFlyFrm(); 78 if( pFlyFrm == GetFrm() ) 79 bRet = sal_True; 80 } 81 82 return bRet; 83 } 84 85 void SwAccessibleFrameBase::GetStates( 86 ::utl::AccessibleStateSetHelper& rStateSet ) 87 { 88 SwAccessibleContext::GetStates( rStateSet ); 89 90 const ViewShell *pVSh = GetMap()->GetShell(); 91 DBG_ASSERT( pVSh, "no shell?" ); 92 sal_Bool bSelectable = pVSh->ISA( SwFEShell ); 93 94 // SELECTABLE 95 if( bSelectable ) 96 rStateSet.AddState( AccessibleStateType::SELECTABLE ); 97 98 // FOCUSABLE 99 if( bSelectable ) 100 rStateSet.AddState( AccessibleStateType::FOCUSABLE ); 101 102 // SELECTED and FOCUSED 103 if( IsSelected() ) 104 { 105 rStateSet.AddState( AccessibleStateType::SELECTED ); 106 ASSERT( bIsSelected, "bSelected out of sync" ); 107 ::vos::ORef < SwAccessibleContext > xThis( this ); 108 GetMap()->SetCursorContext( xThis ); 109 110 Window *pWin = GetWindow(); 111 if( pWin && pWin->HasFocus() ) 112 rStateSet.AddState( AccessibleStateType::FOCUSED ); 113 } 114 if( GetSelectedState() ) 115 rStateSet.AddState( AccessibleStateType::SELECTED ); 116 } 117 118 119 sal_uInt8 SwAccessibleFrameBase::GetNodeType( const SwFlyFrm *pFlyFrm ) 120 { 121 sal_uInt8 nType = ND_TEXTNODE; 122 if( pFlyFrm->Lower() ) 123 { 124 if( pFlyFrm->Lower()->IsNoTxtFrm() ) 125 { 126 const SwCntntFrm *pCntFrm = 127 static_cast<const SwCntntFrm *>( pFlyFrm->Lower() ); 128 nType = pCntFrm->GetNode()->GetNodeType(); 129 } 130 } 131 else 132 { 133 const SwFrmFmt *pFrmFmt = pFlyFrm->GetFmt(); 134 const SwFmtCntnt& rCntnt = pFrmFmt->GetCntnt(); 135 const SwNodeIndex *pNdIdx = rCntnt.GetCntntIdx(); 136 if( pNdIdx ) 137 { 138 const SwCntntNode *pCNd = 139 (pNdIdx->GetNodes())[pNdIdx->GetIndex()+1]->GetCntntNode(); 140 if( pCNd ) 141 nType = pCNd->GetNodeType(); 142 } 143 } 144 145 return nType; 146 } 147 148 SwAccessibleFrameBase::SwAccessibleFrameBase( 149 SwAccessibleMap* pInitMap, 150 sal_Int16 nInitRole, 151 const SwFlyFrm* pFlyFrm ) : 152 SwAccessibleContext( pInitMap, nInitRole, pFlyFrm ), 153 bIsSelected( sal_False ) 154 { 155 vos::OGuard aGuard(Application::GetSolarMutex()); 156 157 const SwFrmFmt *pFrmFmt = pFlyFrm->GetFmt(); 158 const_cast< SwFrmFmt * >( pFrmFmt )->Add( this ); 159 160 SetName( pFrmFmt->GetName() ); 161 162 bIsSelected = IsSelected(); 163 } 164 165 void SwAccessibleFrameBase::_InvalidateCursorPos() 166 { 167 sal_Bool bNewSelected = IsSelected(); 168 sal_Bool bOldSelected; 169 170 { 171 vos::OGuard aGuard( aMutex ); 172 bOldSelected = bIsSelected; 173 bIsSelected = bNewSelected; 174 } 175 176 if( bNewSelected ) 177 { 178 // remember that object as the one that has the caret. This is 179 // neccessary to notify that object if the cursor leaves it. 180 ::vos::ORef < SwAccessibleContext > xThis( this ); 181 GetMap()->SetCursorContext( xThis ); 182 } 183 184 if( bOldSelected != bNewSelected ) 185 { 186 Window *pWin = GetWindow(); 187 if( pWin && pWin->HasFocus() && bNewSelected ) 188 FireStateChangedEvent( AccessibleStateType::FOCUSED, bNewSelected ); 189 //FireStateChangedEvent( AccessibleStateType::SELECTED, bNewSelected ); 190 if( pWin && pWin->HasFocus() && !bNewSelected ) 191 FireStateChangedEvent( AccessibleStateType::FOCUSED, bNewSelected ); 192 if(bNewSelected) 193 { 194 uno::Reference< XAccessible > xParent( GetWeakParent() ); 195 if( xParent.is() ) 196 { 197 SwAccessibleContext *pAcc = 198 static_cast <SwAccessibleContext *>( xParent.get() ); 199 200 AccessibleEventObject aEvent; 201 aEvent.EventId = AccessibleEventId::SELECTION_CHANGED; 202 uno::Reference< XAccessible > xChild(this); 203 aEvent.NewValue <<= xChild; 204 pAcc->FireAccessibleEvent( aEvent ); 205 } 206 } 207 } 208 } 209 210 void SwAccessibleFrameBase::_InvalidateFocus() 211 { 212 Window *pWin = GetWindow(); 213 if( pWin ) 214 { 215 sal_Bool bSelected; 216 217 { 218 vos::OGuard aGuard( aMutex ); 219 bSelected = bIsSelected; 220 } 221 ASSERT( bSelected, "focus object should be selected" ); 222 223 FireStateChangedEvent( AccessibleStateType::FOCUSED, 224 pWin->HasFocus() && bSelected ); 225 } 226 } 227 228 sal_Bool SwAccessibleFrameBase::HasCursor() 229 { 230 vos::OGuard aGuard( aMutex ); 231 return bIsSelected; 232 } 233 234 235 SwAccessibleFrameBase::~SwAccessibleFrameBase() 236 { 237 } 238 239 void SwAccessibleFrameBase::Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew) 240 { 241 sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0 ; 242 const SwFlyFrm *pFlyFrm = static_cast< const SwFlyFrm * >( GetFrm() ); 243 switch( nWhich ) 244 { 245 case RES_NAME_CHANGED: 246 if( pFlyFrm ) 247 { 248 const SwFrmFmt *pFrmFmt = pFlyFrm->GetFmt(); 249 ASSERT( pFrmFmt == GetRegisteredIn(), "invalid frame" ); 250 251 OUString sOldName( GetName() ); 252 ASSERT( !pOld || 253 static_cast < const SwStringMsgPoolItem * >( pOld )->GetString() == String( sOldName ), 254 "invalid old name" ); 255 256 const String& rNewName = pFrmFmt->GetName(); 257 SetName( rNewName ); 258 ASSERT( !pNew || 259 static_cast < const SwStringMsgPoolItem * >( pNew )->GetString() == rNewName, 260 "invalid new name" ); 261 262 if( sOldName != GetName() ) 263 { 264 AccessibleEventObject aEvent; 265 aEvent.EventId = AccessibleEventId::NAME_CHANGED; 266 aEvent.OldValue <<= sOldName; 267 aEvent.NewValue <<= GetName(); 268 FireAccessibleEvent( aEvent ); 269 } 270 } 271 break; 272 case RES_OBJECTDYING: 273 // mba: it seems that this class intentionally does not call code in base class SwClient 274 if( pOld && ( GetRegisteredIn() == static_cast< SwModify *>( static_cast< const SwPtrMsgPoolItem * >( pOld )->pObject ) ) ) 275 GetRegisteredInNonConst()->Remove( this ); 276 break; 277 278 case RES_FMT_CHG: 279 if( pOld && 280 static_cast< const SwFmtChg * >(pNew)->pChangedFmt == GetRegisteredIn() && 281 static_cast< const SwFmtChg * >(pOld)->pChangedFmt->IsFmtInDTOR() ) 282 GetRegisteredInNonConst()->Remove( this ); 283 break; 284 285 default: 286 // mba: former call to base class method removed as it is meant to handle only RES_OBJECTDYING 287 break; 288 } 289 } 290 291 void SwAccessibleFrameBase::Dispose( sal_Bool bRecursive ) 292 { 293 vos::OGuard aGuard(Application::GetSolarMutex()); 294 295 if( GetRegisteredIn() ) 296 GetRegisteredInNonConst()->Remove( this ); 297 298 SwAccessibleContext::Dispose( bRecursive ); 299 } 300 //Get the selection cursor of the document. 301 SwPaM* SwAccessibleFrameBase::GetCrsr() 302 { 303 // get the cursor shell; if we don't have any, we don't have a 304 // cursor/selection either 305 SwPaM* pCrsr = NULL; 306 SwCrsrShell* pCrsrShell = GetCrsrShell(); 307 if( pCrsrShell != NULL && !pCrsrShell->IsTableMode() ) 308 { 309 SwFEShell *pFESh = pCrsrShell->ISA( SwFEShell ) 310 ? static_cast< SwFEShell * >( pCrsrShell ) : 0; 311 if( !pFESh || 312 !(pFESh->IsFrmSelected() || pFESh->IsObjSelected() > 0) ) 313 { 314 // get the selection, and test whether it affects our text node 315 pCrsr = pCrsrShell->GetCrsr( sal_False /* ??? */ ); 316 } 317 } 318 319 return pCrsr; 320 } 321 //Return the selected state of the object. 322 //when the object's anchor are in the selection cursor, we should return true. 323 sal_Bool SwAccessibleFrameBase::GetSelectedState( ) 324 { 325 vos::OGuard aGuard(Application::GetSolarMutex()); 326 327 if(GetMap()->IsDocumentSelAll()) 328 { 329 return sal_True; 330 } 331 332 // SELETED. 333 SwFlyFrm* pFlyFrm = getFlyFrm(); 334 const SwFrmFmt *pFrmFmt = pFlyFrm->GetFmt(); 335 const SwFmtAnchor& pAnchor = pFrmFmt->GetAnchor(); 336 const SwPosition *pPos = pAnchor.GetCntntAnchor(); 337 if( !pPos ) 338 return sal_False; 339 int pIndex = pPos->nContent.GetIndex(); 340 if( pPos->nNode.GetNode().GetTxtNode() ) 341 { 342 SwPaM* pCrsr = GetCrsr(); 343 if( pCrsr != NULL ) 344 { 345 const SwTxtNode* pNode = pPos->nNode.GetNode().GetTxtNode(); 346 sal_uLong nHere = pNode->GetIndex(); 347 348 // iterate over ring 349 SwPaM* pRingStart = pCrsr; 350 do 351 { 352 // ignore, if no mark 353 if( pCrsr->HasMark() ) 354 { 355 // check whether nHere is 'inside' pCrsr 356 SwPosition* pStart = pCrsr->Start(); 357 sal_uLong nStartIndex = pStart->nNode.GetIndex(); 358 SwPosition* pEnd = pCrsr->End(); 359 sal_uLong nEndIndex = pEnd->nNode.GetIndex(); 360 if( ( nHere >= nStartIndex ) && (nHere <= nEndIndex) ) 361 { 362 if( pAnchor.GetAnchorId() == FLY_AS_CHAR ) 363 { 364 if( (nHere == nStartIndex) && (pIndex >= pStart->nContent.GetIndex()) || (nHere > nStartIndex) ) 365 if( (nHere == nEndIndex) && (pIndex < pEnd->nContent.GetIndex()) || (nHere < nEndIndex) ) 366 return sal_True; 367 } 368 else if( pAnchor.GetAnchorId() == FLY_AT_PARA ) 369 { 370 if( ((nHere > nStartIndex) || pStart->nContent.GetIndex() ==0 ) 371 && (nHere < nEndIndex ) ) 372 return sal_True; 373 } 374 break; 375 } 376 // else: this PaM doesn't point to this paragraph 377 } 378 // else: this PaM is collapsed and doesn't select anything 379 380 // next PaM in ring 381 pCrsr = static_cast<SwPaM*>( pCrsr->GetNext() ); 382 } 383 while( pCrsr != pRingStart ); 384 } 385 } 386 return sal_False; 387 } 388 389 SwFlyFrm* SwAccessibleFrameBase::getFlyFrm() const 390 { 391 SwFlyFrm* pFlyFrm = NULL; 392 393 const SwFrm* pFrm = GetFrm(); 394 DBG_ASSERT( pFrm != NULL, "frame expected" ); 395 if( pFrm->IsFlyFrm() ) 396 { 397 pFlyFrm = static_cast<SwFlyFrm*>( const_cast<SwFrm*>( pFrm ) ); 398 } 399 400 return pFlyFrm; 401 } 402 403 sal_Bool SwAccessibleFrameBase::SetSelectedState( sal_Bool ) 404 { 405 sal_Bool bParaSeleted = GetSelectedState() || IsSelected(); 406 407 if(bIsSeletedInDoc != bParaSeleted) 408 { 409 bIsSeletedInDoc = bParaSeleted; 410 FireStateChangedEvent( AccessibleStateType::SELECTED, bParaSeleted ); 411 return sal_True; 412 } 413 return sal_False; 414 } 415