LibreOffice Module sw (master) 1
docruby.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <memory>
21#include <string.h>
22
23#include <com/sun/star/i18n/UnicodeType.hpp>
24#include <com/sun/star/i18n/WordType.hpp>
25#include <com/sun/star/i18n/XBreakIterator.hpp>
26
28
29#include <hintids.hxx>
30#include <doc.hxx>
31#include <IDocumentUndoRedo.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>
40
41using namespace ::com::sun::star::i18n;
42
43/*
44 * Members in the list:
45 * - String - the orig text
46 * - SwFormatRuby - the ruby attribute
47 */
48sal_uInt16 SwDoc::FillRubyList( const SwPaM& rPam, SwRubyList& rList )
49{
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 ))
56 {
57 SwPaM aPam( *pStt );
58 do {
59 std::unique_ptr<SwRubyListEntry> pNew(new SwRubyListEntry);
60 if( pEnd != pStt )
61 {
62 aPam.SetMark();
63 *aPam.GetMark() = *pEnd;
64 }
65 if( SelectNextRubyChars( aPam, *pNew ))
66 {
67 rList.push_back(std::move(pNew));
68 aPam.DeleteMark();
69 }
70 else
71 {
72 if( *aPam.GetPoint() < *pEnd )
73 {
74 // goto next paragraph
75 aPam.DeleteMark();
77 }
78 else
79 break;
80 }
81 } while( 30 > rList.size() && *aPam.GetPoint() < *pEnd );
82 }
83 if( 30 <= rList.size() )
84 break;
85 _pStartCursor = _pStartCursor->GetNext();
86 } while( _pStartCursor != _pStartCursor2 );
87
88 return rList.size();
89}
90
91void SwDoc::SetRubyList( const SwPaM& rPam, const SwRubyList& rList )
92{
93 GetIDocumentUndoRedo().StartUndo( SwUndoId::SETRUBYATTR, nullptr );
95
96 SwRubyList::size_type nListEntry = 0;
97
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 ))
104 {
105
106 SwPaM aPam( *pStt );
107 do {
108 SwRubyListEntry aCheckEntry;
109 if( pEnd != pStt )
110 {
111 aPam.SetMark();
112 *aPam.GetMark() = *pEnd;
113 }
114 if( SelectNextRubyChars( aPam, aCheckEntry ))
115 {
116 const SwRubyListEntry* pEntry = rList[ nListEntry++ ].get();
117 if( aCheckEntry.GetRubyAttr() != pEntry->GetRubyAttr() )
118 {
119 // set/reset the attribute
120 if( !pEntry->GetRubyAttr().GetText().isEmpty() )
121 {
123 }
124 else
125 {
126 ResetAttrs( aPam, true, aDelArr );
127 }
128 }
129
130 if( !pEntry->GetText().isEmpty() &&
131 aCheckEntry.GetText() != pEntry->GetText() )
132 {
133 // text is changed, so replace the original
134 getIDocumentContentOperations().ReplaceRange( aPam, pEntry->GetText(), false );
135 }
136 aPam.DeleteMark();
137 }
138 else
139 {
140 if( *aPam.GetPoint() < *pEnd )
141 {
142 // goto next paragraph
143 aPam.DeleteMark();
144 aPam.Move( fnMoveForward, GoInNode );
145 }
146 else
147 {
148 const SwRubyListEntry* pEntry = rList[ nListEntry++ ].get();
149
150 // set/reset the attribute
151 if( !pEntry->GetRubyAttr().GetText().isEmpty() &&
152 !pEntry->GetText().isEmpty() )
153 {
155 aPam.SetMark();
156 aPam.GetMark()->AdjustContent( -pEntry->GetText().getLength() );
158 aPam, pEntry->GetRubyAttr(), SetAttrMode::DONTEXPAND );
159 }
160 else
161 break;
162 aPam.DeleteMark();
163 }
164 }
165 } while( nListEntry < rList.size() && *aPam.GetPoint() < *pEnd );
166 }
167 if( 30 <= rList.size() )
168 break;
169 _pStartCursor = _pStartCursor->GetNext();
170 } while( _pStartCursor != _pStartCursor2 );
171
172 GetIDocumentUndoRedo().EndUndo( SwUndoId::SETRUBYATTR, nullptr );
173}
174
176{
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();
183
184 bool bHasMark = rPam.HasMark();
185 if( bHasMark )
186 {
187 // in the same node?
188 if( rPam.GetMark()->GetNode() == pPos->GetNode() )
189 {
190 // then use that end
191 const sal_Int32 nTEnd = rPam.GetMark()->GetContentIndex();
192 if( nTEnd < nEnd )
193 nEnd = nTEnd;
194 }
195 rPam.DeleteMark();
196 }
197
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 )
203 {
204 for( size_t nHtIdx = 0; nHtIdx < pHts->Count(); ++nHtIdx )
205 {
206 const SwTextAttr* pHt = pHts->Get(nHtIdx);
207 if( RES_TXTATR_CJK_RUBY == pHt->Which() &&
208 pHt->GetAnyEnd() > nStart )
209 {
210 if( pHt->GetStart() < nEnd )
211 {
212 pAttr = pHt;
213 if( !bHasMark && nStart > pAttr->GetStart() )
214 {
215 nStart = pAttr->GetStart();
216 pPos->SetContent(nStart);
217 }
218 }
219 break;
220 }
221 }
222 }
223
224 if( !bHasMark && nStart && ( !pAttr || nStart != pAttr->GetStart()) )
225 {
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)
233 {
234 nStart = nWordStt;
235 pPos->SetContent(nStart);
236 }
237 }
238
239 bool bAlphaNum = false;
240 sal_Int32 nWordEnd = nEnd;
241 CharClass& rCC = GetAppCharClass();
242 while( nStart < nEnd )
243 {
244 if( pAttr && nStart == pAttr->GetStart() )
245 {
246 pPos->SetContent(nStart);
247 if( !rPam.HasMark() )
248 {
249 rPam.SetMark();
250 pPos->SetContent(pAttr->GetAnyEnd());
251 if( pPos->GetContentIndex() > nEnd )
252 pPos->SetContent(nEnd);
253 rEntry.SetRubyAttr( pAttr->GetRuby() );
254 }
255 break;
256 }
257
258 sal_Int32 nChType = rCC.getType(rText, nStart);
259 bool bIgnoreChar = false, bIsAlphaNum = false, bChkNxtWrd = false;
260 switch( nChType )
261 {
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;
268
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;
276
277 case UnicodeType::OTHER_LETTER:
278 bChkNxtWrd = true;
279 [[fallthrough]];
280 default:
281 bIsAlphaNum = false;
282 break;
283 }
284
285 if( rPam.HasMark() )
286 {
287 if( bIgnoreChar || bIsAlphaNum != bAlphaNum || nStart >= nWordEnd )
288 break;
289 }
290 else if( !bIgnoreChar )
291 {
292 rPam.SetMark();
293 bAlphaNum = bIsAlphaNum;
294 if (bChkNxtWrd)
295 {
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;
304 }
305 }
306 pTNd->GoNext( pPos, SwCursorSkipMode::Chars );
307 nStart = pPos->GetContentIndex();
308 }
309
310 nStart = rPam.GetMark()->GetContentIndex();
311 rEntry.SetText( rText.copy( nStart,
312 rPam.GetPoint()->GetContentIndex() - nStart ));
313 return rPam.HasMark();
314}
315
317{
318}
319
320/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SwBreakIt * g_pBreakIt
Definition: breakit.cxx:34
sal_Int16 getType(const OUString &rStr, sal_Int32 nPos) const
virtual bool ReplaceRange(SwPaM &rPam, const OUString &rNewStr, const bool bRegExReplace)=0
Replace selected range in a TextNode with string.
virtual bool InsertPoolItem(const SwPaM &rRg, const SfxPoolItem &, const SetAttrMode nFlags=SetAttrMode::DEFAULT, SwRootFrame const *pLayout=nullptr, SwTextAttr **ppNewTextAttr=nullptr)=0
Insert an attribute.
virtual bool InsertString(const SwPaM &rRg, const OUString &, const SwInsertFlags nInsertMode=SwInsertFlags::EMPTYEXPAND)=0
Insert string into existing text node at position rRg.Point().
css::uno::Reference< css::i18n::XBreakIterator > const & GetBreakIter() const
Definition: breakit.hxx:62
const css::lang::Locale & GetLocale(const LanguageType aLang)
Definition: breakit.hxx:67
bool GoNext(SwContentIndex *, SwCursorSkipMode nMode) const
Definition: node.cxx:1302
IDocumentContentOperations const & getIDocumentContentOperations() const
Definition: doc.cxx:321
IDocumentUndoRedo & GetIDocumentUndoRedo()
Definition: doc.cxx:150
void ResetAttrs(const SwPaM &rRg, bool bTextAttr=true, const o3tl::sorted_vector< sal_uInt16 > &rAttrs=o3tl::sorted_vector< sal_uInt16 >(), const bool bSendDataChangedEvents=true, SwRootFrame const *pLayout=nullptr)
Reset attributes.
Definition: docfmt.cxx:243
static sal_uInt16 FillRubyList(const SwPaM &rPam, SwRubyList &rList)
Definition: docruby.cxx:48
static bool SelectNextRubyChars(SwPaM &rPam, SwRubyListEntry &rRubyEntry)
Definition: docruby.cxx:175
void SetRubyList(const SwPaM &rPam, const SwRubyList &rList)
Definition: docruby.cxx:91
const OUString & GetText() const
Definition: fmtruby.hxx:61
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:897
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:187
const SwPosition * GetMark() const
Definition: pam.hxx:263
virtual void SetMark()
Unless this is called, the getter method of Mark will return Point.
Definition: pam.cxx:642
std::pair< const SwPosition *, const SwPosition * > StartEnd() const
Because sometimes the cost of the operator<= can add up.
Definition: pam.hxx:277
bool Move(SwMoveFnCollection const &fnMove=fnMoveForward, SwGoInDoc fnGo=GoInContent)
Movement of cursor.
Definition: pam.cxx:668
SwPaM * GetNext()
Definition: pam.hxx:320
void DeleteMark()
Definition: pam.hxx:231
const SwPosition * GetPoint() const
Definition: pam.hxx:261
bool HasMark() const
A PaM marks a selection if Point and Mark are distinct positions.
Definition: pam.hxx:259
void SetText(const OUString &rStr)
Definition: rubylist.hxx:33
void SetRubyAttr(const SwFormatRuby &rAttr)
Definition: rubylist.hxx:37
const OUString & GetText() const
Definition: rubylist.hxx:32
const SwFormatRuby & GetRubyAttr() const
Definition: rubylist.hxx:35
A wrapper around SfxPoolItem to store the start position of (usually) a text portion,...
Definition: txatbase.hxx:44
sal_Int32 GetAnyEnd() const
end (if available), else start
Definition: txatbase.hxx:161
sal_Int32 GetStart() const
Definition: txatbase.hxx:88
const SwFormatRuby & GetRuby() const
Definition: txatbase.hxx:250
sal_uInt16 Which() const
Definition: txatbase.hxx:116
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:111
SwpHints * GetpSwpHints()
Definition: ndtxt.hxx:250
const OUString & GetText() const
Definition: ndtxt.hxx:242
LanguageType GetLang(const sal_Int32 nBegin, const sal_Int32 nLen=0, sal_uInt16 nScript=0) const
Definition: thints.cxx:3462
An SwTextAttr container, stores all directly formatted text portions for a text node.
Definition: ndhints.hxx:68
SwTextAttr * Get(size_t nPos) const
Definition: ndhints.hxx:144
size_t Count() const
Definition: ndhints.hxx:142
std::vector< std::unique_ptr< SwRubyListEntry > > SwRubyList
Definition: doc.hxx:190
constexpr TypedWhichId< SwFormatRuby > RES_TXTATR_CJK_RUBY(53)
CharClass & GetAppCharClass()
Definition: init.cxx:705
bool GoInNode(SwPaM &rPam, SwMoveFnCollection const &fnMove)
Definition: pam.cxx:1170
SwMoveFnCollection const & fnMoveForward
SwPam::Move()/Find() default argument.
Definition: paminit.cxx:61
Marks a position in the document model.
Definition: pam.hxx:37
SwNode & GetNode() const
Definition: pam.hxx:80
void SetContent(sal_Int32 nContentIndex)
Set content index, only valid to call this if the position points to a SwContentNode subclass.
Definition: pam.cxx:266
sal_Int32 GetContentIndex() const
Definition: pam.hxx:84
void AdjustContent(sal_Int32 nDelta)
Adjust content index, only valid to call this if the position points to a SwContentNode subclass.
Definition: pam.cxx:261