1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
23 #include <com/sun/star/i18n/UnicodeType.hpp>
24 #include <com/sun/star/i18n/WordType.hpp>
25 #include <com/sun/star/i18n/XBreakIterator.hpp>
27 #include <unotools/charclass.hxx>
29 #include <hintids.hxx>
31 #include <IDocumentUndoRedo.hxx>
32 #include <IDocumentContentOperations.hxx>
34 #include <txatbase.hxx>
35 #include <rubylist.hxx>
38 #include <breakit.hxx>
41 using namespace ::com::sun::star::i18n
;
44 * Members in the list:
45 * - String - the orig text
46 * - SwFormatRuby - the ruby attribute
48 sal_uInt16
SwDoc::FillRubyList( const SwPaM
& rPam
, SwRubyList
& rList
)
50 const SwPaM
*_pStartCursor
= rPam
.GetNext(),
51 *_pStartCursor2
= _pStartCursor
;
52 bool bCheckEmpty
= &rPam
!= _pStartCursor
;
54 auto [pStt
, pEnd
] = _pStartCursor
->StartEnd(); // SwPosition*
55 if( !bCheckEmpty
|| ( pStt
!= pEnd
&& *pStt
!= *pEnd
))
59 std::unique_ptr
<SwRubyListEntry
> pNew(new SwRubyListEntry
);
63 *aPam
.GetMark() = *pEnd
;
65 if( SelectNextRubyChars( aPam
, *pNew
))
67 rList
.push_back(std::move(pNew
));
72 if( *aPam
.GetPoint() < *pEnd
)
74 // goto next paragraph
76 aPam
.Move( fnMoveForward
, GoInNode
);
81 } while( 30 > rList
.size() && *aPam
.GetPoint() < *pEnd
);
83 if( 30 <= rList
.size() )
85 _pStartCursor
= _pStartCursor
->GetNext();
86 } while( _pStartCursor
!= _pStartCursor2
);
91 void SwDoc::SetRubyList( const SwPaM
& rPam
, const SwRubyList
& rList
)
93 GetIDocumentUndoRedo().StartUndo( SwUndoId::SETRUBYATTR
, nullptr );
94 const o3tl::sorted_vector
<sal_uInt16
> aDelArr
{ RES_TXTATR_CJK_RUBY
};
96 SwRubyList::size_type nListEntry
= 0;
98 const SwPaM
*_pStartCursor
= rPam
.GetNext(),
99 *_pStartCursor2
= _pStartCursor
;
100 bool bCheckEmpty
= &rPam
!= _pStartCursor
;
102 auto [pStt
, pEnd
] = _pStartCursor
->StartEnd(); // SwPosition*
103 if( !bCheckEmpty
|| ( pStt
!= pEnd
&& *pStt
!= *pEnd
))
108 SwRubyListEntry aCheckEntry
;
112 *aPam
.GetMark() = *pEnd
;
114 if( SelectNextRubyChars( aPam
, aCheckEntry
))
116 const SwRubyListEntry
* pEntry
= rList
[ nListEntry
++ ].get();
117 if( aCheckEntry
.GetRubyAttr() != pEntry
->GetRubyAttr() )
119 // set/reset the attribute
120 if( !pEntry
->GetRubyAttr().GetText().isEmpty() )
122 getIDocumentContentOperations().InsertPoolItem( aPam
, pEntry
->GetRubyAttr() );
126 ResetAttrs( aPam
, true, aDelArr
);
130 if( !pEntry
->GetText().isEmpty() &&
131 aCheckEntry
.GetText() != pEntry
->GetText() )
133 // text is changed, so replace the original
134 getIDocumentContentOperations().ReplaceRange( aPam
, pEntry
->GetText(), false );
140 if( *aPam
.GetPoint() < *pEnd
)
142 // goto next paragraph
144 aPam
.Move( fnMoveForward
, GoInNode
);
148 const SwRubyListEntry
* pEntry
= rList
[ nListEntry
++ ].get();
150 // set/reset the attribute
151 if( !pEntry
->GetRubyAttr().GetText().isEmpty() &&
152 !pEntry
->GetText().isEmpty() )
154 getIDocumentContentOperations().InsertString( aPam
, pEntry
->GetText() );
156 aPam
.GetMark()->AdjustContent( -pEntry
->GetText().getLength() );
157 getIDocumentContentOperations().InsertPoolItem(
158 aPam
, pEntry
->GetRubyAttr(), SetAttrMode::DONTEXPAND
);
165 } while( nListEntry
< rList
.size() && *aPam
.GetPoint() < *pEnd
);
167 if( 30 <= rList
.size() )
169 _pStartCursor
= _pStartCursor
->GetNext();
170 } while( _pStartCursor
!= _pStartCursor2
);
172 GetIDocumentUndoRedo().EndUndo( SwUndoId::SETRUBYATTR
, nullptr );
175 bool SwDoc::SelectNextRubyChars( SwPaM
& rPam
, SwRubyListEntry
& rEntry
)
177 // Point must be the startposition, Mark is optional the end position
178 SwPosition
* pPos
= rPam
.GetPoint();
179 const SwTextNode
* pTNd
= pPos
->GetNode().GetTextNode();
180 OUString
const& rText
= pTNd
->GetText();
181 sal_Int32 nStart
= pPos
->GetContentIndex();
182 sal_Int32 nEnd
= rText
.getLength();
184 bool bHasMark
= rPam
.HasMark();
188 if( rPam
.GetMark()->GetNode() == pPos
->GetNode() )
191 const sal_Int32 nTEnd
= rPam
.GetMark()->GetContentIndex();
199 // look where a ruby attribute starts
200 const SwpHints
* pHts
= pTNd
->GetpSwpHints();
201 const SwTextAttr
* pAttr
= nullptr;
204 for( size_t nHtIdx
= 0; nHtIdx
< pHts
->Count(); ++nHtIdx
)
206 const SwTextAttr
* pHt
= pHts
->Get(nHtIdx
);
207 if( RES_TXTATR_CJK_RUBY
== pHt
->Which() &&
208 pHt
->GetAnyEnd() > nStart
)
210 if( pHt
->GetStart() < nEnd
)
213 if( !bHasMark
&& nStart
> pAttr
->GetStart() )
215 nStart
= pAttr
->GetStart();
216 pPos
->SetContent(nStart
);
224 if( !bHasMark
&& nStart
&& ( !pAttr
|| nStart
!= pAttr
->GetStart()) )
226 // skip to the word begin!
227 const sal_Int32 nWordStt
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
229 g_pBreakIt
->GetLocale( pTNd
->GetLang( nStart
)),
230 WordType::ANYWORD_IGNOREWHITESPACES
,
232 if (nWordStt
< nStart
&& nWordStt
>= 0)
235 pPos
->SetContent(nStart
);
239 bool bAlphaNum
= false;
240 sal_Int32 nWordEnd
= nEnd
;
241 CharClass
& rCC
= GetAppCharClass();
242 while( nStart
< nEnd
)
244 if( pAttr
&& nStart
== pAttr
->GetStart() )
246 pPos
->SetContent(nStart
);
247 if( !rPam
.HasMark() )
250 pPos
->SetContent(pAttr
->GetAnyEnd());
251 if( pPos
->GetContentIndex() > nEnd
)
252 pPos
->SetContent(nEnd
);
253 rEntry
.SetRubyAttr( pAttr
->GetRuby() );
258 sal_Int32 nChType
= rCC
.getType(rText
, nStart
);
259 bool bIgnoreChar
= false, bIsAlphaNum
= false, bChkNxtWrd
= false;
262 case UnicodeType::UPPERCASE_LETTER
:
263 case UnicodeType::LOWERCASE_LETTER
:
264 case UnicodeType::TITLECASE_LETTER
:
265 case UnicodeType::DECIMAL_DIGIT_NUMBER
:
266 bChkNxtWrd
= bIsAlphaNum
= true;
269 case UnicodeType::SPACE_SEPARATOR
:
270 case UnicodeType::CONTROL
:
271 /*??*/ case UnicodeType::PRIVATE_USE
:
272 case UnicodeType::START_PUNCTUATION
:
273 case UnicodeType::END_PUNCTUATION
:
277 case UnicodeType::OTHER_LETTER
:
287 if( bIgnoreChar
|| bIsAlphaNum
!= bAlphaNum
|| nStart
>= nWordEnd
)
290 else if( !bIgnoreChar
)
293 bAlphaNum
= bIsAlphaNum
;
296 // search the end of this word
297 nWordEnd
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
299 g_pBreakIt
->GetLocale( pTNd
->GetLang( nStart
)),
300 WordType::ANYWORD_IGNOREWHITESPACES
,
302 if( 0 > nWordEnd
|| nWordEnd
> nEnd
|| nWordEnd
== nStart
)
306 pTNd
->GoNext( pPos
, SwCursorSkipMode::Chars
);
307 nStart
= pPos
->GetContentIndex();
310 nStart
= rPam
.GetMark()->GetContentIndex();
311 rEntry
.SetText( rText
.copy( nStart
,
312 rPam
.GetPoint()->GetContentIndex() - nStart
));
313 return rPam
.HasMark();
316 SwRubyListEntry::~SwRubyListEntry()
320 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */