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 #include <string.h> // fuer strchr() 28 29 #include <com/sun/star/i18n/UnicodeType.hdl> 30 #include <com/sun/star/i18n/WordType.hdl> 31 32 #include <unotools/charclass.hxx> 33 34 #include <hintids.hxx> 35 #include <doc.hxx> 36 #include <IDocumentUndoRedo.hxx> 37 #include <docary.hxx> 38 #include <mvsave.hxx> // Strukturen zum Sichern beim Move/Delete 39 #include <ndtxt.hxx> 40 #include <txatbase.hxx> 41 #include <rubylist.hxx> 42 #include <pam.hxx> 43 #include <swundo.hxx> // fuer die UndoIds 44 #include <breakit.hxx> 45 #include <crsskip.hxx> 46 47 SV_IMPL_PTRARR( SwRubyList, SwRubyListEntryPtr ) 48 49 using namespace ::com::sun::star::i18n; 50 51 52 /* 53 * Members in the list: 54 * - String - the orig text 55 * - SwFmtRuby - the ruby attribut 56 * 57 * 58 */ 59 sal_uInt16 SwDoc::FillRubyList( const SwPaM& rPam, SwRubyList& rList, 60 sal_uInt16 nMode ) 61 { 62 const SwPaM *_pStartCrsr = (SwPaM*)rPam.GetNext(), 63 *__pStartCrsr = _pStartCrsr; 64 sal_Bool bCheckEmpty = &rPam != _pStartCrsr; 65 do { 66 const SwPosition* pStt = _pStartCrsr->Start(), 67 * pEnd = pStt == _pStartCrsr->GetPoint() 68 ? _pStartCrsr->GetMark() 69 : _pStartCrsr->GetPoint(); 70 if( !bCheckEmpty || ( pStt != pEnd && *pStt != *pEnd )) 71 { 72 SwPaM aPam( *pStt ); 73 do { 74 SwRubyListEntry* pNew = new SwRubyListEntry; 75 if( pEnd != pStt ) 76 { 77 aPam.SetMark(); 78 *aPam.GetMark() = *pEnd; 79 } 80 if( _SelectNextRubyChars( aPam, *pNew, nMode )) 81 { 82 rList.Insert( pNew, rList.Count() ); 83 aPam.DeleteMark(); 84 } 85 else 86 { 87 delete pNew; 88 if( *aPam.GetPoint() < *pEnd ) 89 { 90 // goto next paragraph 91 aPam.DeleteMark(); 92 aPam.Move( fnMoveForward, fnGoNode ); 93 } 94 else 95 break; 96 } 97 } while( 30 > rList.Count() && *aPam.GetPoint() < *pEnd ); 98 } 99 } while( 30 > rList.Count() && 100 (_pStartCrsr=(SwPaM *)_pStartCrsr->GetNext()) != __pStartCrsr ); 101 102 return rList.Count(); 103 } 104 105 sal_uInt16 SwDoc::SetRubyList( const SwPaM& rPam, const SwRubyList& rList, 106 sal_uInt16 nMode ) 107 { 108 GetIDocumentUndoRedo().StartUndo( UNDO_SETRUBYATTR, NULL ); 109 SvUShortsSort aDelArr; 110 aDelArr.Insert( RES_TXTATR_CJK_RUBY ); 111 112 sal_uInt16 nListEntry = 0; 113 114 const SwPaM *_pStartCrsr = (SwPaM*)rPam.GetNext(), 115 *__pStartCrsr = _pStartCrsr; 116 sal_Bool bCheckEmpty = &rPam != _pStartCrsr; 117 do { 118 const SwPosition* pStt = _pStartCrsr->Start(), 119 * pEnd = pStt == _pStartCrsr->GetPoint() 120 ? _pStartCrsr->GetMark() 121 : _pStartCrsr->GetPoint(); 122 if( !bCheckEmpty || ( pStt != pEnd && *pStt != *pEnd )) 123 { 124 125 SwPaM aPam( *pStt ); 126 do { 127 SwRubyListEntry aCheckEntry; 128 if( pEnd != pStt ) 129 { 130 aPam.SetMark(); 131 *aPam.GetMark() = *pEnd; 132 } 133 if( _SelectNextRubyChars( aPam, aCheckEntry, nMode )) 134 { 135 const SwRubyListEntry* pEntry = rList[ nListEntry++ ]; 136 if( aCheckEntry.GetRubyAttr() != pEntry->GetRubyAttr() ) 137 { 138 // set/reset the attribut 139 if( pEntry->GetRubyAttr().GetText().Len() ) 140 { 141 InsertPoolItem( aPam, pEntry->GetRubyAttr(), 0 ); 142 } 143 else 144 { 145 ResetAttrs( aPam, sal_True, &aDelArr ); 146 } 147 } 148 149 if( aCheckEntry.GetText() != pEntry->GetText() && 150 pEntry->GetText().Len() ) 151 { 152 // text is changed, so replace the original 153 ReplaceRange( aPam, pEntry->GetText(), false ); 154 } 155 aPam.DeleteMark(); 156 } 157 else 158 { 159 if( *aPam.GetPoint() < *pEnd ) 160 { 161 // goto next paragraph 162 aPam.DeleteMark(); 163 aPam.Move( fnMoveForward, fnGoNode ); 164 } 165 else 166 { 167 const SwRubyListEntry* pEntry = rList[ nListEntry++ ]; 168 169 // set/reset the attribut 170 if( pEntry->GetRubyAttr().GetText().Len() && 171 pEntry->GetText().Len() ) 172 { 173 InsertString( aPam, pEntry->GetText() ); 174 aPam.SetMark(); 175 aPam.GetMark()->nContent -= pEntry->GetText().Len(); 176 InsertPoolItem( aPam, pEntry->GetRubyAttr(), 177 nsSetAttrMode::SETATTR_DONTEXPAND ); 178 } 179 else 180 break; 181 aPam.DeleteMark(); 182 } 183 } 184 } while( nListEntry < rList.Count() && *aPam.GetPoint() < *pEnd ); 185 } 186 } while( 30 > rList.Count() && 187 (_pStartCrsr=(SwPaM *)_pStartCrsr->GetNext()) != __pStartCrsr ); 188 189 GetIDocumentUndoRedo().EndUndo( UNDO_SETRUBYATTR, NULL ); 190 191 return nListEntry; 192 } 193 194 sal_Bool SwDoc::_SelectNextRubyChars( SwPaM& rPam, SwRubyListEntry& rEntry, sal_uInt16 ) 195 { 196 // Point must be the startposition, Mark is optional the end position 197 SwPosition* pPos = rPam.GetPoint(); 198 const SwTxtNode* pTNd = pPos->nNode.GetNode().GetTxtNode(); 199 const String* pTxt = &pTNd->GetTxt(); 200 xub_StrLen nStart = pPos->nContent.GetIndex(), nEnd = pTxt->Len(); 201 202 sal_Bool bHasMark = rPam.HasMark(); 203 if( bHasMark ) 204 { 205 // in the same node? 206 if( rPam.GetMark()->nNode == pPos->nNode ) 207 { 208 // then use that end 209 xub_StrLen nTEnd = rPam.GetMark()->nContent.GetIndex(); 210 if( nTEnd < nEnd ) 211 nEnd = nTEnd; 212 } 213 rPam.DeleteMark(); 214 } 215 216 // ----- search the start 217 // --- look where a ruby attribut starts 218 sal_uInt16 nHtIdx = USHRT_MAX; 219 const SwpHints* pHts = pTNd->GetpSwpHints(); 220 const SwTxtAttr* pAttr = 0; 221 if( pHts ) 222 { 223 const SwTxtAttr* pHt; 224 for( nHtIdx = 0; nHtIdx < pHts->Count(); ++nHtIdx ) 225 if( RES_TXTATR_CJK_RUBY == ( pHt = (*pHts)[ nHtIdx ])->Which() && 226 *pHt->GetAnyEnd() > nStart ) 227 { 228 if( *pHt->GetStart() < nEnd ) 229 { 230 pAttr = pHt; 231 if( !bHasMark && nStart > *pAttr->GetStart() ) 232 { 233 nStart = *pAttr->GetStart(); 234 pPos->nContent = nStart; 235 } 236 } 237 break; 238 } 239 } 240 241 if( !bHasMark && nStart && ( !pAttr || nStart != *pAttr->GetStart()) ) 242 { 243 // skip to the word begin! 244 long nWordStt = pBreakIt->GetBreakIter()->getWordBoundary( 245 *pTxt, nStart, 246 pBreakIt->GetLocale( pTNd->GetLang( nStart )), 247 WordType::ANYWORD_IGNOREWHITESPACES, 248 sal_True ).startPos; 249 if( nWordStt < nStart && -1 != nWordStt ) 250 { 251 nStart = (xub_StrLen)nWordStt; 252 pPos->nContent = nStart; 253 } 254 } 255 256 sal_Bool bAlphaNum = sal_False; 257 long nWordEnd = nEnd; 258 CharClass& rCC = GetAppCharClass(); 259 while( nStart < nEnd ) 260 { 261 if( pAttr && nStart == *pAttr->GetStart() ) 262 { 263 pPos->nContent = nStart; 264 if( !rPam.HasMark() ) 265 { 266 rPam.SetMark(); 267 pPos->nContent = *pAttr->GetAnyEnd(); 268 if( pPos->nContent.GetIndex() > nEnd ) 269 pPos->nContent = nEnd; 270 rEntry.SetRubyAttr( pAttr->GetRuby() ); 271 } 272 break; 273 } 274 275 sal_Int32 nChType = rCC.getType( *pTxt, nStart ); 276 sal_Bool bIgnoreChar = sal_False, bIsAlphaNum = sal_False, bChkNxtWrd = sal_False; 277 switch( nChType ) 278 { 279 case UnicodeType::UPPERCASE_LETTER: 280 case UnicodeType::LOWERCASE_LETTER: 281 case UnicodeType::TITLECASE_LETTER: 282 case UnicodeType::DECIMAL_DIGIT_NUMBER: 283 bChkNxtWrd = bIsAlphaNum = sal_True; 284 break; 285 286 case UnicodeType::SPACE_SEPARATOR: 287 case UnicodeType::CONTROL: 288 /*??*/ case UnicodeType::PRIVATE_USE: 289 case UnicodeType::START_PUNCTUATION: 290 case UnicodeType::END_PUNCTUATION: 291 bIgnoreChar = sal_True; 292 break; 293 294 295 case UnicodeType::OTHER_LETTER: 296 bChkNxtWrd = sal_True; 297 // no break! 298 // case UnicodeType::UNASSIGNED: 299 // case UnicodeType::MODIFIER_LETTER: 300 // case UnicodeType::NON_SPACING_MARK: 301 // case UnicodeType::ENCLOSING_MARK: 302 // case UnicodeType::COMBINING_SPACING_MARK: 303 // case UnicodeType::LETTER_NUMBER: 304 // case UnicodeType::OTHER_NUMBER: 305 // case UnicodeType::LINE_SEPARATOR: 306 // case UnicodeType::PARAGRAPH_SEPARATOR: 307 // case UnicodeType::FORMAT: 308 // case UnicodeType::SURROGATE: 309 // case UnicodeType::DASH_PUNCTUATION: 310 // case UnicodeType::CONNECTOR_PUNCTUATION: 311 ///*?? */case UnicodeType::OTHER_PUNCTUATION: 312 //--> char '!' is to ignore! 313 // case UnicodeType::MATH_SYMBOL: 314 // case UnicodeType::CURRENCY_SYMBOL: 315 // case UnicodeType::MODIFIER_SYMBOL: 316 // case UnicodeType::OTHER_SYMBOL: 317 // case UnicodeType::INITIAL_PUNCTUATION: 318 // case UnicodeType::FINAL_PUNCTUATION: 319 default: 320 bIsAlphaNum = sal_False; 321 break; 322 } 323 324 if( rPam.HasMark() ) 325 { 326 if( bIgnoreChar || bIsAlphaNum != bAlphaNum || nStart >= nWordEnd ) 327 break; 328 } 329 else if( !bIgnoreChar ) 330 { 331 rPam.SetMark(); 332 bAlphaNum = bIsAlphaNum; 333 if( bChkNxtWrd && pBreakIt->GetBreakIter().is() ) 334 { 335 // search the end of this word 336 nWordEnd = pBreakIt->GetBreakIter()->getWordBoundary( 337 *pTxt, nStart, 338 pBreakIt->GetLocale( pTNd->GetLang( nStart )), 339 WordType::ANYWORD_IGNOREWHITESPACES, 340 sal_True ).endPos; 341 if( 0 > nWordEnd || nWordEnd > nEnd || nWordEnd == nStart ) 342 nWordEnd = nEnd; 343 } 344 } 345 pTNd->GoNext( &pPos->nContent, CRSR_SKIP_CHARS ); 346 nStart = pPos->nContent.GetIndex(); 347 } 348 349 nStart = rPam.GetMark()->nContent.GetIndex(); 350 rEntry.SetText( pTxt->Copy( nStart, 351 rPam.GetPoint()->nContent.GetIndex() - nStart )); 352 return rPam.HasMark(); 353 } 354 355 SwRubyListEntry::~SwRubyListEntry() 356 { 357 } 358