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 = pStt == _pStartCursor->GetPoint()
56  ? _pStartCursor->GetMark()
57  : _pStartCursor->GetPoint();
58  if( !bCheckEmpty || ( pStt != pEnd && *pStt != *pEnd ))
59  {
60  SwPaM aPam( *pStt );
61  do {
62  std::unique_ptr<SwRubyListEntry> pNew(new SwRubyListEntry);
63  if( pEnd != pStt )
64  {
65  aPam.SetMark();
66  *aPam.GetMark() = *pEnd;
67  }
68  if( SelectNextRubyChars( aPam, *pNew ))
69  {
70  rList.push_back(std::move(pNew));
71  aPam.DeleteMark();
72  }
73  else
74  {
75  if( *aPam.GetPoint() < *pEnd )
76  {
77  // goto next paragraph
78  aPam.DeleteMark();
79  aPam.Move( fnMoveForward, GoInNode );
80  }
81  else
82  break;
83  }
84  } while( 30 > rList.size() && *aPam.GetPoint() < *pEnd );
85  }
86  if( 30 <= rList.size() )
87  break;
88  _pStartCursor = _pStartCursor->GetNext();
89  } while( _pStartCursor != _pStartCursor2 );
90 
91  return rList.size();
92 }
93 
94 void SwDoc::SetRubyList( const SwPaM& rPam, const SwRubyList& rList )
95 {
98 
99  SwRubyList::size_type nListEntry = 0;
100 
101  const SwPaM *_pStartCursor = rPam.GetNext(),
102  *_pStartCursor2 = _pStartCursor;
103  bool bCheckEmpty = &rPam != _pStartCursor;
104  do {
105  const SwPosition* pStt = _pStartCursor->Start(),
106  * pEnd = pStt == _pStartCursor->GetPoint()
107  ? _pStartCursor->GetMark()
108  : _pStartCursor->GetPoint();
109  if( !bCheckEmpty || ( pStt != pEnd && *pStt != *pEnd ))
110  {
111 
112  SwPaM aPam( *pStt );
113  do {
114  SwRubyListEntry aCheckEntry;
115  if( pEnd != pStt )
116  {
117  aPam.SetMark();
118  *aPam.GetMark() = *pEnd;
119  }
120  if( SelectNextRubyChars( aPam, aCheckEntry ))
121  {
122  const SwRubyListEntry* pEntry = rList[ nListEntry++ ].get();
123  if( aCheckEntry.GetRubyAttr() != pEntry->GetRubyAttr() )
124  {
125  // set/reset the attribute
126  if( !pEntry->GetRubyAttr().GetText().isEmpty() )
127  {
129  }
130  else
131  {
132  ResetAttrs( aPam, true, aDelArr );
133  }
134  }
135 
136  if( !pEntry->GetText().isEmpty() &&
137  aCheckEntry.GetText() != pEntry->GetText() )
138  {
139  // text is changed, so replace the original
140  getIDocumentContentOperations().ReplaceRange( aPam, pEntry->GetText(), false );
141  }
142  aPam.DeleteMark();
143  }
144  else
145  {
146  if( *aPam.GetPoint() < *pEnd )
147  {
148  // goto next paragraph
149  aPam.DeleteMark();
150  aPam.Move( fnMoveForward, GoInNode );
151  }
152  else
153  {
154  const SwRubyListEntry* pEntry = rList[ nListEntry++ ].get();
155 
156  // set/reset the attribute
157  if( !pEntry->GetRubyAttr().GetText().isEmpty() &&
158  !pEntry->GetText().isEmpty() )
159  {
161  aPam.SetMark();
162  aPam.GetMark()->nContent -= pEntry->GetText().getLength();
164  aPam, pEntry->GetRubyAttr(), SetAttrMode::DONTEXPAND );
165  }
166  else
167  break;
168  aPam.DeleteMark();
169  }
170  }
171  } while( nListEntry < rList.size() && *aPam.GetPoint() < *pEnd );
172  }
173  if( 30 <= rList.size() )
174  break;
175  _pStartCursor = _pStartCursor->GetNext();
176  } while( _pStartCursor != _pStartCursor2 );
177 
179 }
180 
182 {
183  // Point must be the startposition, Mark is optional the end position
184  SwPosition* pPos = rPam.GetPoint();
185  const SwTextNode* pTNd = pPos->nNode.GetNode().GetTextNode();
186  OUString const& rText = pTNd->GetText();
187  sal_Int32 nStart = pPos->nContent.GetIndex();
188  sal_Int32 nEnd = rText.getLength();
189 
190  bool bHasMark = rPam.HasMark();
191  if( bHasMark )
192  {
193  // in the same node?
194  if( rPam.GetMark()->nNode == pPos->nNode )
195  {
196  // then use that end
197  const sal_Int32 nTEnd = rPam.GetMark()->nContent.GetIndex();
198  if( nTEnd < nEnd )
199  nEnd = nTEnd;
200  }
201  rPam.DeleteMark();
202  }
203 
204  // search the start
205  // look where a ruby attribute starts
206  const SwpHints* pHts = pTNd->GetpSwpHints();
207  const SwTextAttr* pAttr = nullptr;
208  if( pHts )
209  {
210  for( size_t nHtIdx = 0; nHtIdx < pHts->Count(); ++nHtIdx )
211  {
212  const SwTextAttr* pHt = pHts->Get(nHtIdx);
213  if( RES_TXTATR_CJK_RUBY == pHt->Which() &&
214  pHt->GetAnyEnd() > nStart )
215  {
216  if( pHt->GetStart() < nEnd )
217  {
218  pAttr = pHt;
219  if( !bHasMark && nStart > pAttr->GetStart() )
220  {
221  nStart = pAttr->GetStart();
222  pPos->nContent = nStart;
223  }
224  }
225  break;
226  }
227  }
228  }
229 
230  if( !bHasMark && nStart && ( !pAttr || nStart != pAttr->GetStart()) )
231  {
232  // skip to the word begin!
233  const sal_Int32 nWordStt = g_pBreakIt->GetBreakIter()->getWordBoundary(
234  rText, nStart,
235  g_pBreakIt->GetLocale( pTNd->GetLang( nStart )),
236  WordType::ANYWORD_IGNOREWHITESPACES,
237  true ).startPos;
238  if (nWordStt < nStart && nWordStt >= 0)
239  {
240  nStart = nWordStt;
241  pPos->nContent = nStart;
242  }
243  }
244 
245  bool bAlphaNum = false;
246  sal_Int32 nWordEnd = nEnd;
247  CharClass& rCC = GetAppCharClass();
248  while( nStart < nEnd )
249  {
250  if( pAttr && nStart == pAttr->GetStart() )
251  {
252  pPos->nContent = nStart;
253  if( !rPam.HasMark() )
254  {
255  rPam.SetMark();
256  pPos->nContent = pAttr->GetAnyEnd();
257  if( pPos->nContent.GetIndex() > nEnd )
258  pPos->nContent = nEnd;
259  rEntry.SetRubyAttr( pAttr->GetRuby() );
260  }
261  break;
262  }
263 
264  sal_Int32 nChType = rCC.getType(rText, nStart);
265  bool bIgnoreChar = false, bIsAlphaNum = false, bChkNxtWrd = false;
266  switch( nChType )
267  {
268  case UnicodeType::UPPERCASE_LETTER:
269  case UnicodeType::LOWERCASE_LETTER:
270  case UnicodeType::TITLECASE_LETTER:
271  case UnicodeType::DECIMAL_DIGIT_NUMBER:
272  bChkNxtWrd = bIsAlphaNum = true;
273  break;
274 
275  case UnicodeType::SPACE_SEPARATOR:
276  case UnicodeType::CONTROL:
277 /*??*/ case UnicodeType::PRIVATE_USE:
278  case UnicodeType::START_PUNCTUATION:
279  case UnicodeType::END_PUNCTUATION:
280  bIgnoreChar = true;
281  break;
282 
283  case UnicodeType::OTHER_LETTER:
284  bChkNxtWrd = true;
285  [[fallthrough]];
286  default:
287  bIsAlphaNum = false;
288  break;
289  }
290 
291  if( rPam.HasMark() )
292  {
293  if( bIgnoreChar || bIsAlphaNum != bAlphaNum || nStart >= nWordEnd )
294  break;
295  }
296  else if( !bIgnoreChar )
297  {
298  rPam.SetMark();
299  bAlphaNum = bIsAlphaNum;
300  if (bChkNxtWrd)
301  {
302  // search the end of this word
303  nWordEnd = g_pBreakIt->GetBreakIter()->getWordBoundary(
304  rText, nStart,
305  g_pBreakIt->GetLocale( pTNd->GetLang( nStart )),
306  WordType::ANYWORD_IGNOREWHITESPACES,
307  true ).endPos;
308  if( 0 > nWordEnd || nWordEnd > nEnd || nWordEnd == nStart )
309  nWordEnd = nEnd;
310  }
311  }
312  pTNd->GoNext( &pPos->nContent, CRSR_SKIP_CHARS );
313  nStart = pPos->nContent.GetIndex();
314  }
315 
316  nStart = rPam.GetMark()->nContent.GetIndex();
317  rEntry.SetText( rText.copy( nStart,
318  rPam.GetPoint()->nContent.GetIndex() - nStart ));
319  return rPam.HasMark();
320 }
321 
323 {
324 }
325 
326 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void DeleteMark()
Definition: pam.hxx:177
Marks a position in the document model.
Definition: pam.hxx:35
static sal_uInt16 FillRubyList(const SwPaM &rPam, SwRubyList &rList)
Definition: docruby.cxx:48
bool GoNext(SwIndex *, sal_uInt16 nMode) const
Definition: node.cxx:1274
const OUString & GetText() const
Definition: ndtxt.hxx:215
SwpHints * GetpSwpHints()
Definition: ndtxt.hxx:223
SwNodeIndex nNode
Definition: pam.hxx:37
static bool SelectNextRubyChars(SwPaM &rPam, SwRubyListEntry &rRubyEntry)
Definition: docruby.cxx:181
std::vector< std::unique_ptr< SwRubyListEntry >> SwRubyList
Definition: doc.hxx:185
const SwPosition * GetMark() const
Definition: pam.hxx:209
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:114
void SetRubyList(const SwPaM &rPam, const SwRubyList &rList)
Definition: docruby.cxx:94
sal_Int32 GetAnyEnd() const
end (if available), else start
Definition: txatbase.hxx:157
IDocumentContentOperations const & getIDocumentContentOperations() const
Definition: doc.cxx:315
SwIndex nContent
Definition: pam.hxx:38
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:244
A wrapper around SfxPoolItem to store the start position of (usually) a text portion, with an optional end.
Definition: txatbase.hxx:41
SwBreakIt * g_pBreakIt
Definition: breakit.cxx:33
sal_Int32 GetStart() const
Definition: txatbase.hxx:86
constexpr TypedWhichId< SwFormatRuby > RES_TXTATR_CJK_RUBY(53)
SwPaM * GetNext()
Definition: pam.hxx:264
const OUString & GetText() const
Definition: fmtruby.hxx:61
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:136
bool Move(SwMoveFnCollection const &fnMove=fnMoveForward, SwGoInDoc fnGo=GoInContent)
Movement of cursor.
Definition: pam.cxx:504
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:961
const SwPosition * GetPoint() const
Definition: pam.hxx:207
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:205
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:212
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
LanguageType GetLang(const sal_Int32 nBegin, const sal_Int32 nLen=0, sal_uInt16 nScript=0) const
Definition: thints.cxx:3365
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:478
CharClass & GetAppCharClass()
Definition: init.cxx:703
const SwFormatRuby & GetRuby() const
Definition: txatbase.hxx:234
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:850