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 <tools/urlobj.hxx> 29 #include <hintids.hxx> 30 #include <hints.hxx> 31 #include <unotools/transliterationwrapper.hxx> 32 #include <acmplwrd.hxx> 33 #include <doc.hxx> 34 #include <ndindex.hxx> 35 #include <docary.hxx> 36 #include <ndtxt.hxx> 37 #include <pam.hxx> 38 #include <pagedesc.hxx> 39 #include <poolfmt.hxx> 40 #include <calbck.hxx> 41 #include <editeng/svxacorr.hxx> 42 43 #include <editeng/acorrcfg.hxx> 44 #include <sfx2/docfile.hxx> 45 #include <docsh.hxx> 46 47 #include <vector> 48 /* -----------------------------05.08.2002 12:43------------------------------ 49 50 ---------------------------------------------------------------------------*/ 51 class SwAutoCompleteClient : public SwClient 52 { 53 SwAutoCompleteWord* pAutoCompleteWord; 54 SwDoc* pDoc; 55 #ifdef DBG_UTIL 56 static sal_uLong nSwAutoCompleteClientCount; 57 #endif 58 public: 59 SwAutoCompleteClient(SwAutoCompleteWord& rToTell, SwDoc& rSwDoc); 60 SwAutoCompleteClient(const SwAutoCompleteClient& rClient); 61 ~SwAutoCompleteClient(); 62 63 SwAutoCompleteClient& operator=(const SwAutoCompleteClient& rClient); 64 65 const SwDoc& GetDoc(){return *pDoc;} 66 #ifdef DBG_UTIL 67 static sal_uLong GetElementCount() {return nSwAutoCompleteClientCount;} 68 #endif 69 protected: 70 virtual void Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew); 71 }; 72 /* -----------------------------05.08.2002 12:48------------------------------ 73 74 ---------------------------------------------------------------------------*/ 75 typedef std::vector<SwAutoCompleteClient> SwAutoCompleteClientVector; 76 77 class SwAutoCompleteWord_Impl 78 { 79 SwAutoCompleteClientVector aClientVector; 80 SwAutoCompleteWord& rAutoCompleteWord; 81 public: 82 SwAutoCompleteWord_Impl(SwAutoCompleteWord& rParent) : 83 rAutoCompleteWord(rParent){} 84 void AddDocument(SwDoc& rDoc); 85 void RemoveDocument(const SwDoc& rDoc); 86 }; 87 88 /* -----------------------------05.08.2002 14:11------------------------------ 89 90 ---------------------------------------------------------------------------*/ 91 typedef const SwDoc* SwDocPtr; 92 typedef std::vector<SwDocPtr> SwDocPtrVector; 93 class SwAutoCompleteString : public String 94 { 95 #ifdef DBG_UTIL 96 static sal_uLong nSwAutoCompleteStringCount; 97 #endif 98 SwDocPtrVector aSourceDocs; 99 public: 100 SwAutoCompleteString(const String& rStr, xub_StrLen nPos, xub_StrLen nLen); 101 102 ~SwAutoCompleteString(); 103 void AddDocument(const SwDoc& rDoc); 104 //returns true if last document reference has been removed 105 sal_Bool RemoveDocument(const SwDoc& rDoc); 106 #ifdef DBG_UTIL 107 static sal_uLong GetElementCount() {return nSwAutoCompleteStringCount;} 108 #endif 109 }; 110 #ifdef DBG_UTIL 111 sal_uLong SwAutoCompleteClient::nSwAutoCompleteClientCount = 0; 112 sal_uLong SwAutoCompleteString::nSwAutoCompleteStringCount = 0; 113 #endif 114 /* -----------------------------06.08.2002 08:57------------------------------ 115 116 ---------------------------------------------------------------------------*/ 117 SwAutoCompleteClient::SwAutoCompleteClient(SwAutoCompleteWord& rToTell, SwDoc& rSwDoc) : 118 pAutoCompleteWord(&rToTell), 119 pDoc(&rSwDoc) 120 { 121 pDoc->GetPageDescFromPool(RES_POOLPAGE_STANDARD)->Add(this); 122 #ifdef DBG_UTIL 123 ++nSwAutoCompleteClientCount; 124 #endif 125 } 126 /* -----------------------------05.08.2002 14:07------------------------------ 127 128 ---------------------------------------------------------------------------*/ 129 SwAutoCompleteClient::SwAutoCompleteClient(const SwAutoCompleteClient& rClient) : 130 SwClient(), 131 pAutoCompleteWord(rClient.pAutoCompleteWord), 132 pDoc(rClient.pDoc) 133 { 134 pDoc->GetPageDescFromPool(RES_POOLPAGE_STANDARD)->Add(this); 135 #ifdef DBG_UTIL 136 ++nSwAutoCompleteClientCount; 137 #endif 138 } 139 /* -----------------------------05.08.2002 14:10------------------------------ 140 141 ---------------------------------------------------------------------------*/ 142 SwAutoCompleteClient::~SwAutoCompleteClient() 143 { 144 #ifdef DBG_UTIL 145 --nSwAutoCompleteClientCount; 146 #endif 147 } 148 /* -----------------06.03.2003 15:30----------------- 149 150 --------------------------------------------------*/ 151 SwAutoCompleteClient& SwAutoCompleteClient::operator=(const SwAutoCompleteClient& rClient) 152 { 153 pAutoCompleteWord = rClient.pAutoCompleteWord; 154 pDoc = rClient.pDoc; 155 if(rClient.GetRegisteredIn()) 156 ((SwModify*)rClient.GetRegisteredIn())->Add(this); 157 else if(GetRegisteredIn()) 158 GetRegisteredInNonConst()->Remove(this); 159 return *this; 160 } 161 /* -----------------------------05.08.2002 12:49------------------------------ 162 163 ---------------------------------------------------------------------------*/ 164 void SwAutoCompleteClient::Modify( const SfxPoolItem* pOld, const SfxPoolItem *) 165 { 166 switch( pOld ? pOld->Which() : 0 ) 167 { 168 case RES_REMOVE_UNO_OBJECT: 169 case RES_OBJECTDYING: 170 if( (void*)GetRegisteredIn() == ((SwPtrMsgPoolItem *)pOld)->pObject ) 171 ((SwModify*)GetRegisteredIn())->Remove(this); 172 pAutoCompleteWord->DocumentDying(*pDoc); 173 break; 174 175 } 176 } 177 /* -----------------------------05.08.2002 13:03------------------------------ 178 179 ---------------------------------------------------------------------------*/ 180 void SwAutoCompleteWord_Impl::AddDocument(SwDoc& rDoc) 181 { 182 SwAutoCompleteClientVector::iterator aIt; 183 for(aIt = aClientVector.begin(); aIt != aClientVector.end(); aIt++) 184 { 185 if(&aIt->GetDoc() == &rDoc) 186 return; 187 } 188 aClientVector.push_back(SwAutoCompleteClient(rAutoCompleteWord, rDoc)); 189 } 190 /* -----------------------------05.08.2002 14:33------------------------------ 191 192 ---------------------------------------------------------------------------*/ 193 void SwAutoCompleteWord_Impl::RemoveDocument(const SwDoc& rDoc) 194 { 195 SwAutoCompleteClientVector::iterator aIt; 196 for(aIt = aClientVector.begin(); aIt != aClientVector.end(); aIt++) 197 { 198 if(&aIt->GetDoc() == &rDoc) 199 { 200 aClientVector.erase(aIt); 201 return; 202 } 203 } 204 } 205 /* -----------------------------06.08.2002 08:54------------------------------ 206 207 ---------------------------------------------------------------------------*/ 208 SwAutoCompleteString::SwAutoCompleteString(const String& rStr, xub_StrLen nPos, xub_StrLen nLen) : 209 String( rStr, nPos, nLen ) 210 { 211 #ifdef DBG_UTIL 212 ++nSwAutoCompleteStringCount; 213 #endif 214 } 215 /* -----------------------------05.08.2002 14:22------------------------------ 216 217 ---------------------------------------------------------------------------*/ 218 SwAutoCompleteString::~SwAutoCompleteString() 219 { 220 #ifdef DBG_UTIL 221 --nSwAutoCompleteStringCount; 222 #endif 223 } 224 /* -----------------------------05.08.2002 14:17------------------------------ 225 226 ---------------------------------------------------------------------------*/ 227 void SwAutoCompleteString::AddDocument(const SwDoc& rDoc) 228 { 229 SwDocPtrVector::iterator aIt; 230 for(aIt = aSourceDocs.begin(); aIt != aSourceDocs.end(); aIt++) 231 { 232 if(*aIt == &rDoc) 233 return; 234 } 235 SwDocPtr pNew = &rDoc; 236 aSourceDocs.push_back(pNew); 237 } 238 /* -----------------------------05.08.2002 14:36------------------------------ 239 240 ---------------------------------------------------------------------------*/ 241 sal_Bool SwAutoCompleteString::RemoveDocument(const SwDoc& rDoc) 242 { 243 SwDocPtrVector::iterator aIt; 244 for(aIt = aSourceDocs.begin(); aIt != aSourceDocs.end(); aIt++) 245 { 246 if(*aIt == &rDoc) 247 { 248 aSourceDocs.erase(aIt); 249 return !aSourceDocs.size(); 250 } 251 } 252 return sal_False; 253 } 254 /* --------------------------------------------------------------------------- 255 256 ---------------------------------------------------------------------------*/ 257 SwAutoCompleteWord::SwAutoCompleteWord( sal_uInt16 nWords, sal_uInt16 nMWrdLen ) 258 : aWordLst( 0, 255 ), aLRULst( 0, 255 ), 259 pImpl(new SwAutoCompleteWord_Impl(*this)), 260 nMaxCount( nWords ), 261 nMinWrdLen( nMWrdLen ), 262 bLockWordLst( sal_False ) 263 { 264 } 265 266 SwAutoCompleteWord::~SwAutoCompleteWord() 267 { 268 for(sal_uInt16 nPos = aWordLst.Count(); nPos; nPos--) 269 { 270 SwAutoCompleteString* pCurrent = (SwAutoCompleteString*)aWordLst[ nPos - 1 ]; 271 aWordLst.Remove( nPos - 1 ); 272 delete pCurrent; 273 } 274 delete pImpl; 275 #ifdef DBG_UTIL 276 sal_uLong nStrings = SwAutoCompleteString::GetElementCount(); 277 sal_uLong nClients = SwAutoCompleteClient::GetElementCount(); 278 DBG_ASSERT(!nStrings && !nClients, "AutoComplete: clients or string count mismatch"); 279 #endif 280 } 281 282 sal_Bool SwAutoCompleteWord::InsertWord( const String& rWord, SwDoc& rDoc ) 283 { 284 SwDocShell* pDocShell = rDoc.GetDocShell(); 285 SfxMedium* pMedium = pDocShell ? pDocShell->GetMedium() : 0; 286 // strings from help module should not be added 287 if( pMedium ) 288 { 289 const INetURLObject& rURL = pMedium->GetURLObject(); 290 if ( rURL.GetProtocol() == INET_PROT_VND_SUN_STAR_HELP ) 291 return sal_False; 292 } 293 294 String aNewWord(rWord); 295 aNewWord.EraseAllChars( CH_TXTATR_INWORD ); 296 aNewWord.EraseAllChars( CH_TXTATR_BREAKWORD ); 297 298 pImpl->AddDocument(rDoc); 299 sal_Bool bRet = sal_False; 300 xub_StrLen nWrdLen = aNewWord.Len(); 301 while( nWrdLen && '.' == aNewWord.GetChar( nWrdLen-1 )) 302 --nWrdLen; 303 304 if( !bLockWordLst && nWrdLen >= nMinWrdLen ) 305 { 306 SwAutoCompleteString* pAutoString; 307 StringPtr pNew = pAutoString = new SwAutoCompleteString( aNewWord, 0, nWrdLen ); 308 pAutoString->AddDocument(rDoc); 309 sal_uInt16 nInsPos; 310 if( aWordLst.Insert( pNew, nInsPos ) ) 311 { 312 bRet = sal_True; 313 if( aLRULst.Count() < nMaxCount ) 314 aLRULst.Insert( pNew, 0 ); 315 else 316 { 317 // der letzte muss entfernt werden 318 // damit der neue vorne Platz hat 319 String* pDel = (String*)aLRULst[ nMaxCount - 1 ]; 320 321 void** ppData = (void**)aLRULst.GetData(); 322 memmove( ppData+1, ppData, (nMaxCount - 1) * sizeof( void* )); 323 *ppData = pNew; 324 325 aWordLst.Remove( pDel ); 326 delete (SwAutoCompleteString*)pDel; 327 } 328 } 329 else 330 { 331 delete (SwAutoCompleteString*)pNew; 332 // dann aber auf jedenfall nach "oben" moven 333 pNew = aWordLst[ nInsPos ]; 334 335 //add the document to the already inserted string 336 SwAutoCompleteString* pCurrent = (SwAutoCompleteString*)pNew; 337 pCurrent->AddDocument(rDoc); 338 339 nInsPos = aLRULst.GetPos( (void*)pNew ); 340 ASSERT( USHRT_MAX != nInsPos, "String nicht gefunden" ); 341 if( nInsPos ) 342 { 343 void** ppData = (void**)aLRULst.GetData(); 344 memmove( ppData+1, ppData, nInsPos * sizeof( void* ) ); 345 *ppData = pNew; 346 } 347 } 348 } 349 return bRet; 350 } 351 352 void SwAutoCompleteWord::SetMaxCount( sal_uInt16 nNewMax ) 353 { 354 if( nNewMax < nMaxCount && aLRULst.Count() > nNewMax ) 355 { 356 // dann die unten ueberhaengenden entfernen 357 sal_uInt16 nLRUIndex = nNewMax-1; 358 while( nNewMax < aWordLst.Count() && nLRUIndex < aLRULst.Count()) 359 { 360 sal_uInt16 nPos = aWordLst.GetPos( (String*)aLRULst[ nLRUIndex++ ] ); 361 ASSERT( USHRT_MAX != nPos, "String nicht gefunden" ); 362 void * pDel = aWordLst[nPos]; 363 aWordLst.Remove(nPos); 364 delete (SwAutoCompleteString*)pDel; 365 } 366 aLRULst.Remove( nNewMax-1, aLRULst.Count() - nNewMax ); 367 } 368 nMaxCount = nNewMax; 369 } 370 371 void SwAutoCompleteWord::SetMinWordLen( sal_uInt16 n ) 372 { 373 // will man wirklich alle Worte, die kleiner als die neue Min Laenge 374 // sind entfernen? 375 // JP 02.02.99 - erstmal nicht. 376 377 // JP 11.03.99 - mal testhalber eingebaut 378 if( n < nMinWrdLen ) 379 { 380 for( sal_uInt16 nPos = 0; nPos < aWordLst.Count(); ++nPos ) 381 if( aWordLst[ nPos ]->Len() < n ) 382 { 383 void* pDel = aWordLst[ nPos ]; 384 aWordLst.Remove(nPos); 385 386 sal_uInt16 nDelPos = aLRULst.GetPos( pDel ); 387 ASSERT( USHRT_MAX != nDelPos, "String nicht gefunden" ); 388 aLRULst.Remove( nDelPos ); 389 --nPos; 390 delete (SwAutoCompleteString*)pDel; 391 } 392 } 393 394 nMinWrdLen = n; 395 } 396 397 sal_Bool SwAutoCompleteWord::GetRange( const String& rWord, sal_uInt16& rStt, 398 sal_uInt16& rEnd ) const 399 { 400 const StringPtr pStr = (StringPtr)&rWord; 401 aWordLst.Seek_Entry( pStr, &rStt ); 402 rEnd = rStt; 403 404 const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore(); 405 while( rEnd < aWordLst.Count() && rSCmp.isMatch( rWord, *aWordLst[ rEnd ])) 406 ++rEnd; 407 408 return rStt < rEnd; 409 } 410 411 void SwAutoCompleteWord::CheckChangedList( const SvStringsISortDtor& rNewLst ) 412 { 413 sal_uInt16 nMyLen = aWordLst.Count(), nNewLen = rNewLst.Count(); 414 sal_uInt16 nMyPos = 0, nNewPos = 0; 415 416 for( ; nMyPos < nMyLen && nNewPos < nNewLen; ++nMyPos, ++nNewPos ) 417 { 418 const StringPtr pStr = rNewLst[ nNewPos ]; 419 while( aWordLst[ nMyPos ] != pStr ) 420 { 421 void* pDel = aWordLst[ nMyPos ]; 422 aWordLst.Remove(nMyPos); 423 424 sal_uInt16 nPos = aLRULst.GetPos( pDel ); 425 ASSERT( USHRT_MAX != nPos, "String nicht gefunden" ); 426 aLRULst.Remove( nPos ); 427 delete (SwAutoCompleteString*)pDel; 428 if( nMyPos >= --nMyLen ) 429 break; 430 } 431 } 432 //remove the elements at the end of the array 433 if( nMyPos < nMyLen ) 434 { 435 //clear LRU array first then delete the string object 436 for( ; nNewPos < nMyLen; ++nNewPos ) 437 { 438 void* pDel = aWordLst[ nNewPos ]; 439 sal_uInt16 nPos = aLRULst.GetPos( pDel ); 440 ASSERT( USHRT_MAX != nPos, "String nicht gefunden" ); 441 aLRULst.Remove( nPos ); 442 delete (SwAutoCompleteString*)pDel; 443 } 444 //remove from array 445 aWordLst.Remove( nMyPos, nMyLen - nMyPos ); 446 } 447 } 448 /* -----------------------------05.08.2002 12:54------------------------------ 449 450 ---------------------------------------------------------------------------*/ 451 void SwAutoCompleteWord::DocumentDying(const SwDoc& rDoc) 452 { 453 pImpl->RemoveDocument(rDoc); 454 455 SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get()->GetAutoCorrect(); 456 const sal_Bool bDelete = !pACorr->GetSwFlags().bAutoCmpltKeepList; 457 for(sal_uInt16 nPos = aWordLst.Count(); nPos; nPos--) 458 { 459 SwAutoCompleteString* pCurrent = (SwAutoCompleteString*)aWordLst[ nPos - 1 ]; 460 if(pCurrent->RemoveDocument(rDoc) && bDelete) 461 { 462 aWordLst.Remove( nPos - 1 ); 463 sal_uInt16 nLRUPos = aLRULst.GetPos( (void*)pCurrent ); 464 DBG_ASSERT(nLRUPos < USHRT_MAX, "word not found in LRU list" ); 465 aLRULst.Remove( nLRUPos ); 466 delete pCurrent; 467 } 468 } 469 } 470 471