android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / doc / docruby.cxx
blob1a1b84ffd7f48d80ea025856eddc76282e33cfa4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <memory>
21 #include <string.h>
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>
30 #include <doc.hxx>
31 #include <IDocumentUndoRedo.hxx>
32 #include <IDocumentContentOperations.hxx>
33 #include <ndtxt.hxx>
34 #include <txatbase.hxx>
35 #include <rubylist.hxx>
36 #include <pam.hxx>
37 #include <swundo.hxx>
38 #include <breakit.hxx>
39 #include <swcrsr.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;
53 do {
54 auto [pStt, pEnd] = _pStartCursor->StartEnd(); // SwPosition*
55 if( !bCheckEmpty || ( pStt != pEnd && *pStt != *pEnd ))
57 SwPaM aPam( *pStt );
58 do {
59 std::unique_ptr<SwRubyListEntry> pNew(new SwRubyListEntry);
60 if( pEnd != pStt )
62 aPam.SetMark();
63 *aPam.GetMark() = *pEnd;
65 if( SelectNextRubyChars( aPam, *pNew ))
67 rList.push_back(std::move(pNew));
68 aPam.DeleteMark();
70 else
72 if( *aPam.GetPoint() < *pEnd )
74 // goto next paragraph
75 aPam.DeleteMark();
76 aPam.Move( fnMoveForward, GoInNode );
78 else
79 break;
81 } while( 30 > rList.size() && *aPam.GetPoint() < *pEnd );
83 if( 30 <= rList.size() )
84 break;
85 _pStartCursor = _pStartCursor->GetNext();
86 } while( _pStartCursor != _pStartCursor2 );
88 return rList.size();
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;
101 do {
102 auto [pStt, pEnd] = _pStartCursor->StartEnd(); // SwPosition*
103 if( !bCheckEmpty || ( pStt != pEnd && *pStt != *pEnd ))
106 SwPaM aPam( *pStt );
107 do {
108 SwRubyListEntry aCheckEntry;
109 if( pEnd != pStt )
111 aPam.SetMark();
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() );
124 else
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 );
136 aPam.DeleteMark();
138 else
140 if( *aPam.GetPoint() < *pEnd )
142 // goto next paragraph
143 aPam.DeleteMark();
144 aPam.Move( fnMoveForward, GoInNode );
146 else
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() );
155 aPam.SetMark();
156 aPam.GetMark()->AdjustContent( -pEntry->GetText().getLength() );
157 getIDocumentContentOperations().InsertPoolItem(
158 aPam, pEntry->GetRubyAttr(), SetAttrMode::DONTEXPAND );
160 else
161 break;
162 aPam.DeleteMark();
165 } while( nListEntry < rList.size() && *aPam.GetPoint() < *pEnd );
167 if( 30 <= rList.size() )
168 break;
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();
185 if( bHasMark )
187 // in the same node?
188 if( rPam.GetMark()->GetNode() == pPos->GetNode() )
190 // then use that end
191 const sal_Int32 nTEnd = rPam.GetMark()->GetContentIndex();
192 if( nTEnd < nEnd )
193 nEnd = nTEnd;
195 rPam.DeleteMark();
198 // search the start
199 // look where a ruby attribute starts
200 const SwpHints* pHts = pTNd->GetpSwpHints();
201 const SwTextAttr* pAttr = nullptr;
202 if( pHts )
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 )
212 pAttr = pHt;
213 if( !bHasMark && nStart > pAttr->GetStart() )
215 nStart = pAttr->GetStart();
216 pPos->SetContent(nStart);
219 break;
224 if( !bHasMark && nStart && ( !pAttr || nStart != pAttr->GetStart()) )
226 // skip to the word begin!
227 const sal_Int32 nWordStt = g_pBreakIt->GetBreakIter()->getWordBoundary(
228 rText, nStart,
229 g_pBreakIt->GetLocale( pTNd->GetLang( nStart )),
230 WordType::ANYWORD_IGNOREWHITESPACES,
231 true ).startPos;
232 if (nWordStt < nStart && nWordStt >= 0)
234 nStart = nWordStt;
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() )
249 rPam.SetMark();
250 pPos->SetContent(pAttr->GetAnyEnd());
251 if( pPos->GetContentIndex() > nEnd )
252 pPos->SetContent(nEnd);
253 rEntry.SetRubyAttr( pAttr->GetRuby() );
255 break;
258 sal_Int32 nChType = rCC.getType(rText, nStart);
259 bool bIgnoreChar = false, bIsAlphaNum = false, bChkNxtWrd = false;
260 switch( nChType )
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;
267 break;
269 case UnicodeType::SPACE_SEPARATOR:
270 case UnicodeType::CONTROL:
271 /*??*/ case UnicodeType::PRIVATE_USE:
272 case UnicodeType::START_PUNCTUATION:
273 case UnicodeType::END_PUNCTUATION:
274 bIgnoreChar = true;
275 break;
277 case UnicodeType::OTHER_LETTER:
278 bChkNxtWrd = true;
279 [[fallthrough]];
280 default:
281 bIsAlphaNum = false;
282 break;
285 if( rPam.HasMark() )
287 if( bIgnoreChar || bIsAlphaNum != bAlphaNum || nStart >= nWordEnd )
288 break;
290 else if( !bIgnoreChar )
292 rPam.SetMark();
293 bAlphaNum = bIsAlphaNum;
294 if (bChkNxtWrd)
296 // search the end of this word
297 nWordEnd = g_pBreakIt->GetBreakIter()->getWordBoundary(
298 rText, nStart,
299 g_pBreakIt->GetLocale( pTNd->GetLang( nStart )),
300 WordType::ANYWORD_IGNOREWHITESPACES,
301 true ).endPos;
302 if( 0 > nWordEnd || nWordEnd > nEnd || nWordEnd == nStart )
303 nWordEnd = nEnd;
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: */