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