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 
27 #include <unotools/charclass.hxx>
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 
41 using namespace ::com::sun::star::i18n;
42 
43 /*
44  * Members in the list:
45  * - String - the orig text
46  * - SwFormatRuby - the ruby attribute
47  */
48 sal_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  const SwPosition* pStt = _pStartCursor->Start(),
55  * pEnd = _pStartCursor->End();
56  if( !bCheckEmpty || ( pStt != pEnd && *pStt != *pEnd ))
57  {
58  SwPaM aPam( *pStt );
59  do {
60  std::unique_ptr<SwRubyListEntry> pNew(new SwRubyListEntry);
61  if( pEnd != pStt )
62  {
63  aPam.SetMark();
64  *aPam.GetMark() = *pEnd;
65  }
66  if( SelectNextRubyChars( aPam, *pNew ))
67  {
68  rList.push_back(std::move(pNew));
69  aPam.DeleteMark();
70  }
71  else
72  {
73  if( *aPam.GetPoint() < *pEnd )
74  {
75  // goto next paragraph
76  aPam.DeleteMark();
77  aPam.Move( fnMoveForward, GoInNode );
78  }
79  else
80  break;
81  }
82  } while( 30 > rList.size() && *aPam.GetPoint() < *pEnd );
83  }
84  if( 30 <= rList.size() )
85  break;
86  _pStartCursor = _pStartCursor->GetNext();
87  } while( _pStartCursor != _pStartCursor2 );
88 
89  return rList.size();
90 }
91 
92 void SwDoc::SetRubyList( const SwPaM& rPam, const SwRubyList& rList )
93 {
96 
97  SwRubyList::size_type nListEntry = 0;
98 
99  const SwPaM *_pStartCursor = rPam.GetNext(),
100  *_pStartCursor2 = _pStartCursor;
101  bool bCheckEmpty = &rPam != _pStartCursor;
102  do {
103  const SwPosition* pStt = _pStartCursor->Start(),
104  * pEnd = _pStartCursor->End();
105  if( !bCheckEmpty || ( pStt != pEnd && *pStt != *pEnd ))
106  {
107 
108  SwPaM aPam( *pStt );
109  do {
110  SwRubyListEntry aCheckEntry;
111  if( pEnd != pStt )
112  {
113  aPam.SetMark();
114  *aPam.GetMark() = *pEnd;
115  }
116  if( SelectNextRubyChars( aPam, aCheckEntry ))
117  {
118  const SwRubyListEntry* pEntry = rList[ nListEntry++ ].get();
119  if( aCheckEntry.GetRubyAttr() != pEntry->GetRubyAttr() )
120  {
121  // set/reset the attribute
122  if( !pEntry->GetRubyAttr().GetText().isEmpty() )
123  {
125  }
126  else
127  {
128  ResetAttrs( aPam, true, aDelArr );
129  }
130  }
131 
132  if( !pEntry->GetText().isEmpty() &&
133  aCheckEntry.GetText() != pEntry->GetText() )
134  {
135  // text is changed, so replace the original
137  }
138  aPam.DeleteMark();
139  }
140  else
141  {
142  if( *aPam.GetPoint() < *pEnd )
143  {
144  // goto next paragraph
145  aPam.DeleteMark();
146  aPam.Move( fnMoveForward, GoInNode );
147  }
148  else
149  {
150  const SwRubyListEntry* pEntry = rList[ nListEntry++ ].get();
151 
152  // set/reset the attribute
153  if( !pEntry->GetRubyAttr().GetText().isEmpty() &&
154  !pEntry->GetText().isEmpty() )
155  {
157  aPam.SetMark();
158  aPam.GetMark()->nContent -= pEntry->GetText().getLength();
160  aPam, pEntry->GetRubyAttr(), SetAttrMode::DONTEXPAND );
161  }
162  else
163  break;
164  aPam.DeleteMark();
165  }
166  }
167  } while( nListEntry < rList.size() && *aPam.GetPoint() < *pEnd );
168  }
169  if( 30 <= rList.size() )
170  break;
171  _pStartCursor = _pStartCursor->GetNext();
172  } while( _pStartCursor != _pStartCursor2 );
173 
175 }
176 
178 {
179  // Point must be the startposition, Mark is optional the end position
180  SwPosition* pPos = rPam.GetPoint();
181  const SwTextNode* pTNd = pPos->nNode.GetNode().GetTextNode();
182  OUString const& rText = pTNd->GetText();
183  sal_Int32 nStart = pPos->nContent.GetIndex();
184  sal_Int32 nEnd = rText.getLength();
185 
186  bool bHasMark = rPam.HasMark();
187  if( bHasMark )
188  {
189  // in the same node?
190  if( rPam.GetMark()->nNode == pPos->nNode )
191  {
192  // then use that end
193  const sal_Int32 nTEnd = rPam.GetMark()->nContent.GetIndex();
194  if( nTEnd < nEnd )
195  nEnd = nTEnd;
196  }
197  rPam.DeleteMark();
198  }
199 
200  // search the start
201  // look where a ruby attribute starts
202  const SwpHints* pHts = pTNd->GetpSwpHints();
203  const SwTextAttr* pAttr = nullptr;
204  if( pHts )
205  {
206  for( size_t nHtIdx = 0; nHtIdx < pHts->Count(); ++nHtIdx )
207  {
208  const SwTextAttr* pHt = pHts->Get(nHtIdx);
209  if( RES_TXTATR_CJK_RUBY == pHt->Which() &&
210  pHt->GetAnyEnd() > nStart )
211  {
212  if( pHt->GetStart() < nEnd )
213  {
214  pAttr = pHt;
215  if( !bHasMark && nStart > pAttr->GetStart() )
216  {
217  nStart = pAttr->GetStart();
218  pPos->nContent = nStart;
219  }
220  }
221  break;
222  }
223  }
224  }
225 
226  if( !bHasMark && nStart && ( !pAttr || nStart != pAttr->GetStart()) )
227  {
228  // skip to the word begin!
229  const sal_Int32 nWordStt = g_pBreakIt->GetBreakIter()->getWordBoundary(
230  rText, nStart,
231  g_pBreakIt->GetLocale( pTNd->GetLang( nStart )),
232  WordType::ANYWORD_IGNOREWHITESPACES,
233  true ).startPos;
234  if (nWordStt < nStart && nWordStt >= 0)
235  {
236  nStart = nWordStt;
237  pPos->nContent = nStart;
238  }
239  }
240 
241  bool bAlphaNum = false;
242  sal_Int32 nWordEnd = nEnd;
243  CharClass& rCC = GetAppCharClass();
244  while( nStart < nEnd )
245  {
246  if( pAttr && nStart == pAttr->GetStart() )
247  {
248  pPos->nContent = nStart;
249  if( !rPam.HasMark() )
250  {
251  rPam.SetMark();
252  pPos->nContent = pAttr->GetAnyEnd();
253  if( pPos->nContent.GetIndex() > nEnd )
254  pPos->nContent = nEnd;
255  rEntry.SetRubyAttr( pAttr->GetRuby() );
256  }
257  break;
258  }
259 
260  sal_Int32 nChType = rCC.getType(rText, nStart);
261  bool bIgnoreChar = false, bIsAlphaNum = false, bChkNxtWrd = false;
262  switch( nChType )
263  {
264  case UnicodeType::UPPERCASE_LETTER:
265  case UnicodeType::LOWERCASE_LETTER:
266  case UnicodeType::TITLECASE_LETTER:
267  case UnicodeType::DECIMAL_DIGIT_NUMBER:
268  bChkNxtWrd = bIsAlphaNum = true;
269  break;
270 
271  case UnicodeType::SPACE_SEPARATOR:
272  case UnicodeType::CONTROL:
273 /*??*/ case UnicodeType::PRIVATE_USE:
274  case UnicodeType::START_PUNCTUATION:
275  case UnicodeType::END_PUNCTUATION:
276  bIgnoreChar = true;
277  break;
278 
279  case UnicodeType::OTHER_LETTER:
280  bChkNxtWrd = true;
281  [[fallthrough]];
282  default:
283  bIsAlphaNum = false;
284  break;
285  }
286 
287  if( rPam.HasMark() )
288  {
289  if( bIgnoreChar || bIsAlphaNum != bAlphaNum || nStart >= nWordEnd )
290  break;
291  }
292  else if( !bIgnoreChar )
293  {
294  rPam.SetMark();
295  bAlphaNum = bIsAlphaNum;
296  if (bChkNxtWrd)
297  {
298  // search the end of this word
299  nWordEnd = g_pBreakIt->GetBreakIter()->getWordBoundary(
300  rText, nStart,
301  g_pBreakIt->GetLocale( pTNd->GetLang( nStart )),
302  WordType::ANYWORD_IGNOREWHITESPACES,
303  true ).endPos;
304  if( 0 > nWordEnd || nWordEnd > nEnd || nWordEnd == nStart )
305  nWordEnd = nEnd;
306  }
307  }
308  pTNd->GoNext( &pPos->nContent, CRSR_SKIP_CHARS );
309  nStart = pPos->nContent.GetIndex();
310  }
311 
312  nStart = rPam.GetMark()->nContent.GetIndex();
313  rEntry.SetText( rText.copy( nStart,
314  rPam.GetPoint()->nContent.GetIndex() - nStart ));
315  return rPam.HasMark();
316 }
317 
319 {
320 }
321 
322 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void DeleteMark()
Definition: pam.hxx:178
Marks a position in the document model.
Definition: pam.hxx:36
static sal_uInt16 FillRubyList(const SwPaM &rPam, SwRubyList &rList)
Definition: docruby.cxx:48
bool GoNext(SwIndex *, sal_uInt16 nMode) const
Definition: node.cxx:1273
const OUString & GetText() const
Definition: ndtxt.hxx:218
SwpHints * GetpSwpHints()
Definition: ndtxt.hxx:226
SwNodeIndex nNode
Definition: pam.hxx:38
static bool SelectNextRubyChars(SwPaM &rPam, SwRubyListEntry &rRubyEntry)
Definition: docruby.cxx:177
std::vector< std::unique_ptr< SwRubyListEntry >> SwRubyList
Definition: doc.hxx:184
const SwPosition * GetMark() const
Definition: pam.hxx:210
virtual SwUndoId EndUndo(SwUndoId const eUndoId, SwRewriter const *const pRewriter)=0
Closes undo block.
SwNode & GetNode() const
Definition: ndindex.hxx:119
void SetRubyAttr(const SwFormatRuby &rAttr)
Definition: rubylist.hxx:37
IDocumentUndoRedo & GetIDocumentUndoRedo()
Definition: doc.cxx:144
virtual bool InsertPoolItem(const SwPaM &rRg, const SfxPoolItem &, const SetAttrMode nFlags=SetAttrMode::DEFAULT, SwRootFrame const *pLayout=nullptr, SwTextAttr **ppNewTextAttr=nullptr)=0
Insert an attribute.
sal_uInt16 Which() const
Definition: txatbase.hxx:116
void SetRubyList(const SwPaM &rPam, const SwRubyList &rList)
Definition: docruby.cxx:92
sal_Int32 GetAnyEnd() const
end (if available), else start
Definition: txatbase.hxx:161
IDocumentContentOperations const & getIDocumentContentOperations() const
Definition: doc.cxx:315
SwIndex nContent
Definition: pam.hxx:39
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:242
A wrapper around SfxPoolItem to store the start position of (usually) a text portion, with an optional end.
Definition: txatbase.hxx:43
SwBreakIt * g_pBreakIt
Definition: breakit.cxx:33
sal_Int32 GetStart() const
Definition: txatbase.hxx:88
constexpr TypedWhichId< SwFormatRuby > RES_TXTATR_CJK_RUBY(53)
SwPaM * GetNext()
Definition: pam.hxx:265
const OUString & GetText() const
Definition: fmtruby.hxx:61
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:137
bool Move(SwMoveFnCollection const &fnMove=fnMoveForward, SwGoInDoc fnGo=GoInContent)
Movement of cursor.
Definition: pam.cxx:502
virtual bool InsertString(const SwPaM &rRg, const OUString &, const SwInsertFlags nInsertMode=SwInsertFlags::EMPTYEXPAND)=0
Insert string into existing text node at position rRg.Point().
size_t Count() const
Definition: ndhints.hxx:142
virtual SwUndoId StartUndo(SwUndoId const eUndoId, SwRewriter const *const pRewriter)=0
Opens undo block.
SwTextAttr * Get(size_t nPos) const
Definition: ndhints.hxx:144
bool GoInNode(SwPaM &rPam, SwMoveFnCollection const &fnMove)
Definition: pam.cxx:959
const SwPosition * GetPoint() const
Definition: pam.hxx:208
sal_Int16 getType(const OUString &rStr, sal_Int32 nPos) const
bool HasMark() const
A PaM marks a selection if Point and Mark are distinct positions.
Definition: pam.hxx:206
void SetText(const OUString &rStr)
Definition: rubylist.hxx:33
virtual bool ReplaceRange(SwPaM &rPam, const OUString &rNewStr, const bool bRegExReplace)=0
Replace selected range in a TextNode with string.
const SwPosition * Start() const
Definition: pam.hxx:213
css::uno::Reference< css::i18n::XBreakIterator > const & GetBreakIter() const
Definition: breakit.hxx:62
const SwFormatRuby & GetRubyAttr() const
Definition: rubylist.hxx:35
const css::lang::Locale & GetLocale(const LanguageType aLang)
Definition: breakit.hxx:67
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:79
An SwTextAttr container, stores all directly formatted text portions for a text node.
Definition: ndhints.hxx:67
SwMoveFnCollection const & fnMoveForward
SwPam::Move()/Find() default argument.
Definition: paminit.cxx:59
sal_Int32 GetIndex() const
Definition: index.hxx:91
const SwPosition * End() const
Definition: pam.hxx:218
LanguageType GetLang(const sal_Int32 nBegin, const sal_Int32 nLen=0, sal_uInt16 nScript=0) const
Definition: thints.cxx:3433
const sal_uInt16 CRSR_SKIP_CHARS
Definition: swcrsr.hxx:65
const OUString & GetText() const
Definition: rubylist.hxx:32
virtual void SetMark()
Unless this is called, the getter method of Mark will return Point.
Definition: pam.cxx:476
CharClass & GetAppCharClass()
Definition: init.cxx:705
const SwFormatRuby & GetRuby() const
Definition: txatbase.hxx:250
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:864
bool m_bDetectedRangeSegmentation false