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