xref: /AOO41X/main/sw/source/core/doc/docruby.cxx (revision 69a743679e823ad8f875be547552acb607b8ada5)
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  */
FillRubyList(const SwPaM & rPam,SwRubyList & rList,sal_uInt16 nMode)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 
SetRubyList(const SwPaM & rPam,const SwRubyList & rList,sal_uInt16 nMode)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(
177                                 aPam, pEntry->GetRubyAttr(), 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 
_SelectNextRubyChars(SwPaM & rPam,SwRubyListEntry & rEntry,sal_uInt16)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 
~SwRubyListEntry()355 SwRubyListEntry::~SwRubyListEntry()
356 {
357 }
358