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  } while( 30 > rList.size() &&
87  (_pStartCursor = _pStartCursor->GetNext()) != _pStartCursor2 );
88 
89  return rList.size();
90 }
91 
92 void SwDoc::SetRubyList( const SwPaM& rPam, const SwRubyList& rList )
93 {
95  std::set<sal_uInt16> aDelArr;
96  aDelArr.insert( RES_TXTATR_CJK_RUBY );
97 
98  SwRubyList::size_type nListEntry = 0;
99 
100  const SwPaM *_pStartCursor = rPam.GetNext(),
101  *_pStartCursor2 = _pStartCursor;
102  bool bCheckEmpty = &rPam != _pStartCursor;
103  do {
104  const SwPosition* pStt = _pStartCursor->Start(),
105  * pEnd = pStt == _pStartCursor->GetPoint()
106  ? _pStartCursor->GetMark()
107  : _pStartCursor->GetPoint();
108  if( !bCheckEmpty || ( pStt != pEnd && *pStt != *pEnd ))
109  {
110 
111  SwPaM aPam( *pStt );
112  do {
113  SwRubyListEntry aCheckEntry;
114  if( pEnd != pStt )
115  {
116  aPam.SetMark();
117  *aPam.GetMark() = *pEnd;
118  }
119  if( SelectNextRubyChars( aPam, aCheckEntry ))
120  {
121  const SwRubyListEntry* pEntry = rList[ nListEntry++ ].get();
122  if( aCheckEntry.GetRubyAttr() != pEntry->GetRubyAttr() )
123  {
124  // set/reset the attribute
125  if( !pEntry->GetRubyAttr().GetText().isEmpty() )
126  {
128  }
129  else
130  {
131  ResetAttrs( aPam, true, aDelArr );
132  }
133  }
134 
135  if( !pEntry->GetText().isEmpty() &&
136  aCheckEntry.GetText() != pEntry->GetText() )
137  {
138  // text is changed, so replace the original
139  getIDocumentContentOperations().ReplaceRange( aPam, pEntry->GetText(), false );
140  }
141  aPam.DeleteMark();
142  }
143  else
144  {
145  if( *aPam.GetPoint() < *pEnd )
146  {
147  // goto next paragraph
148  aPam.DeleteMark();
149  aPam.Move( fnMoveForward, GoInNode );
150  }
151  else
152  {
153  const SwRubyListEntry* pEntry = rList[ nListEntry++ ].get();
154 
155  // set/reset the attribute
156  if( !pEntry->GetRubyAttr().GetText().isEmpty() &&
157  !pEntry->GetText().isEmpty() )
158  {
160  aPam.SetMark();
161  aPam.GetMark()->nContent -= pEntry->GetText().getLength();
163  aPam, pEntry->GetRubyAttr(), SetAttrMode::DONTEXPAND );
164  }
165  else
166  break;
167  aPam.DeleteMark();
168  }
169  }
170  } while( nListEntry < rList.size() && *aPam.GetPoint() < *pEnd );
171  }
172  } while( 30 > rList.size() &&
173  (_pStartCursor = _pStartCursor->GetNext()) != _pStartCursor2 );
174 
176 }
177 
179 {
180  // Point must be the startposition, Mark is optional the end position
181  SwPosition* pPos = rPam.GetPoint();
182  const SwTextNode* pTNd = pPos->nNode.GetNode().GetTextNode();
183  OUString const& rText = pTNd->GetText();
184  sal_Int32 nStart = pPos->nContent.GetIndex();
185  sal_Int32 nEnd = rText.getLength();
186 
187  bool bHasMark = rPam.HasMark();
188  if( bHasMark )
189  {
190  // in the same node?
191  if( rPam.GetMark()->nNode == pPos->nNode )
192  {
193  // then use that end
194  const sal_Int32 nTEnd = rPam.GetMark()->nContent.GetIndex();
195  if( nTEnd < nEnd )
196  nEnd = nTEnd;
197  }
198  rPam.DeleteMark();
199  }
200 
201  // search the start
202  // look where a ruby attribute starts
203  const SwpHints* pHts = pTNd->GetpSwpHints();
204  const SwTextAttr* pAttr = nullptr;
205  if( pHts )
206  {
207  for( size_t nHtIdx = 0; nHtIdx < pHts->Count(); ++nHtIdx )
208  {
209  const SwTextAttr* pHt = pHts->Get(nHtIdx);
210  if( RES_TXTATR_CJK_RUBY == pHt->Which() &&
211  pHt->GetAnyEnd() > nStart )
212  {
213  if( pHt->GetStart() < nEnd )
214  {
215  pAttr = pHt;
216  if( !bHasMark && nStart > pAttr->GetStart() )
217  {
218  nStart = pAttr->GetStart();
219  pPos->nContent = nStart;
220  }
221  }
222  break;
223  }
224  }
225  }
226 
227  if( !bHasMark && nStart && ( !pAttr || nStart != pAttr->GetStart()) )
228  {
229  // skip to the word begin!
230  const sal_Int32 nWordStt = g_pBreakIt->GetBreakIter()->getWordBoundary(
231  rText, nStart,
232  g_pBreakIt->GetLocale( pTNd->GetLang( nStart )),
233  WordType::ANYWORD_IGNOREWHITESPACES,
234  true ).startPos;
235  if (nWordStt < nStart && nWordStt >= 0)
236  {
237  nStart = nWordStt;
238  pPos->nContent = nStart;
239  }
240  }
241 
242  bool bAlphaNum = false;
243  sal_Int32 nWordEnd = nEnd;
244  CharClass& rCC = GetAppCharClass();
245  while( nStart < nEnd )
246  {
247  if( pAttr && nStart == pAttr->GetStart() )
248  {
249  pPos->nContent = nStart;
250  if( !rPam.HasMark() )
251  {
252  rPam.SetMark();
253  pPos->nContent = pAttr->GetAnyEnd();
254  if( pPos->nContent.GetIndex() > nEnd )
255  pPos->nContent = nEnd;
256  rEntry.SetRubyAttr( pAttr->GetRuby() );
257  }
258  break;
259  }
260 
261  sal_Int32 nChType = rCC.getType(rText, nStart);
262  bool bIgnoreChar = false, bIsAlphaNum = false, bChkNxtWrd = false;
263  switch( nChType )
264  {
265  case UnicodeType::UPPERCASE_LETTER:
266  case UnicodeType::LOWERCASE_LETTER:
267  case UnicodeType::TITLECASE_LETTER:
268  case UnicodeType::DECIMAL_DIGIT_NUMBER:
269  bChkNxtWrd = bIsAlphaNum = true;
270  break;
271 
272  case UnicodeType::SPACE_SEPARATOR:
273  case UnicodeType::CONTROL:
274 /*??*/ case UnicodeType::PRIVATE_USE:
275  case UnicodeType::START_PUNCTUATION:
276  case UnicodeType::END_PUNCTUATION:
277  bIgnoreChar = true;
278  break;
279 
280  case UnicodeType::OTHER_LETTER:
281  bChkNxtWrd = true;
282  [[fallthrough]];
283  default:
284  bIsAlphaNum = false;
285  break;
286  }
287 
288  if( rPam.HasMark() )
289  {
290  if( bIgnoreChar || bIsAlphaNum != bAlphaNum || nStart >= nWordEnd )
291  break;
292  }
293  else if( !bIgnoreChar )
294  {
295  rPam.SetMark();
296  bAlphaNum = bIsAlphaNum;
297  if (bChkNxtWrd)
298  {
299  // search the end of this word
300  nWordEnd = g_pBreakIt->GetBreakIter()->getWordBoundary(
301  rText, nStart,
302  g_pBreakIt->GetLocale( pTNd->GetLang( nStart )),
303  WordType::ANYWORD_IGNOREWHITESPACES,
304  true ).endPos;
305  if( 0 > nWordEnd || nWordEnd > nEnd || nWordEnd == nStart )
306  nWordEnd = nEnd;
307  }
308  }
309  pTNd->GoNext( &pPos->nContent, CRSR_SKIP_CHARS );
310  nStart = pPos->nContent.GetIndex();
311  }
312 
313  nStart = rPam.GetMark()->nContent.GetIndex();
314  rEntry.SetText( rText.copy( nStart,
315  rPam.GetPoint()->nContent.GetIndex() - nStart ));
316  return rPam.HasMark();
317 }
318 
320 {
321 }
322 
323 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void ResetAttrs(const SwPaM &rRg, bool bTextAttr=true, const std::set< sal_uInt16 > &rAttrs=std::set< sal_uInt16 >(), const bool bSendDataChangedEvents=true, SwRootFrame const *pLayout=nullptr)
Reset attributes.
Definition: docfmt.cxx:239
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:1212
#define RES_TXTATR_CJK_RUBY
Definition: hintids.hxx:144
const OUString & GetText() const
Definition: ndtxt.hxx:210
SwpHints * GetpSwpHints()
Definition: ndtxt.hxx:218
SwNodeIndex nNode
Definition: pam.hxx:37
static bool SelectNextRubyChars(SwPaM &rPam, SwRubyListEntry &rRubyEntry)
Definition: docruby.cxx:178
std::vector< std::unique_ptr< SwRubyListEntry >> SwRubyList
Definition: doc.hxx:182
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:143
sal_uInt16 Which() const
Definition: txatbase.hxx:110
void SetRubyList(const SwPaM &rPam, const SwRubyList &rList)
Definition: docruby.cxx:92
sal_Int32 GetAnyEnd() const
end (if available), else start
Definition: txatbase.hxx:153
IDocumentContentOperations const & getIDocumentContentOperations() const
Definition: doc.cxx:314
SwIndex nContent
Definition: pam.hxx:38
SwBreakIt * g_pBreakIt
Definition: breakit.cxx:33
sal_Int32 GetStart() const
Definition: txatbase.hxx:82
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:481
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:892
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
virtual bool InsertPoolItem(const SwPaM &rRg, const SfxPoolItem &, const SetAttrMode nFlags=SetAttrMode::DEFAULT, SwRootFrame const *pLayout=nullptr, bool bExpandCharToPara=false, SwTextAttr **ppNewTextAttr=nullptr)=0
Insert an attribute.
SwMoveFnCollection const & fnMoveForward
SwPam::Move()/Find() default argument.
Definition: paminit.cxx:59
sal_Int32 GetIndex() const
Definition: index.hxx:95
LanguageType GetLang(const sal_Int32 nBegin, const sal_Int32 nLen=0, sal_uInt16 nScript=0) const
Definition: thints.cxx:3362
const sal_uInt16 CRSR_SKIP_CHARS
Definition: swcrsr.hxx:63
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:455
CharClass & GetAppCharClass()
Definition: init.cxx:709
const SwFormatRuby & GetRuby() const
Definition: txatbase.hxx:230
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:842