diff options
Diffstat (limited to 'sw/source/core/doc/docruby.cxx')
-rw-r--r-- | sw/source/core/doc/docruby.cxx | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/sw/source/core/doc/docruby.cxx b/sw/source/core/doc/docruby.cxx new file mode 100644 index 000000000..f1ce56a0d --- /dev/null +++ b/sw/source/core/doc/docruby.cxx @@ -0,0 +1,327 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <string.h> + +#include <com/sun/star/i18n/UnicodeType.hpp> +#include <com/sun/star/i18n/WordType.hpp> +#include <com/sun/star/i18n/XBreakIterator.hpp> + +#include <unotools/charclass.hxx> + +#include <hintids.hxx> +#include <doc.hxx> +#include <IDocumentUndoRedo.hxx> +#include <IDocumentContentOperations.hxx> +#include <ndtxt.hxx> +#include <txatbase.hxx> +#include <rubylist.hxx> +#include <pam.hxx> +#include <swundo.hxx> +#include <breakit.hxx> +#include <swcrsr.hxx> + +using namespace ::com::sun::star::i18n; + +/* + * Members in the list: + * - String - the orig text + * - SwFormatRuby - the ruby attribute + */ +sal_uInt16 SwDoc::FillRubyList( const SwPaM& rPam, SwRubyList& rList ) +{ + const SwPaM *_pStartCursor = rPam.GetNext(), + *_pStartCursor2 = _pStartCursor; + bool bCheckEmpty = &rPam != _pStartCursor; + do { + const SwPosition* pStt = _pStartCursor->Start(), + * pEnd = pStt == _pStartCursor->GetPoint() + ? _pStartCursor->GetMark() + : _pStartCursor->GetPoint(); + if( !bCheckEmpty || ( pStt != pEnd && *pStt != *pEnd )) + { + SwPaM aPam( *pStt ); + do { + std::unique_ptr<SwRubyListEntry> pNew(new SwRubyListEntry); + if( pEnd != pStt ) + { + aPam.SetMark(); + *aPam.GetMark() = *pEnd; + } + if( SelectNextRubyChars( aPam, *pNew )) + { + rList.push_back(std::move(pNew)); + aPam.DeleteMark(); + } + else + { + if( *aPam.GetPoint() < *pEnd ) + { + // goto next paragraph + aPam.DeleteMark(); + aPam.Move( fnMoveForward, GoInNode ); + } + else + break; + } + } while( 30 > rList.size() && *aPam.GetPoint() < *pEnd ); + } + if( 30 <= rList.size() ) + break; + _pStartCursor = _pStartCursor->GetNext(); + } while( _pStartCursor != _pStartCursor2 ); + + return rList.size(); +} + +void SwDoc::SetRubyList( const SwPaM& rPam, const SwRubyList& rList ) +{ + GetIDocumentUndoRedo().StartUndo( SwUndoId::SETRUBYATTR, nullptr ); + std::set<sal_uInt16> aDelArr; + aDelArr.insert( RES_TXTATR_CJK_RUBY ); + + SwRubyList::size_type nListEntry = 0; + + const SwPaM *_pStartCursor = rPam.GetNext(), + *_pStartCursor2 = _pStartCursor; + bool bCheckEmpty = &rPam != _pStartCursor; + do { + const SwPosition* pStt = _pStartCursor->Start(), + * pEnd = pStt == _pStartCursor->GetPoint() + ? _pStartCursor->GetMark() + : _pStartCursor->GetPoint(); + if( !bCheckEmpty || ( pStt != pEnd && *pStt != *pEnd )) + { + + SwPaM aPam( *pStt ); + do { + SwRubyListEntry aCheckEntry; + if( pEnd != pStt ) + { + aPam.SetMark(); + *aPam.GetMark() = *pEnd; + } + if( SelectNextRubyChars( aPam, aCheckEntry )) + { + const SwRubyListEntry* pEntry = rList[ nListEntry++ ].get(); + if( aCheckEntry.GetRubyAttr() != pEntry->GetRubyAttr() ) + { + // set/reset the attribute + if( !pEntry->GetRubyAttr().GetText().isEmpty() ) + { + getIDocumentContentOperations().InsertPoolItem( aPam, pEntry->GetRubyAttr() ); + } + else + { + ResetAttrs( aPam, true, aDelArr ); + } + } + + if( !pEntry->GetText().isEmpty() && + aCheckEntry.GetText() != pEntry->GetText() ) + { + // text is changed, so replace the original + getIDocumentContentOperations().ReplaceRange( aPam, pEntry->GetText(), false ); + } + aPam.DeleteMark(); + } + else + { + if( *aPam.GetPoint() < *pEnd ) + { + // goto next paragraph + aPam.DeleteMark(); + aPam.Move( fnMoveForward, GoInNode ); + } + else + { + const SwRubyListEntry* pEntry = rList[ nListEntry++ ].get(); + + // set/reset the attribute + if( !pEntry->GetRubyAttr().GetText().isEmpty() && + !pEntry->GetText().isEmpty() ) + { + getIDocumentContentOperations().InsertString( aPam, pEntry->GetText() ); + aPam.SetMark(); + aPam.GetMark()->nContent -= pEntry->GetText().getLength(); + getIDocumentContentOperations().InsertPoolItem( + aPam, pEntry->GetRubyAttr(), SetAttrMode::DONTEXPAND ); + } + else + break; + aPam.DeleteMark(); + } + } + } while( nListEntry < rList.size() && *aPam.GetPoint() < *pEnd ); + } + if( 30 <= rList.size() ) + break; + _pStartCursor = _pStartCursor->GetNext(); + } while( _pStartCursor != _pStartCursor2 ); + + GetIDocumentUndoRedo().EndUndo( SwUndoId::SETRUBYATTR, nullptr ); +} + +bool SwDoc::SelectNextRubyChars( SwPaM& rPam, SwRubyListEntry& rEntry ) +{ + // Point must be the startposition, Mark is optional the end position + SwPosition* pPos = rPam.GetPoint(); + const SwTextNode* pTNd = pPos->nNode.GetNode().GetTextNode(); + OUString const& rText = pTNd->GetText(); + sal_Int32 nStart = pPos->nContent.GetIndex(); + sal_Int32 nEnd = rText.getLength(); + + bool bHasMark = rPam.HasMark(); + if( bHasMark ) + { + // in the same node? + if( rPam.GetMark()->nNode == pPos->nNode ) + { + // then use that end + const sal_Int32 nTEnd = rPam.GetMark()->nContent.GetIndex(); + if( nTEnd < nEnd ) + nEnd = nTEnd; + } + rPam.DeleteMark(); + } + + // search the start + // look where a ruby attribute starts + const SwpHints* pHts = pTNd->GetpSwpHints(); + const SwTextAttr* pAttr = nullptr; + if( pHts ) + { + for( size_t nHtIdx = 0; nHtIdx < pHts->Count(); ++nHtIdx ) + { + const SwTextAttr* pHt = pHts->Get(nHtIdx); + if( RES_TXTATR_CJK_RUBY == pHt->Which() && + pHt->GetAnyEnd() > nStart ) + { + if( pHt->GetStart() < nEnd ) + { + pAttr = pHt; + if( !bHasMark && nStart > pAttr->GetStart() ) + { + nStart = pAttr->GetStart(); + pPos->nContent = nStart; + } + } + break; + } + } + } + + if( !bHasMark && nStart && ( !pAttr || nStart != pAttr->GetStart()) ) + { + // skip to the word begin! + const sal_Int32 nWordStt = g_pBreakIt->GetBreakIter()->getWordBoundary( + rText, nStart, + g_pBreakIt->GetLocale( pTNd->GetLang( nStart )), + WordType::ANYWORD_IGNOREWHITESPACES, + true ).startPos; + if (nWordStt < nStart && nWordStt >= 0) + { + nStart = nWordStt; + pPos->nContent = nStart; + } + } + + bool bAlphaNum = false; + sal_Int32 nWordEnd = nEnd; + CharClass& rCC = GetAppCharClass(); + while( nStart < nEnd ) + { + if( pAttr && nStart == pAttr->GetStart() ) + { + pPos->nContent = nStart; + if( !rPam.HasMark() ) + { + rPam.SetMark(); + pPos->nContent = pAttr->GetAnyEnd(); + if( pPos->nContent.GetIndex() > nEnd ) + pPos->nContent = nEnd; + rEntry.SetRubyAttr( pAttr->GetRuby() ); + } + break; + } + + sal_Int32 nChType = rCC.getType(rText, nStart); + bool bIgnoreChar = false, bIsAlphaNum = false, bChkNxtWrd = false; + switch( nChType ) + { + case UnicodeType::UPPERCASE_LETTER: + case UnicodeType::LOWERCASE_LETTER: + case UnicodeType::TITLECASE_LETTER: + case UnicodeType::DECIMAL_DIGIT_NUMBER: + bChkNxtWrd = bIsAlphaNum = true; + break; + + case UnicodeType::SPACE_SEPARATOR: + case UnicodeType::CONTROL: +/*??*/ case UnicodeType::PRIVATE_USE: + case UnicodeType::START_PUNCTUATION: + case UnicodeType::END_PUNCTUATION: + bIgnoreChar = true; + break; + + case UnicodeType::OTHER_LETTER: + bChkNxtWrd = true; + [[fallthrough]]; + default: + bIsAlphaNum = false; + break; + } + + if( rPam.HasMark() ) + { + if( bIgnoreChar || bIsAlphaNum != bAlphaNum || nStart >= nWordEnd ) + break; + } + else if( !bIgnoreChar ) + { + rPam.SetMark(); + bAlphaNum = bIsAlphaNum; + if (bChkNxtWrd) + { + // search the end of this word + nWordEnd = g_pBreakIt->GetBreakIter()->getWordBoundary( + rText, nStart, + g_pBreakIt->GetLocale( pTNd->GetLang( nStart )), + WordType::ANYWORD_IGNOREWHITESPACES, + true ).endPos; + if( 0 > nWordEnd || nWordEnd > nEnd || nWordEnd == nStart ) + nWordEnd = nEnd; + } + } + pTNd->GoNext( &pPos->nContent, CRSR_SKIP_CHARS ); + nStart = pPos->nContent.GetIndex(); + } + + nStart = rPam.GetMark()->nContent.GetIndex(); + rEntry.SetText( rText.copy( nStart, + rPam.GetPoint()->nContent.GetIndex() - nStart )); + return rPam.HasMark(); +} + +SwRubyListEntry::~SwRubyListEntry() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |