xref: /trunk/main/sw/source/core/doc/docruby.cxx (revision 69a74367)
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