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 <docary.hxx>
34 #include <mvsave.hxx>
35 #include <ndtxt.hxx>
36 #include <txatbase.hxx>
37 #include <rubylist.hxx>
38 #include <pam.hxx>
39 #include <swundo.hxx>
40 #include <breakit.hxx>
41 #include <swcrsr.hxx>
42 
43 using namespace ::com::sun::star::i18n;
44 
45 /*
46  * Members in the list:
47  * - String - the orig text
48  * - SwFormatRuby - the ruby attribute
49  */
50 sal_uInt16 SwDoc::FillRubyList( const SwPaM& rPam, SwRubyList& rList )
51 {
52  const SwPaM *_pStartCursor = rPam.GetNext(),
53  *_pStartCursor2 = _pStartCursor;
54  bool bCheckEmpty = &rPam != _pStartCursor;
55  do {
56  const SwPosition* pStt = _pStartCursor->Start(),
57  * pEnd = pStt == _pStartCursor->GetPoint()
58  ? _pStartCursor->GetMark()
59  : _pStartCursor->GetPoint();
60  if( !bCheckEmpty || ( pStt != pEnd && *pStt != *pEnd ))
61  {
62  SwPaM aPam( *pStt );
63  do {
64  std::unique_ptr<SwRubyListEntry> pNew(new SwRubyListEntry);
65  if( pEnd != pStt )
66  {
67  aPam.SetMark();
68  *aPam.GetMark() = *pEnd;
69  }
70  if( SelectNextRubyChars( aPam, *pNew ))
71  {
72  rList.push_back(std::move(pNew));
73  aPam.DeleteMark();
74  }
75  else
76  {
77  if( *aPam.GetPoint() < *pEnd )
78  {
79  // goto next paragraph
80  aPam.DeleteMark();
81  aPam.Move( fnMoveForward, GoInNode );
82  }
83  else
84  break;
85  }
86  } while( 30 > rList.size() && *aPam.GetPoint() < *pEnd );
87  }
88  } while( 30 > rList.size() &&
89  (_pStartCursor = _pStartCursor->GetNext()) != _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  } while( 30 > rList.size() &&
175  (_pStartCursor = _pStartCursor->GetNext()) != _pStartCursor2 );
176 
178 }
179 
181 {
182  // Point must be the startposition, Mark is optional the end position
183  SwPosition* pPos = rPam.GetPoint();
184  const SwTextNode* pTNd = pPos->nNode.GetNode().GetTextNode();
185  OUString const& rText = pTNd->GetText();
186  sal_Int32 nStart = pPos->nContent.GetIndex();
187  sal_Int32 nEnd = rText.getLength();
188 
189  bool bHasMark = rPam.HasMark();
190  if( bHasMark )
191  {
192  // in the same node?
193  if( rPam.GetMark()->nNode == pPos->nNode )
194  {
195  // then use that end
196  const sal_Int32 nTEnd = rPam.GetMark()->nContent.GetIndex();
197  if( nTEnd < nEnd )
198  nEnd = nTEnd;
199  }
200  rPam.DeleteMark();
201  }
202 
203  // search the start
204  // look where a ruby attribute starts
205  const SwpHints* pHts = pTNd->GetpSwpHints();
206  const SwTextAttr* pAttr = nullptr;
207  if( pHts )
208  {
209  for( size_t nHtIdx = 0; nHtIdx < pHts->Count(); ++nHtIdx )
210  {
211  const SwTextAttr* pHt = pHts->Get(nHtIdx);
212  if( RES_TXTATR_CJK_RUBY == pHt->Which() &&
213  pHt->GetAnyEnd() > nStart )
214  {
215  if( pHt->GetStart() < nEnd )
216  {
217  pAttr = pHt;
218  if( !bHasMark && nStart > pAttr->GetStart() )
219  {
220  nStart = pAttr->GetStart();
221  pPos->nContent = nStart;
222  }
223  }
224  break;
225  }
226  }
227  }
228 
229  if( !bHasMark && nStart && ( !pAttr || nStart != pAttr->GetStart()) )
230  {
231  // skip to the word begin!
232  const sal_Int32 nWordStt = g_pBreakIt->GetBreakIter()->getWordBoundary(
233  rText, nStart,
234  g_pBreakIt->GetLocale( pTNd->GetLang( nStart )),
235  WordType::ANYWORD_IGNOREWHITESPACES,
236  true ).startPos;
237  if (nWordStt < nStart && nWordStt >= 0)
238  {
239  nStart = nWordStt;
240  pPos->nContent = nStart;
241  }
242  }
243 
244  bool bAlphaNum = false;
245  sal_Int32 nWordEnd = nEnd;
246  CharClass& rCC = GetAppCharClass();
247  while( nStart < nEnd )
248  {
249  if( pAttr && nStart == pAttr->GetStart() )
250  {
251  pPos->nContent = nStart;
252  if( !rPam.HasMark() )
253  {
254  rPam.SetMark();
255  pPos->nContent = pAttr->GetAnyEnd();
256  if( pPos->nContent.GetIndex() > nEnd )
257  pPos->nContent = nEnd;
258  rEntry.SetRubyAttr( pAttr->GetRuby() );
259  }
260  break;
261  }
262 
263  sal_Int32 nChType = rCC.getType(rText, nStart);
264  bool bIgnoreChar = false, bIsAlphaNum = false, bChkNxtWrd = false;
265  switch( nChType )
266  {
267  case UnicodeType::UPPERCASE_LETTER:
268  case UnicodeType::LOWERCASE_LETTER:
269  case UnicodeType::TITLECASE_LETTER:
270  case UnicodeType::DECIMAL_DIGIT_NUMBER:
271  bChkNxtWrd = bIsAlphaNum = true;
272  break;
273 
274  case UnicodeType::SPACE_SEPARATOR:
275  case UnicodeType::CONTROL:
276 /*??*/ case UnicodeType::PRIVATE_USE:
277  case UnicodeType::START_PUNCTUATION:
278  case UnicodeType::END_PUNCTUATION:
279  bIgnoreChar = true;
280  break;
281 
282  case UnicodeType::OTHER_LETTER:
283  bChkNxtWrd = true;
284  [[fallthrough]];
285  default:
286  bIsAlphaNum = false;
287  break;
288  }
289 
290  if( rPam.HasMark() )
291  {
292  if( bIgnoreChar || bIsAlphaNum != bAlphaNum || nStart >= nWordEnd )
293  break;
294  }
295  else if( !bIgnoreChar )
296  {
297  rPam.SetMark();
298  bAlphaNum = bIsAlphaNum;
299  if (bChkNxtWrd)
300  {
301  // search the end of this word
302  nWordEnd = g_pBreakIt->GetBreakIter()->getWordBoundary(
303  rText, nStart,
304  g_pBreakIt->GetLocale( pTNd->GetLang( nStart )),
305  WordType::ANYWORD_IGNOREWHITESPACES,
306  true ).endPos;
307  if( 0 > nWordEnd || nWordEnd > nEnd || nWordEnd == nStart )
308  nWordEnd = nEnd;
309  }
310  }
311  pTNd->GoNext( &pPos->nContent, CRSR_SKIP_CHARS );
312  nStart = pPos->nContent.GetIndex();
313  }
314 
315  nStart = rPam.GetMark()->nContent.GetIndex();
316  rEntry.SetText( rText.copy( nStart,
317  rPam.GetPoint()->nContent.GetIndex() - nStart ));
318  return rPam.HasMark();
319 }
320 
322 {
323 }
324 
325 /* 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:256
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:50
bool GoNext(SwIndex *, sal_uInt16 nMode) const
Definition: node.cxx:1215
#define RES_TXTATR_CJK_RUBY
Definition: hintids.hxx:143
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:180
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:118
void SetRubyAttr(const SwFormatRuby &rAttr)
Definition: rubylist.hxx:37
IDocumentUndoRedo & GetIDocumentUndoRedo()
Definition: doc.cxx:176
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:347
SwIndex nContent
Definition: pam.hxx:38
SwBreakIt * g_pBreakIt
Definition: breakit.cxx:34
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:483
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:894
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
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:3386
css::uno::Reference< css::i18n::XBreakIterator > const & GetBreakIter()
Definition: breakit.hxx:62
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:457
CharClass & GetAppCharClass()
Definition: init.cxx:751
const SwFormatRuby & GetRuby() const
Definition: txatbase.hxx:230
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:843