diff options
Diffstat (limited to 'svtools/source/edit')
-rw-r--r-- | svtools/source/edit/editsyntaxhighlighter.cxx | 204 | ||||
-rw-r--r-- | svtools/source/edit/makefile.mk | 63 | ||||
-rw-r--r-- | svtools/source/edit/svmedit.cxx | 1656 | ||||
-rw-r--r-- | svtools/source/edit/svmedit2.cxx | 81 | ||||
-rw-r--r-- | svtools/source/edit/sychconv.cxx | 103 | ||||
-rw-r--r-- | svtools/source/edit/syntaxhighlight.cxx | 909 | ||||
-rw-r--r-- | svtools/source/edit/textdat2.hxx | 306 | ||||
-rw-r--r-- | svtools/source/edit/textdata.cxx | 361 | ||||
-rw-r--r-- | svtools/source/edit/textdoc.cxx | 1047 | ||||
-rw-r--r-- | svtools/source/edit/textdoc.hxx | 148 | ||||
-rw-r--r-- | svtools/source/edit/texteng.cxx | 3303 | ||||
-rw-r--r-- | svtools/source/edit/textund2.hxx | 148 | ||||
-rw-r--r-- | svtools/source/edit/textundo.cxx | 343 | ||||
-rw-r--r-- | svtools/source/edit/textundo.hxx | 84 | ||||
-rw-r--r-- | svtools/source/edit/textview.cxx | 2470 | ||||
-rw-r--r-- | svtools/source/edit/textwindowpeer.cxx | 59 | ||||
-rw-r--r-- | svtools/source/edit/txtattr.cxx | 197 | ||||
-rw-r--r-- | svtools/source/edit/xtextedt.cxx | 421 |
18 files changed, 11903 insertions, 0 deletions
diff --git a/svtools/source/edit/editsyntaxhighlighter.cxx b/svtools/source/edit/editsyntaxhighlighter.cxx new file mode 100644 index 000000000000..01bb7ad41682 --- /dev/null +++ b/svtools/source/edit/editsyntaxhighlighter.cxx @@ -0,0 +1,204 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + +#include <svtools/svmedit.hxx> +#include <svtools/xtextedt.hxx> +#include <svtools/editsyntaxhighlighter.hxx> +#include "../../inc/txtattr.hxx" + + +MultiLineEditSyntaxHighlight::MultiLineEditSyntaxHighlight( Window* pParent, WinBits nWinStyle, + HighlighterLanguage aLanguage): MultiLineEdit(pParent,nWinStyle), mbDoBracketHilight(true) +{ + EnableUpdateData(300); + aHighlighter.initialize( aLanguage ); +} + +MultiLineEditSyntaxHighlight::MultiLineEditSyntaxHighlight( Window* pParent, const ResId& rResId , + HighlighterLanguage aLanguage): MultiLineEdit(pParent,rResId), mbDoBracketHilight(true) +{ + EnableUpdateData(300); + aHighlighter.initialize( aLanguage ); +} + +MultiLineEditSyntaxHighlight::~MultiLineEditSyntaxHighlight() +{ +} + +void MultiLineEditSyntaxHighlight::EnableBracketHilight(bool aHilight) +{ + mbDoBracketHilight = aHilight; +} + +bool MultiLineEditSyntaxHighlight::IsBracketHilight() +{ + return mbDoBracketHilight; +} + +void MultiLineEditSyntaxHighlight::SetText(const String& rNewText) +{ + MultiLineEdit::SetText(rNewText); + UpdateData(); +} + +void MultiLineEditSyntaxHighlight::DoBracketHilight(USHORT aKey) +{ + TextSelection aCurrentPos = GetTextView()->GetSelection(); + xub_StrLen aStartPos = aCurrentPos.GetStart().GetIndex(); + ULONG nStartPara = aCurrentPos.GetStart().GetPara(); + USHORT aCount = 0; + int aChar = -1; + + switch (aKey) + { + case '\'': // no break + case '"': + { + aChar = aKey; + break; + } + case '}' : + { + aChar = '{'; + break; + } + case ')': + { + aChar = '('; + break; + } + case ']': + { + aChar = '['; + break; + } + } + + if (aChar != -1) + { + for (long aPara =nStartPara; aPara>=0;--aPara) + { + if ( aStartPos == 0 ) + continue; + + String aLine( GetTextEngine()->GetText( aPara ) ); + for (USHORT i = ((unsigned long)aPara==nStartPara) ? aStartPos-1 : (USHORT)(aLine.Len()-1); i>0; --i) + { + if (aLine.GetChar(i)==aChar) + { + if (!aCount) + { + GetTextEngine()->SetAttrib( TextAttribFontWeight( WEIGHT_ULTRABOLD ), aPara, i, i+1, TRUE ); + GetTextEngine()->SetAttrib( TextAttribFontColor( Color(0,0,0) ), aPara, i, i+1, TRUE ); + GetTextEngine()->SetAttrib( TextAttribFontWeight( WEIGHT_ULTRABOLD ), nStartPara, aStartPos, aStartPos, TRUE ); + GetTextEngine()->SetAttrib( TextAttribFontColor( Color(0,0,0) ), nStartPara, aStartPos, aStartPos, TRUE ); + return; + } + else + aCount--; + } + if (aLine.GetChar(i)==aKey) + aCount++; + } + } + } +} + +long MultiLineEditSyntaxHighlight::PreNotify( NotifyEvent& rNEvt ) +{ + if ( mbDoBracketHilight && (rNEvt.GetType() == EVENT_KEYINPUT) ) + DoBracketHilight(rNEvt.GetKeyEvent()->GetCharCode()); + + return MultiLineEdit::PreNotify(rNEvt); +} + +Color MultiLineEditSyntaxHighlight::GetColorValue(TokenTypes aToken) +{ + Color aColor; + switch (aHighlighter.GetLanguage()) + { + case HIGHLIGHT_SQL: + { + switch (aToken) + { + case TT_IDENTIFIER: aColor = (ColorData)m_aColorConfig.GetColorValue(svtools::SQLIDENTIFIER).nColor; break; + case TT_NUMBER: aColor = (ColorData)m_aColorConfig.GetColorValue(svtools::SQLNUMBER).nColor; break; + case TT_STRING: aColor = (ColorData)m_aColorConfig.GetColorValue(svtools::SQLSTRING).nColor; break; + case TT_OPERATOR: aColor = (ColorData)m_aColorConfig.GetColorValue(svtools::SQLOPERATOR).nColor; break; + case TT_KEYWORDS: aColor = (ColorData)m_aColorConfig.GetColorValue(svtools::SQLKEYWORD).nColor; break; + case TT_PARAMETER: aColor = (ColorData)m_aColorConfig.GetColorValue(svtools::SQLPARAMETER).nColor; break; + case TT_COMMENT: aColor = (ColorData)m_aColorConfig.GetColorValue(svtools::SQLCOMMENT).nColor; break; + default: aColor = Color(0,0,0); + } + break; + } + case HIGHLIGHT_BASIC: + { + switch (aToken) + { + case TT_IDENTIFIER: aColor = Color(255,0,0); break; + case TT_COMMENT: aColor = Color(0,0,45); break; + case TT_NUMBER: aColor = Color(204,102,204); break; + case TT_STRING: aColor = Color(0,255,45); break; + case TT_OPERATOR: aColor = Color(0,0,100); break; + case TT_KEYWORDS: aColor = Color(0,0,255); break; + case TT_ERROR : aColor = Color(0,255,255); break; + default: aColor = Color(0,0,0); + } + break; + } + default: aColor = Color(0,0,0); + + } + return aColor; +} + +void MultiLineEditSyntaxHighlight::UpdateData() +{ + // syntax highlighting + // this must be possible improved by using notifychange correctly + BOOL bTempModified = GetTextEngine()->IsModified(); + for (unsigned int nLine=0; nLine < GetTextEngine()->GetParagraphCount(); nLine++) + { + String aLine( GetTextEngine()->GetText( nLine ) ); + Range aChanges = aHighlighter.notifyChange( nLine, 0, &aLine, 1 ); + + GetTextEngine()->RemoveAttribs( nLine, TRUE ); + HighlightPortions aPortions; + aHighlighter.getHighlightPortions( nLine, aLine, aPortions ); + for ( USHORT i = 0; i < aPortions.Count(); i++ ) + { + HighlightPortion& r = aPortions[i]; + GetTextEngine()->SetAttrib( TextAttribFontColor( GetColorValue(r.tokenType) ), nLine, r.nBegin, r.nEnd, TRUE ); + } + } + GetTextView()->ShowCursor( false, true ); + GetTextEngine()->SetModified(bTempModified); +} diff --git a/svtools/source/edit/makefile.mk b/svtools/source/edit/makefile.mk new file mode 100644 index 000000000000..58a63be58f78 --- /dev/null +++ b/svtools/source/edit/makefile.mk @@ -0,0 +1,63 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=svtools +TARGET=edit + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/svt.pmk + +# --- Files -------------------------------------------------------- + +SLOFILES= \ + $(SLO)$/textdata.obj \ + $(SLO)$/textdoc.obj \ + $(SLO)$/texteng.obj \ + $(SLO)$/textundo.obj \ + $(SLO)$/textview.obj \ + $(SLO)$/txtattr.obj \ + $(SLO)$/xtextedt.obj \ + $(SLO)$/sychconv.obj \ + $(SLO)$/svmedit.obj \ + $(SLO)$/svmedit2.obj \ + $(SLO)$/textwindowpeer.obj \ + $(SLO)$/syntaxhighlight.obj \ + $(SLO)$/editsyntaxhighlighter.obj + +EXCEPTIONSFILES= \ + $(SLO)$/textview.obj \ + $(SLO)$/textdoc.obj \ + $(SLO)$/texteng.obj \ + $(SLO)$/textwindowpeer.obj + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk diff --git a/svtools/source/edit/svmedit.cxx b/svtools/source/edit/svmedit.cxx new file mode 100644 index 000000000000..daaff472d1cc --- /dev/null +++ b/svtools/source/edit/svmedit.cxx @@ -0,0 +1,1656 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + + +#include <memory> + +#include "unoiface.hxx" + +#include <tools/rc.h> + +#include <vcl/decoview.hxx> +#include <vcl/svapp.hxx> + +#include <svtools/svmedit.hxx> +#include <svtools/xtextedt.hxx> +#include <svl/brdcst.hxx> +#include <svl/undo.hxx> +#include <svtools/textwindowpeer.hxx> +#include <svl/lstner.hxx> +#include <svl/smplhint.hxx> + + +// IDs erstmal aus VCL geklaut, muss mal richtig delivert werden... +#define SV_MENU_EDIT_UNDO 1 +#define SV_MENU_EDIT_CUT 2 +#define SV_MENU_EDIT_COPY 3 +#define SV_MENU_EDIT_PASTE 4 +#define SV_MENU_EDIT_DELETE 5 +#define SV_MENU_EDIT_SELECTALL 6 +#define SV_MENU_EDIT_INSERTSYMBOL 7 +#include <vcl/scrbar.hxx> + +namespace css = ::com::sun::star; + +class TextWindow : public Window +{ +private: + ExtTextEngine* mpExtTextEngine; + ExtTextView* mpExtTextView; + + BOOL mbInMBDown; + BOOL mbFocusSelectionHide; + BOOL mbIgnoreTab; + BOOL mbActivePopup; + BOOL mbSelectOnTab; + +public: + TextWindow( Window* pParent ); + ~TextWindow(); + + ExtTextEngine* GetTextEngine() const { return mpExtTextEngine; } + ExtTextView* GetTextView() const { return mpExtTextView; } + + virtual void MouseMove( const MouseEvent& rMEvt ); + virtual void MouseButtonDown( const MouseEvent& rMEvt ); + virtual void MouseButtonUp( const MouseEvent& rMEvt ); + virtual void KeyInput( const KeyEvent& rKEvent ); + + virtual void Command( const CommandEvent& rCEvt ); + + virtual void Paint( const Rectangle& rRect ); + virtual void Resize(); + + virtual void GetFocus(); + virtual void LoseFocus(); + + BOOL IsAutoFocusHide() const { return mbFocusSelectionHide; } + void SetAutoFocusHide( BOOL bAutoHide ) { mbFocusSelectionHide = bAutoHide; } + + BOOL IsIgnoreTab() const { return mbIgnoreTab; } + void SetIgnoreTab( BOOL bIgnore ) { mbIgnoreTab = bIgnore; } + + void DisableSelectionOnFocus() {mbSelectOnTab = sal_False;} + + virtual + ::com::sun::star::uno::Reference< ::com::sun::star::awt::XWindowPeer > + GetComponentInterface(BOOL bCreate = TRUE); +}; + + +class ImpSvMEdit : public SfxListener +{ +private: + MultiLineEdit* pSvMultiLineEdit; + + TextWindow* mpTextWindow; + ScrollBar* mpHScrollBar; + ScrollBar* mpVScrollBar; + ScrollBarBox* mpScrollBox; + + Point maTextWindowOffset; + xub_StrLen mnTextWidth; + mutable Selection maSelection; + +protected: + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ); + void ImpUpdateSrollBarVis( WinBits nWinStyle ); + void ImpInitScrollBars(); + void ImpSetScrollBarRanges(); + void ImpSetHScrollBarThumbPos(); + DECL_LINK( ScrollHdl, ScrollBar* ); + +public: + ImpSvMEdit( MultiLineEdit* pSvMultiLineEdit, WinBits nWinStyle ); + ~ImpSvMEdit(); + + void SetModified( BOOL bMod ); + BOOL IsModified() const; + + void SetReadOnly( BOOL bRdOnly ); + BOOL IsReadOnly() const; + + void SetMaxTextLen( xub_StrLen nLen ); + xub_StrLen GetMaxTextLen() const; + + void SetInsertMode( BOOL bInsert ); + BOOL IsInsertMode() const; + + void InsertText( const String& rStr ); + String GetSelected() const; + String GetSelected( LineEnd aSeparator ) const; + + void SetSelection( const Selection& rSelection ); + const Selection& GetSelection() const; + + void Cut(); + void Copy(); + void Paste(); + + void SetText( const String& rStr ); + String GetText() const; + String GetText( LineEnd aSeparator ) const; + String GetTextLines() const; + String GetTextLines( LineEnd aSeparator ) const; + + void Resize(); + void GetFocus(); + + BOOL HandleCommand( const CommandEvent& rCEvt ); + + void Enable( BOOL bEnable ); + + Size CalcMinimumSize() const; + Size CalcSize( USHORT nColumns, USHORT nLines ) const; + void GetMaxVisColumnsAndLines( USHORT& rnCols, USHORT& rnLines ) const; + + void SetAlign( WinBits nWinStyle ); + + void InitFromStyle( WinBits nWinStyle ); + + TextWindow* GetTextWindow() { return mpTextWindow; } + ScrollBar* GetHScrollBar() { return mpHScrollBar; } + ScrollBar* GetVScrollBar() { return mpVScrollBar; } + + void SetTextWindowOffset( const Point& rOffset ); +}; + +ImpSvMEdit::ImpSvMEdit( MultiLineEdit* pEdt, WinBits nWinStyle ) + :mpHScrollBar(NULL) + ,mpVScrollBar(NULL) + ,mpScrollBox(NULL) +{ + pSvMultiLineEdit = pEdt; + mnTextWidth = 0; + mpTextWindow = new TextWindow( pEdt ); + mpTextWindow->Show(); + InitFromStyle( nWinStyle ); + StartListening( *mpTextWindow->GetTextEngine() ); +} + +void ImpSvMEdit::ImpUpdateSrollBarVis( WinBits nWinStyle ) +{ + const BOOL bHaveVScroll = (NULL != mpVScrollBar); + const BOOL bHaveHScroll = (NULL != mpHScrollBar); + const BOOL bHaveScrollBox = (NULL != mpScrollBox); + + BOOL bNeedVScroll = ( nWinStyle & WB_VSCROLL ) == WB_VSCROLL; + const BOOL bNeedHScroll = ( nWinStyle & WB_HSCROLL ) == WB_HSCROLL; + + const BOOL bAutoVScroll = ( nWinStyle & WB_AUTOVSCROLL ) == WB_AUTOVSCROLL; + if ( !bNeedVScroll && bAutoVScroll ) + { + TextEngine& rEngine( *mpTextWindow->GetTextEngine() ); + ULONG nOverallTextHeight(0); + for ( ULONG i=0; i<rEngine.GetParagraphCount(); ++i ) + nOverallTextHeight += rEngine.GetTextHeight( i ); + if ( nOverallTextHeight > (ULONG)mpTextWindow->GetOutputSizePixel().Height() ) + bNeedVScroll = true; + } + + const BOOL bNeedScrollBox = bNeedVScroll && bNeedHScroll; + + BOOL bScrollbarsChanged = false; + if ( bHaveVScroll != bNeedVScroll ) + { + delete mpVScrollBar; + mpVScrollBar = bNeedVScroll ? new ScrollBar( pSvMultiLineEdit, WB_VSCROLL|WB_DRAG ) : NULL; + + if ( bNeedVScroll ) + { + mpVScrollBar->Show(); + mpVScrollBar->SetScrollHdl( LINK( this, ImpSvMEdit, ScrollHdl ) ); + } + + bScrollbarsChanged = sal_True; + } + + if ( bHaveHScroll != bNeedHScroll ) + { + delete mpHScrollBar; + mpHScrollBar = bNeedHScroll ? new ScrollBar( pSvMultiLineEdit, WB_HSCROLL|WB_DRAG ) : NULL; + + if ( bNeedHScroll ) + { + mpHScrollBar->Show(); + mpHScrollBar->SetScrollHdl( LINK( this, ImpSvMEdit, ScrollHdl ) ); + } + + bScrollbarsChanged = sal_True; + } + + if ( bHaveScrollBox != bNeedScrollBox ) + { + delete mpScrollBox; + mpScrollBox = bNeedScrollBox ? new ScrollBarBox( pSvMultiLineEdit, WB_SIZEABLE ) : NULL; + + if ( bNeedScrollBox ) + mpScrollBox->Show(); + } + + if ( bScrollbarsChanged ) + { + ImpInitScrollBars(); + Resize(); + } +} + +void ImpSvMEdit::InitFromStyle( WinBits nWinStyle ) +{ + ImpUpdateSrollBarVis( nWinStyle ); + SetAlign( nWinStyle ); + + if ( nWinStyle & WB_NOHIDESELECTION ) + mpTextWindow->SetAutoFocusHide( FALSE ); + else + mpTextWindow->SetAutoFocusHide( TRUE ); + + if ( nWinStyle & WB_READONLY ) + mpTextWindow->GetTextView()->SetReadOnly( TRUE ); + else + mpTextWindow->GetTextView()->SetReadOnly( FALSE ); + + if ( nWinStyle & WB_IGNORETAB ) + { + mpTextWindow->SetIgnoreTab( TRUE ); + } + else + { + mpTextWindow->SetIgnoreTab( FALSE ); + // #103667# MultiLineEdit has the flag, but focusable window also needs this flag + WinBits nStyle = mpTextWindow->GetStyle(); + nStyle |= WINDOW_DLGCTRL_MOD1TAB; + mpTextWindow->SetStyle( nStyle ); + } +} + +ImpSvMEdit::~ImpSvMEdit() +{ + EndListening( *mpTextWindow->GetTextEngine() ); + delete mpTextWindow; + delete mpHScrollBar; + delete mpVScrollBar; + delete mpScrollBox; +} + +void ImpSvMEdit::ImpSetScrollBarRanges() +{ + if ( mpVScrollBar ) + { + ULONG nTextHeight = mpTextWindow->GetTextEngine()->GetTextHeight(); + mpVScrollBar->SetRange( Range( 0, (long)nTextHeight-1 ) ); + } + if ( mpHScrollBar ) + { +// ULONG nTextWidth = mpTextWindow->GetTextEngine()->CalcTextWidth(); + // Es gibt kein Notify bei Breiten-Aenderung... +// ULONG nW = Max( (ULONG)mpTextWindow->GetOutputSizePixel().Width()*5, (ULONG)nTextWidth ); +// mpHScrollBar->SetRange( Range( 0, (long)nW ) ); + mpHScrollBar->SetRange( Range( 0, (long)mnTextWidth-1 ) ); + } +} + +void ImpSvMEdit::ImpInitScrollBars() +{ + static const sal_Unicode sampleText[] = { 'x', '\0' }; + if ( mpHScrollBar || mpVScrollBar ) + { + ImpSetScrollBarRanges(); + Size aCharBox; + aCharBox.Width() = mpTextWindow->GetTextWidth( sampleText ); + aCharBox.Height() = mpTextWindow->GetTextHeight(); + Size aOutSz = mpTextWindow->GetOutputSizePixel(); + if ( mpHScrollBar ) + { + mpHScrollBar->SetVisibleSize( aOutSz.Width() ); + mpHScrollBar->SetPageSize( aOutSz.Width() * 8 / 10 ); + mpHScrollBar->SetLineSize( aCharBox.Width()*10 ); + ImpSetHScrollBarThumbPos(); + } + if ( mpVScrollBar ) + { + mpVScrollBar->SetVisibleSize( aOutSz.Height() ); + mpVScrollBar->SetPageSize( aOutSz.Height() * 8 / 10 ); + mpVScrollBar->SetLineSize( aCharBox.Height() ); + mpVScrollBar->SetThumbPos( mpTextWindow->GetTextView()->GetStartDocPos().Y() ); + } + } +} + +void ImpSvMEdit::ImpSetHScrollBarThumbPos() +{ + long nX = mpTextWindow->GetTextView()->GetStartDocPos().X(); + if ( !mpTextWindow->GetTextEngine()->IsRightToLeft() ) + mpHScrollBar->SetThumbPos( nX ); + else + mpHScrollBar->SetThumbPos( mnTextWidth - mpHScrollBar->GetVisibleSize() - nX ); + +} + +IMPL_LINK( ImpSvMEdit, ScrollHdl, ScrollBar*, pCurScrollBar ) +{ + long nDiffX = 0, nDiffY = 0; + + if ( pCurScrollBar == mpVScrollBar ) + nDiffY = mpTextWindow->GetTextView()->GetStartDocPos().Y() - pCurScrollBar->GetThumbPos(); + else if ( pCurScrollBar == mpHScrollBar ) + nDiffX = mpTextWindow->GetTextView()->GetStartDocPos().X() - pCurScrollBar->GetThumbPos(); + + mpTextWindow->GetTextView()->Scroll( nDiffX, nDiffY ); + // mpTextWindow->GetTextView()->ShowCursor( FALSE, TRUE ); + + return 0; +} + + +// void ImpSvMEdit::ImpModified() +// { +// // Wann wird das gerufen ????????????????????? +// pSvMultiLineEdit->Modify(); +// } + +void ImpSvMEdit::SetAlign( WinBits nWinStyle ) +{ + BOOL bRTL = Application::GetSettings().GetLayoutRTL(); + mpTextWindow->GetTextEngine()->SetRightToLeft( bRTL ); + + if ( nWinStyle & WB_CENTER ) + mpTextWindow->GetTextEngine()->SetTextAlign( TXTALIGN_CENTER ); + else if ( nWinStyle & WB_RIGHT ) + mpTextWindow->GetTextEngine()->SetTextAlign( !bRTL ? TXTALIGN_RIGHT : TXTALIGN_LEFT ); + else if ( nWinStyle & WB_LEFT ) + mpTextWindow->GetTextEngine()->SetTextAlign( !bRTL ? TXTALIGN_LEFT : TXTALIGN_RIGHT ); +} + +void ImpSvMEdit::SetTextWindowOffset( const Point& rOffset ) +{ + maTextWindowOffset = rOffset; + Resize(); +} + +void ImpSvMEdit::SetModified( BOOL bMod ) +{ + mpTextWindow->GetTextEngine()->SetModified( bMod ); +} + +BOOL ImpSvMEdit::IsModified() const +{ + return mpTextWindow->GetTextEngine()->IsModified(); +} + +void ImpSvMEdit::SetInsertMode( BOOL bInsert ) +{ + mpTextWindow->GetTextView()->SetInsertMode( bInsert ); +} + +void ImpSvMEdit::SetReadOnly( BOOL bRdOnly ) +{ + mpTextWindow->GetTextView()->SetReadOnly( bRdOnly ); + // Farbe anpassen ??????????????????????????? +} + +BOOL ImpSvMEdit::IsReadOnly() const +{ + return mpTextWindow->GetTextView()->IsReadOnly(); +} + +void ImpSvMEdit::SetMaxTextLen( xub_StrLen nLen ) +{ + mpTextWindow->GetTextEngine()->SetMaxTextLen( nLen ); +} + +xub_StrLen ImpSvMEdit::GetMaxTextLen() const +{ + return sal::static_int_cast< xub_StrLen >( + mpTextWindow->GetTextEngine()->GetMaxTextLen()); +} + +void ImpSvMEdit::InsertText( const String& rStr ) +{ + mpTextWindow->GetTextView()->InsertText( rStr ); +} + +String ImpSvMEdit::GetSelected() const +{ + return mpTextWindow->GetTextView()->GetSelected(); +} + +String ImpSvMEdit::GetSelected( LineEnd aSeparator ) const +{ + return mpTextWindow->GetTextView()->GetSelected( aSeparator ); +} + +void ImpSvMEdit::Resize() +{ + size_t nIteration = 1; + do + { + WinBits nWinStyle( pSvMultiLineEdit->GetStyle() ); + if ( ( nWinStyle & WB_AUTOVSCROLL ) == WB_AUTOVSCROLL ) + ImpUpdateSrollBarVis( nWinStyle ); + + Size aSz = pSvMultiLineEdit->GetOutputSizePixel(); + Size aEditSize = aSz; + long nSBWidth = pSvMultiLineEdit->GetSettings().GetStyleSettings().GetScrollBarSize(); + nSBWidth = pSvMultiLineEdit->CalcZoom( nSBWidth ); + + if ( mpHScrollBar ) + aSz.Height() -= nSBWidth+1; + if ( mpVScrollBar ) + aSz.Width() -= nSBWidth+1; + + if ( !mpHScrollBar ) + mpTextWindow->GetTextEngine()->SetMaxTextWidth( aSz.Width() ); + else + mpHScrollBar->SetPosSizePixel( 0, aEditSize.Height()-nSBWidth, aSz.Width(), nSBWidth ); + + Point aTextWindowPos( maTextWindowOffset ); + if ( mpVScrollBar ) + { + if( Application::GetSettings().GetLayoutRTL() ) + { + mpVScrollBar->SetPosSizePixel( 0, 0, nSBWidth, aSz.Height() ); + aTextWindowPos.X() += nSBWidth; + } + else + mpVScrollBar->SetPosSizePixel( aEditSize.Width()-nSBWidth, 0, nSBWidth, aSz.Height() ); + } + + if ( mpScrollBox ) + mpScrollBox->SetPosSizePixel( aSz.Width(), aSz.Height(), nSBWidth, nSBWidth ); + + Size aTextWindowSize( aSz ); + aTextWindowSize.Width() -= maTextWindowOffset.X(); + aTextWindowSize.Height() -= maTextWindowOffset.Y(); + if ( aTextWindowSize.Width() < 0 ) + aTextWindowSize.Width() = 0; + if ( aTextWindowSize.Height() < 0 ) + aTextWindowSize.Height() = 0; + + Size aOldTextWindowSize( mpTextWindow->GetSizePixel() ); + mpTextWindow->SetPosSizePixel( aTextWindowPos, aTextWindowSize ); + if ( aOldTextWindowSize == aTextWindowSize ) + break; + + // Changing the text window size might effectively have changed the need for + // scrollbars, so do another iteration. + ++nIteration; + OSL_ENSURE( nIteration < 3, "ImpSvMEdit::Resize: isn't this expected to terminate with the second iteration?" ); + + } while ( nIteration <= 3 ); // artificial break after four iterations + + ImpInitScrollBars(); +} + +void ImpSvMEdit::GetFocus() +{ + mpTextWindow->GrabFocus(); +} + +void ImpSvMEdit::Cut() +{ + if ( !mpTextWindow->GetTextView()->IsReadOnly() ) + mpTextWindow->GetTextView()->Cut(); +} + +void ImpSvMEdit::Copy() +{ + mpTextWindow->GetTextView()->Copy(); +} + +void ImpSvMEdit::Paste() +{ + if ( !mpTextWindow->GetTextView()->IsReadOnly() ) + mpTextWindow->GetTextView()->Paste(); +} + +void ImpSvMEdit::SetText( const String& rStr ) +{ + BOOL bWasModified = mpTextWindow->GetTextEngine()->IsModified(); + mpTextWindow->GetTextEngine()->SetText( rStr ); + if ( !bWasModified ) + mpTextWindow->GetTextEngine()->SetModified( FALSE ); + + mpTextWindow->GetTextView()->SetSelection( TextSelection() ); + + WinBits nWinStyle( pSvMultiLineEdit->GetStyle() ); + if ( ( nWinStyle & WB_AUTOVSCROLL ) == WB_AUTOVSCROLL ) + ImpUpdateSrollBarVis( nWinStyle ); +} + +String ImpSvMEdit::GetText() const +{ + return mpTextWindow->GetTextEngine()->GetText(); +} + +String ImpSvMEdit::GetText( LineEnd aSeparator ) const +{ + return mpTextWindow->GetTextEngine()->GetText( aSeparator ); +} + +String ImpSvMEdit::GetTextLines() const +{ + return mpTextWindow->GetTextEngine()->GetTextLines(); +} + +String ImpSvMEdit::GetTextLines( LineEnd aSeparator ) const +{ + return mpTextWindow->GetTextEngine()->GetTextLines( aSeparator ); +} + +void ImpSvMEdit::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( rHint.ISA( TextHint ) ) + { + const TextHint& rTextHint = (const TextHint&)rHint; + if( rTextHint.GetId() == TEXT_HINT_VIEWSCROLLED ) + { + if ( mpHScrollBar ) + ImpSetHScrollBarThumbPos(); + if ( mpVScrollBar ) + mpVScrollBar->SetThumbPos( mpTextWindow->GetTextView()->GetStartDocPos().Y() ); + } + else if( rTextHint.GetId() == TEXT_HINT_TEXTHEIGHTCHANGED ) + { + if ( mpTextWindow->GetTextView()->GetStartDocPos().Y() ) + { + long nOutHeight = mpTextWindow->GetOutputSizePixel().Height(); + long nTextHeight = mpTextWindow->GetTextEngine()->GetTextHeight(); + if ( nTextHeight < nOutHeight ) + mpTextWindow->GetTextView()->Scroll( 0, mpTextWindow->GetTextView()->GetStartDocPos().Y() ); + } + + ImpSetScrollBarRanges(); + } + else if( rTextHint.GetId() == TEXT_HINT_TEXTFORMATTED ) + { + if ( mpHScrollBar ) + { + ULONG nWidth = mpTextWindow->GetTextEngine()->CalcTextWidth(); + if ( nWidth != mnTextWidth ) + { + mnTextWidth = sal::static_int_cast< xub_StrLen >(nWidth); + mpHScrollBar->SetRange( Range( 0, (long)mnTextWidth-1 ) ); + ImpSetHScrollBarThumbPos(); + } + } + } + else if( rTextHint.GetId() == TEXT_HINT_MODIFIED ) + { + pSvMultiLineEdit->Modify(); + } + } +} + +void ImpSvMEdit::SetSelection( const Selection& rSelection ) +{ + String aText = mpTextWindow->GetTextEngine()->GetText(); + + Selection aNewSelection( rSelection ); + if ( aNewSelection.Min() < 0 ) + aNewSelection.Min() = 0; + else if ( aNewSelection.Min() > aText.Len() ) + aNewSelection.Min() = aText.Len(); + if ( aNewSelection.Max() < 0 ) + aNewSelection.Max() = 0; + else if ( aNewSelection.Max() > aText.Len() ) + aNewSelection.Max() = aText.Len(); + + long nEnd = Max( aNewSelection.Min(), aNewSelection.Max() ); + TextSelection aTextSel; + ULONG nPara = 0; + USHORT nChar = 0; + USHORT x = 0; + while ( x <= nEnd ) + { + if ( x == aNewSelection.Min() ) + aTextSel.GetStart() = TextPaM( nPara, nChar ); + if ( x == aNewSelection.Max() ) + aTextSel.GetEnd() = TextPaM( nPara, nChar ); + + if ( ( x < aText.Len() ) && ( aText.GetChar( x ) == '\n' ) ) + { + nPara++; + nChar = 0; + } + else + nChar++; + x++; + } + mpTextWindow->GetTextView()->SetSelection( aTextSel ); +} + +const Selection& ImpSvMEdit::GetSelection() const +{ + maSelection = Selection(); + TextSelection aTextSel( mpTextWindow->GetTextView()->GetSelection() ); + aTextSel.Justify(); + // Selektion flachklopfen => jeder Umbruch ein Zeichen... + + ExtTextEngine* pExtTextEngine = mpTextWindow->GetTextEngine(); + // Absaetze davor: + ULONG n; + for ( n = 0; n < aTextSel.GetStart().GetPara(); n++ ) + { + maSelection.Min() += pExtTextEngine->GetTextLen( n ); + maSelection.Min()++; + } + + // Erster Absatz mit Selektion: + maSelection.Max() = maSelection.Min(); + maSelection.Min() += aTextSel.GetStart().GetIndex(); + + for ( n = aTextSel.GetStart().GetPara(); n < aTextSel.GetEnd().GetPara(); n++ ) + { + maSelection.Max() += pExtTextEngine->GetTextLen( n ); + maSelection.Max()++; + } + + maSelection.Max() += aTextSel.GetEnd().GetIndex(); + + return maSelection; +} + +Size ImpSvMEdit::CalcMinimumSize() const +{ + Size aSz( mpTextWindow->GetTextEngine()->CalcTextWidth(), + mpTextWindow->GetTextEngine()->GetTextHeight() ); + + if ( mpHScrollBar ) + aSz.Height() += mpHScrollBar->GetSizePixel().Height(); + if ( mpVScrollBar ) + aSz.Width() += mpVScrollBar->GetSizePixel().Width(); + + return aSz; +} + +Size ImpSvMEdit::CalcSize( USHORT nColumns, USHORT nLines ) const +{ + static const sal_Unicode sampleText[] = { 'X', '\0' }; + + Size aSz; + Size aCharSz; + aCharSz.Width() = mpTextWindow->GetTextWidth( sampleText ); + aCharSz.Height() = mpTextWindow->GetTextHeight(); + + if ( nLines ) + aSz.Height() = nLines*aCharSz.Height(); + else + aSz.Height() = mpTextWindow->GetTextEngine()->GetTextHeight(); + + if ( nColumns ) + aSz.Width() = nColumns*aCharSz.Width(); + else + aSz.Width() = mpTextWindow->GetTextEngine()->CalcTextWidth(); + + if ( mpHScrollBar ) + aSz.Height() += mpHScrollBar->GetSizePixel().Height(); + if ( mpVScrollBar ) + aSz.Width() += mpVScrollBar->GetSizePixel().Width(); + + return aSz; +} + +void ImpSvMEdit::GetMaxVisColumnsAndLines( USHORT& rnCols, USHORT& rnLines ) const +{ + static const sal_Unicode sampleText[] = { 'x', '\0' }; + Size aOutSz = mpTextWindow->GetOutputSizePixel(); + Size aCharSz( mpTextWindow->GetTextWidth( sampleText ), mpTextWindow->GetTextHeight() ); + rnCols = (USHORT) (aOutSz.Width()/aCharSz.Width()); + rnLines = (USHORT) (aOutSz.Height()/aCharSz.Height()); +} + +void ImpSvMEdit::Enable( BOOL bEnable ) +{ + mpTextWindow->Enable( bEnable ); + if ( mpHScrollBar ) + mpHScrollBar->Enable( bEnable ); + if ( mpVScrollBar ) + mpVScrollBar->Enable( bEnable ); +} + +BOOL ImpSvMEdit::HandleCommand( const CommandEvent& rCEvt ) +{ + BOOL bDone = FALSE; + if ( ( rCEvt.GetCommand() == COMMAND_WHEEL ) || + ( rCEvt.GetCommand() == COMMAND_STARTAUTOSCROLL ) || + ( rCEvt.GetCommand() == COMMAND_AUTOSCROLL ) ) + { + mpTextWindow->HandleScrollCommand( rCEvt, mpHScrollBar, mpVScrollBar ); + bDone = TRUE; + } + return bDone; +} + + +TextWindow::TextWindow( Window* pParent ) : Window( pParent ) +{ + mbInMBDown = FALSE; + mbSelectOnTab = TRUE; + mbFocusSelectionHide = FALSE; + mbIgnoreTab = FALSE; + mbActivePopup = FALSE; + mbSelectOnTab = TRUE; + + SetPointer( Pointer( POINTER_TEXT ) ); + + mpExtTextEngine = new ExtTextEngine; + mpExtTextEngine->SetMaxTextLen( STRING_MAXLEN ); + if( pParent->GetStyle() & WB_BORDER ) + mpExtTextEngine->SetLeftMargin( 2 ); + mpExtTextEngine->SetLocale( GetSettings().GetLocale() ); + mpExtTextView = new ExtTextView( mpExtTextEngine, this ); + mpExtTextEngine->InsertView( mpExtTextView ); + mpExtTextEngine->EnableUndo( TRUE ); + mpExtTextView->ShowCursor(); + + Color aBackgroundColor = GetSettings().GetStyleSettings().GetWorkspaceColor(); + SetBackground( aBackgroundColor ); + pParent->SetBackground( aBackgroundColor ); +} + +TextWindow::~TextWindow() +{ + delete mpExtTextView; + delete mpExtTextEngine; +} + +void TextWindow::MouseMove( const MouseEvent& rMEvt ) +{ + mpExtTextView->MouseMove( rMEvt ); + Window::MouseMove( rMEvt ); +} + +void TextWindow::MouseButtonDown( const MouseEvent& rMEvt ) +{ + mbInMBDown = TRUE; // Dann im GetFocus nicht alles selektieren wird + mpExtTextView->MouseButtonDown( rMEvt ); + Window::MouseButtonDown( rMEvt ); + GrabFocus(); + mbInMBDown = FALSE; +} + +void TextWindow::MouseButtonUp( const MouseEvent& rMEvt ) +{ + mpExtTextView->MouseButtonUp( rMEvt ); + Window::MouseButtonUp( rMEvt ); +} + +void TextWindow::KeyInput( const KeyEvent& rKEvent ) +{ + BOOL bDone = FALSE; + USHORT nCode = rKEvent.GetKeyCode().GetCode(); + if ( nCode == com::sun::star::awt::Key::SELECT_ALL || + ( (nCode == KEY_A) && rKEvent.GetKeyCode().IsMod1() && !rKEvent.GetKeyCode().IsMod2() ) + ) + { + mpExtTextView->SetSelection( TextSelection( TextPaM( 0, 0 ), TextPaM( 0xFFFF, 0xFFFF ) ) ); + bDone = TRUE; + } + else if ( (nCode == KEY_S) && rKEvent.GetKeyCode().IsShift() && rKEvent.GetKeyCode().IsMod1() ) + { + if ( Edit::GetGetSpecialCharsFunction() ) + { + // Damit die Selektion erhalten bleibt + mbActivePopup = TRUE; + XubString aChars = Edit::GetGetSpecialCharsFunction()( this, GetFont() ); + if ( aChars.Len() ) + { + mpExtTextView->InsertText( aChars ); + mpExtTextView->GetTextEngine()->SetModified( TRUE ); + } + mbActivePopup = FALSE; + bDone = TRUE; + } + } + else if ( nCode == KEY_TAB ) + { + if ( !mbIgnoreTab || rKEvent.GetKeyCode().IsMod1() ) + bDone = mpExtTextView->KeyInput( rKEvent ); + } + else + { + bDone = mpExtTextView->KeyInput( rKEvent ); + } + + if ( !bDone ) + Window::KeyInput( rKEvent ); +} + +void TextWindow::Paint( const Rectangle& rRect ) +{ + mpExtTextView->Paint( rRect ); +} + +void TextWindow::Resize() +{ +} + +void TextWindow::Command( const CommandEvent& rCEvt ) +{ + if ( rCEvt.GetCommand() == COMMAND_CONTEXTMENU ) + { + PopupMenu* pPopup = Edit::CreatePopupMenu(); + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + if ( rStyleSettings.GetOptions() & STYLE_OPTION_HIDEDISABLED ) + pPopup->SetMenuFlags( MENU_FLAG_HIDEDISABLEDENTRIES ); + if ( !mpExtTextView->HasSelection() ) + { + pPopup->EnableItem( SV_MENU_EDIT_CUT, FALSE ); + pPopup->EnableItem( SV_MENU_EDIT_COPY, FALSE ); + pPopup->EnableItem( SV_MENU_EDIT_DELETE, FALSE ); + } + if ( mpExtTextView->IsReadOnly() ) + { + pPopup->EnableItem( SV_MENU_EDIT_CUT, FALSE ); + pPopup->EnableItem( SV_MENU_EDIT_PASTE, FALSE ); + pPopup->EnableItem( SV_MENU_EDIT_DELETE, FALSE ); + pPopup->EnableItem( SV_MENU_EDIT_INSERTSYMBOL, FALSE ); + } + if ( !mpExtTextView->GetTextEngine()->HasUndoManager() || !mpExtTextView->GetTextEngine()->GetUndoManager().GetUndoActionCount() ) + { + pPopup->EnableItem( SV_MENU_EDIT_UNDO, FALSE ); + } +// if ( ( maSelection.Min() == 0 ) && ( maSelection.Max() == maText.Len() ) ) +// { +// pPopup->EnableItem( SV_MENU_EDIT_SELECTALL, FALSE ); +// } + if ( !Edit::GetGetSpecialCharsFunction() ) + { + USHORT nPos = pPopup->GetItemPos( SV_MENU_EDIT_INSERTSYMBOL ); + pPopup->RemoveItem( nPos ); + pPopup->RemoveItem( nPos-1 ); + } + + mbActivePopup = TRUE; + Point aPos = rCEvt.GetMousePosPixel(); + if ( !rCEvt.IsMouseEvent() ) + { + // !!! Irgendwann einmal Menu zentriert in der Selektion anzeigen !!! + Size aSize = GetOutputSizePixel(); + aPos = Point( aSize.Width()/2, aSize.Height()/2 ); + } +// pPopup->RemoveDisabledEntries(); + USHORT n = pPopup->Execute( this, aPos ); + Edit::DeletePopupMenu( pPopup ); + switch ( n ) + { + case SV_MENU_EDIT_UNDO: mpExtTextView->Undo(); + mpExtTextEngine->SetModified( TRUE ); + mpExtTextEngine->Broadcast( TextHint( TEXT_HINT_MODIFIED ) ); + break; + case SV_MENU_EDIT_CUT: mpExtTextView->Cut(); + mpExtTextEngine->SetModified( TRUE ); + mpExtTextEngine->Broadcast( TextHint( TEXT_HINT_MODIFIED ) ); + break; + case SV_MENU_EDIT_COPY: mpExtTextView->Copy(); + break; + case SV_MENU_EDIT_PASTE: mpExtTextView->Paste(); + mpExtTextEngine->SetModified( TRUE ); + mpExtTextEngine->Broadcast( TextHint( TEXT_HINT_MODIFIED ) ); + break; + case SV_MENU_EDIT_DELETE: mpExtTextView->DeleteSelected(); + mpExtTextEngine->SetModified( TRUE ); + mpExtTextEngine->Broadcast( TextHint( TEXT_HINT_MODIFIED ) ); + break; + case SV_MENU_EDIT_SELECTALL: mpExtTextView->SetSelection( TextSelection( TextPaM( 0, 0 ), TextPaM( 0xFFFFFFFF, 0xFFFF ) ) ); + break; + case SV_MENU_EDIT_INSERTSYMBOL: + { + XubString aChars = Edit::GetGetSpecialCharsFunction()( this, GetFont() ); + if ( aChars.Len() ) + { + mpExtTextView->InsertText( aChars ); + mpExtTextEngine->SetModified( TRUE ); + mpExtTextEngine->Broadcast( TextHint( TEXT_HINT_MODIFIED ) ); + } + } + break; + } + mbActivePopup = FALSE; + } + else + { + mpExtTextView->Command( rCEvt ); + } + Window::Command( rCEvt ); +} + +void TextWindow::GetFocus() +{ + Window::GetFocus(); + if ( !mbActivePopup ) + { + BOOL bGotoCursor = !mpExtTextView->IsReadOnly(); + if ( mbFocusSelectionHide && IsReallyVisible() && !mpExtTextView->IsReadOnly() + && ( mbSelectOnTab && + (!mbInMBDown || ( GetSettings().GetStyleSettings().GetSelectionOptions() & SELECTION_OPTION_FOCUS ) )) ) + { + // Alles selektieren, aber nicht scrollen + BOOL bAutoScroll = mpExtTextView->IsAutoScroll(); + mpExtTextView->SetAutoScroll( FALSE ); + mpExtTextView->SetSelection( TextSelection( TextPaM( 0, 0 ), TextPaM( 0xFFFF, 0xFFFF ) ) ); + mpExtTextView->SetAutoScroll( bAutoScroll ); + bGotoCursor = FALSE; + } + mpExtTextView->SetPaintSelection( TRUE ); + mpExtTextView->ShowCursor( bGotoCursor ); + } +} + +void TextWindow::LoseFocus() +{ + Window::LoseFocus(); + + if ( mbFocusSelectionHide && !mbActivePopup ) + mpExtTextView->SetPaintSelection( FALSE ); +} + +// virtual +::css::uno::Reference< ::css::awt::XWindowPeer > +TextWindow::GetComponentInterface(BOOL bCreate) +{ + ::css::uno::Reference< ::css::awt::XWindowPeer > xPeer( + Window::GetComponentInterface(false)); + if (!xPeer.is() && bCreate) + { + xPeer = new ::svt::TextWindowPeer(*GetTextView(), true); + SetComponentInterface(xPeer); + } + return xPeer; +} + +MultiLineEdit::MultiLineEdit( Window* pParent, WinBits nWinStyle ) + : Edit( pParent, nWinStyle ) +{ + SetType( WINDOW_MULTILINEEDIT ); + pImpSvMEdit = new ImpSvMEdit( this, nWinStyle ); + ImplInitSettings( TRUE, TRUE, TRUE ); + pUpdateDataTimer = 0; + + SetCompoundControl( TRUE ); + SetStyle( ImplInitStyle( nWinStyle ) ); +} + +MultiLineEdit::MultiLineEdit( Window* pParent, const ResId& rResId ) + : Edit( pParent, rResId.SetRT( RSC_MULTILINEEDIT ) ) +{ + SetType( WINDOW_MULTILINEEDIT ); + WinBits nWinStyle = rResId.GetWinBits(); + pImpSvMEdit = new ImpSvMEdit( this, nWinStyle ); + ImplInitSettings( TRUE, TRUE, TRUE ); + pUpdateDataTimer = 0; + + USHORT nMaxLen = Edit::GetMaxTextLen(); + if ( nMaxLen ) + SetMaxTextLen( nMaxLen ); + + SetText( Edit::GetText() ); + + if ( IsVisible() ) + pImpSvMEdit->Resize(); + + SetCompoundControl( TRUE ); + SetStyle( ImplInitStyle( nWinStyle ) ); + + // Base Edit ctor could call Show already, but that would cause problems + // with accessibility, as Show might (indirectly) trigger a call to virtual + // GetComponentInterface, which is the Edit's base version instead of the + // MultiLineEdit's version while in the base Edit ctor: + if ((GetStyle() & WB_HIDE) == 0) + Show(); +} + +MultiLineEdit::~MultiLineEdit() +{ + { + ::std::auto_ptr< ImpSvMEdit > pDelete( pImpSvMEdit ); + pImpSvMEdit = NULL; + } + delete pUpdateDataTimer; +} + +WinBits MultiLineEdit::ImplInitStyle( WinBits nStyle ) +{ + if ( !(nStyle & WB_NOTABSTOP) ) + nStyle |= WB_TABSTOP; + + if ( !(nStyle & WB_NOGROUP) ) + nStyle |= WB_GROUP; + + if ( !(nStyle & WB_IGNORETAB )) + nStyle |= WINDOW_DLGCTRL_MOD1TAB; + + return nStyle; +} + + +void MultiLineEdit::ImplInitSettings( BOOL /*bFont*/, BOOL /*bForeground*/, BOOL bBackground ) +{ + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + + // Der Font muss immer mit manipuliert werden, weil die TextEngine + // sich nicht um TextColor/Background kuemmert + + Color aTextColor = rStyleSettings.GetFieldTextColor(); + if ( IsControlForeground() ) + aTextColor = GetControlForeground(); + if ( !IsEnabled() ) + aTextColor = rStyleSettings.GetDisableColor(); + + Font aFont = rStyleSettings.GetFieldFont(); + if ( IsControlFont() ) + aFont.Merge( GetControlFont() ); + aFont.SetTransparent( IsPaintTransparent() ); + SetZoomedPointFont( aFont ); + Font TheFont = GetFont(); + TheFont.SetColor( aTextColor ); + if( IsPaintTransparent() ) + TheFont.SetFillColor( Color( COL_TRANSPARENT ) ); + else + TheFont.SetFillColor( IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor() ); + pImpSvMEdit->GetTextWindow()->SetFont( TheFont ); + pImpSvMEdit->GetTextWindow()->GetTextEngine()->SetFont( TheFont ); + pImpSvMEdit->GetTextWindow()->SetTextColor( aTextColor ); + + if ( bBackground ) + { + if( IsPaintTransparent() ) + { + pImpSvMEdit->GetTextWindow()->SetPaintTransparent( TRUE ); + pImpSvMEdit->GetTextWindow()->SetBackground(); + pImpSvMEdit->GetTextWindow()->SetControlBackground(); + SetBackground(); + SetControlBackground(); + } + else + { + if( IsControlBackground() ) + pImpSvMEdit->GetTextWindow()->SetBackground( GetControlBackground() ); + else + pImpSvMEdit->GetTextWindow()->SetBackground( rStyleSettings.GetFieldColor() ); + // Auch am MultiLineEdit einstellen, weil die TextComponent + // ggf. die Scrollbars hidet. + SetBackground( pImpSvMEdit->GetTextWindow()->GetBackground() ); + } + } +} + +void MultiLineEdit::Modify() +{ + aModifyHdlLink.Call( this ); + + CallEventListeners( VCLEVENT_EDIT_MODIFY ); + + if ( pUpdateDataTimer ) + pUpdateDataTimer->Start(); +} + +IMPL_LINK( MultiLineEdit, ImpUpdateDataHdl, Timer*, EMPTYARG ) +{ + UpdateData(); + return 0; +} + +void MultiLineEdit::UpdateData() +{ + aUpdateDataHdlLink.Call( this ); +} + +void MultiLineEdit::SetModifyFlag() +{ + pImpSvMEdit->SetModified( TRUE ); +} + +void MultiLineEdit::ClearModifyFlag() +{ + pImpSvMEdit->SetModified( FALSE ); +} + +BOOL MultiLineEdit::IsModified() const +{ + return pImpSvMEdit->IsModified(); +} + +void MultiLineEdit::EnableUpdateData( ULONG nTimeout ) +{ + if ( !nTimeout ) + DisableUpdateData(); + else + { + if ( !pUpdateDataTimer ) + { + pUpdateDataTimer = new Timer; + pUpdateDataTimer->SetTimeoutHdl( LINK( this, MultiLineEdit, ImpUpdateDataHdl ) ); + } + pUpdateDataTimer->SetTimeout( nTimeout ); + } +} + +void MultiLineEdit::SetReadOnly( BOOL bReadOnly ) +{ + pImpSvMEdit->SetReadOnly( bReadOnly ); + Edit::SetReadOnly( bReadOnly ); + + // #94921# ReadOnly can be overwritten in InitFromStyle() when WB not set. + WinBits nStyle = GetStyle(); + if ( bReadOnly ) + nStyle |= WB_READONLY; + else + nStyle &= ~WB_READONLY; + SetStyle( nStyle ); +} + +BOOL MultiLineEdit::IsReadOnly() const +{ + return pImpSvMEdit->IsReadOnly(); +} + +void MultiLineEdit::SetMaxTextLen( xub_StrLen nMaxLen ) +{ + pImpSvMEdit->SetMaxTextLen( nMaxLen ); +} + +xub_StrLen MultiLineEdit::GetMaxTextLen() const +{ + return pImpSvMEdit->GetMaxTextLen(); +} + +void MultiLineEdit::ReplaceSelected( const String& rStr ) +{ + pImpSvMEdit->InsertText( rStr ); +} + +void MultiLineEdit::DeleteSelected() +{ + pImpSvMEdit->InsertText( String() ); +} + +String MultiLineEdit::GetSelected() const +{ + return pImpSvMEdit->GetSelected(); +} + +String MultiLineEdit::GetSelected( LineEnd aSeparator ) const +{ + return pImpSvMEdit->GetSelected( aSeparator ); +} + +void MultiLineEdit::Cut() +{ + pImpSvMEdit->Cut(); +} + +void MultiLineEdit::Copy() +{ + pImpSvMEdit->Copy(); +} + +void MultiLineEdit::Paste() +{ + pImpSvMEdit->Paste(); +} + +void MultiLineEdit::SetText( const String& rStr ) +{ + pImpSvMEdit->SetText( rStr ); +} + +String MultiLineEdit::GetText() const +{ + return pImpSvMEdit->GetText(); +} + +String MultiLineEdit::GetText( LineEnd aSeparator ) const +{ + return pImpSvMEdit->GetText( aSeparator ); +} + +String MultiLineEdit::GetTextLines() const +{ + return pImpSvMEdit->GetTextLines(); +} + +String MultiLineEdit::GetTextLines( LineEnd aSeparator ) const +{ + return pImpSvMEdit->GetTextLines( aSeparator ); +} + +void MultiLineEdit::Resize() +{ + pImpSvMEdit->Resize(); +} + +void MultiLineEdit::GetFocus() +{ + if ( !pImpSvMEdit ) // might be called from within the dtor, when pImpSvMEdit == NULL is a valid state + return; + + Edit::GetFocus(); + pImpSvMEdit->GetFocus(); +} + +void MultiLineEdit::SetSelection( const Selection& rSelection ) +{ + pImpSvMEdit->SetSelection( rSelection ); +} + +const Selection& MultiLineEdit::GetSelection() const +{ + return pImpSvMEdit->GetSelection(); +} + +Size MultiLineEdit::CalcMinimumSize() const +{ + Size aSz = pImpSvMEdit->CalcMinimumSize(); + + sal_Int32 nLeft, nTop, nRight, nBottom; + ((Window*)this)->GetBorder( nLeft, nTop, nRight, nBottom ); + aSz.Width() += nLeft+nRight; + aSz.Height() += nTop+nBottom; + + return aSz; +} + +Size MultiLineEdit::CalcAdjustedSize( const Size& rPrefSize ) const +{ + Size aSz = rPrefSize; + sal_Int32 nLeft, nTop, nRight, nBottom; + ((Window*)this)->GetBorder( nLeft, nTop, nRight, nBottom ); + + // In der Hoehe auf ganze Zeilen justieren + + long nHeight = aSz.Height() - nTop - nBottom; + long nLineHeight = pImpSvMEdit->CalcSize( 1, 1 ).Height(); + long nLines = nHeight / nLineHeight; + if ( nLines < 1 ) + nLines = 1; + + aSz.Height() = nLines * nLineHeight; + aSz.Height() += nTop+nBottom; + + return aSz; +} + +Size MultiLineEdit::CalcSize( USHORT nColumns, USHORT nLines ) const +{ + Size aSz = pImpSvMEdit->CalcSize( nColumns, nLines ); + + sal_Int32 nLeft, nTop, nRight, nBottom; + ((Window*)this)->GetBorder( nLeft, nTop, nRight, nBottom ); + aSz.Width() += nLeft+nRight; + aSz.Height() += nTop+nBottom; + return aSz; +} + +void MultiLineEdit::GetMaxVisColumnsAndLines( USHORT& rnCols, USHORT& rnLines ) const +{ + pImpSvMEdit->GetMaxVisColumnsAndLines( rnCols, rnLines ); +} + +void MultiLineEdit::StateChanged( StateChangedType nType ) +{ + if( nType == STATE_CHANGE_ENABLE ) + { + pImpSvMEdit->Enable( IsEnabled() ); + ImplInitSettings( TRUE, FALSE, FALSE ); + } + else if( nType == STATE_CHANGE_READONLY ) + { + pImpSvMEdit->SetReadOnly( IsReadOnly() ); + } + else if ( nType == STATE_CHANGE_ZOOM ) + { + pImpSvMEdit->GetTextWindow()->SetZoom( GetZoom() ); + ImplInitSettings( TRUE, FALSE, FALSE ); + Resize(); + } + else if ( nType == STATE_CHANGE_CONTROLFONT ) + { + ImplInitSettings( TRUE, FALSE, FALSE ); + Resize(); + Invalidate(); + } + else if ( nType == STATE_CHANGE_CONTROLFOREGROUND ) + { + ImplInitSettings( FALSE, TRUE, FALSE ); + Invalidate(); + } + else if ( nType == STATE_CHANGE_CONTROLBACKGROUND ) + { + ImplInitSettings( FALSE, FALSE, TRUE ); + Invalidate(); + } + else if ( nType == STATE_CHANGE_STYLE ) + { + pImpSvMEdit->InitFromStyle( GetStyle() ); + SetStyle( ImplInitStyle( GetStyle() ) ); + } + else if ( nType == STATE_CHANGE_INITSHOW ) + { + if( IsPaintTransparent() ) + { + pImpSvMEdit->GetTextWindow()->SetPaintTransparent( TRUE ); + pImpSvMEdit->GetTextWindow()->SetBackground(); + pImpSvMEdit->GetTextWindow()->SetControlBackground(); + SetBackground(); + SetControlBackground(); + } + } + + Control::StateChanged( nType ); +} + +void MultiLineEdit::DataChanged( const DataChangedEvent& rDCEvt ) +{ + if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS) && + (rDCEvt.GetFlags() & SETTINGS_STYLE) ) + { + ImplInitSettings( TRUE, TRUE, TRUE ); + Resize(); + Invalidate(); + } + else + Control::DataChanged( rDCEvt ); +} + +void MultiLineEdit::Draw( OutputDevice* pDev, const Point& rPos, const Size& rSize, ULONG nFlags ) +{ + ImplInitSettings( TRUE, TRUE, TRUE ); + + Point aPos = pDev->LogicToPixel( rPos ); + Size aSize = pDev->LogicToPixel( rSize ); + Font aFont = pImpSvMEdit->GetTextWindow()->GetDrawPixelFont( pDev ); + aFont.SetTransparent( TRUE ); + OutDevType eOutDevType = pDev->GetOutDevType(); + + pDev->Push(); + pDev->SetMapMode(); + pDev->SetFont( aFont ); + pDev->SetTextFillColor(); + + // Border/Background + pDev->SetLineColor(); + pDev->SetFillColor(); + BOOL bBorder = !(nFlags & WINDOW_DRAW_NOBORDER ) && (GetStyle() & WB_BORDER); + BOOL bBackground = !(nFlags & WINDOW_DRAW_NOBACKGROUND) && IsControlBackground(); + if ( bBorder || bBackground ) + { + Rectangle aRect( aPos, aSize ); + if ( bBorder ) + { + DecorationView aDecoView( pDev ); + aRect = aDecoView.DrawFrame( aRect, FRAME_DRAW_DOUBLEIN ); + } + if ( bBackground ) + { + pDev->SetFillColor( GetControlBackground() ); + pDev->DrawRect( aRect ); + } + } + + // Inhalt + if ( ( nFlags & WINDOW_DRAW_MONO ) || ( eOutDevType == OUTDEV_PRINTER ) ) + pDev->SetTextColor( Color( COL_BLACK ) ); + else + { + if ( !(nFlags & WINDOW_DRAW_NODISABLE ) && !IsEnabled() ) + { + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + pDev->SetTextColor( rStyleSettings.GetDisableColor() ); + } + else + { + pDev->SetTextColor( GetTextColor() ); + } + } + + XubString aText = GetText(); + Size aTextSz( pDev->GetTextWidth( aText ), pDev->GetTextHeight() ); + ULONG nLines = (ULONG) (aSize.Height() / aTextSz.Height()); + if ( !nLines ) + nLines = 1; + aTextSz.Height() = nLines*aTextSz.Height(); + long nOnePixel = GetDrawPixel( pDev, 1 ); + long nOffX = 3*nOnePixel; + long nOffY = 2*nOnePixel; + + // Clipping? + if ( ( nOffY < 0 ) || ( (nOffY+aTextSz.Height()) > aSize.Height() ) || ( (nOffX+aTextSz.Width()) > aSize.Width() ) ) + { + Rectangle aClip( aPos, aSize ); + if ( aTextSz.Height() > aSize.Height() ) + aClip.Bottom() += aTextSz.Height() - aSize.Height() + 1; // Damit HP-Drucker nicht 'weg-optimieren' + pDev->IntersectClipRegion( aClip ); + } + + TextEngine aTE; + aTE.SetText( GetText() ); + aTE.SetMaxTextWidth( aSize.Width() ); + aTE.SetFont( aFont ); + aTE.SetTextAlign( pImpSvMEdit->GetTextWindow()->GetTextEngine()->GetTextAlign() ); + aTE.Draw( pDev, Point( aPos.X() + nOffX, aPos.Y() + nOffY ) ); + + pDev->Pop(); +} + +long MultiLineEdit::Notify( NotifyEvent& rNEvt ) +{ + long nDone = 0; + if( rNEvt.GetType() == EVENT_COMMAND ) + { + nDone = pImpSvMEdit->HandleCommand( *rNEvt.GetCommandEvent() ); + } + return nDone ? nDone : Edit::Notify( rNEvt ); +} + +long MultiLineEdit::PreNotify( NotifyEvent& rNEvt ) +{ + long nDone = 0; + +#if (OSL_DEBUG_LEVEL > 1) && defined(DBG_UTIL) + if( rNEvt.GetType() == EVENT_KEYINPUT ) + { + const KeyEvent& rKEvent = *rNEvt.GetKeyEvent(); + if ( ( rKEvent.GetKeyCode().GetCode() == KEY_W ) && rKEvent.GetKeyCode().IsMod1() && rKEvent.GetKeyCode().IsMod2() ) + { + SetRightToLeft( !IsRightToLeft() ); + } + } +#endif + + if( ( rNEvt.GetType() == EVENT_KEYINPUT ) && ( !GetTextView()->IsCursorEnabled() ) ) + { + const KeyEvent& rKEvent = *rNEvt.GetKeyEvent(); + if ( !rKEvent.GetKeyCode().IsShift() && ( rKEvent.GetKeyCode().GetGroup() == KEYGROUP_CURSOR ) ) + { + nDone = 1; + TextSelection aSel = pImpSvMEdit->GetTextWindow()->GetTextView()->GetSelection(); + if ( aSel.HasRange() ) + { + aSel.GetStart() = aSel.GetEnd(); + pImpSvMEdit->GetTextWindow()->GetTextView()->SetSelection( aSel ); + } + else + { + switch ( rKEvent.GetKeyCode().GetCode() ) + { + case KEY_UP: + { + if ( pImpSvMEdit->GetVScrollBar() ) + pImpSvMEdit->GetVScrollBar()->DoScrollAction( SCROLL_LINEUP ); + } + break; + case KEY_DOWN: + { + if ( pImpSvMEdit->GetVScrollBar() ) + pImpSvMEdit->GetVScrollBar()->DoScrollAction( SCROLL_LINEDOWN ); + } + break; + case KEY_PAGEUP : + { + if ( pImpSvMEdit->GetVScrollBar() ) + pImpSvMEdit->GetVScrollBar()->DoScrollAction( SCROLL_PAGEUP ); + } + break; + case KEY_PAGEDOWN: + { + if ( pImpSvMEdit->GetVScrollBar() ) + pImpSvMEdit->GetVScrollBar()->DoScrollAction( SCROLL_PAGEDOWN ); + } + break; + case KEY_LEFT: + { + if ( pImpSvMEdit->GetHScrollBar() ) + pImpSvMEdit->GetHScrollBar()->DoScrollAction( SCROLL_LINEUP ); + } + break; + case KEY_RIGHT: + { + if ( pImpSvMEdit->GetHScrollBar() ) + pImpSvMEdit->GetHScrollBar()->DoScrollAction( SCROLL_LINEDOWN ); + } + break; + case KEY_HOME: + { + if ( rKEvent.GetKeyCode().IsMod1() ) + pImpSvMEdit->GetTextWindow()->GetTextView()-> + SetSelection( TextSelection( TextPaM( 0, 0 ) ) ); + } + break; + case KEY_END: + { + if ( rKEvent.GetKeyCode().IsMod1() ) + pImpSvMEdit->GetTextWindow()->GetTextView()-> + SetSelection( TextSelection( TextPaM( 0xFFFF, 0xFFFF ) ) ); + } + break; + default: + { + nDone = 0; + } + } + } + } + } + + return nDone ? nDone : Edit::PreNotify( rNEvt ); +} + +// +// Internas fuer abgeleitete Klassen, z.B. TextComponent + +ExtTextEngine* MultiLineEdit::GetTextEngine() const +{ + return pImpSvMEdit->GetTextWindow()->GetTextEngine(); +} + +ExtTextView* MultiLineEdit::GetTextView() const +{ + return pImpSvMEdit->GetTextWindow()->GetTextView(); +} + +ScrollBar* MultiLineEdit::GetHScrollBar() const +{ + return pImpSvMEdit->GetHScrollBar(); +} + + +ScrollBar* MultiLineEdit::GetVScrollBar() const +{ + return pImpSvMEdit->GetVScrollBar(); +} + +void MultiLineEdit::EnableFocusSelectionHide( BOOL bHide ) +{ + pImpSvMEdit->GetTextWindow()->SetAutoFocusHide( bHide ); +} + +BOOL MultiLineEdit::IsFocusSelectionHideEnabled() const +{ + return pImpSvMEdit->GetTextWindow()->IsAutoFocusHide(); +} + + +void MultiLineEdit::SetLeftMargin( USHORT n ) +{ + if ( GetTextEngine() ) + GetTextEngine()->SetLeftMargin( n ); +} + +USHORT MultiLineEdit::GetLeftMargin() const +{ + if ( GetTextEngine() ) + return GetTextEngine()->GetLeftMargin(); + else + return 0; +} + +void MultiLineEdit::SetRightToLeft( BOOL bRightToLeft ) +{ + if ( GetTextEngine() ) + { + GetTextEngine()->SetRightToLeft( bRightToLeft ); + GetTextView()->ShowCursor(); + } +} + +BOOL MultiLineEdit::IsRightToLeft() const +{ + BOOL bRightToLeft = FALSE; + + if ( GetTextEngine() ) + bRightToLeft = GetTextEngine()->IsRightToLeft(); + + return bRightToLeft; +} + +// virtual +::css::uno::Reference< ::css::awt::XWindowPeer > +MultiLineEdit::GetComponentInterface(BOOL bCreate) +{ + ::css::uno::Reference< ::css::awt::XWindowPeer > xPeer( + Edit::GetComponentInterface(false)); + if (!xPeer.is() && bCreate) + { + ::std::auto_ptr< VCLXMultiLineEdit > xEdit(new VCLXMultiLineEdit()); + xEdit->SetWindow(this); + xPeer = xEdit.release(); + SetComponentInterface(xPeer); + } + return xPeer; +} +/*-- 11.08.2004 11:29:23--------------------------------------------------- + + -----------------------------------------------------------------------*/ +void MultiLineEdit::DisableSelectionOnFocus() +{ + pImpSvMEdit->GetTextWindow()->DisableSelectionOnFocus(); +} diff --git a/svtools/source/edit/svmedit2.cxx b/svtools/source/edit/svmedit2.cxx new file mode 100644 index 000000000000..19eba618a828 --- /dev/null +++ b/svtools/source/edit/svmedit2.cxx @@ -0,0 +1,81 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + +#include <svtools/svmedit2.hxx> +#include <svtools/xtextedt.hxx> + +ExtMultiLineEdit::ExtMultiLineEdit( Window* pParent, WinBits nWinStyle ) : + + MultiLineEdit( pParent, nWinStyle ) + +{ +} + +ExtMultiLineEdit::ExtMultiLineEdit( Window* pParent, const ResId& rResId ) : + + MultiLineEdit( pParent, rResId ) + +{ +} + +ExtMultiLineEdit::~ExtMultiLineEdit() +{ +} + +void ExtMultiLineEdit::InsertText( const String& rNew, BOOL ) +{ + GetTextView()->InsertText( rNew, FALSE ); +} + +void ExtMultiLineEdit::SetAutoScroll( BOOL bAutoScroll ) +{ + GetTextView()->SetAutoScroll( bAutoScroll ); +} + +void ExtMultiLineEdit::EnableCursor( BOOL bEnable ) +{ + GetTextView()->EnableCursor( bEnable ); +} + +void ExtMultiLineEdit::SetAttrib( const TextAttrib& rAttr, ULONG nPara, USHORT nStart, USHORT nEnd ) +{ + GetTextEngine()->SetAttrib( rAttr, nPara, nStart, nEnd ); +} + +void ExtMultiLineEdit::SetLeftMargin( USHORT nLeftMargin ) +{ + GetTextEngine()->SetLeftMargin( nLeftMargin ); +} + +ULONG ExtMultiLineEdit::GetParagraphCount() const +{ + return GetTextEngine()->GetParagraphCount(); +} + diff --git a/svtools/source/edit/sychconv.cxx b/svtools/source/edit/sychconv.cxx new file mode 100644 index 000000000000..3efa510bfc5a --- /dev/null +++ b/svtools/source/edit/sychconv.cxx @@ -0,0 +1,103 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" +#include "sychconv.hxx" +#include <vcl/outdev.hxx> + +BOOL SymCharConverter::Convert( Font& rFont, UniString& rString, OutputDevice* pDev ) +{ + // hibyte 0 = exact matching + // 1 = little differences, + // 2 = the converted character does not look like the original but got the same meaning + // 3 = the destination does not match looking and meaning of the original + + static USHORT __READONLY_DATA aWingdingsToStarBatsTable[ 256 - 32 ] = + { + 0x0020, 0x0238, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0174, 0x02BA, 0x017B, 0x017C, 0x037C, 0x037C, 0x037C, 0x037C, + 0x0000, 0x0000, 0x0372, 0x0272, 0x0372, 0x0000, 0x0000, 0x0374, 0x0279, 0x0000, 0x027A, 0x0000, 0x0178, 0x0278, 0x0000, 0x0137, + 0x027E, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x017D, 0x0000, 0x0000, 0x0000, 0x0021, 0x03AC, 0x00AD, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x01C0, 0x0000, 0x0000, 0x0286, 0x0286, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0022, 0x0023, 0x0024, 0x0025, + 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x0133, 0x0000, 0x0000, 0x0000, 0x0000, 0x0193, 0x0194, 0x0000, + 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, + 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01A5, 0x0095, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x002E, 0x0024, 0x0125, 0x0000, 0x0000, 0x0000, 0x014B, 0x024D, 0x014E, 0x014A, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x002F, 0x0000, 0x0000, 0x0000, 0x0035, 0x0000, 0x0000, 0x0000, + 0x0030, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0031, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01B1, + 0x01AF, 0x01B2, 0x01B0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0150, 0x0032, 0x0033, 0x0034, 0x01C8 + }; + + static USHORT __READONLY_DATA aMonotypeSortsToStarBatsTable[ 256 - 32 ]= + { + 0x0020, 0x00cb, 0x00cb, 0x00cb, 0x00cb, 0x0074, 0x00ba, 0x0021, 0x00cc, 0x007b, 0x0036, 0x007d, 0x007e, 0x0037, 0x0038, 0x0038, + 0x0039, 0x0038, 0x0038, 0x0039, 0x003a, 0x004f, 0x0050, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0086, 0x0086, 0x0086, + 0x0052, 0x00cd, 0x0044, 0x0045, 0x0046, 0x0047, 0x0041, 0x0041, 0x0058, 0x0057, 0x0075, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, + 0x005a, 0x004b, 0x004b, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004e, 0x004b, 0x004b, 0x00ce, 0x00ce, 0x00ce, + 0x00ce, 0x00ce, 0x00ce, 0x00ce, 0x00cf, 0x00cf, 0x00cf, 0x00cf, 0x00cf, 0x00cf, 0x00b9, 0x00b9, 0x003b, 0x003c, 0x003d, 0x003e, + 0x003f, 0x003e, 0x0040, 0x00c5, 0x00c4, 0x002b, 0x002c, 0x00d0, 0x00d1, 0x00d1, 0x00d1, 0x0091, 0x0092, 0x0093, 0x0094, 0x0000, + 0x00d2, 0x00d3, 0x00d2, 0x00d3, 0x00d2, 0x00d3, 0x00d2, 0x00d3, 0x00d2, 0x00d3, 0x00d2, 0x00d3, 0x00d2, 0x00d3, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x00d4, 0x00d4, 0x00d4, 0x00d6, 0x00d6, 0x00d4, 0x00d4, 0x00d5, 0x002a, 0x00d6, 0x00d7, 0x0068, 0x0069, 0x006a, 0x006b, + 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, + 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, + 0x0063, 0x0064, 0x0065, 0x0066, 0x0030, 0x0031, 0x00d8, 0x00d9, 0x00da, 0x00bc, 0x00db, 0x00bc, 0x00bc, 0x00bc, 0x00bc, 0x0031, + 0x0031, 0x0031, 0x002f, 0x002f, 0x002f, 0x00be, 0x00be, 0x0031, 0x0031, 0x00af, 0x00af, 0x00af, 0x00af, 0x00af, 0x00af, 0x00af, + 0x0000, 0x00af, 0x0035, 0x00dc, 0x00da, 0x00dc, 0x00db, 0x00da, 0x00dc, 0x00db, 0x00dc, 0x00dc, 0x00dc, 0x00dc, 0x00af, 0x0000 + }; + + const USHORT* pTransTable = NULL; + + BOOL bIsAvailable = ( pDev ) ? pDev->IsFontAvailable( rFont.GetName() ) : FALSE; + if ( !bIsAvailable ) + { + if ( rFont.GetName().CompareToAscii( RTL_CONSTASCII_STRINGPARAM( "Wingdings" ) ) == COMPARE_EQUAL ) + pTransTable = &aWingdingsToStarBatsTable[ 0 ]; + else if ( rFont.GetName().CompareToAscii( RTL_CONSTASCII_STRINGPARAM( "Monotype Sorts" ) ) == COMPARE_EQUAL ) + pTransTable = &aMonotypeSortsToStarBatsTable[ 0 ]; + } + if ( pTransTable ) + { + sal_Unicode c; + for ( UINT16 i = rString.Len(); i--; ) + { + c = rString.GetChar( i ); + c -= 32; + c = ( ((UINT16)c) >= 224 ) ? 0 : (sal_Unicode) pTransTable[ c ]; + if ( !c ) // if character is out of range or not matching + c = 0xA5; // we will default a StarBats-Bullet + rString.SetChar( i, c ); + } + rFont.SetCharSet( RTL_TEXTENCODING_SYMBOL ); + rFont.SetName( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "StarBats" ) ) ); + return TRUE; + } + else return FALSE; +}; diff --git a/svtools/source/edit/syntaxhighlight.cxx b/svtools/source/edit/syntaxhighlight.cxx new file mode 100644 index 000000000000..5729eb712bfe --- /dev/null +++ b/svtools/source/edit/syntaxhighlight.cxx @@ -0,0 +1,909 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + +#include <svtools/syntaxhighlight.hxx> + +#include <unotools/charclass.hxx> +#include <tools/debug.hxx> + + +SV_IMPL_VARARR(HighlightPortions, HighlightPortion) + + +// ########################################################################## +// ATTENTION: all these words needs to be in small caps +// ########################################################################## +static const char* strListBasicKeyWords[] = { + "access", + "alias", + "and", + "any", + "append", + "as", + "base", + "binary", + "boolean", + "byref", + "byte", + "byval", + "call", + "case", + "cdecl", + "classmodule", + "close", + "compare", + "compatible", + "const", + "currency", + "date", + "declare", + "defbool", + "defcur", + "defdate", + "defdbl", + "deferr", + "defint", + "deflng", + "defobj", + "defsng", + "defstr", + "defvar", + "dim", + "do", + "double", + "each", + "else", + "elseif", + "end", + "end enum", + "end function", + "end if", + "end select", + "end sub", + "end type", + "endif", + "enum", + "eqv", + "erase", + "error", + "exit", + "explicit", + "for", + "function", + "get", + "global", + "gosub", + "goto", + "if", + "imp", + "implements", + "in", + "input", + "integer", + "is", + "let", + "lib", + "like", + "line", + "line input", + "local", + "lock", + "long", + "loop", + "lprint", + "lset", + "mod", + "name", + "new", + "next", + "not", + "object", + "on", + "open", + "option", + "optional", + "or", + "output", + "preserve", + "print", + "private", + "property", + "public", + "random", + "read", + "redim", + "rem", + "resume", + "return", + "rset", + "select", + "set", + "shared", + "single", + "static", + "step", + "stop", + "string", + "sub", + "system", + "text", + "then", + "to", + "type", + "typeof", + "until", + "variant", + "wend", + "while", + "with", + "write", + "xor" +}; + + +static const char* strListSqlKeyWords[] = { + "all", + "and", + "any", + "as", + "asc", + "avg", + "between", + "by", + "cast", + "corresponding", + "count", + "create", + "cross", + "delete", + "desc", + "distinct", + "drop", + "escape", + "except", + "exists", + "false", + "from", + "full", + "global", + "group", + "having", + "in", + "inner", + "insert", + "intersect", + "into", + "is", + "join", + "left", + "like", + "local", + "match", + "max", + "min", + "natural", + "not", + "null", + "on", + "or", + "order", + "outer", + "right", + "select", + "set", + "some", + "sum", + "table", + "temporary", + "true", + "union", + "unique", + "unknown", + "update", + "using", + "values", + "where" +}; + + +extern "C" int CDECL compare_strings( const void *arg1, const void *arg2 ) +{ + return strcmp( (char *)arg1, *(char **)arg2 ); +} + + +class LetterTable +{ + bool IsLetterTab[256]; + +public: + LetterTable( void ); + + inline bool isLetter( sal_Unicode c ) + { + bool bRet = (c < 256) ? IsLetterTab[c] : isLetterUnicode( c ); + return bRet; + } + bool isLetterUnicode( sal_Unicode c ); +}; + +class BasicSimpleCharClass +{ + static LetterTable aLetterTable; + +public: + static BOOL isAlpha( sal_Unicode c, bool bCompatible ) + { + BOOL bRet = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + || (bCompatible && aLetterTable.isLetter( c )); + return bRet; + } + + static BOOL isDigit( sal_Unicode c ) + { + BOOL bRet = (c >= '0' && c <= '9'); + return bRet; + } + + static BOOL isAlphaNumeric( sal_Unicode c, bool bCompatible ) + { + BOOL bRet = isDigit( c ) || isAlpha( c, bCompatible ); + return bRet; + } +}; + +LetterTable BasicSimpleCharClass::aLetterTable; + +LetterTable::LetterTable( void ) +{ + for( int i = 0 ; i < 256 ; ++i ) + IsLetterTab[i] = false; + + IsLetterTab[0xC0] = true; // À , CAPITAL LETTER A WITH GRAVE ACCENT + IsLetterTab[0xC1] = true; // Á , CAPITAL LETTER A WITH ACUTE ACCENT + IsLetterTab[0xC2] = true; // Â , CAPITAL LETTER A WITH CIRCUMFLEX ACCENT + IsLetterTab[0xC3] = true; // Ã , CAPITAL LETTER A WITH TILDE + IsLetterTab[0xC4] = true; // Ä , CAPITAL LETTER A WITH DIAERESIS + IsLetterTab[0xC5] = true; // Å , CAPITAL LETTER A WITH RING ABOVE + IsLetterTab[0xC6] = true; // Æ , CAPITAL LIGATURE AE + IsLetterTab[0xC7] = true; // Ç , CAPITAL LETTER C WITH CEDILLA + IsLetterTab[0xC8] = true; // È , CAPITAL LETTER E WITH GRAVE ACCENT + IsLetterTab[0xC9] = true; // É , CAPITAL LETTER E WITH ACUTE ACCENT + IsLetterTab[0xCA] = true; // Ê , CAPITAL LETTER E WITH CIRCUMFLEX ACCENT + IsLetterTab[0xCB] = true; // Ë , CAPITAL LETTER E WITH DIAERESIS + IsLetterTab[0xCC] = true; // Ì , CAPITAL LETTER I WITH GRAVE ACCENT + IsLetterTab[0xCD] = true; // Í , CAPITAL LETTER I WITH ACUTE ACCENT + IsLetterTab[0xCE] = true; // Î , CAPITAL LETTER I WITH CIRCUMFLEX ACCENT + IsLetterTab[0xCF] = true; // Ï , CAPITAL LETTER I WITH DIAERESIS + IsLetterTab[0xD0] = true; // Ð , CAPITAL LETTER ETH + IsLetterTab[0xD1] = true; // Ñ , CAPITAL LETTER N WITH TILDE + IsLetterTab[0xD2] = true; // Ò , CAPITAL LETTER O WITH GRAVE ACCENT + IsLetterTab[0xD3] = true; // Ó , CAPITAL LETTER O WITH ACUTE ACCENT + IsLetterTab[0xD4] = true; // Ô , CAPITAL LETTER O WITH CIRCUMFLEX ACCENT + IsLetterTab[0xD5] = true; // Õ , CAPITAL LETTER O WITH TILDE + IsLetterTab[0xD6] = true; // Ö , CAPITAL LETTER O WITH DIAERESIS + IsLetterTab[0xD8] = true; // Ø , CAPITAL LETTER O WITH STROKE + IsLetterTab[0xD9] = true; // Ù , CAPITAL LETTER U WITH GRAVE ACCENT + IsLetterTab[0xDA] = true; // Ú , CAPITAL LETTER U WITH ACUTE ACCENT + IsLetterTab[0xDB] = true; // Û , CAPITAL LETTER U WITH CIRCUMFLEX ACCENT + IsLetterTab[0xDC] = true; // Ü , CAPITAL LETTER U WITH DIAERESIS + IsLetterTab[0xDD] = true; // Ý , CAPITAL LETTER Y WITH ACUTE ACCENT + IsLetterTab[0xDE] = true; // Þ , CAPITAL LETTER THORN + IsLetterTab[0xDF] = true; // ß , SMALL LETTER SHARP S + IsLetterTab[0xE0] = true; // à , SMALL LETTER A WITH GRAVE ACCENT + IsLetterTab[0xE1] = true; // á , SMALL LETTER A WITH ACUTE ACCENT + IsLetterTab[0xE2] = true; // â , SMALL LETTER A WITH CIRCUMFLEX ACCENT + IsLetterTab[0xE3] = true; // ã , SMALL LETTER A WITH TILDE + IsLetterTab[0xE4] = true; // ä , SMALL LETTER A WITH DIAERESIS + IsLetterTab[0xE5] = true; // å , SMALL LETTER A WITH RING ABOVE + IsLetterTab[0xE6] = true; // æ , SMALL LIGATURE AE + IsLetterTab[0xE7] = true; // ç , SMALL LETTER C WITH CEDILLA + IsLetterTab[0xE8] = true; // è , SMALL LETTER E WITH GRAVE ACCENT + IsLetterTab[0xE9] = true; // é , SMALL LETTER E WITH ACUTE ACCENT + IsLetterTab[0xEA] = true; // ê , SMALL LETTER E WITH CIRCUMFLEX ACCENT + IsLetterTab[0xEB] = true; // ë , SMALL LETTER E WITH DIAERESIS + IsLetterTab[0xEC] = true; // ì , SMALL LETTER I WITH GRAVE ACCENT + IsLetterTab[0xED] = true; // í , SMALL LETTER I WITH ACUTE ACCENT + IsLetterTab[0xEE] = true; // î , SMALL LETTER I WITH CIRCUMFLEX ACCENT + IsLetterTab[0xEF] = true; // ï , SMALL LETTER I WITH DIAERESIS + IsLetterTab[0xF0] = true; // ð , SMALL LETTER ETH + IsLetterTab[0xF1] = true; // ñ , SMALL LETTER N WITH TILDE + IsLetterTab[0xF2] = true; // ò , SMALL LETTER O WITH GRAVE ACCENT + IsLetterTab[0xF3] = true; // ó , SMALL LETTER O WITH ACUTE ACCENT + IsLetterTab[0xF4] = true; // ô , SMALL LETTER O WITH CIRCUMFLEX ACCENT + IsLetterTab[0xF5] = true; // õ , SMALL LETTER O WITH TILDE + IsLetterTab[0xF6] = true; // ö , SMALL LETTER O WITH DIAERESIS + IsLetterTab[0xF8] = true; // ø , SMALL LETTER O WITH OBLIQUE BAR + IsLetterTab[0xF9] = true; // ù , SMALL LETTER U WITH GRAVE ACCENT + IsLetterTab[0xFA] = true; // ú , SMALL LETTER U WITH ACUTE ACCENT + IsLetterTab[0xFB] = true; // û , SMALL LETTER U WITH CIRCUMFLEX ACCENT + IsLetterTab[0xFC] = true; // ü , SMALL LETTER U WITH DIAERESIS + IsLetterTab[0xFD] = true; // ý , SMALL LETTER Y WITH ACUTE ACCENT + IsLetterTab[0xFE] = true; // þ , SMALL LETTER THORN + IsLetterTab[0xFF] = true; // ÿ , SMALL LETTER Y WITH DIAERESIS +} + +bool LetterTable::isLetterUnicode( sal_Unicode c ) +{ + static CharClass* pCharClass = NULL; + if( pCharClass == NULL ) + pCharClass = new CharClass( Application::GetSettings().GetLocale() ); + String aStr( c ); + bool bRet = pCharClass->isLetter( aStr, 0 ); + return bRet; +} + +// Hilfsfunktion: Zeichen-Flag Testen +BOOL SimpleTokenizer_Impl::testCharFlags( sal_Unicode c, USHORT nTestFlags ) +{ + bool bRet = false; + if( c != 0 && c <= 255 ) + { + bRet = ( (aCharTypeTab[c] & nTestFlags) != 0 ); + } + else if( c > 255 ) + { + bRet = (( CHAR_START_IDENTIFIER | CHAR_IN_IDENTIFIER ) & nTestFlags) != 0 + ? BasicSimpleCharClass::isAlpha( c, true ) : false; + } + return bRet; +} + +void SimpleTokenizer_Impl::setKeyWords( const char** ppKeyWords, UINT16 nCount ) +{ + ppListKeyWords = ppKeyWords; + nKeyWordCount = nCount; +} + +// Neues Token holen +BOOL SimpleTokenizer_Impl::getNextToken( /*out*/TokenTypes& reType, + /*out*/const sal_Unicode*& rpStartPos, /*out*/const sal_Unicode*& rpEndPos ) +{ + reType = TT_UNKNOWN; + + // Position merken + rpStartPos = mpActualPos; + + // Zeichen untersuchen + sal_Unicode c = peekChar(); + if( c == CHAR_EOF ) + return FALSE; + + // Zeichen lesen + getChar(); + + //*** Alle Moeglichkeiten durchgehen *** + // Space? + if ( (testCharFlags( c, CHAR_SPACE ) == TRUE) ) + { + while( testCharFlags( peekChar(), CHAR_SPACE ) == TRUE ) + getChar(); + + reType = TT_WHITESPACE; + } + + // Identifier? + else if ( (testCharFlags( c, CHAR_START_IDENTIFIER ) == TRUE) ) + { + BOOL bIdentifierChar; + do + { + // Naechstes Zeichen holen + c = peekChar(); + bIdentifierChar = testCharFlags( c, CHAR_IN_IDENTIFIER ); + if( bIdentifierChar ) + getChar(); + } + while( bIdentifierChar ); + + reType = TT_IDENTIFIER; + + // Schluesselwort-Tabelle + if (ppListKeyWords != NULL) + { + int nCount = mpActualPos - rpStartPos; + + // No keyword if string contains char > 255 + bool bCanBeKeyword = true; + for( int i = 0 ; i < nCount ; i++ ) + { + if( rpStartPos[i] > 255 ) + { + bCanBeKeyword = false; + break; + } + } + + if( bCanBeKeyword ) + { + String aKWString(rpStartPos, sal::static_int_cast< xub_StrLen >(nCount) ); + ByteString aByteStr( aKWString, RTL_TEXTENCODING_ASCII_US ); + aByteStr.ToLowerAscii(); + if ( bsearch( aByteStr.GetBuffer(), ppListKeyWords, nKeyWordCount, sizeof( char* ), + compare_strings ) ) + { + reType = TT_KEYWORDS; + + if ( aByteStr.Equals( "rem" ) ) + { + // Alle Zeichen bis Zeilen-Ende oder EOF entfernen + sal_Unicode cPeek = peekChar(); + while( cPeek != CHAR_EOF && testCharFlags( cPeek, CHAR_EOL ) == FALSE ) + { + c = getChar(); + cPeek = peekChar(); + } + + reType = TT_COMMENT; + } + } + } + } + } + + // Operator? + // only for BASIC '\'' should be a comment, otherwise it is a normal string and handled there + else if ( ( testCharFlags( c, CHAR_OPERATOR ) == TRUE ) || ( (c == '\'') && (aLanguage==HIGHLIGHT_BASIC)) ) + { + // paramters for SQL view + if ( (c==':') || (c=='?')) + { + if (c!='?') + { + BOOL bIdentifierChar; + do + { + // Naechstes Zeichen holen + c = peekChar(); + bIdentifierChar = BasicSimpleCharClass::isAlpha( c, true ); + if( bIdentifierChar ) + getChar(); + } + while( bIdentifierChar ); + } + reType = TT_PARAMETER; + } + else if ((c=='-')) + { + sal_Unicode cPeekNext = peekChar(); + if (cPeekNext=='-') + { + // Alle Zeichen bis Zeilen-Ende oder EOF entfernen + while( cPeekNext != CHAR_EOF && testCharFlags( cPeekNext, CHAR_EOL ) == FALSE ) + { + getChar(); + cPeekNext = peekChar(); + } + reType = TT_COMMENT; + } + } + else if (c=='/') + { + sal_Unicode cPeekNext = peekChar(); + if (cPeekNext=='/') + { + // Alle Zeichen bis Zeilen-Ende oder EOF entfernen + while( cPeekNext != CHAR_EOF && testCharFlags( cPeekNext, CHAR_EOL ) == FALSE ) + { + getChar(); + cPeekNext = peekChar(); + } + reType = TT_COMMENT; + } + } + else + { + // Kommentar ? + if ( c == '\'' ) + { + c = getChar(); // '/' entfernen + + // Alle Zeichen bis Zeilen-Ende oder EOF entfernen + sal_Unicode cPeek = peekChar(); + while( cPeek != CHAR_EOF && testCharFlags( cPeek, CHAR_EOL ) == FALSE ) + { + getChar(); + cPeek = peekChar(); + } + + reType = TT_COMMENT; + } + + // Echter Operator, kann hier einfach behandelt werden, + // da nicht der wirkliche Operator, wie z.B. += interessiert, + // sondern nur die Tatsache, dass es sich um einen handelt. + if( reType != TT_COMMENT ) + { + reType = TT_OPERATOR; + } + + } + } + + // Objekt-Trenner? Muss vor Number abgehandelt werden + else if( c == '.' && ( peekChar() < '0' || peekChar() > '9' ) ) + { + reType = TT_OPERATOR; + } + + // Zahl? + else if( testCharFlags( c, CHAR_START_NUMBER ) == TRUE ) + { + reType = TT_NUMBER; + + // Zahlensystem, 10 = normal, wird bei Oct/Hex geaendert + int nRadix = 10; + + // Ist es eine Hex- oder Oct-Zahl? + if( c == '&' ) + { + // Octal? + if( peekChar() == 'o' || peekChar() == 'O' ) + { + // o entfernen + getChar(); + nRadix = 8; // Octal-Basis + + // Alle Ziffern einlesen + while( testCharFlags( peekChar(), CHAR_IN_OCT_NUMBER ) ) + c = getChar(); + } + // Hex? + else if( peekChar() == 'h' || peekChar() == 'H' ) + { + // x entfernen + getChar(); + nRadix = 16; // Hex-Basis + + // Alle Ziffern einlesen und puffern + while( testCharFlags( peekChar(), CHAR_IN_HEX_NUMBER ) ) + c = getChar(); + } + else + { + reType = TT_OPERATOR; + } + } + + // Wenn nicht Oct oder Hex als double ansehen + if( reType == TT_NUMBER && nRadix == 10 ) + { + // Flag, ob das letzte Zeichen ein Exponent war + BOOL bAfterExpChar = FALSE; + + // Alle Ziffern einlesen + while( testCharFlags( peekChar(), CHAR_IN_NUMBER ) || + (bAfterExpChar && peekChar() == '+' ) || + (bAfterExpChar && peekChar() == '-' ) ) + // Nach Exponent auch +/- OK + { + c = getChar(); // Zeichen lesen + bAfterExpChar = ( c == 'e' || c == 'E' ); + } + } + + // reType = TT_NUMBER; + } + + // String? + else if( testCharFlags( c, CHAR_START_STRING ) == TRUE ) + { + // Merken, welches Zeichen den String eroeffnet hat + sal_Unicode cEndString = c; + if( c == '[' ) + cEndString = ']'; + + // Alle Ziffern einlesen und puffern + while( peekChar() != cEndString ) + { + // #58846 EOF vor getChar() abfangen, damit EOF micht verloren geht + if( peekChar() == CHAR_EOF ) + { + // ERROR: unterminated string literal + reType = TT_ERROR; + break; + } + c = getChar(); + if( testCharFlags( c, CHAR_EOL ) == TRUE ) + { + // ERROR: unterminated string literal + reType = TT_ERROR; + break; + } + } + + // Zeichen lesen + if( reType != TT_ERROR ) + { + getChar(); + if( cEndString == ']' ) + reType = TT_IDENTIFIER; + else + reType = TT_STRING; + } + } + + // Zeilenende? + else if( testCharFlags( c, CHAR_EOL ) == TRUE ) + { + // Falls ein weiteres anderes EOL-Char folgt, weg damit + sal_Unicode cNext = peekChar(); + if( cNext != c && testCharFlags( cNext, CHAR_EOL ) == TRUE ) + getChar(); + + // Positions-Daten auf Zeilen-Beginn setzen + nCol = 0; + nLine++; + + reType = TT_EOL; + } + + // Alles andere bleibt TT_UNKNOWN + + + // End-Position eintragen + rpEndPos = mpActualPos; + return TRUE; +} + +String SimpleTokenizer_Impl::getTokStr + ( /*out*/const sal_Unicode* pStartPos, /*out*/const sal_Unicode* pEndPos ) +{ + return String( pStartPos, (USHORT)( pEndPos - pStartPos ) ); +} + +#ifdef DBG_UTIL +// TEST: Token ausgeben +String SimpleTokenizer_Impl::getFullTokenStr( /*out*/TokenTypes eType, + /*out*/const sal_Unicode* pStartPos, /*out*/const sal_Unicode* pEndPos ) +{ + String aOut; + switch( eType ) + { + case TT_UNKNOWN: aOut = String( RTL_CONSTASCII_USTRINGPARAM("TT_UNKNOWN:") ); break; + case TT_IDENTIFIER: aOut = String( RTL_CONSTASCII_USTRINGPARAM("TT_IDENTIFIER:") ); break; + case TT_WHITESPACE: aOut = String( RTL_CONSTASCII_USTRINGPARAM("TT_WHITESPACE:") ); break; + case TT_NUMBER: aOut = String( RTL_CONSTASCII_USTRINGPARAM("TT_NUMBER:") ); break; + case TT_STRING: aOut = String( RTL_CONSTASCII_USTRINGPARAM("TT_STRING:") ); break; + case TT_EOL: aOut = String( RTL_CONSTASCII_USTRINGPARAM("TT_EOL:") ); break; + case TT_COMMENT: aOut = String( RTL_CONSTASCII_USTRINGPARAM("TT_COMMENT:") ); break; + case TT_ERROR: aOut = String( RTL_CONSTASCII_USTRINGPARAM("TT_ERROR:") ); break; + case TT_OPERATOR: aOut = String( RTL_CONSTASCII_USTRINGPARAM("TT_OPERATOR:") ); break; + case TT_KEYWORDS: aOut = String( RTL_CONSTASCII_USTRINGPARAM("TT_KEYWORD:") ); break; + case TT_PARAMETER: aOut = String( RTL_CONSTASCII_USTRINGPARAM("TT_PARAMETER:") ); break; + } + if( eType != TT_EOL ) + { + aOut += String( pStartPos, (USHORT)( pEndPos - pStartPos ) ); + } + aOut += String( RTL_CONSTASCII_USTRINGPARAM("\n") ); + return aOut; +} +#endif + +SimpleTokenizer_Impl::SimpleTokenizer_Impl( HighlighterLanguage aLang ): aLanguage(aLang) +{ + memset( aCharTypeTab, 0, sizeof( aCharTypeTab ) ); + + // Zeichen-Tabelle fuellen + USHORT i; + + // Zulaessige Zeichen fuer Identifier + USHORT nHelpMask = (USHORT)( CHAR_START_IDENTIFIER | CHAR_IN_IDENTIFIER ); + for( i = 'a' ; i <= 'z' ; i++ ) + aCharTypeTab[i] |= nHelpMask; + for( i = 'A' ; i <= 'Z' ; i++ ) + aCharTypeTab[i] |= nHelpMask; + // '_' extra eintragen + aCharTypeTab[(int)'_'] |= nHelpMask; + // AB 23.6.97: '$' ist auch erlaubt + aCharTypeTab[(int)'$'] |= nHelpMask; + + // Ziffern (Identifier und Number ist moeglich) + nHelpMask = (USHORT)( CHAR_IN_IDENTIFIER | CHAR_START_NUMBER | + CHAR_IN_NUMBER | CHAR_IN_HEX_NUMBER ); + for( i = '0' ; i <= '9' ; i++ ) + aCharTypeTab[i] |= nHelpMask; + + // e und E sowie . von Hand ergaenzen + aCharTypeTab[(int)'e'] |= CHAR_IN_NUMBER; + aCharTypeTab[(int)'E'] |= CHAR_IN_NUMBER; + aCharTypeTab[(int)'.'] |= (USHORT)( CHAR_IN_NUMBER | CHAR_START_NUMBER ); + aCharTypeTab[(int)'&'] |= CHAR_START_NUMBER; + + // Hex-Ziffern + for( i = 'a' ; i <= 'f' ; i++ ) + aCharTypeTab[i] |= CHAR_IN_HEX_NUMBER; + for( i = 'A' ; i <= 'F' ; i++ ) + aCharTypeTab[i] |= CHAR_IN_HEX_NUMBER; + + // Oct-Ziffern + for( i = '0' ; i <= '7' ; i++ ) + aCharTypeTab[i] |= CHAR_IN_OCT_NUMBER; + + // String-Beginn/End-Zeichen + aCharTypeTab[(int)'\''] |= CHAR_START_STRING; + aCharTypeTab[(int)'\"'] |= CHAR_START_STRING; + aCharTypeTab[(int)'['] |= CHAR_START_STRING; + aCharTypeTab[(int)'`'] |= CHAR_START_STRING; + + // Operator-Zeichen + aCharTypeTab[(int)'!'] |= CHAR_OPERATOR; + aCharTypeTab[(int)'%'] |= CHAR_OPERATOR; + // aCharTypeTab[(int)'&'] |= CHAR_OPERATOR; Removed because of #i14140 + aCharTypeTab[(int)'('] |= CHAR_OPERATOR; + aCharTypeTab[(int)')'] |= CHAR_OPERATOR; + aCharTypeTab[(int)'*'] |= CHAR_OPERATOR; + aCharTypeTab[(int)'+'] |= CHAR_OPERATOR; + aCharTypeTab[(int)','] |= CHAR_OPERATOR; + aCharTypeTab[(int)'-'] |= CHAR_OPERATOR; + aCharTypeTab[(int)'/'] |= CHAR_OPERATOR; + aCharTypeTab[(int)':'] |= CHAR_OPERATOR; + aCharTypeTab[(int)'<'] |= CHAR_OPERATOR; + aCharTypeTab[(int)'='] |= CHAR_OPERATOR; + aCharTypeTab[(int)'>'] |= CHAR_OPERATOR; + aCharTypeTab[(int)'?'] |= CHAR_OPERATOR; + aCharTypeTab[(int)'^'] |= CHAR_OPERATOR; + aCharTypeTab[(int)'|'] |= CHAR_OPERATOR; + aCharTypeTab[(int)'~'] |= CHAR_OPERATOR; + aCharTypeTab[(int)'{'] |= CHAR_OPERATOR; + aCharTypeTab[(int)'}'] |= CHAR_OPERATOR; + // aCharTypeTab[(int)'['] |= CHAR_OPERATOR; Removed because of #i17826 + aCharTypeTab[(int)']'] |= CHAR_OPERATOR; + aCharTypeTab[(int)';'] |= CHAR_OPERATOR; + + // Space + aCharTypeTab[(int)' ' ] |= CHAR_SPACE; + aCharTypeTab[(int)'\t'] |= CHAR_SPACE; + + // Zeilen-Ende-Zeichen + aCharTypeTab[(int)'\r'] |= CHAR_EOL; + aCharTypeTab[(int)'\n'] |= CHAR_EOL; + + ppListKeyWords = NULL; +} + +SimpleTokenizer_Impl::~SimpleTokenizer_Impl( void ) +{ +} + +SimpleTokenizer_Impl* getSimpleTokenizer( void ) +{ + static SimpleTokenizer_Impl* pSimpleTokenizer = NULL; + if( !pSimpleTokenizer ) + pSimpleTokenizer = new SimpleTokenizer_Impl(); + return pSimpleTokenizer; +} + +// Heraussuchen der jeweils naechsten Funktion aus einem JavaScript-Modul +UINT16 SimpleTokenizer_Impl::parseLine( UINT32 nParseLine, const String* aSource ) +{ + // Position auf den Anfang des Source-Strings setzen + mpStringBegin = mpActualPos = aSource->GetBuffer(); + + // Zeile und Spalte initialisieren + nLine = nParseLine; + nCol = 0L; + + // Variablen fuer die Out-Parameter + TokenTypes eType; + const sal_Unicode* pStartPos; + const sal_Unicode* pEndPos; + + // Schleife ueber alle Tokens + UINT16 nTokenCount = 0; + while( getNextToken( eType, pStartPos, pEndPos ) ) + nTokenCount++; + + return nTokenCount; +} + +void SimpleTokenizer_Impl::getHighlightPortions( UINT32 nParseLine, const String& rLine, + /*out*/HighlightPortions& portions ) +{ + // Position auf den Anfang des Source-Strings setzen + mpStringBegin = mpActualPos = rLine.GetBuffer(); + + // Zeile und Spalte initialisieren + nLine = nParseLine; + nCol = 0L; + + // Variablen fuer die Out-Parameter + TokenTypes eType; + const sal_Unicode* pStartPos; + const sal_Unicode* pEndPos; + + // Schleife ueber alle Tokens + while( getNextToken( eType, pStartPos, pEndPos ) ) + { + HighlightPortion portion; + + portion.nBegin = (UINT16)(pStartPos - mpStringBegin); + portion.nEnd = (UINT16)(pEndPos - mpStringBegin); + portion.tokenType = eType; + + portions.Insert(portion, portions.Count()); + } +} + + +////////////////////////////////////////////////////////////////////////// +// Implementierung des SyntaxHighlighter + +SyntaxHighlighter::SyntaxHighlighter() +{ + m_pSimpleTokenizer = 0; + m_pKeyWords = NULL; + m_nKeyWordCount = 0; +} + +SyntaxHighlighter::~SyntaxHighlighter() +{ + delete m_pSimpleTokenizer; + delete m_pKeyWords; +} + +void SyntaxHighlighter::initialize( HighlighterLanguage eLanguage_ ) +{ + eLanguage = eLanguage_; + delete m_pSimpleTokenizer; + m_pSimpleTokenizer = new SimpleTokenizer_Impl(eLanguage); + + switch (eLanguage) + { + case HIGHLIGHT_BASIC: + m_pSimpleTokenizer->setKeyWords( strListBasicKeyWords, + sizeof( strListBasicKeyWords ) / sizeof( char* )); + break; + case HIGHLIGHT_SQL: + m_pSimpleTokenizer->setKeyWords( strListSqlKeyWords, + sizeof( strListSqlKeyWords ) / sizeof( char* )); + break; + default: + m_pSimpleTokenizer->setKeyWords( NULL, 0 ); + } +} + +const Range SyntaxHighlighter::notifyChange( UINT32 nLine, INT32 nLineCountDifference, + const String* pChangedLines, UINT32 nArrayLength) +{ + (void)nLineCountDifference; + + for( UINT32 i=0 ; i < nArrayLength ; i++ ) + m_pSimpleTokenizer->parseLine(nLine+i, &pChangedLines[i]); + + return Range( nLine, nLine + nArrayLength-1 ); +} + +void SyntaxHighlighter::getHighlightPortions( UINT32 nLine, const String& rLine, + /*out*/HighlightPortions& portions ) +{ + m_pSimpleTokenizer->getHighlightPortions( nLine, rLine, portions ); +} diff --git a/svtools/source/edit/textdat2.hxx b/svtools/source/edit/textdat2.hxx new file mode 100644 index 000000000000..222e8abee5a4 --- /dev/null +++ b/svtools/source/edit/textdat2.hxx @@ -0,0 +1,306 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +#ifndef _TEXTDAT2_HXX +#define _TEXTDAT2_HXX + +#include <svl/svarray.hxx> +#include <tools/list.hxx> +#include <vcl/seleng.hxx> +#include <vcl/virdev.hxx> +#include <vcl/cursor.hxx> + +class TextNode; +class TextView; + +#define PORTIONKIND_TEXT 0 +#define PORTIONKIND_TAB 1 + +#define DELMODE_SIMPLE 0 +#define DELMODE_RESTOFWORD 1 +#define DELMODE_RESTOFCONTENT 2 + +#define DEL_LEFT 1 +#define DEL_RIGHT 2 +#define TRAVEL_X_DONTKNOW 0xFFFF +#define MAXCHARSINPARA 0x3FFF-CHARPOSGROW + +#define LINE_SEP 0x0A + + +class TETextPortion +{ +private: + USHORT nLen; + long nWidth; + BYTE nKind; + BYTE nRightToLeft; + + TETextPortion() { nLen = 0; nKind = PORTIONKIND_TEXT; nWidth = -1; nRightToLeft = 0;} + +public: + TETextPortion( USHORT nL ) { + nLen = nL; + nKind = PORTIONKIND_TEXT; + nWidth= -1; + nRightToLeft = 0; + } + + USHORT GetLen() const { return nLen; } + USHORT& GetLen() { return nLen; } + + long GetWidth()const { return nWidth; } + long& GetWidth() { return nWidth; } + + BYTE GetKind() const { return nKind; } + BYTE& GetKind() { return nKind; } + + BYTE GetRightToLeft() const { return nRightToLeft; } + BYTE& GetRightToLeft() { return nRightToLeft; } + BOOL IsRightToLeft() const { return (nRightToLeft&1); } + + BOOL HasValidSize() const { return nWidth != (-1); } +}; + + + +typedef TETextPortion* TextPortionPtr; +SV_DECL_PTRARR( TextPortionArray, TextPortionPtr, 0, 8 ) + +class TETextPortionList : public TextPortionArray +{ +public: + TETextPortionList(); + ~TETextPortionList(); + + void Reset(); + USHORT FindPortion( USHORT nCharPos, USHORT& rPortionStart, BOOL bPreferStartingPortion = FALSE ); + USHORT GetPortionStartIndex( USHORT nPortion ); + void DeleteFromPortion( USHORT nDelFrom ); +}; + +struct TEWritingDirectionInfo +{ + BYTE nType; + USHORT nStartPos; + USHORT nEndPos; + TEWritingDirectionInfo( BYTE _Type, USHORT _Start, USHORT _End ) + { + nType = _Type; + nStartPos = _Start; + nEndPos = _End; + } +}; + +SV_DECL_VARARR( TEWritingDirectionInfos, TEWritingDirectionInfo, 0, 4 ) + +class TextLine +{ +private: + USHORT mnStart; + USHORT mnEnd; + USHORT mnStartPortion; + USHORT mnEndPortion; + + short mnStartX; + + BOOL mbInvalid; // fuer geschickte Formatierung/Ausgabe + +public: + TextLine() { + mnStart = mnEnd = 0; + mnStartPortion = mnEndPortion = 0; + mnStartX = 0; + mbInvalid = TRUE; + } + + BOOL IsIn( USHORT nIndex ) const + { return ( (nIndex >= mnStart ) && ( nIndex < mnEnd ) ); } + + BOOL IsIn( USHORT nIndex, BOOL bInclEnd ) const + { return ( ( nIndex >= mnStart ) && ( bInclEnd ? ( nIndex <= mnEnd ) : ( nIndex < mnEnd ) ) ); } + + void SetStart( USHORT n ) { mnStart = n; } + USHORT GetStart() const { return mnStart; } + USHORT& GetStart() { return mnStart; } + + void SetEnd( USHORT n ) { mnEnd = n; } + USHORT GetEnd() const { return mnEnd; } + USHORT& GetEnd() { return mnEnd; } + + void SetStartPortion( USHORT n ) { mnStartPortion = n; } + USHORT GetStartPortion() const { return mnStartPortion; } + USHORT& GetStartPortion() { return mnStartPortion; } + + void SetEndPortion( USHORT n ) { mnEndPortion = n; } + USHORT GetEndPortion() const { return mnEndPortion; } + USHORT& GetEndPortion() { return mnEndPortion; } + + USHORT GetLen() const { return mnEnd - mnStart; } + + BOOL IsInvalid() const { return mbInvalid; } + BOOL IsValid() const { return !mbInvalid; } + void SetInvalid() { mbInvalid = TRUE; } + void SetValid() { mbInvalid = FALSE; } + + BOOL IsEmpty() const { return (mnEnd > mnStart) ? FALSE : TRUE; } + + short GetStartX() const { return mnStartX; } + void SetStartX( short n ) { mnStartX = n; } + + inline BOOL operator == ( const TextLine& rLine ) const; + inline BOOL operator != ( const TextLine& rLine ) const; +}; + +typedef TextLine* TextLinePtr; + SV_DECL_PTRARR_DEL( TextLines, TextLinePtr, 1, 4 ) + +inline BOOL TextLine::operator == ( const TextLine& rLine ) const +{ + return ( ( mnStart == rLine.mnStart ) && + ( mnEnd == rLine.mnEnd ) && + ( mnStartPortion == rLine.mnStartPortion ) && + ( mnEndPortion == rLine.mnEndPortion ) ); +} + +inline BOOL TextLine::operator != ( const TextLine& rLine ) const +{ + return !( *this == rLine ); +} + + + +class TEParaPortion +{ +private: + TextNode* mpNode; + + TextLines maLines; + TETextPortionList maTextPortions; + TEWritingDirectionInfos maWritingDirectionInfos; + + + USHORT mnInvalidPosStart; + short mnInvalidDiff; + + BOOL mbInvalid; + BOOL mbSimple; // nur lineares Tippen + + + TEParaPortion( const TEParaPortion& ) {;} + +public: + TEParaPortion( TextNode* pNode ); + ~TEParaPortion(); + + + BOOL IsInvalid() const { return mbInvalid; } + BOOL IsSimpleInvalid() const { return mbSimple; } + void SetNotSimpleInvalid() { mbSimple = FALSE; } + void SetValid() { mbInvalid = FALSE; mbSimple = TRUE;} + + void MarkInvalid( USHORT nStart, short nDiff); + void MarkSelectionInvalid( USHORT nStart, USHORT nEnd ); + + USHORT GetInvalidPosStart() const { return mnInvalidPosStart; } + short GetInvalidDiff() const { return mnInvalidDiff; } + + TextNode* GetNode() const { return mpNode; } + TextLines& GetLines() { return maLines; } + TETextPortionList& GetTextPortions() { return maTextPortions; } + TEWritingDirectionInfos& GetWritingDirectionInfos() { return maWritingDirectionInfos; } + + + USHORT GetLineNumber( USHORT nIndex, BOOL bInclEnd ); + void CorrectValuesBehindLastFormattedLine( USHORT nLastFormattedLine ); +}; + + +class TEParaPortions : public ToolsList<TEParaPortion*> +{ +public: + TEParaPortions(); + ~TEParaPortions(); + void Reset(); +}; + + +class TextSelFunctionSet: public FunctionSet +{ +private: + TextView* mpView; + +public: + TextSelFunctionSet( TextView* pView ); + + virtual void BeginDrag(); + + virtual void CreateAnchor(); + + virtual BOOL SetCursorAtPoint( const Point& rPointPixel, BOOL bDontSelectAtCursor = FALSE ); + + virtual BOOL IsSelectionAtPoint( const Point& rPointPixel ); + virtual void DeselectAll(); + + virtual void DeselectAtPoint( const Point& ); + virtual void DestroyAnchor(); +}; + + +class IdleFormatter : public Timer +{ +private: + TextView* mpView; + USHORT mnRestarts; + +public: + IdleFormatter(); + ~IdleFormatter(); + + void DoIdleFormat( TextView* pV, USHORT nMaxRestarts ); + void ForceTimeout(); + TextView* GetView() { return mpView; } +}; + +struct TextDDInfo +{ + Cursor maCursor; + TextPaM maDropPos; + + BOOL mbStarterOfDD; + BOOL mbVisCursor; + + TextDDInfo() + { + maCursor.SetStyle( CURSOR_SHADOW ); + mbStarterOfDD = FALSE; + mbVisCursor = FALSE; + } +}; + +#endif // _TEXTDAT2_HXX diff --git a/svtools/source/edit/textdata.cxx b/svtools/source/edit/textdata.cxx new file mode 100644 index 000000000000..32bdfe40a3fb --- /dev/null +++ b/svtools/source/edit/textdata.cxx @@ -0,0 +1,361 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + +#include <svtools/textdata.hxx> +#include <textdat2.hxx> + +#include <tools/debug.hxx> + +SV_IMPL_PTRARR( TextLines, TextLinePtr ); +SV_IMPL_VARARR( TEWritingDirectionInfos, TEWritingDirectionInfo ); + + +// ------------------------------------------------------------------------- +// (+) class TextSelection +// ------------------------------------------------------------------------- + +TextSelection::TextSelection() +{ +} + +TextSelection::TextSelection( const TextPaM& rPaM ) : + maStartPaM( rPaM ), maEndPaM( rPaM ) +{ +} + +TextSelection::TextSelection( const TextPaM& rStart, const TextPaM& rEnd ) : + maStartPaM( rStart ), maEndPaM( rEnd ) +{ +} + +void TextSelection::Justify() +{ + if ( maEndPaM < maStartPaM ) + { + TextPaM aTemp( maStartPaM ); + maStartPaM = maEndPaM; + maEndPaM = aTemp; + } +} + + +// ------------------------------------------------------------------------- +// (+) class TETextPortionList +// ------------------------------------------------------------------------- +TETextPortionList::TETextPortionList() +{ +} + +TETextPortionList::~TETextPortionList() +{ + Reset(); +} + +void TETextPortionList::Reset() +{ + for ( USHORT nPortion = 0; nPortion < Count(); nPortion++ ) + delete GetObject( nPortion ); + Remove( 0, Count() ); +} + +void TETextPortionList::DeleteFromPortion( USHORT nDelFrom ) +{ + DBG_ASSERT( ( nDelFrom < Count() ) || ( (nDelFrom == 0) && (Count() == 0) ), "DeleteFromPortion: Out of range" ); + for ( USHORT nP = nDelFrom; nP < Count(); nP++ ) + delete GetObject( nP ); + Remove( nDelFrom, Count()-nDelFrom ); +} + +USHORT TETextPortionList::FindPortion( USHORT nCharPos, USHORT& nPortionStart, BOOL bPreferStartingPortion ) +{ + // Bei nCharPos an Portion-Grenze wird die linke Portion gefunden + USHORT nTmpPos = 0; + for ( USHORT nPortion = 0; nPortion < Count(); nPortion++ ) + { + TETextPortion* pPortion = GetObject( nPortion ); + nTmpPos = nTmpPos + pPortion->GetLen(); + if ( nTmpPos >= nCharPos ) + { + // take this one if we don't prefer the starting portion, or if it's the last one + if ( ( nTmpPos != nCharPos ) || !bPreferStartingPortion || ( nPortion == Count() - 1 ) ) + { + nPortionStart = nTmpPos - pPortion->GetLen(); + return nPortion; + } + } + } + DBG_ERROR( "FindPortion: Nicht gefunden!" ); + return ( Count() - 1 ); +} + +/* +USHORT TETextPortionList::GetPortionStartIndex( USHORT nPortion ) +{ + USHORT nPos = 0; + for ( USHORT nP = 0; nP < nPortion; nP++ ) + { + TETextPortion* pPortion = GetObject( nP ); + nPos += pPortion->GetLen(); + } + return nPos; +} +*/ + + +// ------------------------------------------------------------------------- +// (+) class TEParaPortion +// ------------------------------------------------------------------------- +TEParaPortion::TEParaPortion( TextNode* pN ) +{ + mpNode = pN; + mnInvalidPosStart = mnInvalidDiff = 0; + mbInvalid = TRUE; + mbSimple = FALSE; +} + +TEParaPortion::~TEParaPortion() +{ +} + +void TEParaPortion::MarkInvalid( USHORT nStart, short nDiff ) +{ + if ( mbInvalid == FALSE ) + { + mnInvalidPosStart = ( nDiff >= 0 ) ? nStart : ( nStart + nDiff ); + mnInvalidDiff = nDiff; + } + else + { + // Einfaches hintereinander tippen + if ( ( nDiff > 0 ) && ( mnInvalidDiff > 0 ) && + ( ( mnInvalidPosStart+mnInvalidDiff ) == nStart ) ) + { + mnInvalidDiff = mnInvalidDiff + nDiff; + } + // Einfaches hintereinander loeschen + else if ( ( nDiff < 0 ) && ( mnInvalidDiff < 0 ) && ( mnInvalidPosStart == nStart ) ) + { + mnInvalidPosStart = mnInvalidPosStart + nDiff; + mnInvalidDiff = mnInvalidDiff + nDiff; + } + else + { + DBG_ASSERT( ( nDiff >= 0 ) || ( (nStart+nDiff) >= 0 ), "MarkInvalid: Diff out of Range" ); + mnInvalidPosStart = Min( mnInvalidPosStart, (USHORT) ( (nDiff < 0) ? nStart+nDiff : nDiff ) ); + mnInvalidDiff = 0; + mbSimple = FALSE; + } + } + + maWritingDirectionInfos.Remove( 0, maWritingDirectionInfos.Count() ); + + mbInvalid = TRUE; +} + +void TEParaPortion::MarkSelectionInvalid( USHORT nStart, USHORT /*nEnd*/ ) +{ + if ( mbInvalid == FALSE ) + { + mnInvalidPosStart = nStart; +// nInvalidPosEnd = nEnd; + } + else + { + mnInvalidPosStart = Min( mnInvalidPosStart, nStart ); +// nInvalidPosEnd = pNode->Len(); + } + + maWritingDirectionInfos.Remove( 0, maWritingDirectionInfos.Count() ); + + mnInvalidDiff = 0; + mbInvalid = TRUE; + mbSimple = FALSE; +} + +USHORT TEParaPortion::GetLineNumber( USHORT nChar, BOOL bInclEnd ) +{ + for ( USHORT nLine = 0; nLine < maLines.Count(); nLine++ ) + { + TextLine* pLine = maLines.GetObject( nLine ); + if ( ( bInclEnd && ( pLine->GetEnd() >= nChar ) ) || + ( pLine->GetEnd() > nChar ) ) + { + return nLine; + } + } + + // Dann sollte es am Ende der letzten Zeile sein! + DBG_ASSERT( nChar == maLines[ maLines.Count() - 1 ]->GetEnd(), "Index voll daneben!" ); + DBG_ASSERT( !bInclEnd, "Zeile nicht gefunden: FindLine" ); + return ( maLines.Count() - 1 ); +} + + +void TEParaPortion::CorrectValuesBehindLastFormattedLine( USHORT nLastFormattedLine ) +{ + USHORT nLines = maLines.Count(); + DBG_ASSERT( nLines, "CorrectPortionNumbersFromLine: Leere Portion?" ); + if ( nLastFormattedLine < ( nLines - 1 ) ) + { + const TextLine* pLastFormatted = maLines[ nLastFormattedLine ]; + const TextLine* pUnformatted = maLines[ nLastFormattedLine+1 ]; + short nPortionDiff = pUnformatted->GetStartPortion() - pLastFormatted->GetEndPortion(); + short nTextDiff = pUnformatted->GetStart() - pLastFormatted->GetEnd(); + nTextDiff++; // LastFormatted->GetEnd() war incl. => 1 zuviel abgezogen! + + // Die erste unformatierte muss genau eine Portion hinter der letzten der + // formatierten beginnen: + // Wenn in der geaenderten Zeile eine Portion gesplittet wurde, + // kann nLastEnd > nNextStart sein! + short nPDiff = sal::static_int_cast< short >(-( nPortionDiff-1 )); + short nTDiff = sal::static_int_cast< short >(-( nTextDiff-1 )); + if ( nPDiff || nTDiff ) + { + for ( USHORT nL = nLastFormattedLine+1; nL < nLines; nL++ ) + { + TextLine* pLine = maLines[ nL ]; + + pLine->GetStartPortion() = pLine->GetStartPortion() + nPDiff; + pLine->GetEndPortion() = pLine->GetEndPortion() + nPDiff; + + pLine->GetStart() = pLine->GetStart() + nTDiff; + pLine->GetEnd() = pLine->GetEnd() + nTDiff; + + pLine->SetValid(); + } + } + } +} + +// ------------------------------------------------------------------------- +// (+) class TEParaPortions +// ------------------------------------------------------------------------- +TEParaPortions::TEParaPortions() +{ +} + +TEParaPortions::~TEParaPortions() +{ + Reset(); +} + +void TEParaPortions::Reset() +{ + TEParaPortions::iterator aIter( begin() ); + while ( aIter != end() ) + delete *aIter++; + clear(); +} + +// ------------------------------------------------------------------------- +// (+) class IdleFormatter +// ------------------------------------------------------------------------- +IdleFormatter::IdleFormatter() +{ + mpView = 0; + mnRestarts = 0; +} + +IdleFormatter::~IdleFormatter() +{ + mpView = 0; +} + +void IdleFormatter::DoIdleFormat( TextView* pV, USHORT nMaxRestarts ) +{ + mpView = pV; + + if ( IsActive() ) + mnRestarts++; + + if ( mnRestarts > nMaxRestarts ) + { + mnRestarts = 0; + ((Link&)GetTimeoutHdl()).Call( this ); + } + else + { + Start(); + } +} + +void IdleFormatter::ForceTimeout() +{ + if ( IsActive() ) + { + Stop(); + mnRestarts = 0; + ((Link&)GetTimeoutHdl()).Call( this ); + } +} + +TYPEINIT1( TextHint, SfxSimpleHint ); + +TextHint::TextHint( ULONG Id ) : SfxSimpleHint( Id ) +{ + mnValue = 0; +} + +TextHint::TextHint( ULONG Id, ULONG nValue ) : SfxSimpleHint( Id ) +{ + mnValue = nValue; +} + +TEIMEInfos::TEIMEInfos( const TextPaM& rPos, const String& rOldTextAfterStartPos ) +: aOldTextAfterStartPos( rOldTextAfterStartPos ) +{ + aPos = rPos; + nLen = 0; + bCursor = TRUE; + pAttribs = NULL; + bWasCursorOverwrite = FALSE; +} + +TEIMEInfos::~TEIMEInfos() +{ + delete[] pAttribs; +} + +void TEIMEInfos::CopyAttribs( const USHORT* pA, USHORT nL ) +{ + nLen = nL; + delete pAttribs; + pAttribs = new USHORT[ nL ]; + memcpy( pAttribs, pA, nL*sizeof(USHORT) ); +} + +void TEIMEInfos::DestroyAttribs() +{ + delete pAttribs; + pAttribs = NULL; + nLen = 0; +} + + diff --git a/svtools/source/edit/textdoc.cxx b/svtools/source/edit/textdoc.cxx new file mode 100644 index 000000000000..d4470904077e --- /dev/null +++ b/svtools/source/edit/textdoc.cxx @@ -0,0 +1,1047 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + +#include <textdoc.hxx> + +#include <stdlib.h> + +SV_IMPL_PTRARR( TextCharAttribs, TextCharAttribPtr ); + + + +// Vergleichmethode wird von QuickSort gerufen... + +EXTERN_C +#if defined( PM2 ) && (!defined( CSET ) && !defined ( MTW ) && !defined( WTC )) +int _stdcall +#else +#ifdef WNT +#if _MSC_VER >= 1200 +int __cdecl +#else +int _cdecl +#endif +#else +int +#endif +#endif + +CompareStart( const void* pFirst, const void* pSecond ) +{ + if ( (*((TextCharAttrib**)pFirst))->GetStart() < (*((TextCharAttrib**)pSecond))->GetStart() ) + return (-1); + else if ( (*((TextCharAttrib**)pFirst))->GetStart() > (*((TextCharAttrib**)pSecond))->GetStart() ) + return (1); + return 0; +} + + +// ------------------------------------------------------------------------- +// (+) class TextCharAttrib +// ------------------------------------------------------------------------- +TextCharAttrib::TextCharAttrib( const TextAttrib& rAttr, USHORT nStart, USHORT nEnd ) +{ + mpAttr = rAttr.Clone(); + mnStart = nStart, + mnEnd = nEnd; +} + +TextCharAttrib::TextCharAttrib( const TextCharAttrib& rTextCharAttrib ) +{ + mpAttr = rTextCharAttrib.GetAttr().Clone(); + mnStart = rTextCharAttrib.mnStart; + mnEnd = rTextCharAttrib.mnEnd; +} + +TextCharAttrib::~TextCharAttrib() +{ + delete mpAttr; +} + +// ------------------------------------------------------------------------- +// (+) class TextCharAttribList +// ------------------------------------------------------------------------- + +TextCharAttribList::TextCharAttribList() +{ + mbHasEmptyAttribs = FALSE; +} + +TextCharAttribList::~TextCharAttribList() +{ + // PTRARR_DEL +} + +void TextCharAttribList::Clear( BOOL bDestroyAttribs ) +{ + if ( bDestroyAttribs ) + TextCharAttribs::DeleteAndDestroy( 0, Count() ); + else + TextCharAttribs::Remove( 0, Count() ); +} + + +void TextCharAttribList::InsertAttrib( TextCharAttrib* pAttrib ) +{ + if ( pAttrib->IsEmpty() ) + mbHasEmptyAttribs = TRUE; + + const USHORT nCount = Count(); + const USHORT nStart = pAttrib->GetStart(); // vielleicht besser fuer Comp.Opt. + BOOL bInserted = FALSE; + for ( USHORT x = 0; x < nCount; x++ ) + { + TextCharAttrib* pCurAttrib = GetObject( x ); + if ( pCurAttrib->GetStart() > nStart ) + { + Insert( pAttrib, x ); + bInserted = TRUE; + break; + } + } + if ( !bInserted ) + Insert( pAttrib, nCount ); +} + +void TextCharAttribList::ResortAttribs() +{ + if ( Count() ) + qsort( (void*)GetData(), Count(), sizeof( TextCharAttrib* ), CompareStart ); +} + +TextCharAttrib* TextCharAttribList::FindAttrib( USHORT nWhich, USHORT nPos ) +{ + // Rueckwaerts, falls eins dort endet, das naechste startet. + // => Das startende gilt... + + for ( USHORT nAttr = Count(); nAttr; ) + { + TextCharAttrib* pAttr = GetObject( --nAttr ); + + if ( pAttr->GetEnd() < nPos ) + return 0; + + if ( ( pAttr->Which() == nWhich ) && pAttr->IsIn(nPos) ) + return pAttr; + } + return NULL; +} + +TextCharAttrib* TextCharAttribList::FindNextAttrib( USHORT nWhich, USHORT nFromPos, USHORT nMaxPos ) const +{ + DBG_ASSERT( nWhich, "FindNextAttrib: Which?" ); + const USHORT nAttribs = Count(); + for ( USHORT nAttr = 0; nAttr < nAttribs; nAttr++ ) + { + TextCharAttrib* pAttr = GetObject( nAttr ); + if ( ( pAttr->GetStart() >= nFromPos ) && + ( pAttr->GetEnd() <= nMaxPos ) && + ( pAttr->Which() == nWhich ) ) + return pAttr; + } + return NULL; +} + +BOOL TextCharAttribList::HasAttrib( USHORT nWhich ) const +{ + for ( USHORT nAttr = Count(); nAttr; ) + { + const TextCharAttrib* pAttr = GetObject( --nAttr ); + if ( pAttr->Which() == nWhich ) + return TRUE; + } + return FALSE; +} + +BOOL TextCharAttribList::HasBoundingAttrib( USHORT nBound ) +{ + // Rueckwaerts, falls eins dort endet, das naechste startet. + // => Das startende gilt... + for ( USHORT nAttr = Count(); nAttr; ) + { + TextCharAttrib* pAttr = GetObject( --nAttr ); + + if ( pAttr->GetEnd() < nBound ) + return FALSE; + + if ( ( pAttr->GetStart() == nBound ) || ( pAttr->GetEnd() == nBound ) ) + return TRUE; + } + return FALSE; +} + +TextCharAttrib* TextCharAttribList::FindEmptyAttrib( USHORT nWhich, USHORT nPos ) +{ + if ( !mbHasEmptyAttribs ) + return 0; + + const USHORT nAttribs = Count(); + for ( USHORT nAttr = 0; nAttr < nAttribs; nAttr++ ) + { + TextCharAttrib* pAttr = GetObject( nAttr ); + if ( pAttr->GetStart() > nPos ) + return 0; + + if ( ( pAttr->GetStart() == nPos ) && ( pAttr->GetEnd() == nPos ) && ( pAttr->Which() == nWhich ) ) + return pAttr; + } + return 0; +} + +void TextCharAttribList::DeleteEmptyAttribs() +{ + for ( USHORT nAttr = 0; nAttr < Count(); nAttr++ ) + { + TextCharAttrib* pAttr = GetObject( nAttr ); + if ( pAttr->IsEmpty() ) + { + Remove( nAttr ); + delete pAttr; + nAttr--; + } + } + mbHasEmptyAttribs = FALSE; +} + +#ifdef DBG_UTIL +BOOL TextCharAttribList::DbgCheckAttribs() +{ + BOOL bOK = TRUE; + for ( USHORT nAttr = 0; nAttr < Count(); nAttr++ ) + { + TextCharAttrib* pAttr = GetObject( nAttr ); + if ( pAttr->GetStart() > pAttr->GetEnd() ) + { + bOK = FALSE; + DBG_ERROR( "Attr verdreht" ); + } + } + return bOK; +} +#endif + +// ------------------------------------------------------------------------- +// (+) class TextNode +// ------------------------------------------------------------------------- + +TextNode::TextNode( const String& rText ) : + maText( rText ) +{ +} + +void TextNode::ExpandAttribs( USHORT nIndex, USHORT nNew ) +{ + if ( !nNew ) + return; + + BOOL bResort = FALSE; + USHORT nAttribs = maCharAttribs.Count(); + for ( USHORT nAttr = 0; nAttr < nAttribs; nAttr++ ) + { + TextCharAttrib* pAttrib = maCharAttribs.GetAttrib( nAttr ); + if ( pAttrib->GetEnd() >= nIndex ) + { + // Alle Attribute hinter der Einfuegeposition verschieben... + if ( pAttrib->GetStart() > nIndex ) + { + pAttrib->MoveForward( nNew ); + } + // 0: Leeres Attribut expandieren, wenn an Einfuegestelle + else if ( pAttrib->IsEmpty() ) + { + // Index nicht pruefen, leeres durfte nur dort liegen. + // Wenn spaeter doch Ueberpruefung: + // Spezialfall: Start == 0; AbsLen == 1, nNew = 1 => Expand, weil durch Absatzumbruch! + // Start <= nIndex, End >= nIndex => Start=End=nIndex! +// if ( pAttrib->GetStart() == nIndex ) + pAttrib->Expand( nNew ); + } + // 1: Attribut startet davor, geht bis Index... + else if ( pAttrib->GetEnd() == nIndex ) // Start muss davor liegen + { + // Nur expandieren, wenn kein Feature, + // und wenn nicht in ExcludeListe! + // Sonst geht z.B. ein UL bis zum neuen ULDB, beide expandieren + if ( !maCharAttribs.FindEmptyAttrib( pAttrib->Which(), nIndex ) ) + { + pAttrib->Expand( nNew ); + } + else + bResort = TRUE; + } + // 2: Attribut startet davor, geht hinter Index... + else if ( ( pAttrib->GetStart() < nIndex ) && ( pAttrib->GetEnd() > nIndex ) ) + { + pAttrib->Expand( nNew ); + } + // 3: Attribut startet auf Index... + else if ( pAttrib->GetStart() == nIndex ) + { + if ( nIndex == 0 ) + { + pAttrib->Expand( nNew ); +// bResort = TRUE; // es gibt ja keine Features mehr... + } + else + pAttrib->MoveForward( nNew ); + } + } + + DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Expand: Attribut verdreht!" ); + DBG_ASSERT( ( pAttrib->GetEnd() <= maText.Len() ), "Expand: Attrib groesser als Absatz!" ); + DBG_ASSERT( !pAttrib->IsEmpty(), "Leeres Attribut nach ExpandAttribs?" ); + } + + if ( bResort ) + maCharAttribs.ResortAttribs(); + +#ifdef EDITDEBUG + DBG_ASSERT( CheckOrderedList( (TextCharAttribs*)&maCharAttribs ), "Expand: Start-Liste verdreht" ); +#endif +} + +void TextNode::CollapsAttribs( USHORT nIndex, USHORT nDeleted ) +{ + if ( !nDeleted ) + return; + + BOOL bResort = FALSE; + USHORT nEndChanges = nIndex+nDeleted; + + for ( USHORT nAttr = 0; nAttr < maCharAttribs.Count(); nAttr++ ) + { + TextCharAttrib* pAttrib = maCharAttribs.GetAttrib( nAttr ); + BOOL bDelAttr = FALSE; + if ( pAttrib->GetEnd() >= nIndex ) + { + // Alles Attribute hinter der Einfuegeposition verschieben... + if ( pAttrib->GetStart() >= nEndChanges ) + { + pAttrib->MoveBackward( nDeleted ); + } + // 1. Innenliegende Attribute loeschen... + else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() <= nEndChanges ) ) + { + // Spezialfall: Attrubt deckt genau den Bereich ab + // => als leeres Attribut behalten. + if ( ( pAttrib->GetStart() == nIndex ) && ( pAttrib->GetEnd() == nEndChanges ) ) + pAttrib->GetEnd() = nIndex; // leer + else + bDelAttr = TRUE; + } + // 2. Attribut beginnt davor, endet drinnen oder dahinter... + else if ( ( pAttrib->GetStart() <= nIndex ) && ( pAttrib->GetEnd() > nIndex ) ) + { + if ( pAttrib->GetEnd() <= nEndChanges ) // endet drinnen + pAttrib->GetEnd() = nIndex; + else + pAttrib->Collaps( nDeleted ); // endet dahinter + } + // 3. Attribut beginnt drinnen, endet dahinter... + else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() > nEndChanges ) ) + { + // Features duerfen nicht expandieren! + pAttrib->GetStart() = nEndChanges; + pAttrib->MoveBackward( nDeleted ); + } + } + + DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Collaps: Attribut verdreht!" ); + DBG_ASSERT( ( pAttrib->GetEnd() <= maText.Len()) || bDelAttr, "Collaps: Attrib groesser als Absatz!" ); + if ( bDelAttr /* || pAttrib->IsEmpty() */ ) + { + bResort = TRUE; + maCharAttribs.RemoveAttrib( nAttr ); + delete pAttrib; + nAttr--; + } + else if ( pAttrib->IsEmpty() ) + maCharAttribs.HasEmptyAttribs() = TRUE; + } + + if ( bResort ) + maCharAttribs.ResortAttribs(); + +#ifdef EDITDEBUG + DBG_ASSERT( CheckOrderedList( (TextCharAttribs)&maCharAttribs ), "Collaps: Start-Liste verdreht" ); +#endif +} + +void TextNode::InsertText( USHORT nPos, const String& rText ) +{ + maText.Insert( rText, nPos ); + ExpandAttribs( nPos, rText.Len() ); +} + +void TextNode::InsertText( USHORT nPos, sal_Unicode c ) +{ + maText.Insert( c, nPos ); + ExpandAttribs( nPos, 1 ); +} + +void TextNode::RemoveText( USHORT nPos, USHORT nChars ) +{ + maText.Erase( nPos, nChars ); + CollapsAttribs( nPos, nChars ); +} + +TextNode* TextNode::Split( USHORT nPos, BOOL bKeepEndingAttribs ) +{ + String aNewText; + if ( nPos < maText.Len() ) + { + aNewText = maText.Copy( nPos ); + maText.Erase( nPos ); + } + TextNode* pNew = new TextNode( aNewText ); + + for ( USHORT nAttr = 0; nAttr < maCharAttribs.Count(); nAttr++ ) + { + TextCharAttrib* pAttrib = maCharAttribs.GetAttrib( nAttr ); + if ( pAttrib->GetEnd() < nPos ) + { + // bleiben unveraendert.... + ; + } + else if ( pAttrib->GetEnd() == nPos ) + { + // muessen als leeres Attribut kopiert werden. + // !FindAttrib nur sinnvoll, wenn Rueckwaerts durch Liste! + if ( bKeepEndingAttribs && !pNew->maCharAttribs.FindAttrib( pAttrib->Which(), 0 ) ) + { + TextCharAttrib* pNewAttrib = new TextCharAttrib( *pAttrib ); + pNewAttrib->GetStart() = 0; + pNewAttrib->GetEnd() = 0; + pNew->maCharAttribs.InsertAttrib( pNewAttrib ); + } + } + else if ( pAttrib->IsInside( nPos ) || ( !nPos && !pAttrib->GetStart() ) ) + { + // Wenn ganz vorne gecuttet wird, muss das Attribut erhalten bleiben! + // muessen kopiert und geaendert werden + TextCharAttrib* pNewAttrib = new TextCharAttrib( *pAttrib ); + pNewAttrib->GetStart() = 0; + pNewAttrib->GetEnd() = pAttrib->GetEnd()-nPos; + pNew->maCharAttribs.InsertAttrib( pNewAttrib ); + // stutzen: + pAttrib->GetEnd() = nPos; + } + else + { + DBG_ASSERT( pAttrib->GetStart() >= nPos, "Start < nPos!" ); + DBG_ASSERT( pAttrib->GetEnd() >= nPos, "End < nPos!" ); + // alle dahinter verschieben in den neuen Node (this) + maCharAttribs.RemoveAttrib( nAttr ); + pNew->maCharAttribs.InsertAttrib( pAttrib ); + pAttrib->GetStart() = pAttrib->GetStart() - nPos; + pAttrib->GetEnd() = pAttrib->GetEnd() - nPos; + nAttr--; + } + } + return pNew; +} + +void TextNode::Append( const TextNode& rNode ) +{ + USHORT nOldLen = maText.Len(); + + maText += rNode.GetText(); + +#ifdef EDITDEBUG + DBG_ASSERT( maCharAttribs.DbgCheckAttribs(), "Attribute VOR AppendAttribs kaputt" ); +#endif + + const USHORT nAttribs = rNode.GetCharAttribs().Count(); + for ( USHORT nAttr = 0; nAttr < nAttribs; nAttr++ ) + { + TextCharAttrib* pAttrib = rNode.GetCharAttribs().GetAttrib( nAttr ); + BOOL bMelted = FALSE; + if ( pAttrib->GetStart() == 0 ) + { + // Evtl koennen Attribute zusammengefasst werden: + USHORT nTmpAttribs = maCharAttribs.Count(); + for ( USHORT nTmpAttr = 0; nTmpAttr < nTmpAttribs; nTmpAttr++ ) + { + TextCharAttrib* pTmpAttrib = maCharAttribs.GetAttrib( nTmpAttr ); + + if ( pTmpAttrib->GetEnd() == nOldLen ) + { + if ( ( pTmpAttrib->Which() == pAttrib->Which() ) && + ( pTmpAttrib->GetAttr() == pAttrib->GetAttr() ) ) + { + pTmpAttrib->GetEnd() = + pTmpAttrib->GetEnd() + pAttrib->GetLen(); + bMelted = TRUE; + break; // es kann nur eins von der Sorte an der Stelle geben + } + } + } + } + + if ( !bMelted ) + { + TextCharAttrib* pNewAttrib = new TextCharAttrib( *pAttrib ); + pNewAttrib->GetStart() = pNewAttrib->GetStart() + nOldLen; + pNewAttrib->GetEnd() = pNewAttrib->GetEnd() + nOldLen; + maCharAttribs.InsertAttrib( pNewAttrib ); + } + } + +#ifdef EDITDEBUG + DBG_ASSERT( maCharAttribs.DbgCheckAttribs(), "Attribute NACH AppendAttribs kaputt" ); +#endif +} + +// ------------------------------------------------------------------------- +// (+) class TextDoc +// ------------------------------------------------------------------------- + +TextDoc::TextDoc() +{ + mnLeftMargin = 0; +}; + +TextDoc::~TextDoc() +{ + DestroyTextNodes(); +} + +void TextDoc::Clear() +{ + DestroyTextNodes(); +} + +void TextDoc::DestroyTextNodes() +{ + for ( ULONG nNode = 0; nNode < maTextNodes.Count(); nNode++ ) + delete maTextNodes.GetObject( nNode ); + maTextNodes.clear(); +} + +String TextDoc::GetText( const sal_Unicode* pSep ) const +{ + ULONG nLen = GetTextLen( pSep ); + ULONG nNodes = maTextNodes.Count(); + + if ( nLen > STRING_MAXLEN ) + { + DBG_ERROR( "Text zu gross fuer String" ); + return String(); + } + + String aASCIIText; + ULONG nLastNode = nNodes-1; + for ( ULONG nNode = 0; nNode < nNodes; nNode++ ) + { + TextNode* pNode = maTextNodes.GetObject( nNode ); + String aTmp( pNode->GetText() ); + aASCIIText += aTmp; + if ( pSep && ( nNode != nLastNode ) ) + aASCIIText += pSep; + } + + return aASCIIText; +} + +XubString TextDoc::GetText( ULONG nPara ) const +{ + XubString aText; + TextNode* pNode = ( nPara < maTextNodes.Count() ) ? maTextNodes.GetObject( nPara ) : 0; + if ( pNode ) + aText = pNode->GetText(); + + return aText; +} + + +ULONG TextDoc::GetTextLen( const xub_Unicode* pSep, const TextSelection* pSel ) const +{ + ULONG nLen = 0; + ULONG nNodes = maTextNodes.Count(); + if ( nNodes ) + { + ULONG nStartNode = 0; + ULONG nEndNode = nNodes-1; + if ( pSel ) + { + nStartNode = pSel->GetStart().GetPara(); + nEndNode = pSel->GetEnd().GetPara(); + } + + for ( ULONG nNode = nStartNode; nNode <= nEndNode; nNode++ ) + { + TextNode* pNode = maTextNodes.GetObject( nNode ); + + USHORT nS = 0; + ULONG nE = pNode->GetText().Len(); + if ( pSel && ( nNode == pSel->GetStart().GetPara() ) ) + nS = pSel->GetStart().GetIndex(); + if ( pSel && ( nNode == pSel->GetEnd().GetPara() ) ) + nE = pSel->GetEnd().GetIndex(); + + nLen += ( nE - nS ); + } + + if ( pSep ) + nLen += (nEndNode-nStartNode) * String( pSep ).Len(); + } + + return nLen; +} + +TextPaM TextDoc::InsertText( const TextPaM& rPaM, xub_Unicode c ) +{ + DBG_ASSERT( c != 0x0A, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" ); + DBG_ASSERT( c != 0x0D, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" ); + + TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() ); + pNode->InsertText( rPaM.GetIndex(), c ); + + TextPaM aPaM( rPaM.GetPara(), rPaM.GetIndex()+1 ); + return aPaM; +} + +TextPaM TextDoc::InsertText( const TextPaM& rPaM, const XubString& rStr ) +{ + DBG_ASSERT( rStr.Search( 0x0A ) == STRING_NOTFOUND, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" ); + DBG_ASSERT( rStr.Search( 0x0D ) == STRING_NOTFOUND, "TextDoc::InsertText: Zeilentrenner in Absatz nicht erlaubt!" ); + + TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() ); + pNode->InsertText( rPaM.GetIndex(), rStr ); + + TextPaM aPaM( rPaM.GetPara(), rPaM.GetIndex()+rStr.Len() ); + return aPaM; +} + +TextPaM TextDoc::InsertParaBreak( const TextPaM& rPaM, BOOL bKeepEndingAttribs ) +{ + TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() ); + TextNode* pNew = pNode->Split( rPaM.GetIndex(), bKeepEndingAttribs ); + + maTextNodes.Insert( pNew, rPaM.GetPara()+1 ); + + TextPaM aPaM( rPaM.GetPara()+1, 0 ); + return aPaM; +} + +TextPaM TextDoc::ConnectParagraphs( TextNode* pLeft, TextNode* pRight ) +{ + USHORT nPrevLen = pLeft->GetText().Len(); + pLeft->Append( *pRight ); + + // der rechte verschwindet. + ULONG nRight = maTextNodes.GetPos( pRight ); + maTextNodes.Remove( nRight ); + delete pRight; + + ULONG nLeft = maTextNodes.GetPos( pLeft ); + TextPaM aPaM( nLeft, nPrevLen ); + return aPaM; +} + +TextPaM TextDoc::RemoveChars( const TextPaM& rPaM, USHORT nChars ) +{ + TextNode* pNode = maTextNodes.GetObject( rPaM.GetPara() ); + pNode->RemoveText( rPaM.GetIndex(), nChars ); + + return rPaM; +} + +BOOL TextDoc::IsValidPaM( const TextPaM& rPaM ) +{ + if ( rPaM.GetPara() >= maTextNodes.Count() ) + { + DBG_ERROR( "PaM: Para out of range" ); + return FALSE; + } + TextNode * pNode = maTextNodes.GetObject( rPaM.GetPara() ); + if ( rPaM.GetIndex() > pNode->GetText().Len() ) + { + DBG_ERROR( "PaM: Index out of range" ); + return FALSE; + } + return TRUE; +} + +/* + +void TextDoc::InsertAttribInSelection( TextNode* pNode, USHORT nStart, USHORT nEnd, const SfxPoolItem& rPoolItem ) +{ + DBG_ASSERT( pNode, "Wohin mit dem Attribut?" ); + DBG_ASSERT( nEnd <= pNode->Len(), "InsertAttrib: Attribut zu gross!" ); + + // fuer Optimierung: + // dieses endet am Anfang der Selektion => kann erweitert werden + TextCharAttrib* pEndingAttrib = 0; + // dieses startet am Ende der Selektion => kann erweitert werden + TextCharAttrib* pStartingAttrib = 0; + + DBG_ASSERT( nStart <= nEnd, "Kleiner Rechenfehler in InsertAttribInSelection" ); + + RemoveAttribs( pNode, nStart, nEnd, pStartingAttrib, pEndingAttrib, rPoolItem.Which() ); + + if ( pStartingAttrib && pEndingAttrib && + ( *(pStartingAttrib->GetItem()) == rPoolItem ) && + ( *(pEndingAttrib->GetItem()) == rPoolItem ) ) + { + // wird ein groesses Attribut. + pEndingAttrib->GetEnd() = pStartingAttrib->GetEnd(); + pCurPool->Remove( *(pStartingAttrib->GetItem()) ); + pNode->GetCharAttribs().GetAttribs().Remove( pNode->GetCharAttribs().GetAttribs().GetPos( pStartingAttrib ) ); + delete pStartingAttrib; + } + else if ( pStartingAttrib && ( *(pStartingAttrib->GetItem()) == rPoolItem ) ) + pStartingAttrib->GetStart() = nStart; + else if ( pEndingAttrib && ( *(pEndingAttrib->GetItem()) == rPoolItem ) ) + pEndingAttrib->GetEnd() = nEnd; + else + InsertAttrib( rPoolItem, pNode, nStart, nEnd ); + + if ( pStartingAttrib ) + pNode->GetCharAttribs().ResortAttribs(); +} + +BOOL TextDoc::RemoveAttribs( TextNode* pNode, USHORT nStart, USHORT nEnd, USHORT nWhich ) +{ + TextCharAttrib* pStarting; + TextCharAttrib* pEnding; + return RemoveAttribs( pNode, nStart, nEnd, pStarting, pEnding, nWhich ); +} + +BOOL TextDoc::RemoveAttribs( TextNode* pNode, USHORT nStart, USHORT nEnd, TextCharAttrib*& rpStarting, TextCharAttrib*& rpEnding, USHORT nWhich ) +{ + DBG_ASSERT( pNode, "Wohin mit dem Attribut?" ); + DBG_ASSERT( nEnd <= pNode->Len(), "InsertAttrib: Attribut zu gross!" ); + + // dieses endet am Anfang der Selektion => kann erweitert werden + rpEnding = 0; + // dieses startet am Ende der Selektion => kann erweitert werden + rpStarting = 0; + + BOOL bChanged = FALSE; + + DBG_ASSERT( nStart <= nEnd, "Kleiner Rechenfehler in InsertAttribInSelection" ); + + // ueber die Attribute iterieren... + USHORT nAttr = 0; + TextCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr ); + while ( pAttr ) + { + BOOL bRemoveAttrib = FALSE; + if ( !nWhich || ( pAttr->Which() == nWhich ) ) + { + // Attribut beginnt in Selection + if ( ( pAttr->GetStart() >= nStart ) && ( pAttr->GetStart() <= nEnd ) ) + { + bChanged = TRUE; + if ( pAttr->GetEnd() > nEnd ) + { + pAttr->GetStart() = nEnd; // dann faengt es dahinter an + rpStarting = pAttr; + break; // es kann kein weiteres Attrib hier liegen + } + else if ( !pAttr->IsFeature() || ( pAttr->GetStart() == nStart ) ) + { + // Feature nur loeschen, wenn genau an der Stelle + bRemoveAttrib = TRUE; + } + } + + // Attribut endet in Selection + else if ( ( pAttr->GetEnd() >= nStart ) && ( pAttr->GetEnd() <= nEnd ) ) + { + bChanged = TRUE; + if ( ( pAttr->GetStart() < nStart ) && !pAttr->IsFeature() ) + { + pAttr->GetEnd() = nStart; // dann hoert es hier auf + rpEnding = pAttr; + } + else if ( !pAttr->IsFeature() || ( pAttr->GetStart() == nStart ) ) + { + // Feature nur loeschen, wenn genau an der Stelle + bRemoveAttrib = TRUE; + } + } + // Attribut ueberlappt die Selektion + else if ( ( pAttr->GetStart() <= nStart ) && ( pAttr->GetEnd() >= nEnd ) ) + { + bChanged = TRUE; + if ( pAttr->GetStart() == nStart ) + { + pAttr->GetStart() = nEnd; + rpStarting = pAttr; + break; // es kann weitere Attribute geben! + } + else if ( pAttr->GetEnd() == nEnd ) + { + pAttr->GetEnd() = nStart; + rpEnding = pAttr; + break; // es kann weitere Attribute geben! + } + else // Attribut muss gesplittet werden... + { + USHORT nOldEnd = pAttr->GetEnd(); + pAttr->GetEnd() = nStart; + rpEnding = pAttr; +// ULONG nSavePos = pNode->GetCharAttribs().GetStartList().GetCurPos(); + InsertAttrib( *pAttr->GetItem(), pNode, nEnd, nOldEnd ); +// pNode->GetCharAttribs().GetStartList().Seek( nSavePos ); + break; // es kann weitere Attribute geben! + } + } + } + if ( bRemoveAttrib ) + { + DBG_ASSERT( ( pAttr != rpStarting ) && ( pAttr != rpEnding ), "Loeschen und behalten des gleichen Attributs ?" ); + pNode->GetCharAttribs().GetAttribs().Remove(nAttr); + pCurPool->Remove( *pAttr->GetItem() ); + delete pAttr; + nAttr--; + } + nAttr++; + pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr ); + } + return bChanged; +} + +#pragma SEG_FUNCDEF(editdoc_3f) + +void TextDoc::InsertAttrib( const SfxPoolItem& rPoolItem, TextNode* pNode, USHORT nStart, USHORT nEnd ) +{ + // Diese Methode prueft nicht mehr, ob ein entspr. Attribut + // schon an der Stelle existiert! + + // pruefen, ob neues Attrib oder einfach nur Ende eines Attribs... +// const SfxPoolItem& rDefItem = pNode->GetContentAttribs().GetItem( rPoolItem.Which() ); +// BOOL bCreateAttrib = ( rDefItem != rPoolItem ); + + // Durch den Verlust der Exclude-Liste geht es nicht mehr, dass ich + // kein neues Attribut benoetige und nur das alte nicht expandiere... +// if ( !bCreateAttrib ) + { + // => Wenn schon Default-Item, dann wenigstens nur dann einstellen, + // wenn davor wirklich ein entsprechendes Attribut. +// if ( pNode->GetCharAttribs().FindAttrib( rPoolItem.Which(), nStart ) ) +// bCreateAttrib = TRUE; + // Aber kleiner Trost: + // Die wenigsten schreiben, aendern das Attr, schreiben, und + // stellen dann wieder das Default-Attr ein. + } + + // 22.9.95: + // Die Uberlegung, einfach das andere Attribut nicht zu expandieren, war + // sowieso falsch, da das DefAttr aus einer Vorlage kommen kann, + // die irgendwann verschwindet! +// if ( bCreateAttrib ) +// { + TextCharAttrib* pAttrib = MakeCharAttrib( *pCurPool, rPoolItem, nStart, nEnd ); + DBG_ASSERT( pAttrib, "MakeCharAttrib fehlgeschlagen!" ); + pNode->GetCharAttribs().InsertAttrib( pAttrib ); +// } +// else +// { +// TextCharAttrib* pTmpAttrib = +// pNode->GetCharAttribs().FindAnyAttrib( rPoolItem.Which() ); +// if ( pTmpAttrib ) // sonst benoetige ich es sowieso nicht.... +// { +// aExcludeList.Insert( pTmpAttrib->GetItem() ); +// } +// } +} + +#pragma SEG_FUNCDEF(editdoc_40) + +void TextDoc::InsertAttrib( TextNode* pNode, USHORT nStart, USHORT nEnd, const SfxPoolItem& rPoolItem ) +{ + if ( nStart != nEnd ) + { + InsertAttribInSelection( pNode, nStart, nEnd, rPoolItem ); + } + else + { + // Pruefen, ob schon ein neues Attribut mit der WhichId an der Stelle: + TextCharAttrib* pAttr = pNode->GetCharAttribs().FindEmptyAttrib( rPoolItem.Which(), nStart ); + if ( pAttr ) + { + // Attribut entfernen.... + pNode->GetCharAttribs().GetAttribs().Remove( + pNode->GetCharAttribs().GetAttribs().GetPos( pAttr ) ); + } + + // pruefen, ob ein 'gleiches' Attribut an der Stelle liegt. + pAttr = pNode->GetCharAttribs().FindAttrib( rPoolItem.Which(), nStart ); + if ( pAttr ) + { + if ( pAttr->IsInside( nStart ) ) // splitten + { + // ??????????????????????????????? + // eigentlich noch pruefen, ob wirklich splittet, oder return ! + // ??????????????????????????????? + USHORT nOldEnd = pAttr->GetEnd(); + pAttr->GetEnd() = nStart; + pAttr = MakeCharAttrib( *pCurPool, *(pAttr->GetItem()), nStart, nOldEnd ); + pNode->GetCharAttribs().InsertAttrib( pAttr ); + } + else if ( pAttr->GetEnd() == nStart ) + { + DBG_ASSERT( !pAttr->IsEmpty(), "Doch noch ein leeres Attribut?" ); + // pruefen, ob genau das gleiche Attribut + if ( *(pAttr->GetItem()) == rPoolItem ) + return; + } + } + InsertAttrib( rPoolItem, pNode, nStart, nStart ); + } +} + +#pragma SEG_FUNCDEF(editdoc_41) + +void TextDoc::FindAttribs( TextNode* pNode, USHORT nStartPos, USHORT nEndPos, SfxItemSet& rCurSet ) +{ + DBG_ASSERT( pNode, "Wo soll ich suchen ?" ); + DBG_ASSERT( nStartPos <= nEndPos, "Ungueltiger Bereich!" ); + + USHORT nAttr = 0; + TextCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr ); + // keine Selection... + if ( nStartPos == nEndPos ) + { + while ( pAttr && ( pAttr->GetStart() <= nEndPos) ) + { + const SfxPoolItem* pItem = 0; + // Attribut liegt dadrueber... + if ( ( pAttr->GetStart() < nStartPos ) && ( pAttr->GetEnd() > nStartPos ) ) + pItem = pAttr->GetItem(); + // Attribut endet hier, ist nicht leer + else if ( ( pAttr->GetStart() < nStartPos ) && ( pAttr->GetEnd() == nStartPos ) ) + { + if ( !pNode->GetCharAttribs().FindEmptyAttrib( pAttr->GetItem()->Which(), nStartPos ) ) + pItem = pAttr->GetItem(); + } + // Attribut endet hier, ist leer + else if ( ( pAttr->GetStart() == nStartPos ) && ( pAttr->GetEnd() == nStartPos ) ) + { +// if ( aExcludeList.FindAttrib( pAttr->GetItem()->Which() ) ) + pItem = pAttr->GetItem(); +// else if ( pNode->Len() == 0 ) // Sonderfall +// pItem = pAttr->GetItem(); + } + // Attribut beginnt hier + else if ( ( pAttr->GetStart() == nStartPos ) && ( pAttr->GetEnd() > nStartPos ) ) + { + if ( nStartPos == 0 ) // Sonderfall + pItem = pAttr->GetItem(); + } + + if ( pItem ) + { + USHORT nWhich = pItem->Which(); + if ( rCurSet.GetItemState( nWhich ) == SFX_ITEM_OFF ) + { + rCurSet.Put( *pItem ); + } + else if ( rCurSet.GetItemState( nWhich ) == SFX_ITEM_ON ) + { + const SfxPoolItem& rItem = rCurSet.Get( nWhich ); + if ( rItem != *pItem ) + { + rCurSet.InvalidateItem( nWhich ); + } + } + } + nAttr++; + pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr ); + } + } + else // Selektion + { + while ( pAttr && ( pAttr->GetStart() < nEndPos) ) + { + const SfxPoolItem* pItem = 0; + // Attribut liegt dadrueber... + if ( ( pAttr->GetStart() <= nStartPos ) && ( pAttr->GetEnd() >= nEndPos ) ) + pItem = pAttr->GetItem(); + // Attribut startet mitten drin... + else if ( pAttr->GetStart() >= nStartPos ) + { + // !!! pItem = pAttr->GetItem(); + // einfach nur pItem reicht nicht, da ich z.B. bei Shadow + // niemals ein ungleiches Item finden wuerde, da ein solche + // seine Anwesenheit durch Abwesenheit repraesentiert! + // if ( ... ) + // Es muesste geprueft werden, on genau das gleiche Attribut + // an der Bruchstelle aufsetzt, was recht aufwendig ist. + // Da ich beim Einfuegen von Attributen aber etwas optimiere + // tritt der Fall nicht so schnell auf... + // Also aus Geschwindigkeitsgruenden: + rCurSet.InvalidateItem( pAttr->GetItem()->Which() ); + + } + // Attribut endet mitten drin... + else if ( pAttr->GetEnd() > nStartPos ) + { + // pItem = pAttr->GetItem(); + // s.o. + + // -----------------31.05.95 16:01------------------- + // Ist falsch, wenn das gleiche Attribut sofort wieder + // eingestellt wird! + // => Sollte am besten nicht vorkommen, also gleich beim + // Setzen von Attributen richtig machen! + // -------------------------------------------------- + rCurSet.InvalidateItem( pAttr->GetItem()->Which() ); + } + + if ( pItem ) + { + USHORT nWhich = pItem->Which(); + if ( rCurSet.GetItemState( nWhich ) == SFX_ITEM_OFF ) + { + rCurSet.Put( *pItem ); + } + else if ( rCurSet.GetItemState( nWhich ) == SFX_ITEM_ON ) + { + const SfxPoolItem& rItem = rCurSet.Get( nWhich ); + if ( rItem != *pItem ) + { + rCurSet.InvalidateItem( nWhich ); + } + } + } + nAttr++; + pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr ); + } + } +} + + +*/ + + diff --git a/svtools/source/edit/textdoc.hxx b/svtools/source/edit/textdoc.hxx new file mode 100644 index 000000000000..0c875b4fe07f --- /dev/null +++ b/svtools/source/edit/textdoc.hxx @@ -0,0 +1,148 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _TEXTDOC_HXX +#define _TEXTDOC_HXX + +#include <svl/svarray.hxx> +#include <svtools/textdata.hxx> +#include <txtattr.hxx> + +#include <tools/debug.hxx> +#include <tools/string.hxx> +#include <tools/list.hxx> + +typedef TextCharAttrib* TextCharAttribPtr; +SV_DECL_PTRARR_DEL( TextCharAttribs, TextCharAttribPtr, 0, 4 ) + +class TextCharAttribList : private TextCharAttribs +{ +private: + BOOL mbHasEmptyAttribs; + + TextCharAttribList( const TextCharAttribList& ) : TextCharAttribs() {} + +public: + TextCharAttribList(); + ~TextCharAttribList(); + + void Clear( BOOL bDestroyAttribs ); + USHORT Count() const { return TextCharAttribs::Count(); } + + TextCharAttrib* GetAttrib( USHORT n ) const { return TextCharAttribs::GetObject( n ); } + void RemoveAttrib( USHORT n ) { TextCharAttribs::Remove( n, 1 ); } + + void InsertAttrib( TextCharAttrib* pAttrib ); + + void DeleteEmptyAttribs(); + void ResortAttribs(); + + BOOL HasEmptyAttribs() const { return mbHasEmptyAttribs; } + BOOL& HasEmptyAttribs() { return mbHasEmptyAttribs; } + + TextCharAttrib* FindAttrib( USHORT nWhich, USHORT nPos ); + TextCharAttrib* FindNextAttrib( USHORT nWhich, USHORT nFromPos, USHORT nMaxPos = 0xFFFF ) const; + TextCharAttrib* FindEmptyAttrib( USHORT nWhich, USHORT nPos ); + BOOL HasAttrib( USHORT nWhich ) const; + BOOL HasBoundingAttrib( USHORT nBound ); + +#ifdef DBG_UTIL + BOOL DbgCheckAttribs(); +#endif +}; + + +class TextNode +{ +private: + String maText; + TextCharAttribList maCharAttribs; + + TextNode( const TextNode& ) {;} +protected: + void ExpandAttribs( USHORT nIndex, USHORT nNewChars ); + void CollapsAttribs( USHORT nIndex, USHORT nDelChars ); + +public: + TextNode( const String& rText ); + + + const String& GetText() const { return maText; } + + const TextCharAttribList& GetCharAttribs() const { return maCharAttribs; } + TextCharAttribList& GetCharAttribs() { return maCharAttribs; } + + void InsertText( USHORT nPos, const String& rText ); + void InsertText( USHORT nPos, sal_Unicode c ); + void RemoveText( USHORT nPos, USHORT nChars ); + + TextNode* Split( USHORT nPos, BOOL bKeepEndigAttribs ); + void Append( const TextNode& rNode ); +}; + +class TextDoc +{ +private: + ToolsList<TextNode*> maTextNodes; + USHORT mnLeftMargin; + +protected: + void DestroyTextNodes(); + +public: + TextDoc(); + ~TextDoc(); + + void Clear(); + + ToolsList<TextNode*>& GetNodes() { return maTextNodes; } + const ToolsList<TextNode*>& GetNodes() const { return maTextNodes; } + + TextPaM RemoveChars( const TextPaM& rPaM, USHORT nChars ); + TextPaM InsertText( const TextPaM& rPaM, sal_Unicode c ); + TextPaM InsertText( const TextPaM& rPaM, const String& rStr ); + + TextPaM InsertParaBreak( const TextPaM& rPaM, BOOL bKeepEndingAttribs ); + TextPaM ConnectParagraphs( TextNode* pLeft, TextNode* pRight ); + + ULONG GetTextLen( const sal_Unicode* pSep, const TextSelection* pSel = NULL ) const; + String GetText( const sal_Unicode* pSep ) const; + String GetText( ULONG nPara ) const; + + void SetLeftMargin( USHORT n ) { mnLeftMargin = n; } + USHORT GetLeftMargin() const { return mnLeftMargin; } + +// BOOL RemoveAttribs( TextNode* pNode, USHORT nStart, USHORT nEnd ), USHORT nWhich = 0 ); +// BOOL RemoveAttribs( TextNode* pNode, USHORT nStart, USHORT nEnd, TextCharAttrib*& rpStarting, TextCharAttrib*& rpEnding, USHORT nWhich = 0 ); +// void InsertAttrib( const EditCharAttrib* pAttr ); +// void InsertAttribInSelection( const EditCharAttrib* pAttr ); +// void FindAttribs( TextNode* pNode, USHORT nStartPos, USHORT nEndPos, SfxItemSet& rCurSet ); + + BOOL IsValidPaM( const TextPaM& rPaM ); +}; + +#endif // _TEXTDOC_HXX diff --git a/svtools/source/edit/texteng.cxx b/svtools/source/edit/texteng.cxx new file mode 100644 index 000000000000..e0e136089d78 --- /dev/null +++ b/svtools/source/edit/texteng.cxx @@ -0,0 +1,3303 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + + +#include <tools/stream.hxx> + +#include <svtools/texteng.hxx> +#include <svtools/textview.hxx> +#include <textdoc.hxx> +#include <textdat2.hxx> +#include <textundo.hxx> +#include <textund2.hxx> +#include <svl/ctloptions.hxx> +#include <vcl/window.hxx> + +#include <vcl/edit.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/beans/PropertyValues.hpp> + +#ifndef _COM_SUN_STAR_TEXT_XBREAKITERATOR_HPP_ +#include <com/sun/star/i18n/XBreakIterator.hpp> +#endif + +#ifndef _COM_SUN_STAR_TEXT_CHARACTERITERATORMODE_HPP_ +#include <com/sun/star/i18n/CharacterIteratorMode.hpp> +#endif + +#ifndef _COM_SUN_STAR_TEXT_WORDTYPE_HPP_ +#include <com/sun/star/i18n/WordType.hpp> +#endif + +#ifndef _COM_SUN_STAR_I18N_XEXTENDEDINPUTSEQUENCECHECKER_HDL_ +#include <com/sun/star/i18n/XExtendedInputSequenceChecker.hpp> +#endif +#include <com/sun/star/i18n/InputSequenceCheckMode.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> + +#include <comphelper/processfactory.hxx> + +#include <unotools/localedatawrapper.hxx> +#include <vcl/unohelp.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/unohelp.hxx> +#include <vcl/metric.hxx> + +#include <unicode/ubidi.h> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::rtl; + +typedef TextView* TextViewPtr; +SV_DECL_PTRARR( TextViews, TextViewPtr, 0, 1 ) +// SV_IMPL_PTRARR( TextViews, TextViewPtr ); + +SV_DECL_VARARR_SORT( TESortedPositions, ULONG, 16, 8 ) +SV_IMPL_VARARR_SORT( TESortedPositions, ULONG ) + +#define RESDIFF 10 +#define SCRLRANGE 20 // 1/20 der Breite/Hoehe scrollen, wenn im QueryDrop + + +// ------------------------------------------------------------------------- +// (-) class TextEngine +// ------------------------------------------------------------------------- +TextEngine::TextEngine() +{ + mpDoc = 0; + mpTEParaPortions = 0; + + mpViews = new TextViews; + mpActiveView = NULL; + + mbIsFormatting = FALSE; + mbFormatted = FALSE; + mbUpdate = TRUE; + mbModified = FALSE; + mbUndoEnabled = FALSE; + mbIsInUndo = FALSE; + mbDowning = FALSE; + mbRightToLeft = FALSE; + mbHasMultiLineParas = FALSE; + + meAlign = TXTALIGN_LEFT; + + mnMaxTextWidth = 0; + mnMaxTextLen = 0; + mnCurTextWidth = 0xFFFFFFFF; + mnCurTextHeight = 0; + + mpUndoManager = NULL; + mpIMEInfos = NULL; + mpLocaleDataWrapper = NULL; + + mpIdleFormatter = new IdleFormatter; + mpIdleFormatter->SetTimeoutHdl( LINK( this, TextEngine, IdleFormatHdl ) ); + + mpRefDev = new VirtualDevice; + + ImpInitLayoutMode( mpRefDev ); + + ImpInitDoc(); + + maTextColor = COL_BLACK; + Font aFont; + aFont.SetTransparent( FALSE ); + Color aFillColor( aFont.GetFillColor() ); + aFillColor.SetTransparency( 0 ); + aFont.SetFillColor( aFillColor ); + SetFont( aFont ); +} + +TextEngine::~TextEngine() +{ + mbDowning = TRUE; + + delete mpIdleFormatter; + delete mpDoc; + delete mpTEParaPortions; + delete mpViews; // nur die Liste, nicht die Vies + delete mpRefDev; + delete mpUndoManager; + delete mpIMEInfos; + delete mpLocaleDataWrapper; +} + +void TextEngine::InsertView( TextView* pTextView ) +{ + mpViews->Insert( pTextView, mpViews->Count() ); + pTextView->SetSelection( TextSelection() ); + + if ( !GetActiveView() ) + SetActiveView( pTextView ); +} + +void TextEngine::RemoveView( TextView* pTextView ) +{ + USHORT nPos = mpViews->GetPos( pTextView ); + if( nPos != USHRT_MAX ) + { + pTextView->HideCursor(); + mpViews->Remove( nPos, 1 ); + if ( pTextView == GetActiveView() ) + SetActiveView( 0 ); + } +} + +USHORT TextEngine::GetViewCount() const +{ + return mpViews->Count(); +} + +TextView* TextEngine::GetView( USHORT nView ) const +{ + return mpViews->GetObject( nView ); +} + +TextView* TextEngine::GetActiveView() const +{ + return mpActiveView; +} + +void TextEngine::SetActiveView( TextView* pTextView ) +{ + if ( pTextView != mpActiveView ) + { + if ( mpActiveView ) + mpActiveView->HideSelection(); + + mpActiveView = pTextView; + + if ( mpActiveView ) + mpActiveView->ShowSelection(); + } +} + +void TextEngine::SetFont( const Font& rFont ) +{ + if ( rFont != maFont ) + { + maFont = rFont; + // #i40221# As the font's color now defaults to transparent (since i35764) + // we have to choose a useful textcolor in this case. + // Otherwise maTextColor and maFont.GetColor() are both transparent.... + if( rFont.GetColor() == COL_TRANSPARENT ) + maTextColor = COL_BLACK; + else + maTextColor = rFont.GetColor(); + + // Wegen Selektion keinen Transparenten Font zulassen... + // (Sonst spaeter in ImplPaint den Hintergrund anders loeschen...) + maFont.SetTransparent( FALSE ); + // Tell VCL not to use the font color, use text color from OutputDevice + maFont.SetColor( COL_TRANSPARENT ); + Color aFillColor( maFont.GetFillColor() ); + aFillColor.SetTransparency( 0 ); + maFont.SetFillColor( aFillColor ); + + maFont.SetAlign( ALIGN_TOP ); + mpRefDev->SetFont( maFont); + Size aTextSize; + aTextSize.Width() = mpRefDev->GetTextWidth( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( " " ) ) ); + aTextSize.Height() = mpRefDev->GetTextHeight(); + if ( !aTextSize.Width() ) + aTextSize.Width() = mpRefDev->GetTextWidth( String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "XXXX" ) ) ); + + mnDefTab = (USHORT)aTextSize.Width(); + if ( !mnDefTab ) + mnDefTab = 1; + mnCharHeight = (USHORT)aTextSize.Height(); +/* + // #93746# Doesn't work with CJK HalfWidth/FullWidth + FontMetric aRealFont( mpRefDev->GetFontMetric() ); + if ( aRealFont.GetPitch() == PITCH_FIXED ) + { + String aX100; + aX100.Fill( 100, 'X' ); + mnFixCharWidth100 = (USHORT)mpRefDev->GetTextWidth( aX100 ); + } + else +*/ + mnFixCharWidth100 = 0; + + FormatFullDoc(); + UpdateViews(); + + for ( USHORT nView = mpViews->Count(); nView; ) + { + TextView* pView = mpViews->GetObject( --nView ); + pView->GetWindow()->SetInputContext( InputContext( GetFont(), !pView->IsReadOnly() ? INPUTCONTEXT_TEXT|INPUTCONTEXT_EXTTEXTINPUT : 0 ) ); + } + } +} + +void TextEngine::SetDefTab( USHORT nDefTab ) +{ + mnDefTab = nDefTab; + // evtl neu setzen? +} + +void TextEngine::SetMaxTextLen( ULONG nLen ) +{ + mnMaxTextLen = nLen; +} + +void TextEngine::SetMaxTextWidth( ULONG nMaxWidth ) +{ + if ( nMaxWidth != mnMaxTextWidth ) + { + mnMaxTextWidth = Min( nMaxWidth, (ULONG)0x7FFFFFFF ); + FormatFullDoc(); + UpdateViews(); + } +} + +static sal_Unicode static_aLFText[] = { '\n', 0 }; +static sal_Unicode static_aCRText[] = { '\r', 0 }; +static sal_Unicode static_aCRLFText[] = { '\r', '\n', 0 }; + +static inline const sal_Unicode* static_getLineEndText( LineEnd aLineEnd ) +{ + const sal_Unicode* pRet = NULL; + + switch( aLineEnd ) + { + case LINEEND_LF: pRet = static_aLFText;break; + case LINEEND_CR: pRet = static_aCRText;break; + case LINEEND_CRLF: pRet = static_aCRLFText;break; + } + return pRet; +} + +void TextEngine::ReplaceText(const TextSelection& rSel, const String& rText) +{ + ImpInsertText( rSel, rText ); +} + +String TextEngine::GetText( LineEnd aSeparator ) const +{ + return mpDoc->GetText( static_getLineEndText( aSeparator ) ); +} + +String TextEngine::GetTextLines( LineEnd aSeparator ) const +{ + String aText; + ULONG nParas = mpTEParaPortions->Count(); + const sal_Unicode* pSep = static_getLineEndText( aSeparator ); + for ( ULONG nP = 0; nP < nParas; nP++ ) + { + TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nP ); + + USHORT nLines = pTEParaPortion->GetLines().Count(); + for ( USHORT nL = 0; nL < nLines; nL++ ) + { + TextLine* pLine = pTEParaPortion->GetLines()[nL]; + aText += pTEParaPortion->GetNode()->GetText().Copy( pLine->GetStart(), pLine->GetEnd() - pLine->GetStart() ); + if ( pSep && ( ( (nP+1) < nParas ) || ( (nL+1) < nLines ) ) ) + aText += pSep; + } + } + return aText; +} + +String TextEngine::GetText( ULONG nPara ) const +{ + return mpDoc->GetText( nPara ); +} + +ULONG TextEngine::GetTextLen( LineEnd aSeparator ) const +{ + return mpDoc->GetTextLen( static_getLineEndText( aSeparator ) ); +} + +ULONG TextEngine::GetTextLen( const TextSelection& rSel, LineEnd aSeparator ) const +{ + TextSelection aSel( rSel ); + aSel.Justify(); + ValidateSelection( aSel ); + return mpDoc->GetTextLen( static_getLineEndText( aSeparator ), &aSel ); +} + +USHORT TextEngine::GetTextLen( ULONG nPara ) const +{ + return mpDoc->GetNodes().GetObject( nPara )->GetText().Len(); +} + +void TextEngine::SetUpdateMode( BOOL bUpdate ) +{ + if ( bUpdate != mbUpdate ) + { + mbUpdate = bUpdate; + if ( mbUpdate ) + { + FormatAndUpdate( GetActiveView() ); + if ( GetActiveView() ) + GetActiveView()->ShowCursor(); + } + } +} + +BOOL TextEngine::DoesKeyMoveCursor( const KeyEvent& rKeyEvent ) +{ + BOOL bDoesMove = FALSE; + + switch ( rKeyEvent.GetKeyCode().GetCode() ) + { + case KEY_UP: + case KEY_DOWN: + case KEY_LEFT: + case KEY_RIGHT: + case KEY_HOME: + case KEY_END: + case KEY_PAGEUP: + case KEY_PAGEDOWN: + { + if ( !rKeyEvent.GetKeyCode().IsMod2() ) + bDoesMove = TRUE; + } + break; + } + return bDoesMove; +} + +BOOL TextEngine::DoesKeyChangeText( const KeyEvent& rKeyEvent ) +{ + BOOL bDoesChange = FALSE; + + KeyFuncType eFunc = rKeyEvent.GetKeyCode().GetFunction(); + if ( eFunc != KEYFUNC_DONTKNOW ) + { + switch ( eFunc ) + { + case KEYFUNC_UNDO: + case KEYFUNC_REDO: + case KEYFUNC_CUT: + case KEYFUNC_PASTE: bDoesChange = TRUE; + break; + default: // wird dann evtl. unten bearbeitet. + eFunc = KEYFUNC_DONTKNOW; + } + } + if ( eFunc == KEYFUNC_DONTKNOW ) + { + switch ( rKeyEvent.GetKeyCode().GetCode() ) + { + case KEY_DELETE: + case KEY_BACKSPACE: + { + if ( !rKeyEvent.GetKeyCode().IsMod2() ) + bDoesChange = TRUE; + } + break; + case KEY_RETURN: + case KEY_TAB: + { + if ( !rKeyEvent.GetKeyCode().IsMod1() && !rKeyEvent.GetKeyCode().IsMod2() ) + bDoesChange = TRUE; + } + break; + default: + { + bDoesChange = TextEngine::IsSimpleCharInput( rKeyEvent ); + } + } + } + return bDoesChange; +} + +BOOL TextEngine::IsSimpleCharInput( const KeyEvent& rKeyEvent ) +{ + if( rKeyEvent.GetCharCode() >= 32 && rKeyEvent.GetCharCode() != 127 && + KEY_MOD1 != (rKeyEvent.GetKeyCode().GetModifier() & ~KEY_SHIFT) && // (ssa) #i45714#: + KEY_MOD2 != (rKeyEvent.GetKeyCode().GetModifier() & ~KEY_SHIFT) ) // check for Ctrl and Alt separately + { + return TRUE; + } + return FALSE; +} + +void TextEngine::ImpInitDoc() +{ + if ( mpDoc ) + mpDoc->Clear(); + else + mpDoc = new TextDoc; + + delete mpTEParaPortions; + mpTEParaPortions = new TEParaPortions; + + TextNode* pNode = new TextNode( String() ); + mpDoc->GetNodes().Insert( pNode, 0 ); + + TEParaPortion* pIniPortion = new TEParaPortion( pNode ); + mpTEParaPortions->Insert( pIniPortion, (ULONG)0 ); + + mbFormatted = FALSE; + + ImpParagraphRemoved( TEXT_PARA_ALL ); + ImpParagraphInserted( 0 ); +} + +String TextEngine::GetText( const TextSelection& rSel, LineEnd aSeparator ) const +{ + String aText; + + if ( !rSel.HasRange() ) + return aText; + + TextSelection aSel( rSel ); + aSel.Justify(); + + ULONG nStartPara = aSel.GetStart().GetPara(); + ULONG nEndPara = aSel.GetEnd().GetPara(); + const sal_Unicode* pSep = static_getLineEndText( aSeparator ); + for ( ULONG nNode = aSel.GetStart().GetPara(); nNode <= nEndPara; nNode++ ) + { + TextNode* pNode = mpDoc->GetNodes().GetObject( nNode ); + + USHORT nStartPos = 0; + USHORT nEndPos = pNode->GetText().Len(); + if ( nNode == nStartPara ) + nStartPos = aSel.GetStart().GetIndex(); + if ( nNode == nEndPara ) // kann auch == nStart sein! + nEndPos = aSel.GetEnd().GetIndex(); + + aText += pNode->GetText().Copy( nStartPos, nEndPos-nStartPos ); + if ( nNode < nEndPara ) + aText += pSep; + } + return aText; +} + +void TextEngine::ImpRemoveText() +{ + ImpInitDoc(); + + TextPaM aStartPaM( 0, 0 ); + TextSelection aEmptySel( aStartPaM, aStartPaM ); + for ( USHORT nView = 0; nView < mpViews->Count(); nView++ ) + { + TextView* pView = mpViews->GetObject( nView ); + pView->ImpSetSelection( aEmptySel ); + } + ResetUndo(); +} + +void TextEngine::SetText( const XubString& rText ) +{ + ImpRemoveText(); + + BOOL bUndoCurrentlyEnabled = IsUndoEnabled(); + // Der von Hand reingesteckte Text kann nicht vom Anwender rueckgaengig gemacht werden. + EnableUndo( FALSE ); + + TextPaM aStartPaM( 0, 0 ); + TextSelection aEmptySel( aStartPaM, aStartPaM ); + + TextPaM aPaM = aStartPaM; + if ( rText.Len() ) + aPaM = ImpInsertText( aEmptySel, rText ); + + for ( USHORT nView = 0; nView < mpViews->Count(); nView++ ) + { + TextView* pView = mpViews->GetObject( nView ); + pView->ImpSetSelection( aEmptySel ); + + // Wenn kein Text, dann auch Kein Format&Update + // => Der Text bleibt stehen. + if ( !rText.Len() && GetUpdateMode() ) + pView->Invalidate(); + } + + if( !rText.Len() ) // sonst muss spaeter noch invalidiert werden, !bFormatted reicht. + mnCurTextHeight = 0; + + FormatAndUpdate(); + + EnableUndo( bUndoCurrentlyEnabled ); + DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "Undo nach SetText?" ); +} + + +void TextEngine::CursorMoved( ULONG nNode ) +{ + // Leere Attribute loeschen, aber nur, wenn Absatz nicht leer! + TextNode* pNode = mpDoc->GetNodes().GetObject( nNode ); + if ( pNode && pNode->GetCharAttribs().HasEmptyAttribs() && pNode->GetText().Len() ) + pNode->GetCharAttribs().DeleteEmptyAttribs(); +} + +void TextEngine::ImpRemoveChars( const TextPaM& rPaM, USHORT nChars, SfxUndoAction* ) +{ + DBG_ASSERT( nChars, "ImpRemoveChars - 0 Chars?!" ); + if ( IsUndoEnabled() && !IsInUndo() ) + { + // Attribute muessen hier vorm RemoveChars fuer UNDO gesichert werden! + TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() ); + XubString aStr( pNode->GetText().Copy( rPaM.GetIndex(), nChars ) ); + + // Pruefen, ob Attribute geloescht oder geaendert werden: + USHORT nStart = rPaM.GetIndex(); + USHORT nEnd = nStart + nChars; + for ( USHORT nAttr = pNode->GetCharAttribs().Count(); nAttr; ) + { + TextCharAttrib* pAttr = pNode->GetCharAttribs().GetAttrib( --nAttr ); + if ( ( pAttr->GetEnd() >= nStart ) && ( pAttr->GetStart() < nEnd ) ) + { +// TextSelection aSel( rPaM ); +// aSel.GetEnd().GetIndex() += nChars; +// TextUndoSetAttribs* pAttrUndo = CreateAttribUndo( aSel ); +// InsertUndo( pAttrUndo ); + break; // for + } + } +// if ( pCurUndo && ( CreateTextPaM( pCurUndo->GetEPaM() ) == rPaM ) ) +// pCurUndo->GetStr() += aStr; +// else + InsertUndo( new TextUndoRemoveChars( this, rPaM, aStr ) ); + } + + mpDoc->RemoveChars( rPaM, nChars ); + ImpCharsRemoved( rPaM.GetPara(), rPaM.GetIndex(), nChars ); +} + +TextPaM TextEngine::ImpConnectParagraphs( ULONG nLeft, ULONG nRight ) +{ + DBG_ASSERT( nLeft != nRight, "Den gleichen Absatz zusammenfuegen ?" ); + + TextNode* pLeft = mpDoc->GetNodes().GetObject( nLeft ); + TextNode* pRight = mpDoc->GetNodes().GetObject( nRight ); + + if ( IsUndoEnabled() && !IsInUndo() ) + InsertUndo( new TextUndoConnectParas( this, nLeft, pLeft->GetText().Len() ) ); + + // Erstmal Portions suchen, da pRight nach ConnectParagraphs weg. + TEParaPortion* pLeftPortion = mpTEParaPortions->GetObject( nLeft ); + TEParaPortion* pRightPortion = mpTEParaPortions->GetObject( nRight ); + DBG_ASSERT( pLeft && pLeftPortion, "Blinde Portion in ImpConnectParagraphs(1)" ); + DBG_ASSERT( pRight && pRightPortion, "Blinde Portion in ImpConnectParagraphs(2)" ); + + TextPaM aPaM = mpDoc->ConnectParagraphs( pLeft, pRight ); + ImpParagraphRemoved( nRight ); + + pLeftPortion->MarkSelectionInvalid( aPaM.GetIndex(), pLeft->GetText().Len() ); + + mpTEParaPortions->Remove( nRight ); + delete pRightPortion; + // der rechte Node wird von EditDoc::ConnectParagraphs() geloescht. + + return aPaM; +} + +TextPaM TextEngine::ImpDeleteText( const TextSelection& rSel ) +{ + if ( !rSel.HasRange() ) + return rSel.GetStart(); + + TextSelection aSel( rSel ); + aSel.Justify(); + TextPaM aStartPaM( aSel.GetStart() ); + TextPaM aEndPaM( aSel.GetEnd() ); + + CursorMoved( aStartPaM.GetPara() ); // nur damit neu eingestellte Attribute verschwinden... + CursorMoved( aEndPaM.GetPara() ); // nur damit neu eingestellte Attribute verschwinden... + + DBG_ASSERT( mpDoc->IsValidPaM( aStartPaM ), "Index im Wald in ImpDeleteText" ); + DBG_ASSERT( mpDoc->IsValidPaM( aEndPaM ), "Index im Wald in ImpDeleteText" ); + + ULONG nStartNode = aStartPaM.GetPara(); + ULONG nEndNode = aEndPaM.GetPara(); + + // Alle Nodes dazwischen entfernen.... + for ( ULONG z = nStartNode+1; z < nEndNode; z++ ) + { + // Immer nStartNode+1, wegen Remove()! + ImpRemoveParagraph( nStartNode+1 ); + } + + if ( nStartNode != nEndNode ) + { + // Den Rest des StartNodes... + TextNode* pLeft = mpDoc->GetNodes().GetObject( nStartNode ); + USHORT nChars = pLeft->GetText().Len() - aStartPaM.GetIndex(); + if ( nChars ) + { + ImpRemoveChars( aStartPaM, nChars ); + TEParaPortion* pPortion = mpTEParaPortions->GetObject( nStartNode ); + DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(3)" ); + pPortion->MarkSelectionInvalid( aStartPaM.GetIndex(), pLeft->GetText().Len() ); + } + + // Den Anfang des EndNodes.... + nEndNode = nStartNode+1; // Die anderen Absaetze wurden geloescht + nChars = aEndPaM.GetIndex(); + if ( nChars ) + { + aEndPaM.GetPara() = nEndNode; + aEndPaM.GetIndex() = 0; + ImpRemoveChars( aEndPaM, nChars ); + TEParaPortion* pPortion = mpTEParaPortions->GetObject( nEndNode ); + DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(4)" ); + pPortion->MarkSelectionInvalid( 0, pPortion->GetNode()->GetText().Len() ); + } + + // Zusammenfuegen.... + aStartPaM = ImpConnectParagraphs( nStartNode, nEndNode ); + } + else + { + USHORT nChars; + nChars = aEndPaM.GetIndex() - aStartPaM.GetIndex(); + ImpRemoveChars( aStartPaM, nChars ); + TEParaPortion* pPortion = mpTEParaPortions->GetObject( nStartNode ); + DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(5)" ); + pPortion->MarkInvalid( aEndPaM.GetIndex(), aStartPaM.GetIndex() - aEndPaM.GetIndex() ); + } + +// UpdateSelections(); + TextModified(); + return aStartPaM; +} + +void TextEngine::ImpRemoveParagraph( ULONG nPara ) +{ + TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); + TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara ); + + // Der Node wird vom Undo verwaltet und ggf. zerstoert! + /* delete */ mpDoc->GetNodes().Remove( nPara ); + if ( IsUndoEnabled() && !IsInUndo() ) + InsertUndo( new TextUndoDelPara( this, pNode, nPara ) ); + else + delete pNode; + + mpTEParaPortions->Remove( nPara ); + delete pPortion; + + ImpParagraphRemoved( nPara ); +} + +uno::Reference < i18n::XExtendedInputSequenceChecker > TextEngine::GetInputSequenceChecker() const +{ + uno::Reference < i18n::XExtendedInputSequenceChecker > xISC; +// if ( !xISC.is() ) + { + uno::Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory(); + uno::Reference< uno::XInterface > xI = xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.InputSequenceChecker" ) ); + if ( xI.is() ) + { + Any x = xI->queryInterface( ::getCppuType((const uno::Reference< i18n::XExtendedInputSequenceChecker >*)0) ); + x >>= xISC; + } + } + return xISC; +} + +BOOL TextEngine::IsInputSequenceCheckingRequired( sal_Unicode c, const TextSelection& rCurSel ) const +{ + uno::Reference< i18n::XBreakIterator > xBI = ((TextEngine *) this)->GetBreakIterator(); + SvtCTLOptions aCTLOptions; + + // get the index that really is first + USHORT nFirstPos = rCurSel.GetStart().GetIndex(); + USHORT nMaxPos = rCurSel.GetEnd().GetIndex(); + if (nMaxPos < nFirstPos) + nFirstPos = nMaxPos; + + sal_Bool bIsSequenceChecking = + aCTLOptions.IsCTLFontEnabled() && + aCTLOptions.IsCTLSequenceChecking() && + nFirstPos != 0 && /* first char needs not to be checked */ + xBI.is() && i18n::ScriptType::COMPLEX == xBI->getScriptType( rtl::OUString( c ), 0 ); + + return bIsSequenceChecking; +} + +TextPaM TextEngine::ImpInsertText( const TextSelection& rCurSel, sal_Unicode c, BOOL bOverwrite ) +{ + return ImpInsertText( c, rCurSel, bOverwrite, sal_False ); +} + +TextPaM TextEngine::ImpInsertText( sal_Unicode c, const TextSelection& rCurSel, BOOL bOverwrite, BOOL bIsUserInput ) +{ + DBG_ASSERT( c != '\n', "Zeilenumbruch bei InsertText ?" ); + DBG_ASSERT( c != '\r', "Zeilenumbruch bei InsertText ?" ); + + TextPaM aPaM( rCurSel.GetStart() ); + TextNode* pNode = mpDoc->GetNodes().GetObject( aPaM.GetPara() ); + + if ( pNode->GetText().Len() < STRING_MAXLEN ) + { + BOOL bDoOverwrite = ( bOverwrite && + ( aPaM.GetIndex() < pNode->GetText().Len() ) ) ? TRUE : FALSE; + + BOOL bUndoAction = ( rCurSel.HasRange() || bDoOverwrite ); + + if ( bUndoAction ) + UndoActionStart( TEXTUNDO_INSERT ); + + if ( rCurSel.HasRange() ) + { + aPaM = ImpDeleteText( rCurSel ); + } + else if ( bDoOverwrite ) + { + // Wenn Selektion, dann kein Zeichen ueberschreiben + TextSelection aTmpSel( aPaM ); + aTmpSel.GetEnd().GetIndex()++; + ImpDeleteText( aTmpSel ); + } + + if (bIsUserInput && IsInputSequenceCheckingRequired( c, rCurSel )) + { + uno::Reference < i18n::XExtendedInputSequenceChecker > xISC = GetInputSequenceChecker(); + SvtCTLOptions aCTLOptions; + + if (xISC.is()) + { + xub_StrLen nTmpPos = aPaM.GetIndex(); + sal_Int16 nCheckMode = aCTLOptions.IsCTLSequenceCheckingRestricted() ? + i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC; + + // the text that needs to be checked is only the one + // before the current cursor position + rtl::OUString aOldText( mpDoc->GetText( aPaM.GetPara() ).Copy(0, nTmpPos) ); + rtl::OUString aNewText( aOldText ); + if (aCTLOptions.IsCTLSequenceCheckingTypeAndReplace()) + { + xISC->correctInputSequence( aNewText, nTmpPos - 1, c, nCheckMode ); + + // find position of first character that has changed + sal_Int32 nOldLen = aOldText.getLength(); + sal_Int32 nNewLen = aNewText.getLength(); + const sal_Unicode *pOldTxt = aOldText.getStr(); + const sal_Unicode *pNewTxt = aNewText.getStr(); + sal_Int32 nChgPos = 0; + while ( nChgPos < nOldLen && nChgPos < nNewLen && + pOldTxt[nChgPos] == pNewTxt[nChgPos] ) + ++nChgPos; + + xub_StrLen nChgLen = static_cast< xub_StrLen >(nNewLen - nChgPos); + String aChgText( aNewText.copy( nChgPos ), nChgLen ); + + // select text from first pos to be changed to current pos + TextSelection aSel( TextPaM( aPaM.GetPara(), (USHORT) nChgPos ), aPaM ); + + if (aChgText.Len()) + // ImpInsertText implicitly handles undo... + return ImpInsertText( aSel, aChgText ); + else + return aPaM; + } + else + { + // should the character be ignored (i.e. not get inserted) ? + if (!xISC->checkInputSequence( aOldText, nTmpPos - 1, c, nCheckMode )) + return aPaM; // nothing to be done -> no need for undo + } + } + + // at this point now we will insert the character 'normally' some lines below... + } + + + if ( IsUndoEnabled() && !IsInUndo() ) + { + TextUndoInsertChars* pNewUndo = new TextUndoInsertChars( this, aPaM, c ); + BOOL bTryMerge = ( !bDoOverwrite && ( c != ' ' ) ) ? TRUE : FALSE; + InsertUndo( pNewUndo, bTryMerge ); + } + + TEParaPortion* pPortion = mpTEParaPortions->GetObject( aPaM.GetPara() ); + pPortion->MarkInvalid( aPaM.GetIndex(), 1 ); + if ( c == '\t' ) + pPortion->SetNotSimpleInvalid(); + aPaM = mpDoc->InsertText( aPaM, c ); + ImpCharsInserted( aPaM.GetPara(), aPaM.GetIndex()-1, 1 ); + + TextModified(); + + if ( bUndoAction ) + UndoActionEnd( TEXTUNDO_INSERT ); + } + + return aPaM; +} + + +TextPaM TextEngine::ImpInsertText( const TextSelection& rCurSel, const XubString& rStr ) +{ + UndoActionStart( TEXTUNDO_INSERT ); + + TextPaM aPaM; + + if ( rCurSel.HasRange() ) + aPaM = ImpDeleteText( rCurSel ); + else + aPaM = rCurSel.GetEnd(); + + XubString aText( rStr ); + aText.ConvertLineEnd( LINEEND_LF ); + + USHORT nStart = 0; + while ( nStart < aText.Len() ) + { + USHORT nEnd = aText.Search( LINE_SEP, nStart ); + if ( nEnd == STRING_NOTFOUND ) + nEnd = aText.Len(); // nicht dereferenzieren! + + // Start == End => Leerzeile + if ( nEnd > nStart ) + { + ULONG nL = aPaM.GetIndex(); + nL += ( nEnd-nStart ); + if ( nL > STRING_MAXLEN ) + { + USHORT nDiff = (USHORT) (nL-STRING_MAXLEN); + nEnd = nEnd - nDiff; + } + + XubString aLine( aText, nStart, nEnd-nStart ); + if ( IsUndoEnabled() && !IsInUndo() ) + InsertUndo( new TextUndoInsertChars( this, aPaM, aLine ) ); + + TEParaPortion* pPortion = mpTEParaPortions->GetObject( aPaM.GetPara() ); + pPortion->MarkInvalid( aPaM.GetIndex(), aLine.Len() ); + if ( aLine.Search( '\t' ) != STRING_NOTFOUND ) + pPortion->SetNotSimpleInvalid(); + + aPaM = mpDoc->InsertText( aPaM, aLine ); + ImpCharsInserted( aPaM.GetPara(), aPaM.GetIndex()-aLine.Len(), aLine.Len() ); + + } + if ( nEnd < aText.Len() ) + aPaM = ImpInsertParaBreak( aPaM ); + + nStart = nEnd+1; + + if ( nStart < nEnd ) // #108611# overflow + break; + } + + UndoActionEnd( TEXTUNDO_INSERT ); + + TextModified(); + return aPaM; +} + +TextPaM TextEngine::ImpInsertParaBreak( const TextSelection& rCurSel, BOOL bKeepEndingAttribs ) +{ + TextPaM aPaM; + if ( rCurSel.HasRange() ) + aPaM = ImpDeleteText( rCurSel ); + else + aPaM = rCurSel.GetEnd(); + + return ImpInsertParaBreak( aPaM, bKeepEndingAttribs ); +} + +TextPaM TextEngine::ImpInsertParaBreak( const TextPaM& rPaM, BOOL bKeepEndingAttribs ) +{ + if ( IsUndoEnabled() && !IsInUndo() ) + InsertUndo( new TextUndoSplitPara( this, rPaM.GetPara(), rPaM.GetIndex() ) ); + + TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() ); + BOOL bFirstParaContentChanged = rPaM.GetIndex() < pNode->GetText().Len(); + + TextPaM aPaM( mpDoc->InsertParaBreak( rPaM, bKeepEndingAttribs ) ); + + TEParaPortion* pPortion = mpTEParaPortions->GetObject( rPaM.GetPara() ); + DBG_ASSERT( pPortion, "Blinde Portion in ImpInsertParaBreak" ); + pPortion->MarkInvalid( rPaM.GetIndex(), 0 ); + + TextNode* pNewNode = mpDoc->GetNodes().GetObject( aPaM.GetPara() ); + TEParaPortion* pNewPortion = new TEParaPortion( pNewNode ); + mpTEParaPortions->Insert( pNewPortion, aPaM.GetPara() ); + ImpParagraphInserted( aPaM.GetPara() ); + + CursorMoved( rPaM.GetPara() ); // falls leeres Attribut entstanden. + TextModified(); + + if ( bFirstParaContentChanged ) + Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, rPaM.GetPara() ) ); + + return aPaM; +} + +Rectangle TextEngine::PaMtoEditCursor( const TextPaM& rPaM, BOOL bSpecial ) +{ + DBG_ASSERT( GetUpdateMode(), "Darf bei Update=FALSE nicht erreicht werden: PaMtoEditCursor" ); + + Rectangle aEditCursor; + long nY = 0; + + if ( !mbHasMultiLineParas ) + { + nY = rPaM.GetPara() * mnCharHeight; + } + else + { + for ( ULONG nPortion = 0; nPortion < rPaM.GetPara(); nPortion++ ) + { + TEParaPortion* pPortion = mpTEParaPortions->GetObject(nPortion); + nY += pPortion->GetLines().Count() * mnCharHeight; + } + } + + aEditCursor = GetEditCursor( rPaM, bSpecial ); + aEditCursor.Top() += nY; + aEditCursor.Bottom() += nY; + return aEditCursor; +} + +Rectangle TextEngine::GetEditCursor( const TextPaM& rPaM, BOOL bSpecial, BOOL bPreferPortionStart ) +{ + if ( !IsFormatted() && !IsFormatting() ) + FormatAndUpdate(); + + TEParaPortion* pPortion = mpTEParaPortions->GetObject( rPaM.GetPara() ); + //TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() ); + + /* + bSpecial: Wenn hinter dem letzten Zeichen einer umgebrochenen Zeile, + am Ende der Zeile bleiben, nicht am Anfang der naechsten. + Zweck: - END => wirklich hinter das letzte Zeichen + - Selektion.... + bSpecial: If behind the last character of a made up line, stay at the + end of the line, not at the start of the next line. + Purpose: - really END = > behind the last character + - to selection... + + */ + + long nY = 0; + USHORT nCurIndex = 0; + TextLine* pLine = 0; + for ( USHORT nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ ) + { + TextLine* pTmpLine = pPortion->GetLines().GetObject( nLine ); + if ( ( pTmpLine->GetStart() == rPaM.GetIndex() ) || ( pTmpLine->IsIn( rPaM.GetIndex(), bSpecial ) ) ) + { + pLine = pTmpLine; + break; + } + + nCurIndex = nCurIndex + pTmpLine->GetLen(); + nY += mnCharHeight; + } + if ( !pLine ) + { + // Cursor am Ende des Absatzes. + DBG_ASSERT( rPaM.GetIndex() == nCurIndex, "Index voll daneben in GetEditCursor!" ); + + pLine = pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1 ); + nY -= mnCharHeight; + nCurIndex = nCurIndex - pLine->GetLen(); + } + + Rectangle aEditCursor; + + aEditCursor.Top() = nY; + nY += mnCharHeight; + aEditCursor.Bottom() = nY-1; + + // innerhalb der Zeile suchen.... + long nX = ImpGetXPos( rPaM.GetPara(), pLine, rPaM.GetIndex(), bPreferPortionStart ); + aEditCursor.Left() = aEditCursor.Right() = nX; + return aEditCursor; +} + +long TextEngine::ImpGetXPos( ULONG nPara, TextLine* pLine, USHORT nIndex, BOOL bPreferPortionStart ) +{ + DBG_ASSERT( ( nIndex >= pLine->GetStart() ) && ( nIndex <= pLine->GetEnd() ) , "ImpGetXPos muss richtig gerufen werden!" ); + + BOOL bDoPreferPortionStart = bPreferPortionStart; + // Assure that the portion belongs to this line: + if ( nIndex == pLine->GetStart() ) + bDoPreferPortionStart = TRUE; + else if ( nIndex == pLine->GetEnd() ) + bDoPreferPortionStart = FALSE; + + TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara ); + + USHORT nTextPortionStart = 0; + USHORT nTextPortion = pParaPortion->GetTextPortions().FindPortion( nIndex, nTextPortionStart, bDoPreferPortionStart ); + + DBG_ASSERT( ( nTextPortion >= pLine->GetStartPortion() ) && ( nTextPortion <= pLine->GetEndPortion() ), "GetXPos: Portion not in current line! " ); + + TETextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion ); + + long nX = ImpGetPortionXOffset( nPara, pLine, nTextPortion ); + + long nPortionTextWidth = pPortion->GetWidth(); + + if ( nTextPortionStart != nIndex ) + { + // Search within portion... + if ( nIndex == ( nTextPortionStart + pPortion->GetLen() ) ) + { + // End of Portion + if ( ( pPortion->GetKind() == PORTIONKIND_TAB ) || + ( !IsRightToLeft() && !pPortion->IsRightToLeft() ) || + ( IsRightToLeft() && pPortion->IsRightToLeft() ) ) + { + nX += nPortionTextWidth; + if ( ( pPortion->GetKind() == PORTIONKIND_TAB ) && ( (nTextPortion+1) < pParaPortion->GetTextPortions().Count() ) ) + { + TETextPortion* pNextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion+1 ); + if ( ( pNextPortion->GetKind() != PORTIONKIND_TAB ) && ( + ( !IsRightToLeft() && pNextPortion->IsRightToLeft() ) || + ( IsRightToLeft() && !pNextPortion->IsRightToLeft() ) ) ) + { +// nX += pNextPortion->GetWidth(); + // End of the tab portion, use start of next for cursor pos + DBG_ASSERT( !bPreferPortionStart, "ImpGetXPos - How can we this tab portion here???" ); + nX = ImpGetXPos( nPara, pLine, nIndex, TRUE ); + } + + } + } + } + else if ( pPortion->GetKind() == PORTIONKIND_TEXT ) + { + DBG_ASSERT( nIndex != pLine->GetStart(), "Strange behavior in new ImpGetXPos()" ); + + long nPosInPortion = (long)CalcTextWidth( nPara, nTextPortionStart, nIndex-nTextPortionStart ); + + if ( ( !IsRightToLeft() && !pPortion->IsRightToLeft() ) || + ( IsRightToLeft() && pPortion->IsRightToLeft() ) ) + { + nX += nPosInPortion; + } + else + { + nX += nPortionTextWidth - nPosInPortion; + } + } + } + else // if ( nIndex == pLine->GetStart() ) + { + if ( ( pPortion->GetKind() != PORTIONKIND_TAB ) && + ( ( !IsRightToLeft() && pPortion->IsRightToLeft() ) || + ( IsRightToLeft() && !pPortion->IsRightToLeft() ) ) ) + { + nX += nPortionTextWidth; + } + } + + return nX; +} + +const TextAttrib* TextEngine::FindAttrib( const TextPaM& rPaM, USHORT nWhich ) const +{ + const TextAttrib* pAttr = NULL; + const TextCharAttrib* pCharAttr = FindCharAttrib( rPaM, nWhich ); + if ( pCharAttr ) + pAttr = &pCharAttr->GetAttr(); + return pAttr; +} + +const TextCharAttrib* TextEngine::FindCharAttrib( const TextPaM& rPaM, USHORT nWhich ) const +{ + const TextCharAttrib* pAttr = NULL; + TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() ); + if ( pNode && ( rPaM.GetIndex() < pNode->GetText().Len() ) ) + pAttr = pNode->GetCharAttribs().FindAttrib( nWhich, rPaM.GetIndex() ); + return pAttr; +} + +BOOL TextEngine::HasAttrib( USHORT nWhich ) const +{ + BOOL bAttr = FALSE; + for ( ULONG n = mpDoc->GetNodes().Count(); --n && !bAttr; ) + { + TextNode* pNode = mpDoc->GetNodes().GetObject( n ); + bAttr = pNode->GetCharAttribs().HasAttrib( nWhich ); + } + return bAttr; +} + +TextPaM TextEngine::GetPaM( const Point& rDocPos, BOOL bSmart ) +{ + DBG_ASSERT( GetUpdateMode(), "Darf bei Update=FALSE nicht erreicht werden: GetPaM" ); + + long nY = 0; + for ( ULONG nPortion = 0; nPortion < mpTEParaPortions->Count(); nPortion++ ) + { + TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion ); + long nTmpHeight = pPortion->GetLines().Count() * mnCharHeight; + nY += nTmpHeight; + if ( nY > rDocPos.Y() ) + { + nY -= nTmpHeight; + Point aPosInPara( rDocPos ); + aPosInPara.Y() -= nY; + + TextPaM aPaM( nPortion, 0 ); + aPaM.GetIndex() = ImpFindIndex( nPortion, aPosInPara, bSmart ); + return aPaM; + } + } + + // Nicht gefunden - Dann den letzten sichtbare... + ULONG nLastNode = mpDoc->GetNodes().Count() - 1; + TextNode* pLast = mpDoc->GetNodes().GetObject( nLastNode ); + return TextPaM( nLastNode, pLast->GetText().Len() ); +} + +USHORT TextEngine::ImpFindIndex( ULONG nPortion, const Point& rPosInPara, BOOL bSmart ) +{ + DBG_ASSERT( IsFormatted(), "GetPaM: Nicht formatiert" ); + TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion ); + + USHORT nCurIndex = 0; + + long nY = 0; + TextLine* pLine = 0; + USHORT nLine; + for ( nLine = 0; nLine < pPortion->GetLines().Count(); nLine++ ) + { + TextLine* pTmpLine = pPortion->GetLines().GetObject( nLine ); + nY += mnCharHeight; + if ( nY > rPosInPara.Y() ) // das war 'se + { + pLine = pTmpLine; + break; // richtige Y-Position intressiert nicht + } + } + DBG_ASSERT( pLine, "ImpFindIndex: pLine ?" ); + + nCurIndex = GetCharPos( nPortion, nLine, rPosInPara.X(), bSmart ); + + if ( nCurIndex && ( nCurIndex == pLine->GetEnd() ) && + ( pLine != pPortion->GetLines().GetObject( pPortion->GetLines().Count()-1) ) ) + { + uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator(); + sal_Int32 nCount = 1; + nCurIndex = (USHORT)xBI->previousCharacters( pPortion->GetNode()->GetText(), nCurIndex, GetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount ); + } + return nCurIndex; +} + +USHORT TextEngine::GetCharPos( ULONG nPortion, USHORT nLine, long nXPos, BOOL ) +{ + + TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion ); + TextLine* pLine = pPortion->GetLines().GetObject( nLine ); + + USHORT nCurIndex = pLine->GetStart(); + + long nTmpX = pLine->GetStartX(); + if ( nXPos <= nTmpX ) + return nCurIndex; + + for ( USHORT i = pLine->GetStartPortion(); i <= pLine->GetEndPortion(); i++ ) + { + TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( i ); + nTmpX += pTextPortion->GetWidth(); + + if ( nTmpX > nXPos ) + { + if( pTextPortion->GetLen() > 1 ) + { + nTmpX -= pTextPortion->GetWidth(); // vor die Portion stellen + // Optimieren: Kein GetTextBreak, wenn feste Fontbreite... + Font aFont; + SeekCursor( nPortion, nCurIndex+1, aFont, NULL ); + mpRefDev->SetFont( aFont); + long nPosInPortion = nXPos-nTmpX; + if ( IsRightToLeft() != pTextPortion->IsRightToLeft() ) + nPosInPortion = pTextPortion->GetWidth() - nPosInPortion; + nCurIndex = mpRefDev->GetTextBreak( pPortion->GetNode()->GetText(), nPosInPortion, nCurIndex ); + // MT: GetTextBreak should assure that we are not withing a CTL cell... + } + return nCurIndex; + } + nCurIndex = nCurIndex + pTextPortion->GetLen(); + } + return nCurIndex; +} + + +ULONG TextEngine::GetTextHeight() const +{ + DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=FALSE nicht verwendet werden: GetTextHeight" ); + + if ( !IsFormatted() && !IsFormatting() ) + ((TextEngine*)this)->FormatAndUpdate(); + + return mnCurTextHeight; +} + +ULONG TextEngine::GetTextHeight( ULONG nParagraph ) const +{ + DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=FALSE nicht verwendet werden: GetTextHeight" ); + + if ( !IsFormatted() && !IsFormatting() ) + ((TextEngine*)this)->FormatAndUpdate(); + + return CalcParaHeight( nParagraph ); +} + +ULONG TextEngine::CalcTextWidth( ULONG nPara ) +{ + ULONG nParaWidth = 0; + TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara ); + for ( USHORT nLine = pPortion->GetLines().Count(); nLine; ) + { + ULONG nLineWidth = 0; + TextLine* pLine = pPortion->GetLines().GetObject( --nLine ); + for ( USHORT nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ ) + { + TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( nTP ); + nLineWidth += pTextPortion->GetWidth(); + } + if ( nLineWidth > nParaWidth ) + nParaWidth = nLineWidth; + } + return nParaWidth; +} + +ULONG TextEngine::CalcTextWidth() +{ + if ( !IsFormatted() && !IsFormatting() ) + FormatAndUpdate(); + + if ( mnCurTextWidth == 0xFFFFFFFF ) + { + mnCurTextWidth = 0; + for ( ULONG nPara = mpTEParaPortions->Count(); nPara; ) + { + ULONG nParaWidth = CalcTextWidth( --nPara ); + if ( nParaWidth > mnCurTextWidth ) + mnCurTextWidth = nParaWidth; + } + } + return mnCurTextWidth+1;// Ein breiter, da in CreateLines bei >= umgebrochen wird. +} + +ULONG TextEngine::CalcTextHeight() +{ + DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=FALSE nicht verwendet werden: CalcTextHeight" ); + + ULONG nY = 0; + for ( ULONG nPortion = mpTEParaPortions->Count(); nPortion; ) + nY += CalcParaHeight( --nPortion ); + return nY; +} + +ULONG TextEngine::CalcTextWidth( ULONG nPara, USHORT nPortionStart, USHORT nLen, const Font* pFont ) +{ + // Innerhalb des Textes darf es keinen Portionwechsel (Attribut/Tab) geben! + DBG_ASSERT( mpDoc->GetNodes().GetObject( nPara )->GetText().Search( '\t', nPortionStart ) >= (nPortionStart+nLen), "CalcTextWidth: Tab!" ); + + ULONG nWidth; + if ( mnFixCharWidth100 ) + { + nWidth = (ULONG)nLen*mnFixCharWidth100/100; + } + else + { + if ( pFont ) + { + if ( !mpRefDev->GetFont().IsSameInstance( *pFont ) ) + mpRefDev->SetFont( *pFont ); + } + else + { + Font aFont; + SeekCursor( nPara, nPortionStart+1, aFont, NULL ); + mpRefDev->SetFont( aFont ); + } + TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); + nWidth = (ULONG)mpRefDev->GetTextWidth( pNode->GetText(), nPortionStart, nLen ); + + } + return nWidth; +} + + +USHORT TextEngine::GetLineCount( ULONG nParagraph ) const +{ + DBG_ASSERT( nParagraph < mpTEParaPortions->Count(), "GetLineCount: Out of range" ); + + TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph ); + if ( pPPortion ) + return pPPortion->GetLines().Count(); + + return 0xFFFF; +} + +USHORT TextEngine::GetLineLen( ULONG nParagraph, USHORT nLine ) const +{ + DBG_ASSERT( nParagraph < mpTEParaPortions->Count(), "GetLineCount: Out of range" ); + + TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph ); + if ( pPPortion && ( nLine < pPPortion->GetLines().Count() ) ) + { + TextLine* pLine = pPPortion->GetLines().GetObject( nLine ); + return pLine->GetLen(); + } + + return 0xFFFF; +} + +ULONG TextEngine::CalcParaHeight( ULONG nParagraph ) const +{ + ULONG nHeight = 0; + + TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph ); + DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetParaHeight" ); + if ( pPPortion ) + nHeight = pPPortion->GetLines().Count() * mnCharHeight; + + return nHeight; +} + +void TextEngine::UpdateSelections() +{ +} + +Range TextEngine::GetInvalidYOffsets( ULONG nPortion ) +{ + TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPortion ); + USHORT nLines = pTEParaPortion->GetLines().Count(); + USHORT nLastInvalid, nFirstInvalid = 0; + USHORT nLine; + for ( nLine = 0; nLine < nLines; nLine++ ) + { + TextLine* pL = pTEParaPortion->GetLines().GetObject( nLine ); + if ( pL->IsInvalid() ) + { + nFirstInvalid = nLine; + break; + } + } + + for ( nLastInvalid = nFirstInvalid; nLastInvalid < nLines; nLastInvalid++ ) + { + TextLine* pL = pTEParaPortion->GetLines().GetObject( nLine ); + if ( pL->IsValid() ) + break; + } + + if ( nLastInvalid >= nLines ) + nLastInvalid = nLines-1; + + return Range( nFirstInvalid*mnCharHeight, ((nLastInvalid+1)*mnCharHeight)-1 ); +} + +ULONG TextEngine::GetParagraphCount() const +{ + return mpDoc->GetNodes().Count(); +} + +void TextEngine::EnableUndo( BOOL bEnable ) +{ + // Beim Umschalten des Modus Liste loeschen: + if ( bEnable != IsUndoEnabled() ) + ResetUndo(); + + mbUndoEnabled = bEnable; +} + +SfxUndoManager& TextEngine::GetUndoManager() +{ + if ( !mpUndoManager ) + mpUndoManager = new TextUndoManager( this ); + return *mpUndoManager; +} + +void TextEngine::UndoActionStart( USHORT nId ) +{ + if ( IsUndoEnabled() && !IsInUndo() ) + { + String aComment; + // ... + GetUndoManager().EnterListAction( aComment, XubString(), nId ); + } +} + +void TextEngine::UndoActionEnd( USHORT ) +{ + if ( IsUndoEnabled() && !IsInUndo() ) + GetUndoManager().LeaveListAction(); +} + +void TextEngine::InsertUndo( TextUndo* pUndo, BOOL bTryMerge ) +{ + DBG_ASSERT( !IsInUndo(), "InsertUndo im Undomodus!" ); + GetUndoManager().AddUndoAction( pUndo, bTryMerge ); +} + +void TextEngine::ResetUndo() +{ + if ( mpUndoManager ) + mpUndoManager->Clear(); +} + +void TextEngine::InsertContent( TextNode* pNode, ULONG nPara ) +{ + DBG_ASSERT( pNode, "NULL-Pointer in InsertContent! " ); + DBG_ASSERT( IsInUndo(), "InsertContent nur fuer Undo()!" ); + TEParaPortion* pNew = new TEParaPortion( pNode ); + mpTEParaPortions->Insert( pNew, nPara ); + mpDoc->GetNodes().Insert( pNode, nPara ); + ImpParagraphInserted( nPara ); +} + +TextPaM TextEngine::SplitContent( ULONG nNode, USHORT nSepPos ) +{ + #ifdef DBG_UTIL + TextNode* pNode = mpDoc->GetNodes().GetObject( nNode ); + DBG_ASSERT( pNode, "Ungueltiger Node in SplitContent" ); + DBG_ASSERT( IsInUndo(), "SplitContent nur fuer Undo()!" ); + DBG_ASSERT( nSepPos <= pNode->GetText().Len(), "Index im Wald: SplitContent" ); + #endif + TextPaM aPaM( nNode, nSepPos ); + return ImpInsertParaBreak( aPaM ); +} + +TextPaM TextEngine::ConnectContents( ULONG nLeftNode ) +{ + DBG_ASSERT( IsInUndo(), "ConnectContent nur fuer Undo()!" ); + return ImpConnectParagraphs( nLeftNode, nLeftNode+1 ); +} + +void TextEngine::SeekCursor( ULONG nPara, USHORT nPos, Font& rFont, OutputDevice* pOutDev ) +{ + rFont = maFont; + if ( pOutDev ) + pOutDev->SetTextColor( maTextColor ); + + TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); + USHORT nAttribs = pNode->GetCharAttribs().Count(); + for ( USHORT nAttr = 0; nAttr < nAttribs; nAttr++ ) + { + TextCharAttrib* pAttrib = pNode->GetCharAttribs().GetAttrib( nAttr ); + if ( pAttrib->GetStart() > nPos ) + break; + + // Beim Seeken nicht die Attr beruecksichtigen, die dort beginnen! + // Leere Attribute werden beruecksichtigt( verwendet), da diese + // gerade eingestellt wurden. + // 12.4.95: Doch keine Leeren Attribute verwenden: + // - Wenn gerade eingestellt und leer => keine Auswirkung auf Font + // In einem leeren Absatz eingestellte Zeichen werden sofort wirksam. + if ( ( ( pAttrib->GetStart() < nPos ) && ( pAttrib->GetEnd() >= nPos ) ) + || !pNode->GetText().Len() ) + { + if ( pAttrib->Which() != TEXTATTR_FONTCOLOR ) + { + pAttrib->GetAttr().SetFont( rFont ); + } + else + { + if ( pOutDev ) + pOutDev->SetTextColor( ((TextAttribFontColor&)pAttrib->GetAttr()).GetColor() ); + } + } + } + + if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetPara() == nPara ) && + ( nPos > mpIMEInfos->aPos.GetIndex() ) && ( nPos <= ( mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen ) ) ) + { + sal_uInt16 nAttr = mpIMEInfos->pAttribs[ nPos - mpIMEInfos->aPos.GetIndex() - 1 ]; + if ( nAttr & EXTTEXTINPUT_ATTR_UNDERLINE ) + rFont.SetUnderline( UNDERLINE_SINGLE ); + else if ( nAttr & EXTTEXTINPUT_ATTR_BOLDUNDERLINE ) + rFont.SetUnderline( UNDERLINE_BOLD ); + else if ( nAttr & EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE ) + rFont.SetUnderline( UNDERLINE_DOTTED ); + else if ( nAttr & EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE ) + rFont.SetUnderline( UNDERLINE_DOTTED ); + if ( nAttr & EXTTEXTINPUT_ATTR_REDTEXT ) + rFont.SetColor( Color( COL_RED ) ); + else if ( nAttr & EXTTEXTINPUT_ATTR_HALFTONETEXT ) + rFont.SetColor( Color( COL_LIGHTGRAY ) ); + if ( nAttr & EXTTEXTINPUT_ATTR_HIGHLIGHT ) + { + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + rFont.SetColor( rStyleSettings.GetHighlightTextColor() ); + rFont.SetFillColor( rStyleSettings.GetHighlightColor() ); + rFont.SetTransparent( FALSE ); + } + else if ( nAttr & EXTTEXTINPUT_ATTR_GRAYWAVELINE ) + { + rFont.SetUnderline( UNDERLINE_WAVE ); +// if( pOut ) +// pOut->SetTextLineColor( Color( COL_LIGHTGRAY ) ); + } + } +} + +void TextEngine::SetUpdateMode( BOOL bUp, TextView* pCurView, BOOL bForceUpdate ) +{ + BOOL bChanged = ( GetUpdateMode() != bUp ); + + mbUpdate = bUp; + if ( mbUpdate && ( bChanged || bForceUpdate ) ) + FormatAndUpdate( pCurView ); +} + +void TextEngine::FormatAndUpdate( TextView* pCurView ) +{ + if ( mbDowning ) + return ; + + if ( IsInUndo() ) + IdleFormatAndUpdate( pCurView ); + else + { + FormatDoc(); + UpdateViews( pCurView ); + } +} + + +void TextEngine::IdleFormatAndUpdate( TextView* pCurView, USHORT nMaxTimerRestarts ) +{ + mpIdleFormatter->DoIdleFormat( pCurView, nMaxTimerRestarts ); +} + +void TextEngine::TextModified() +{ + mbFormatted = FALSE; + mbModified = TRUE; +} + +void TextEngine::UpdateViews( TextView* pCurView ) +{ + if ( !GetUpdateMode() || IsFormatting() || maInvalidRec.IsEmpty() ) + return; + + DBG_ASSERT( IsFormatted(), "UpdateViews: Doc nicht formatiert!" ); + + for ( USHORT nView = 0; nView < mpViews->Count(); nView++ ) + { + TextView* pView = mpViews->GetObject( nView ); + pView->HideCursor(); + + Rectangle aClipRec( maInvalidRec ); + Size aOutSz = pView->GetWindow()->GetOutputSizePixel(); + Rectangle aVisArea( pView->GetStartDocPos(), aOutSz ); + aClipRec.Intersection( aVisArea ); + if ( !aClipRec.IsEmpty() ) + { + // in Fensterkoordinaten umwandeln.... + Point aNewPos = pView->GetWindowPos( aClipRec.TopLeft() ); + if ( IsRightToLeft() ) + aNewPos.X() -= aOutSz.Width() - 1; + aClipRec.SetPos( aNewPos ); + + if ( pView == pCurView ) + pView->ImpPaint( aClipRec, !pView->GetWindow()->IsPaintTransparent() ); + else + pView->GetWindow()->Invalidate( aClipRec ); + } + } + + if ( pCurView ) + { + pCurView->ShowCursor( pCurView->IsAutoScroll() ); + } + + maInvalidRec = Rectangle(); +} + +IMPL_LINK( TextEngine, IdleFormatHdl, Timer *, EMPTYARG ) +{ + FormatAndUpdate( mpIdleFormatter->GetView() ); + return 0; +} + +void TextEngine::CheckIdleFormatter() +{ + mpIdleFormatter->ForceTimeout(); +} + +void TextEngine::FormatFullDoc() +{ + for ( ULONG nPortion = 0; nPortion < mpTEParaPortions->Count(); nPortion++ ) + { + TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPortion ); USHORT nLen = pTEParaPortion->GetNode()->GetText().Len(); + pTEParaPortion->MarkSelectionInvalid( 0, nLen ); + } + mbFormatted = FALSE; + FormatDoc(); +} + +void TextEngine::FormatDoc() +{ + if ( IsFormatted() || !GetUpdateMode() || IsFormatting() ) + return; + + mbIsFormatting = TRUE; + mbHasMultiLineParas = FALSE; + + long nY = 0; + BOOL bGrow = FALSE; + + maInvalidRec = Rectangle(); // leermachen + for ( ULONG nPara = 0; nPara < mpTEParaPortions->Count(); nPara++ ) + { + TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); + if ( pTEParaPortion->IsInvalid() ) + { + ULONG nOldParaWidth = 0xFFFFFFFF; + if ( mnCurTextWidth != 0xFFFFFFFF ) + nOldParaWidth = CalcTextWidth( nPara ); + + ImpFormattingParagraph( nPara ); + + if ( CreateLines( nPara ) ) + bGrow = TRUE; + + // InvalidRec nur einmal setzen... + if ( maInvalidRec.IsEmpty() ) + { + // Bei Paperwidth 0 (AutoPageSize) bleibt es sonst Empty()... + long nWidth = (long)mnMaxTextWidth; + if ( !nWidth ) + nWidth = 0x7FFFFFFF; + Range aInvRange( GetInvalidYOffsets( nPara ) ); + maInvalidRec = Rectangle( Point( 0, nY+aInvRange.Min() ), + Size( nWidth, aInvRange.Len() ) ); + } + else + { + maInvalidRec.Bottom() = nY + CalcParaHeight( nPara ); + } + + if ( mnCurTextWidth != 0xFFFFFFFF ) + { + ULONG nNewParaWidth = CalcTextWidth( nPara ); + if ( nNewParaWidth >= mnCurTextWidth ) + mnCurTextWidth = nNewParaWidth; + else if ( ( nOldParaWidth != 0xFFFFFFFF ) && ( nOldParaWidth >= mnCurTextWidth ) ) + mnCurTextWidth = 0xFFFFFFFF; + } + } + else if ( bGrow ) + { + maInvalidRec.Bottom() = nY + CalcParaHeight( nPara ); + } + nY += CalcParaHeight( nPara ); + if ( !mbHasMultiLineParas && pTEParaPortion->GetLines().Count() > 1 ) + mbHasMultiLineParas = TRUE; + } + + if ( !maInvalidRec.IsEmpty() ) + { + ULONG nNewHeight = CalcTextHeight(); + long nDiff = nNewHeight - mnCurTextHeight; + if ( nNewHeight < mnCurTextHeight ) + { + maInvalidRec.Bottom() = (long)Max( nNewHeight, mnCurTextHeight ); + if ( maInvalidRec.IsEmpty() ) + { + maInvalidRec.Top() = 0; + // Left und Right werden nicht ausgewertet, aber wegen IsEmpty gesetzt. + maInvalidRec.Left() = 0; + maInvalidRec.Right() = mnMaxTextWidth; + } + } + + mnCurTextHeight = nNewHeight; + if ( nDiff ) + { + mbFormatted = TRUE; + ImpTextHeightChanged(); + } + } + + mbIsFormatting = FALSE; + mbFormatted = TRUE; + + ImpTextFormatted(); +} + +void TextEngine::CreateAndInsertEmptyLine( ULONG nPara ) +{ + TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); + TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); + + TextLine* pTmpLine = new TextLine; + pTmpLine->SetStart( pNode->GetText().Len() ); + pTmpLine->SetEnd( pTmpLine->GetStart() ); + pTEParaPortion->GetLines().Insert( pTmpLine, pTEParaPortion->GetLines().Count() ); + + if ( ImpGetAlign() == TXTALIGN_CENTER ) + pTmpLine->SetStartX( (short)(mnMaxTextWidth / 2) ); + else if ( ImpGetAlign() == TXTALIGN_RIGHT ) + pTmpLine->SetStartX( (short)mnMaxTextWidth ); + else + pTmpLine->SetStartX( mpDoc->GetLeftMargin() ); + + BOOL bLineBreak = pNode->GetText().Len() ? TRUE : FALSE; + + TETextPortion* pDummyPortion = new TETextPortion( 0 ); + pDummyPortion->GetWidth() = 0; + pTEParaPortion->GetTextPortions().Insert( pDummyPortion, pTEParaPortion->GetTextPortions().Count() ); + + if ( bLineBreak == TRUE ) + { + // -2: Die neue ist bereits eingefuegt. + #ifdef DBG_UTIL + TextLine* pLastLine = pTEParaPortion->GetLines().GetObject( pTEParaPortion->GetLines().Count()-2 ); + DBG_ASSERT( pLastLine, "Weicher Umbruch, keine Zeile ?!" ); + #endif + USHORT nPos = (USHORT) pTEParaPortion->GetTextPortions().Count() - 1 ; + pTmpLine->SetStartPortion( nPos ); + pTmpLine->SetEndPortion( nPos ); + } +} + +void TextEngine::ImpBreakLine( ULONG nPara, TextLine* pLine, TETextPortion*, USHORT nPortionStart, long nRemainingWidth ) +{ + TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); + + // Font sollte noch eingestellt sein. + USHORT nMaxBreakPos = mpRefDev->GetTextBreak( pNode->GetText(), nRemainingWidth, nPortionStart ); + + DBG_ASSERT( nMaxBreakPos < pNode->GetText().Len(), "Break?!" ); + + if ( nMaxBreakPos == STRING_LEN ) // GetTextBreak() ist anderer Auffassung als GetTextSize() + nMaxBreakPos = pNode->GetText().Len() - 1; + + uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator(); + i18n::LineBreakHyphenationOptions aHyphOptions( NULL, uno::Sequence< beans::PropertyValue >(), 1 ); + + i18n::LineBreakUserOptions aUserOptions; + aUserOptions.forbiddenBeginCharacters = ImpGetLocaleDataWrapper()->getForbiddenCharacters().beginLine; + aUserOptions.forbiddenEndCharacters = ImpGetLocaleDataWrapper()->getForbiddenCharacters().endLine; + aUserOptions.applyForbiddenRules = sal_True; + aUserOptions.allowPunctuationOutsideMargin = sal_False; + aUserOptions.allowHyphenateEnglish = sal_False; + + static const com::sun::star::lang::Locale aDefLocale; + i18n::LineBreakResults aLBR = xBI->getLineBreak( pNode->GetText(), nMaxBreakPos, aDefLocale, pLine->GetStart(), aHyphOptions, aUserOptions ); + USHORT nBreakPos = (USHORT)aLBR.breakIndex; + if ( nBreakPos <= pLine->GetStart() ) + { + nBreakPos = nMaxBreakPos; + if ( nBreakPos <= pLine->GetStart() ) + nBreakPos = pLine->GetStart() + 1; // Sonst Endlosschleife! + } + + + // die angeknackste Portion ist die End-Portion + pLine->SetEnd( nBreakPos ); + USHORT nEndPortion = SplitTextPortion( nPara, nBreakPos ); + + sal_Bool bBlankSeparator = ( ( nBreakPos >= pLine->GetStart() ) && + ( pNode->GetText().GetChar( nBreakPos ) == ' ' ) ) ? sal_True : sal_False; + if ( bBlankSeparator ) + { + // Blanks am Zeilenende generell unterdruecken... + TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); + TETextPortion* pTP = pTEParaPortion->GetTextPortions().GetObject( nEndPortion ); + DBG_ASSERT( nBreakPos > pLine->GetStart(), "SplitTextPortion am Anfang der Zeile?" ); + pTP->GetWidth() = (long)CalcTextWidth( nPara, nBreakPos-pTP->GetLen(), pTP->GetLen()-1 ); + } + pLine->SetEndPortion( nEndPortion ); +} + +USHORT TextEngine::SplitTextPortion( ULONG nPara, USHORT nPos ) +{ + + // Die Portion bei nPos wird geplittet, wenn bei nPos nicht + // sowieso ein Wechsel ist + if ( nPos == 0 ) + return 0; + + USHORT nSplitPortion; + USHORT nTmpPos = 0; + TETextPortion* pTextPortion = 0; + TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); + USHORT nPortions = pTEParaPortion->GetTextPortions().Count(); + for ( nSplitPortion = 0; nSplitPortion < nPortions; nSplitPortion++ ) + { + TETextPortion* pTP = pTEParaPortion->GetTextPortions().GetObject(nSplitPortion); + nTmpPos = nTmpPos + pTP->GetLen(); + if ( nTmpPos >= nPos ) + { + if ( nTmpPos == nPos ) // dann braucht nichts geteilt werden + return nSplitPortion; + pTextPortion = pTP; + break; + } + } + + DBG_ASSERT( pTextPortion, "Position ausserhalb des Bereichs!" ); + + USHORT nOverlapp = nTmpPos - nPos; + pTextPortion->GetLen() = pTextPortion->GetLen() - nOverlapp; + TETextPortion* pNewPortion = new TETextPortion( nOverlapp ); + pTEParaPortion->GetTextPortions().Insert( pNewPortion, nSplitPortion+1 ); + pTextPortion->GetWidth() = (long)CalcTextWidth( nPara, nPos-pTextPortion->GetLen(), pTextPortion->GetLen() ); + + return nSplitPortion; +} + +void TextEngine::CreateTextPortions( ULONG nPara, USHORT nStartPos ) +{ + TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); + TextNode* pNode = pTEParaPortion->GetNode(); + DBG_ASSERT( pNode->GetText().Len(), "CreateTextPortions sollte nicht fuer leere Absaetze verwendet werden!" ); + + TESortedPositions aPositions; + ULONG nZero = 0; + aPositions.Insert( nZero ); + + USHORT nAttribs = pNode->GetCharAttribs().Count(); + for ( USHORT nAttr = 0; nAttr < nAttribs; nAttr++ ) + { + TextCharAttrib* pAttrib = pNode->GetCharAttribs().GetAttrib( nAttr ); + + // Start und Ende in das Array eintragen... + // Die InsertMethode laesst keine doppelten Werte zu.... + aPositions.Insert( pAttrib->GetStart() ); + aPositions.Insert( pAttrib->GetEnd() ); + } + aPositions.Insert( pNode->GetText().Len() ); + + const TEWritingDirectionInfos& rWritingDirections = pTEParaPortion->GetWritingDirectionInfos(); + for ( USHORT nD = 0; nD < rWritingDirections.Count(); nD++ ) + aPositions.Insert( rWritingDirections[nD].nStartPos ); + + if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetPara() == nPara ) ) + { + sal_uInt16 nLastAttr = 0xFFFF; + for( sal_uInt16 n = 0; n < mpIMEInfos->nLen; n++ ) + { + if ( mpIMEInfos->pAttribs[n] != nLastAttr ) + { + aPositions.Insert( mpIMEInfos->aPos.GetIndex() + n ); + nLastAttr = mpIMEInfos->pAttribs[n]; + } + } + } + + USHORT nTabPos = pNode->GetText().Search( '\t', 0 ); + while ( nTabPos != STRING_NOTFOUND ) + { + aPositions.Insert( nTabPos ); + aPositions.Insert( nTabPos + 1 ); + nTabPos = pNode->GetText().Search( '\t', nTabPos+1 ); + } + + // Ab ... loeschen: + // Leider muss die Anzahl der TextPortions mit aPositions.Count() + // nicht uebereinstimmen, da evtl. Zeilenumbrueche... + USHORT nPortionStart = 0; + USHORT nInvPortion = 0; + USHORT nP; + for ( nP = 0; nP < pTEParaPortion->GetTextPortions().Count(); nP++ ) + { + TETextPortion* pTmpPortion = pTEParaPortion->GetTextPortions().GetObject(nP); + nPortionStart = nPortionStart + pTmpPortion->GetLen(); + if ( nPortionStart >= nStartPos ) + { + nPortionStart = nPortionStart - pTmpPortion->GetLen(); + nInvPortion = nP; + break; + } + } + DBG_ASSERT( nP < pTEParaPortion->GetTextPortions().Count() || !pTEParaPortion->GetTextPortions().Count(), "Nichts zum loeschen: CreateTextPortions" ); + if ( nInvPortion && ( nPortionStart+pTEParaPortion->GetTextPortions().GetObject(nInvPortion)->GetLen() > nStartPos ) ) + { + // lieber eine davor... + // Aber nur wenn es mitten in der Portion war, sonst ist es evtl. + // die einzige in der Zeile davor ! + nInvPortion--; + nPortionStart = nPortionStart - pTEParaPortion->GetTextPortions().GetObject(nInvPortion)->GetLen(); + } + pTEParaPortion->GetTextPortions().DeleteFromPortion( nInvPortion ); + + // Eine Portion kann auch durch einen Zeilenumbruch entstanden sein: + aPositions.Insert( nPortionStart ); + + USHORT nInvPos; + #ifdef DBG_UTIL + BOOL bFound = + #endif + aPositions.Seek_Entry( nPortionStart, &nInvPos ); + DBG_ASSERT( bFound && ( nInvPos < (aPositions.Count()-1) ), "InvPos ?!" ); + for ( USHORT i = nInvPos+1; i < aPositions.Count(); i++ ) + { + TETextPortion* pNew = new TETextPortion( (USHORT)aPositions[i] - (USHORT)aPositions[i-1] ); + pTEParaPortion->GetTextPortions().Insert( pNew, pTEParaPortion->GetTextPortions().Count()); + } + + DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "Keine Portions?!" ); +#ifdef EDITDEBUG + DBG_ASSERT( pTEParaPortion->DbgCheckTextPortions(), "Portions kaputt?" ); +#endif +} + +void TextEngine::RecalcTextPortion( ULONG nPara, USHORT nStartPos, short nNewChars ) +{ + TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); + DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "Keine Portions!" ); + DBG_ASSERT( nNewChars, "RecalcTextPortion mit Diff == 0" ); + + TextNode* const pNode = pTEParaPortion->GetNode(); + if ( nNewChars > 0 ) + { + // Wenn an nStartPos ein Attribut beginnt/endet, oder vor nStartPos + // ein Tab steht, faengt eine neue Portion an, + // ansonsten wird die Portion an nStartPos erweitert. + // Oder wenn ganz vorne ( StartPos 0 ) und dann ein Tab + + if ( ( pNode->GetCharAttribs().HasBoundingAttrib( nStartPos ) ) || + ( nStartPos && ( pNode->GetText().GetChar( nStartPos - 1 ) == '\t' ) ) || + ( ( !nStartPos && ( nNewChars < pNode->GetText().Len() ) && pNode->GetText().GetChar( nNewChars ) == '\t' ) ) ) + { + USHORT nNewPortionPos = 0; + if ( nStartPos ) + nNewPortionPos = SplitTextPortion( nPara, nStartPos ) + 1; +// else if ( ( pTEParaPortion->GetTextPortions().Count() == 1 ) && +// !pTEParaPortion->GetTextPortions()[0]->GetLen() ) +// pTEParaPortion->GetTextPortions().Reset(); // DummyPortion + + // Eine leere Portion kann hier stehen, wenn der Absatz leer war, + // oder eine Zeile durch einen harten Zeilenumbruch entstanden ist. + if ( ( nNewPortionPos < pTEParaPortion->GetTextPortions().Count() ) && + !pTEParaPortion->GetTextPortions()[nNewPortionPos]->GetLen() ) + { + // Dann die leere Portion verwenden. + USHORT & r = + pTEParaPortion->GetTextPortions()[nNewPortionPos]->GetLen(); + r = r + nNewChars; + } + else + { + TETextPortion* pNewPortion = new TETextPortion( nNewChars ); + pTEParaPortion->GetTextPortions().Insert( pNewPortion, nNewPortionPos ); + } + } + else + { + USHORT nPortionStart; + const USHORT nTP = pTEParaPortion->GetTextPortions(). + FindPortion( nStartPos, nPortionStart ); + TETextPortion* const pTP = pTEParaPortion->GetTextPortions()[ nTP ]; + DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden" ); + pTP->GetLen() = pTP->GetLen() + nNewChars; + pTP->GetWidth() = (-1); + } + } + else + { + // Portion schrumpfen oder ggf. entfernen. + // Vor Aufruf dieser Methode muss sichergestellt sein, dass + // keine Portions in dem geloeschten Bereich lagen! + + // Es darf keine reinragende oder im Bereich startende Portion geben, + // also muss nStartPos <= nPos <= nStartPos - nNewChars(neg.) sein + USHORT nPortion = 0; + USHORT nPos = 0; + USHORT nEnd = nStartPos-nNewChars; + USHORT nPortions = pTEParaPortion->GetTextPortions().Count(); + TETextPortion* pTP = 0; + for ( nPortion = 0; nPortion < nPortions; nPortion++ ) + { + pTP = pTEParaPortion->GetTextPortions()[ nPortion ]; + if ( ( nPos+pTP->GetLen() ) > nStartPos ) + { + DBG_ASSERT( nPos <= nStartPos, "Start falsch!" ); + DBG_ASSERT( nPos+pTP->GetLen() >= nEnd, "End falsch!" ); + break; + } + nPos = nPos + pTP->GetLen(); + } + DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden" ); + if ( ( nPos == nStartPos ) && ( (nPos+pTP->GetLen()) == nEnd ) ) + { + // Portion entfernen; + pTEParaPortion->GetTextPortions().Remove( nPortion ); + delete pTP; + } + else + { + DBG_ASSERT( pTP->GetLen() > (-nNewChars), "Portion zu klein zum schrumpfen!" ); + pTP->GetLen() = pTP->GetLen() + nNewChars; + } + DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "RecalcTextPortions: Keine mehr da!" ); + } + +#ifdef EDITDEBUG + DBG_ASSERT( pTEParaPortion->DbgCheckTextPortions(), "Portions kaputt?" ); +#endif +} + +void TextEngine::ImpPaint( OutputDevice* pOutDev, const Point& rStartPos, Rectangle const* pPaintArea, TextSelection const* pPaintRange, TextSelection const* pSelection ) +{ + if ( !GetUpdateMode() ) + return; + + if ( !IsFormatted() ) + FormatDoc(); + + bool bTransparent = false; + Window* pOutWin = dynamic_cast<Window*>(pOutDev); + bTransparent = (pOutWin && pOutWin->IsPaintTransparent()); + + long nY = rStartPos.Y(); + + TextPaM const* pSelStart = 0; + TextPaM const* pSelEnd = 0; + if ( pSelection && pSelection->HasRange() ) + { + BOOL bInvers = pSelection->GetEnd() < pSelection->GetStart(); + pSelStart = !bInvers ? &pSelection->GetStart() : &pSelection->GetEnd(); + pSelEnd = bInvers ? &pSelection->GetStart() : &pSelection->GetEnd(); + } + DBG_ASSERT( !pPaintRange || ( pPaintRange->GetStart() < pPaintRange->GetEnd() ), "ImpPaint: Paint-Range?!" ); + + const StyleSettings& rStyleSettings = pOutDev->GetSettings().GetStyleSettings(); + + // -------------------------------------------------- + // Ueber alle Absaetze... + // -------------------------------------------------- + for ( ULONG nPara = 0; nPara < mpTEParaPortions->Count(); nPara++ ) + { + TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara ); + // falls beim Tippen Idle-Formatierung, asynchrones Paint. + if ( pPortion->IsInvalid() ) + return; + + ULONG nParaHeight = CalcParaHeight( nPara ); + USHORT nIndex = 0; + if ( ( !pPaintArea || ( ( nY + (long)nParaHeight ) > pPaintArea->Top() ) ) + && ( !pPaintRange || ( ( nPara >= pPaintRange->GetStart().GetPara() ) && ( nPara <= pPaintRange->GetEnd().GetPara() ) ) ) ) + { + // -------------------------------------------------- + // Ueber die Zeilen des Absatzes... + // -------------------------------------------------- + USHORT nLines = pPortion->GetLines().Count(); + for ( USHORT nLine = 0; nLine < nLines; nLine++ ) + { + TextLine* pLine = pPortion->GetLines().GetObject(nLine); + Point aTmpPos( rStartPos.X() + pLine->GetStartX(), nY ); + + if ( ( !pPaintArea || ( ( nY + mnCharHeight ) > pPaintArea->Top() ) ) + && ( !pPaintRange || ( + ( TextPaM( nPara, pLine->GetStart() ) < pPaintRange->GetEnd() ) && + ( TextPaM( nPara, pLine->GetEnd() ) > pPaintRange->GetStart() ) ) ) ) + { + // -------------------------------------------------- + // Ueber die Portions der Zeile... + // -------------------------------------------------- + nIndex = pLine->GetStart(); + for ( USHORT y = pLine->GetStartPortion(); y <= pLine->GetEndPortion(); y++ ) + { + DBG_ASSERT( pPortion->GetTextPortions().Count(), "Zeile ohne Textportion im Paint!" ); + TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( y ); + DBG_ASSERT( pTextPortion, "NULL-Pointer im Portioniterator in UpdateViews" ); + + ImpInitLayoutMode( pOutDev /*, pTextPortion->IsRightToLeft() */); + + long nTxtWidth = pTextPortion->GetWidth(); + aTmpPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nIndex, nIndex ); + + // nur ausgeben, was im sichtbaren Bereich beginnt: + if ( ( ( aTmpPos.X() + nTxtWidth ) >= 0 ) + && ( !pPaintRange || ( + ( TextPaM( nPara, nIndex ) < pPaintRange->GetEnd() ) && + ( TextPaM( nPara, nIndex + pTextPortion->GetLen() ) > pPaintRange->GetStart() ) ) ) ) + { + switch ( pTextPortion->GetKind() ) + { + case PORTIONKIND_TEXT: + { + { + Font aFont; + SeekCursor( nPara, nIndex+1, aFont, pOutDev ); + if( bTransparent ) + aFont.SetTransparent( TRUE ); + else if ( pSelection ) + aFont.SetTransparent( FALSE ); + pOutDev->SetFont( aFont ); + + USHORT nTmpIndex = nIndex; + USHORT nEnd = nTmpIndex + pTextPortion->GetLen(); + Point aPos = aTmpPos; + if ( pPaintRange ) + { + // evtl soll nicht alles ausgegeben werden... + if ( ( pPaintRange->GetStart().GetPara() == nPara ) + && ( nTmpIndex < pPaintRange->GetStart().GetIndex() ) ) + { + nTmpIndex = pPaintRange->GetStart().GetIndex(); + } + if ( ( pPaintRange->GetEnd().GetPara() == nPara ) + && ( nEnd > pPaintRange->GetEnd().GetIndex() ) ) + { + nEnd = pPaintRange->GetEnd().GetIndex(); + } + } + + BOOL bDone = FALSE; + if ( pSelStart ) + { + // liegt ein Teil in der Selektion??? + TextPaM aTextStart( nPara, nTmpIndex ); + TextPaM aTextEnd( nPara, nEnd ); + if ( ( aTextStart < *pSelEnd ) && ( aTextEnd > *pSelStart ) ) + { + USHORT nL; + + // 1) Bereich vor Selektion + if ( aTextStart < *pSelStart ) + { + nL = pSelStart->GetIndex() - nTmpIndex; + pOutDev->SetFont( aFont); + aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL ); + pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nL ); + nTmpIndex = nTmpIndex + nL; + + } + // 2) Bereich mit Selektion + nL = nEnd-nTmpIndex; + if ( aTextEnd > *pSelEnd ) + nL = pSelEnd->GetIndex() - nTmpIndex; + if ( nL ) + { + Color aOldTextColor = pOutDev->GetTextColor(); + pOutDev->SetTextColor( rStyleSettings.GetHighlightTextColor() ); + pOutDev->SetTextFillColor( rStyleSettings.GetHighlightColor() ); + aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL ); + pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nL ); + pOutDev->SetTextColor( aOldTextColor ); + pOutDev->SetTextFillColor(); + nTmpIndex = nTmpIndex + nL; + } + + // 3) Bereich nach Selektion + if ( nTmpIndex < nEnd ) + { + nL = nEnd-nTmpIndex; + aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL ); + pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nEnd-nTmpIndex ); + } + bDone = TRUE; + } + } + if ( !bDone ) + { + aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nEnd ); + pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nEnd-nTmpIndex ); + } + } + + } + break; + case PORTIONKIND_TAB: + { + // Bei HideSelection() nur Range, pSelection = 0. + if ( pSelStart || pPaintRange ) + { + Rectangle aTabArea( aTmpPos, Point( aTmpPos.X()+nTxtWidth, aTmpPos.Y()+mnCharHeight-1 ) ); + BOOL bDone = FALSE; + if ( pSelStart ) + { + // liegt der Tab in der Selektion??? + TextPaM aTextStart( nPara, nIndex ); + TextPaM aTextEnd( nPara, nIndex+1 ); + if ( ( aTextStart < *pSelEnd ) && ( aTextEnd > *pSelStart ) ) + { + Color aOldColor = pOutDev->GetFillColor(); + pOutDev->SetFillColor( rStyleSettings.GetHighlightColor() ); + pOutDev->DrawRect( aTabArea ); + pOutDev->SetFillColor( aOldColor ); + bDone = TRUE; + } + } + if ( !bDone ) + { + pOutDev->Erase( aTabArea ); + } + } +#ifdef EDITDEBUG + Rectangle aTabArea( aTmpPos, Point( aTmpPos.X()+nTxtWidth, aTmpPos.Y()+mnCharHeight-1 ) ); + Color aOldColor = pOutDev->GetFillColor(); + pOutDev->SetFillColor( (y%2) ? COL_RED : COL_GREEN ); + pOutDev->DrawRect( aTabArea ); + pOutDev->SetFillColor( aOldColor ); +#endif + } + break; + default: DBG_ERROR( "ImpPaint: Unknown Portion-Type !" ); + } + } + + nIndex = nIndex + pTextPortion->GetLen(); + } + } + + nY += mnCharHeight; + + if ( pPaintArea && ( nY >= pPaintArea->Bottom() ) ) + break; // keine sichtbaren Aktionen mehr... + } + } + else + { + nY += nParaHeight; + } + + if ( pPaintArea && ( nY > pPaintArea->Bottom() ) ) + break; // keine sichtbaren Aktionen mehr... + } +} + +BOOL TextEngine::CreateLines( ULONG nPara ) +{ + // BOOL: Aenderung der Hoehe des Absatzes Ja/Nein - TRUE/FALSE + + TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); + TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); + DBG_ASSERT( pTEParaPortion->IsInvalid(), "CreateLines: Portion nicht invalid!" ); + + USHORT nOldLineCount = pTEParaPortion->GetLines().Count(); + + // --------------------------------------------------------------- + // Schnelle Sonderbehandlung fuer leere Absaetze... + // --------------------------------------------------------------- + if ( pTEParaPortion->GetNode()->GetText().Len() == 0 ) + { + // schnelle Sonderbehandlung... + if ( pTEParaPortion->GetTextPortions().Count() ) + pTEParaPortion->GetTextPortions().Reset(); + if ( pTEParaPortion->GetLines().Count() ) + pTEParaPortion->GetLines().DeleteAndDestroy( 0, pTEParaPortion->GetLines().Count() ); + CreateAndInsertEmptyLine( nPara ); + pTEParaPortion->SetValid(); + return nOldLineCount != pTEParaPortion->GetLines().Count(); + } + + // --------------------------------------------------------------- + // Initialisierung...... + // --------------------------------------------------------------- + + if ( pTEParaPortion->GetLines().Count() == 0 ) + { + TextLine* pL = new TextLine; + pTEParaPortion->GetLines().Insert( pL, 0 ); + } + + const short nInvalidDiff = pTEParaPortion->GetInvalidDiff(); + const USHORT nInvalidStart = pTEParaPortion->GetInvalidPosStart(); + const USHORT nInvalidEnd = nInvalidStart + Abs( nInvalidDiff ); + BOOL bQuickFormat = FALSE; + + if ( !pTEParaPortion->GetWritingDirectionInfos().Count() ) + ImpInitWritingDirections( nPara ); + + if ( pTEParaPortion->GetWritingDirectionInfos().Count() == 1 ) + { + if ( pTEParaPortion->IsSimpleInvalid() && ( nInvalidDiff > 0 ) ) + { + bQuickFormat = TRUE; + } + else if ( ( pTEParaPortion->IsSimpleInvalid() ) && ( nInvalidDiff < 0 ) ) + { + // pruefen, ob loeschen ueber Portiongrenzen erfolgte... + USHORT nStart = nInvalidStart; // DOPPELT !!!!!!!!!!!!!!! + USHORT nEnd = nStart - nInvalidDiff; // neg. + bQuickFormat = TRUE; + USHORT nPos = 0; + USHORT nPortions = pTEParaPortion->GetTextPortions().Count(); + for ( USHORT nTP = 0; nTP < nPortions; nTP++ ) + { + // Es darf kein Start/Ende im geloeschten Bereich liegen. + TETextPortion* const pTP = pTEParaPortion->GetTextPortions().GetObject( nTP ); + nPos = nPos + pTP->GetLen(); + if ( ( nPos > nStart ) && ( nPos < nEnd ) ) + { + bQuickFormat = FALSE; + break; + } + } + } + } + + if ( bQuickFormat ) + RecalcTextPortion( nPara, nInvalidStart, nInvalidDiff ); + else + CreateTextPortions( nPara, nInvalidStart ); + + // --------------------------------------------------------------- + // Zeile mit InvalidPos suchen, eine Zeile davor beginnen... + // Zeilen flaggen => nicht removen ! + // --------------------------------------------------------------- + + USHORT nLine = pTEParaPortion->GetLines().Count()-1; + for ( USHORT nL = 0; nL <= nLine; nL++ ) + { + TextLine* pLine = pTEParaPortion->GetLines().GetObject( nL ); + if ( pLine->GetEnd() > nInvalidStart ) + { + nLine = nL; + break; + } + pLine->SetValid(); + } + // Eine Zeile davor beginnen... + // Wenn ganz hinten getippt wird, kann sich die Zeile davor nicht aendern. + if ( nLine && ( !pTEParaPortion->IsSimpleInvalid() || ( nInvalidEnd < pNode->GetText().Len() ) || ( nInvalidDiff <= 0 ) ) ) + nLine--; + + TextLine* pLine = pTEParaPortion->GetLines().GetObject( nLine ); + + // --------------------------------------------------------------- + // Ab hier alle Zeilen durchformatieren... + // --------------------------------------------------------------- + USHORT nDelFromLine = 0xFFFF; + BOOL bLineBreak = FALSE; + + USHORT nIndex = pLine->GetStart(); + TextLine aSaveLine( *pLine ); + + Font aFont; + + BOOL bCalcPortion = TRUE; + + while ( nIndex < pNode->GetText().Len() ) + { + BOOL bEOL = FALSE; + BOOL bEOC = FALSE; + USHORT nPortionStart = 0; + USHORT nPortionEnd = 0; + + USHORT nTmpPos = nIndex; + USHORT nTmpPortion = pLine->GetStartPortion(); + long nTmpWidth = mpDoc->GetLeftMargin(); +// long nXWidth = mnMaxTextWidth ? ( mnMaxTextWidth - mpDoc->GetLeftMargin() ) : 0x7FFFFFFF; + // Margin nicht abziehen, ist schon in TmpWidth enthalten. + long nXWidth = mnMaxTextWidth ? mnMaxTextWidth : 0x7FFFFFFF; + if ( nXWidth < nTmpWidth ) + nXWidth = nTmpWidth; + + // Portion suchen, die nicht mehr in Zeile passt.... + TETextPortion* pPortion = 0; + BOOL bBrokenLine = FALSE; + bLineBreak = FALSE; + + while ( ( nTmpWidth <= nXWidth ) && !bEOL && ( nTmpPortion < pTEParaPortion->GetTextPortions().Count() ) ) + { + nPortionStart = nTmpPos; + pPortion = pTEParaPortion->GetTextPortions().GetObject( nTmpPortion ); + DBG_ASSERT( pPortion->GetLen(), "Leere Portion in CreateLines ?!" ); + if ( pNode->GetText().GetChar( nTmpPos ) == '\t' ) + { + long nCurPos = nTmpWidth-mpDoc->GetLeftMargin(); + nTmpWidth = ((nCurPos/mnDefTab)+1)*mnDefTab+mpDoc->GetLeftMargin(); + pPortion->GetWidth() = nTmpWidth - nCurPos - mpDoc->GetLeftMargin(); + // Wenn dies das erste Token in der Zeile ist, und + // nTmpWidth > aPaperSize.Width, habe ich eine Endlos-Schleife! + if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) ) + { + // Aber was jetzt ? Tab passend machen! + pPortion->GetWidth() = nXWidth-1; + nTmpWidth = pPortion->GetWidth(); + bEOL = TRUE; + bBrokenLine = TRUE; + } + pPortion->GetKind() = PORTIONKIND_TAB; + } + else + { + + if ( bCalcPortion || !pPortion->HasValidSize() ) + pPortion->GetWidth() = (long)CalcTextWidth( nPara, nTmpPos, pPortion->GetLen() ); + nTmpWidth += pPortion->GetWidth(); + + pPortion->GetRightToLeft() = ImpGetRightToLeft( nPara, nTmpPos+1 ); + pPortion->GetKind() = PORTIONKIND_TEXT; + } + + nTmpPos = nTmpPos + pPortion->GetLen(); + nPortionEnd = nTmpPos; + nTmpPortion++; + } + + // das war evtl. eine Portion zu weit: + BOOL bFixedEnd = FALSE; + if ( nTmpWidth > nXWidth ) + { + nPortionEnd = nTmpPos; + nTmpPos = nTmpPos - pPortion->GetLen(); + nPortionStart = nTmpPos; + nTmpPortion--; + bEOL = FALSE; + bEOC = FALSE; + + nTmpWidth -= pPortion->GetWidth(); + if ( pPortion->GetKind() == PORTIONKIND_TAB ) + { + bEOL = TRUE; + bFixedEnd = TRUE; + } + } + else + { + bEOL = TRUE; + bEOC = TRUE; + pLine->SetEnd( nPortionEnd ); + DBG_ASSERT( pTEParaPortion->GetTextPortions().Count(), "Keine TextPortions?" ); + pLine->SetEndPortion( (USHORT)pTEParaPortion->GetTextPortions().Count() - 1 ); + } + + if ( bFixedEnd ) + { + pLine->SetEnd( nPortionStart ); + pLine->SetEndPortion( nTmpPortion-1 ); + } + else if ( bLineBreak || bBrokenLine ) + { + pLine->SetEnd( nPortionStart+1 ); + pLine->SetEndPortion( nTmpPortion-1 ); + bEOC = FALSE; // wurde oben gesetzt, vielleich mal die if's umstellen? + } + else if ( !bEOL ) + { + DBG_ASSERT( (nPortionEnd-nPortionStart) == pPortion->GetLen(), "Doch eine andere Portion?!" ); + long nRemainingWidth = mnMaxTextWidth - nTmpWidth; + ImpBreakLine( nPara, pLine, pPortion, nPortionStart, nRemainingWidth ); + } + + if ( ( ImpGetAlign() == TXTALIGN_CENTER ) || ( ImpGetAlign() == TXTALIGN_RIGHT ) ) + { + // Ausrichten... + long nTextWidth = 0; + for ( USHORT nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ ) + { + TETextPortion* pTextPortion = pTEParaPortion->GetTextPortions().GetObject( nTP ); + nTextWidth += pTextPortion->GetWidth(); + } + long nSpace = mnMaxTextWidth - nTextWidth; + if ( nSpace > 0 ) + { + if ( ImpGetAlign() == TXTALIGN_CENTER ) + pLine->SetStartX( (USHORT)(nSpace / 2) ); + else // TXTALIGN_RIGHT + pLine->SetStartX( (USHORT)nSpace ); + } + } + else + { + pLine->SetStartX( mpDoc->GetLeftMargin() ); + } + + // ----------------------------------------------------------------- + // pruefen, ob die Zeile neu ausgegeben werden muss... + // ----------------------------------------------------------------- + pLine->SetInvalid(); + + if ( pTEParaPortion->IsSimpleInvalid() ) + { + // Aenderung durch einfache Textaenderung... + // Formatierung nicht abbrechen, da Portions evtl. wieder + // gesplittet werden muessen! + // Wenn irgendwann mal abbrechbar, dann fogende Zeilen Validieren! + // Aber ggf. als Valid markieren, damit weniger Ausgabe... + if ( pLine->GetEnd() < nInvalidStart ) + { + if ( *pLine == aSaveLine ) + { + pLine->SetValid(); + } + } + else + { + USHORT nStart = pLine->GetStart(); + USHORT nEnd = pLine->GetEnd(); + + if ( nStart > nInvalidEnd ) + { + if ( ( ( nStart-nInvalidDiff ) == aSaveLine.GetStart() ) && + ( ( nEnd-nInvalidDiff ) == aSaveLine.GetEnd() ) ) + { + pLine->SetValid(); + if ( bCalcPortion && bQuickFormat ) + { + bCalcPortion = FALSE; + pTEParaPortion->CorrectValuesBehindLastFormattedLine( nLine ); + break; + } + } + } + else if ( bQuickFormat && ( nEnd > nInvalidEnd) ) + { + // Wenn die ungueltige Zeile so endet, dass die naechste an + // der 'gleichen' Textstelle wie vorher beginnt, also nicht + // anders umgebrochen wird, brauche ich dort auch nicht die + // textbreiten neu bestimmen: + if ( nEnd == ( aSaveLine.GetEnd() + nInvalidDiff ) ) + { + bCalcPortion = FALSE; + pTEParaPortion->CorrectValuesBehindLastFormattedLine( nLine ); + break; + } + } + } + } + + nIndex = pLine->GetEnd(); // naechste Zeile Start = letzte Zeile Ende + // weil nEnd hinter das letzte Zeichen zeigt! + + USHORT nEndPortion = pLine->GetEndPortion(); + + // Naechste Zeile oder ggf. neue Zeile.... + pLine = 0; + if ( nLine < pTEParaPortion->GetLines().Count()-1 ) + pLine = pTEParaPortion->GetLines().GetObject( ++nLine ); + if ( pLine && ( nIndex >= pNode->GetText().Len() ) ) + { + nDelFromLine = nLine; + break; + } + if ( !pLine && ( nIndex < pNode->GetText().Len() ) ) + { + pLine = new TextLine; + pTEParaPortion->GetLines().Insert( pLine, ++nLine ); + } + if ( pLine ) + { + aSaveLine = *pLine; + pLine->SetStart( nIndex ); + pLine->SetEnd( nIndex ); + pLine->SetStartPortion( nEndPortion+1 ); + pLine->SetEndPortion( nEndPortion+1 ); + } + } // while ( Index < Len ) + + if ( nDelFromLine != 0xFFFF ) + pTEParaPortion->GetLines().DeleteAndDestroy( nDelFromLine, pTEParaPortion->GetLines().Count() - nDelFromLine ); + + DBG_ASSERT( pTEParaPortion->GetLines().Count(), "Keine Zeile nach CreateLines!" ); + + if ( bLineBreak == TRUE ) + CreateAndInsertEmptyLine( nPara ); + + pTEParaPortion->SetValid(); + + return nOldLineCount != pTEParaPortion->GetLines().Count(); +} + +String TextEngine::GetWord( const TextPaM& rCursorPos, TextPaM* pStartOfWord ) +{ + String aWord; + if ( rCursorPos.GetPara() < mpDoc->GetNodes().Count() ) + { + TextSelection aSel( rCursorPos ); + TextNode* pNode = mpDoc->GetNodes().GetObject( rCursorPos.GetPara() ); + uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator(); + i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), rCursorPos.GetIndex(), GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True ); + aSel.GetStart().GetIndex() = (USHORT)aBoundary.startPos; + aSel.GetEnd().GetIndex() = (USHORT)aBoundary.endPos; + aWord = pNode->GetText().Copy( aSel.GetStart().GetIndex(), aSel.GetEnd().GetIndex() - aSel.GetStart().GetIndex() ); + if ( pStartOfWord ) + *pStartOfWord = aSel.GetStart(); + } + return aWord; +} + +BOOL TextEngine::Read( SvStream& rInput, const TextSelection* pSel ) +{ + BOOL bUpdate = GetUpdateMode(); + SetUpdateMode( FALSE ); + + UndoActionStart( TEXTUNDO_READ ); + TextSelection aSel; + if ( pSel ) + aSel = *pSel; + else + { + ULONG nParas = mpDoc->GetNodes().Count(); + TextNode* pNode = mpDoc->GetNodes().GetObject( nParas - 1 ); + aSel = TextPaM( nParas-1 , pNode->GetText().Len() ); + } + + if ( aSel.HasRange() ) + aSel = ImpDeleteText( aSel ); + + ByteString aLine; + BOOL bDone = rInput.ReadLine( aLine ); + String aTmpStr( aLine, rInput.GetStreamCharSet() ), aStr; + while ( bDone ) + { + aSel = ImpInsertText( aSel, aTmpStr ); + bDone = rInput.ReadLine( aLine ); + aTmpStr = String( aLine, rInput.GetStreamCharSet() ); + if ( bDone ) + aSel = ImpInsertParaBreak( aSel.GetEnd() ); + } + + UndoActionEnd( TEXTUNDO_READ ); + + TextSelection aNewSel( aSel.GetEnd(), aSel.GetEnd() ); + + // Damit bei FormatAndUpdate nicht auf die ungueltige Selektion zugegriffen wird. + if ( GetActiveView() ) + GetActiveView()->ImpSetSelection( aNewSel ); + + SetUpdateMode( bUpdate ); + FormatAndUpdate( GetActiveView() ); + + return rInput.GetError() ? FALSE : TRUE; +} + +BOOL TextEngine::Write( SvStream& rOutput, const TextSelection* pSel, BOOL bHTML ) +{ + TextSelection aSel; + if ( pSel ) + aSel = *pSel; + else + { + ULONG nParas = mpDoc->GetNodes().Count(); + TextNode* pNode = mpDoc->GetNodes().GetObject( nParas - 1 ); + aSel.GetStart() = TextPaM( 0, 0 ); + aSel.GetEnd() = TextPaM( nParas-1, pNode->GetText().Len() ); + } + + if ( bHTML ) + { + rOutput.WriteLine( "<HTML>" ); + rOutput.WriteLine( "<BODY>" ); + } + + for ( ULONG nPara = aSel.GetStart().GetPara(); nPara <= aSel.GetEnd().GetPara(); nPara++ ) + { + TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); + + USHORT nStartPos = 0; + USHORT nEndPos = pNode->GetText().Len(); + if ( nPara == aSel.GetStart().GetPara() ) + nStartPos = aSel.GetStart().GetIndex(); + if ( nPara == aSel.GetEnd().GetPara() ) + nEndPos = aSel.GetEnd().GetIndex(); + + String aText; + if ( !bHTML ) + { + aText = pNode->GetText().Copy( nStartPos, nEndPos-nStartPos ); + } + else + { + aText.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "<P STYLE=\"margin-bottom: 0cm\">" ) ); + + if ( nStartPos == nEndPos ) + { + // Leerzeilen werden von Writer wegoptimiert + aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "<BR>" ) ); + } + else + { + USHORT nTmpStart = nStartPos; + USHORT nTmpEnd = nEndPos; + do + { + TextCharAttrib* pAttr = pNode->GetCharAttribs().FindNextAttrib( TEXTATTR_HYPERLINK, nTmpStart, nEndPos ); + nTmpEnd = pAttr ? pAttr->GetStart() : nEndPos; + + // Text vor dem Attribut + aText += pNode->GetText().Copy( nTmpStart, nTmpEnd-nTmpStart ); + + if ( pAttr ) + { + nTmpEnd = Min( pAttr->GetEnd(), nEndPos ); + + // z.B. <A HREF="http://www.mopo.de/">Morgenpost</A> + aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "<A HREF=\"" ) ); + aText += ((const TextAttribHyperLink&) pAttr->GetAttr() ).GetURL(); + aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "\">" ) ); + nTmpStart = pAttr->GetStart(); + aText += pNode->GetText().Copy( nTmpStart, nTmpEnd-nTmpStart ); + aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "</A>" ) ); + + nTmpStart = pAttr->GetEnd(); + } + } while ( nTmpEnd < nEndPos ); + } + + aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "</P>" ) ); + } + rOutput.WriteLine( ByteString( aText, rOutput.GetStreamCharSet() ) ); + } + + if ( bHTML ) + { + rOutput.WriteLine( "</BODY>" ); + rOutput.WriteLine( "</HTML>" ); + } + + return rOutput.GetError() ? FALSE : TRUE; +} + +void TextEngine::RemoveAttribs( ULONG nPara, BOOL bIdleFormatAndUpdate ) +{ + if ( nPara < mpDoc->GetNodes().Count() ) + { + TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); + if ( pNode->GetCharAttribs().Count() ) + { + pNode->GetCharAttribs().Clear( TRUE ); + + TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); + pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() ); + + mbFormatted = FALSE; + + if ( bIdleFormatAndUpdate ) + IdleFormatAndUpdate( NULL, 0xFFFF ); + else + FormatAndUpdate( NULL ); + } + } +} +void TextEngine::RemoveAttribs( ULONG nPara, USHORT nWhich, BOOL bIdleFormatAndUpdate ) +{ + if ( nPara < mpDoc->GetNodes().Count() ) + { + TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); + if ( pNode->GetCharAttribs().Count() ) + { + TextCharAttribList& rAttribs = pNode->GetCharAttribs(); + USHORT nAttrCount = rAttribs.Count(); + for(USHORT nAttr = nAttrCount; nAttr; --nAttr) + { + if(rAttribs.GetAttrib( nAttr - 1 )->Which() == nWhich) + rAttribs.RemoveAttrib( nAttr -1 ); + } + TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); + pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() ); + mbFormatted = FALSE; + if(bIdleFormatAndUpdate) + IdleFormatAndUpdate( NULL, 0xFFFF ); + else + FormatAndUpdate( NULL ); + } + } +} +void TextEngine::RemoveAttrib( ULONG nPara, const TextCharAttrib& rAttrib ) +{ + if ( nPara < mpDoc->GetNodes().Count() ) + { + TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); + if ( pNode->GetCharAttribs().Count() ) + { + TextCharAttribList& rAttribs = pNode->GetCharAttribs(); + USHORT nAttrCount = rAttribs.Count(); + for(USHORT nAttr = nAttrCount; nAttr; --nAttr) + { + if(rAttribs.GetAttrib( nAttr - 1 ) == &rAttrib) + { + rAttribs.RemoveAttrib( nAttr -1 ); + break; + } + } + TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); + pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() ); + mbFormatted = FALSE; + FormatAndUpdate( NULL ); + } + } +} + +void TextEngine::SetAttrib( const TextAttrib& rAttr, ULONG nPara, USHORT nStart, USHORT nEnd, BOOL bIdleFormatAndUpdate ) +{ + // Es wird hier erstmal nicht geprueft, ob sich Attribute ueberlappen! + // Diese Methode ist erstmal nur fuer einen Editor, der fuer eine Zeile + // _schnell_ das Syntax-Highlight einstellen will. + + // Da die TextEngine z.Zt fuer Editoren gedacht ist gibt es auch kein + // Undo fuer Attribute! + + if ( nPara < mpDoc->GetNodes().Count() ) + { + TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); + TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara ); + + USHORT nMax = pNode->GetText().Len(); + if ( nStart > nMax ) + nStart = nMax; + if ( nEnd > nMax ) + nEnd = nMax; + + pNode->GetCharAttribs().InsertAttrib( new TextCharAttrib( rAttr, nStart, nEnd ) ); + pTEParaPortion->MarkSelectionInvalid( nStart, nEnd ); + + mbFormatted = FALSE; + if ( bIdleFormatAndUpdate ) + IdleFormatAndUpdate( NULL, 0xFFFF ); + else + FormatAndUpdate( NULL ); + } +} + +void TextEngine::SetTextAlign( TxtAlign eAlign ) +{ + if ( eAlign != meAlign ) + { + meAlign = eAlign; + FormatFullDoc(); + UpdateViews(); + } +} + + +void TextEngine::ValidateSelection( TextSelection& rSel ) const +{ + ValidatePaM( rSel.GetStart() ); + ValidatePaM( rSel.GetEnd() ); +} + +void TextEngine::ValidatePaM( TextPaM& rPaM ) const +{ + ULONG nMaxPara = mpDoc->GetNodes().Count() - 1; + if ( rPaM.GetPara() > nMaxPara ) + { + rPaM.GetPara() = nMaxPara; + rPaM.GetIndex() = 0xFFFF; + } + + USHORT nMaxIndex = GetTextLen( rPaM.GetPara() ); + if ( rPaM.GetIndex() > nMaxIndex ) + rPaM.GetIndex() = nMaxIndex; +} + + +// Status & Selektionsanpassung + +void TextEngine::ImpParagraphInserted( ULONG nPara ) +{ + // Die aktive View braucht nicht angepasst werden, aber bei allen + // passiven muss die Selektion angepasst werden: + if ( mpViews->Count() > 1 ) + { + for ( USHORT nView = mpViews->Count(); nView; ) + { + TextView* pView = mpViews->GetObject( --nView ); + if ( pView != GetActiveView() ) + { +// BOOL bInvers = pView->maSelection.GetEnd() < pView->maSelection.GetStart(); +// TextPaM& rMin = !bInvers ? pView->maSelection.GetStart(): pView->maSelection.GetEnd(); +// TextPaM& rMax = bInvers ? pView->maSelection.GetStart() : pView->maSelection.GetEnd(); +// +// if ( rMin.GetPara() >= nPara ) +// rMin.GetPara()++; +// if ( rMax.GetPara() >= nPara ) +// rMax.GetPara()++; + for ( int n = 0; n <= 1; n++ ) + { + TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd(); + if ( rPaM.GetPara() >= nPara ) + rPaM.GetPara()++; + } + } + } + } + Broadcast( TextHint( TEXT_HINT_PARAINSERTED, nPara ) ); +} + +void TextEngine::ImpParagraphRemoved( ULONG nPara ) +{ + if ( mpViews->Count() > 1 ) + { + for ( USHORT nView = mpViews->Count(); nView; ) + { + TextView* pView = mpViews->GetObject( --nView ); + if ( pView != GetActiveView() ) + { + ULONG nParas = mpDoc->GetNodes().Count(); + for ( int n = 0; n <= 1; n++ ) + { + TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd(); + if ( rPaM.GetPara() > nPara ) + rPaM.GetPara()--; + else if ( rPaM.GetPara() == nPara ) + { + rPaM.GetIndex() = 0; + if ( rPaM.GetPara() >= nParas ) + rPaM.GetPara()--; + } + } + } + } + } + Broadcast( TextHint( TEXT_HINT_PARAREMOVED, nPara ) ); +} + +void TextEngine::ImpCharsRemoved( ULONG nPara, USHORT nPos, USHORT nChars ) +{ + if ( mpViews->Count() > 1 ) + { + for ( USHORT nView = mpViews->Count(); nView; ) + { + TextView* pView = mpViews->GetObject( --nView ); + if ( pView != GetActiveView() ) + { + USHORT nEnd = nPos+nChars; + for ( int n = 0; n <= 1; n++ ) + { + TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd(); + if ( rPaM.GetPara() == nPara ) + { + if ( rPaM.GetIndex() > nEnd ) + rPaM.GetIndex() = rPaM.GetIndex() - nChars; + else if ( rPaM.GetIndex() > nPos ) + rPaM.GetIndex() = nPos; + } + } + } + } + } + Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, nPara ) ); +} + +void TextEngine::ImpCharsInserted( ULONG nPara, USHORT nPos, USHORT nChars ) +{ + if ( mpViews->Count() > 1 ) + { + for ( USHORT nView = mpViews->Count(); nView; ) + { + TextView* pView = mpViews->GetObject( --nView ); + if ( pView != GetActiveView() ) + { + for ( int n = 0; n <= 1; n++ ) + { + TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd(); + if ( rPaM.GetPara() == nPara ) + { + if ( rPaM.GetIndex() >= nPos ) + rPaM.GetIndex() = rPaM.GetIndex() + nChars; + } + } + } + } + } + Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, nPara ) ); +} + +void TextEngine::ImpFormattingParagraph( ULONG nPara ) +{ + Broadcast( TextHint( TEXT_HINT_FORMATPARA, nPara ) ); +} + +void TextEngine::ImpTextHeightChanged() +{ + Broadcast( TextHint( TEXT_HINT_TEXTHEIGHTCHANGED ) ); +} + +void TextEngine::ImpTextFormatted() +{ + Broadcast( TextHint( TEXT_HINT_TEXTFORMATTED ) ); +} + +void TextEngine::Draw( OutputDevice* pDev, const Point& rPos ) +{ + ImpPaint( pDev, rPos, NULL ); +} + +void TextEngine::SetLeftMargin( USHORT n ) +{ + mpDoc->SetLeftMargin( n ); +} + +USHORT TextEngine::GetLeftMargin() const +{ + return mpDoc->GetLeftMargin(); +} + +uno::Reference< i18n::XBreakIterator > TextEngine::GetBreakIterator() +{ + if ( !mxBreakIterator.is() ) + mxBreakIterator = vcl::unohelper::CreateBreakIterator(); + DBG_ASSERT( mxBreakIterator.is(), "Could not create BreakIterator" ); + return mxBreakIterator; +} + +void TextEngine::SetLocale( const ::com::sun::star::lang::Locale& rLocale ) +{ + maLocale = rLocale; + delete mpLocaleDataWrapper; + mpLocaleDataWrapper = NULL; +} + +::com::sun::star::lang::Locale TextEngine::GetLocale() +{ + if ( !maLocale.Language.getLength() ) + { + maLocale = Application::GetSettings().GetUILocale(); + } + return maLocale; +} + +LocaleDataWrapper* TextEngine::ImpGetLocaleDataWrapper() +{ + if ( !mpLocaleDataWrapper ) + mpLocaleDataWrapper = new LocaleDataWrapper( vcl::unohelper::GetMultiServiceFactory(), GetLocale() ); + + return mpLocaleDataWrapper; +} + +void TextEngine::SetRightToLeft( BOOL bR2L ) +{ + if ( mbRightToLeft != bR2L ) + { + mbRightToLeft = bR2L; + meAlign = bR2L ? TXTALIGN_RIGHT : TXTALIGN_LEFT; + FormatFullDoc(); + UpdateViews(); + } +} + +void TextEngine::ImpInitWritingDirections( ULONG nPara ) +{ + TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara ); + TEWritingDirectionInfos& rInfos = pParaPortion->GetWritingDirectionInfos(); + rInfos.Remove( 0, rInfos.Count() ); + + if ( pParaPortion->GetNode()->GetText().Len() ) + { + const UBiDiLevel nBidiLevel = IsRightToLeft() ? 1 /*RTL*/ : 0 /*LTR*/; + String aText( pParaPortion->GetNode()->GetText() ); + + // + // Bidi functions from icu 2.0 + // + UErrorCode nError = U_ZERO_ERROR; + UBiDi* pBidi = ubidi_openSized( aText.Len(), 0, &nError ); + nError = U_ZERO_ERROR; + + ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.GetBuffer()), aText.Len(), nBidiLevel, NULL, &nError ); // UChar != sal_Unicode in MinGW + nError = U_ZERO_ERROR; + + long nCount = ubidi_countRuns( pBidi, &nError ); + + int32_t nStart = 0; + int32_t nEnd; + UBiDiLevel nCurrDir; + + for ( USHORT nIdx = 0; nIdx < nCount; ++nIdx ) + { + ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir ); + rInfos.Insert( TEWritingDirectionInfo( nCurrDir, (USHORT)nStart, (USHORT)nEnd ), rInfos.Count() ); + nStart = nEnd; + } + + ubidi_close( pBidi ); + } + + // No infos mean no CTL and default dir is L2R... + if ( !rInfos.Count() ) + rInfos.Insert( TEWritingDirectionInfo( 0, 0, (USHORT)pParaPortion->GetNode()->GetText().Len() ), rInfos.Count() ); + +} + +BYTE TextEngine::ImpGetRightToLeft( ULONG nPara, USHORT nPos, USHORT* pStart, USHORT* pEnd ) +{ + BYTE nRightToLeft = 0; + + TextNode* pNode = mpDoc->GetNodes().GetObject( nPara ); + if ( pNode && pNode->GetText().Len() ) + { + TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara ); + if ( !pParaPortion->GetWritingDirectionInfos().Count() ) + ImpInitWritingDirections( nPara ); + + TEWritingDirectionInfos& rDirInfos = pParaPortion->GetWritingDirectionInfos(); + for ( USHORT n = 0; n < rDirInfos.Count(); n++ ) + { + if ( ( rDirInfos[n].nStartPos <= nPos ) && ( rDirInfos[n].nEndPos >= nPos ) ) + { + nRightToLeft = rDirInfos[n].nType; + if ( pStart ) + *pStart = rDirInfos[n].nStartPos; + if ( pEnd ) + *pEnd = rDirInfos[n].nEndPos; + break; + } + } + } + return nRightToLeft; +} + +long TextEngine::ImpGetPortionXOffset( ULONG nPara, TextLine* pLine, USHORT nTextPortion ) +{ + long nX = pLine->GetStartX(); + + TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara ); + + for ( USHORT i = pLine->GetStartPortion(); i < nTextPortion; i++ ) + { + TETextPortion* pPortion = pParaPortion->GetTextPortions().GetObject( i ); + nX += pPortion->GetWidth(); + } + + TETextPortion* pDestPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion ); + if ( pDestPortion->GetKind() != PORTIONKIND_TAB ) + { + if ( !IsRightToLeft() && pDestPortion->GetRightToLeft() ) + { + // Portions behind must be added, visual before this portion + sal_uInt16 nTmpPortion = nTextPortion+1; + while ( nTmpPortion <= pLine->GetEndPortion() ) + { + TETextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion ); + if ( pNextTextPortion->GetRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) ) + nX += pNextTextPortion->GetWidth(); + else + break; + nTmpPortion++; + } + // Portions before must be removed, visual behind this portion + nTmpPortion = nTextPortion; + while ( nTmpPortion > pLine->GetStartPortion() ) + { + --nTmpPortion; + TETextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion ); + if ( pPrevTextPortion->GetRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) ) + nX -= pPrevTextPortion->GetWidth(); + else + break; + } + } + else if ( IsRightToLeft() && !pDestPortion->IsRightToLeft() ) + { + // Portions behind must be removed, visual behind this portion + sal_uInt16 nTmpPortion = nTextPortion+1; + while ( nTmpPortion <= pLine->GetEndPortion() ) + { + TETextPortion* pNextTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion ); + if ( !pNextTextPortion->IsRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) ) + nX += pNextTextPortion->GetWidth(); + else + break; + nTmpPortion++; + } + // Portions before must be added, visual before this portion + nTmpPortion = nTextPortion; + while ( nTmpPortion > pLine->GetStartPortion() ) + { + --nTmpPortion; + TETextPortion* pPrevTextPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion ); + if ( !pPrevTextPortion->IsRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) ) + nX -= pPrevTextPortion->GetWidth(); + else + break; + } + } + } +/* + if ( IsRightToLeft() ) + { + // Switch X postions... + DBG_ASSERT( GetMaxTextWidth(), "GetPortionXOffset - max text width?!" ); + DBG_ASSERT( nX <= (long)GetMaxTextWidth(), "GetPortionXOffset - position out of paper size!" ); + nX = GetMaxTextWidth() - nX; + nX -= pDestPortion->GetWidth(); + } +*/ + + return nX; +} + +void TextEngine::ImpInitLayoutMode( OutputDevice* pOutDev, BOOL bDrawingR2LPortion ) +{ + ULONG nLayoutMode = pOutDev->GetLayoutMode(); + + nLayoutMode &= ~(TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_COMPLEX_DISABLED | TEXT_LAYOUT_BIDI_STRONG ); + if ( bDrawingR2LPortion ) + nLayoutMode |= TEXT_LAYOUT_BIDI_RTL; + + pOutDev->SetLayoutMode( nLayoutMode ); +} + +TxtAlign TextEngine::ImpGetAlign() const +{ + TxtAlign eAlign = meAlign; + if ( IsRightToLeft() ) + { + if ( eAlign == TXTALIGN_LEFT ) + eAlign = TXTALIGN_RIGHT; + else if ( eAlign == TXTALIGN_RIGHT ) + eAlign = TXTALIGN_LEFT; + } + return eAlign; +} + +long TextEngine::ImpGetOutputOffset( ULONG nPara, TextLine* pLine, USHORT nIndex, USHORT nIndex2 ) +{ + TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara ); + + USHORT nPortionStart; + USHORT nPortion = pPortion->GetTextPortions().FindPortion( nIndex, nPortionStart, TRUE ); + + TETextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( nPortion ); + + long nX; + + if ( ( nIndex == nPortionStart ) && ( nIndex == nIndex2 ) ) + { + // Output of full portion, so we need portion x offset. + // Use ImpGetPortionXOffset, because GetXPos may deliver left or right position from portioon, depending on R2L, L2R + nX = ImpGetPortionXOffset( nPara, pLine, nPortion ); + if ( IsRightToLeft() ) + { + nX = -nX -pTextPortion->GetWidth(); + } + } + else + { + nX = ImpGetXPos( nPara, pLine, nIndex, nIndex == nPortionStart ); + if ( nIndex2 != nIndex ) + { + long nX2 = ImpGetXPos( nPara, pLine, nIndex2, FALSE ); + if ( ( !IsRightToLeft() && ( nX2 < nX ) ) || + ( IsRightToLeft() && ( nX2 > nX ) ) ) + { + nX = nX2; + } + } + if ( IsRightToLeft() ) + { + nX = -nX; + } + } + + return nX; +} diff --git a/svtools/source/edit/textund2.hxx b/svtools/source/edit/textund2.hxx new file mode 100644 index 000000000000..18cf9331328e --- /dev/null +++ b/svtools/source/edit/textund2.hxx @@ -0,0 +1,148 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _TEXTUND2_HXX +#define _TEXTUND2_HXX + +#include <textundo.hxx> + + +class TextUndoDelPara : public TextUndo +{ +private: + BOOL mbDelObject; + ULONG mnPara; + TextNode* mpNode; // Zeigt auf das gueltige, nicht zerstoerte Objekt! + +public: + TYPEINFO(); + TextUndoDelPara( TextEngine* pTextEngine, TextNode* pNode, ULONG nPara ); + ~TextUndoDelPara(); + + virtual void Undo(); + virtual void Redo(); +}; + + +class TextUndoConnectParas : public TextUndo +{ +private: + ULONG mnPara; + USHORT mnSepPos; + +public: + TYPEINFO(); + TextUndoConnectParas( TextEngine* pTextEngine, ULONG nPara, USHORT nSepPos ); + ~TextUndoConnectParas(); + + virtual void Undo(); + virtual void Redo(); +}; + + +class TextUndoSplitPara : public TextUndo +{ +private: + ULONG mnPara; + USHORT mnSepPos; + +public: + TYPEINFO(); + TextUndoSplitPara( TextEngine* pTextEngine, ULONG nPara, USHORT nSepPos ); + ~TextUndoSplitPara(); + + virtual void Undo(); + virtual void Redo(); +}; + + +class TextUndoInsertChars : public TextUndo +{ +private: + TextPaM maTextPaM; + String maText; + +public: + TYPEINFO(); + TextUndoInsertChars( TextEngine* pTextEngine, const TextPaM& rTextPaM, const String& rStr ); + +// const TextPaM& GetTextPaM() { return aTextPaM; } +// String& GetStr() { return aText; } + + virtual void Undo(); + virtual void Redo(); + + virtual BOOL Merge( SfxUndoAction *pNextAction ); +}; + + +class TextUndoRemoveChars : public TextUndo +{ +private: + TextPaM maTextPaM; + String maText; + +public: + TYPEINFO(); + TextUndoRemoveChars( TextEngine* pTextEngine, const TextPaM& rTextPaM, const String& rStr ); + +// const TextPaM& GetTextPaM() { return aTextPaM; } +// String& GetStr() { return aText; } + + virtual void Undo(); + virtual void Redo(); +}; + + +class TextUndoSetAttribs: public TextUndo +{ +private: + TextSelection maSelection; +// SfxItemSet aNewAttribs; +// TextInfoArray aPrevAttribs; +// BYTE nSpecial; +// BOOL bSetIsRemove; +// USHORT nRemoveWhich; +// +// void ImpSetSelection( TextView* pView ); + + +public: + TYPEINFO(); + TextUndoSetAttribs( TextEngine* pTextEngine, const TextSelection& rESel ); + ~TextUndoSetAttribs(); + +// TextInfoArray& GetTextInfos() { return aPrevAttribs; } +// SfxItemSet& GetNewAttribs() { return aNewAttribs; } +// void SetSpecial( BYTE n ) { nSpecial = n; } +// void SetRemoveAttribs( BOOL b ) { bSetIsRemove = b; } +// void SetRemoveWhich( USHORT n ) { nRemoveWhich = n; } + + virtual void Undo(); + virtual void Redo(); +}; + +#endif // _TEXTUND2_HXX diff --git a/svtools/source/edit/textundo.cxx b/svtools/source/edit/textundo.cxx new file mode 100644 index 000000000000..4c243de16c31 --- /dev/null +++ b/svtools/source/edit/textundo.cxx @@ -0,0 +1,343 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + +#include <svtools/texteng.hxx> +#include <svtools/textview.hxx> +#include <textundo.hxx> +#include <textund2.hxx> +#include <svtools/textdata.hxx> +#include <textdoc.hxx> +#include <textdat2.hxx> + +TYPEINIT1( TextUndo, SfxUndoAction ); +TYPEINIT1( TextUndoDelPara, TextUndo ); +TYPEINIT1( TextUndoConnectParas, TextUndo ); +TYPEINIT1( TextUndoSplitPara, TextUndo ); +TYPEINIT1( TextUndoInsertChars, TextUndo ); +TYPEINIT1( TextUndoRemoveChars, TextUndo ); +TYPEINIT1( TextUndoSetAttribs, TextUndo ); + + +TextUndoManager::TextUndoManager( TextEngine* p ) +{ + mpTextEngine = p; +} + +TextUndoManager::~TextUndoManager() +{ +} + +BOOL __EXPORT TextUndoManager::Undo( USHORT nCount ) +{ + if ( GetUndoActionCount() == 0 ) + return FALSE; + + UndoRedoStart(); + + mpTextEngine->SetIsInUndo( TRUE ); + BOOL bDone = SfxUndoManager::Undo( nCount ); + mpTextEngine->SetIsInUndo( FALSE ); + + UndoRedoEnd(); + + return bDone; +} + +BOOL __EXPORT TextUndoManager::Redo( USHORT nCount ) +{ + if ( GetRedoActionCount() == 0 ) + return FALSE; + + + UndoRedoStart(); + + mpTextEngine->SetIsInUndo( TRUE ); + BOOL bDone = SfxUndoManager::Redo( nCount ); + mpTextEngine->SetIsInUndo( FALSE ); + + UndoRedoEnd(); + + return bDone; +} + +void TextUndoManager::UndoRedoStart() +{ + DBG_ASSERT( GetView(), "Undo/Redo: Active View?" ); + +// if ( GetView() ) +// GetView()->HideSelection(); +} + +void TextUndoManager::UndoRedoEnd() +{ + if ( GetView() ) + { + TextSelection aNewSel( GetView()->GetSelection() ); + aNewSel.GetStart() = aNewSel.GetEnd(); + GetView()->ImpSetSelection( aNewSel ); + } + + mpTextEngine->UpdateSelections(); + + mpTextEngine->FormatAndUpdate( GetView() ); +} + + +TextUndo::TextUndo( USHORT nI, TextEngine* p ) +{ + mnId = nI; + mpTextEngine = p; +} + +TextUndo::~TextUndo() +{ +} + +USHORT __EXPORT TextUndo::GetId() const +{ + //nId sollte mal entfallen => GetId ueberall ueberladen... + return mnId; +} + +XubString __EXPORT TextUndo::GetComment() const +{ +// return mpTextEngine->GetUndoComment( this ); + return String(); +} + +void TextUndo::SetSelection( const TextSelection& rSel ) +{ + if ( GetView() ) + GetView()->ImpSetSelection( rSel ); +} + + +TextUndoDelPara::TextUndoDelPara( TextEngine* pTextEngine, TextNode* pNode, ULONG nPara ) + : TextUndo( TEXTUNDO_DELCONTENT, pTextEngine ) +{ + mpNode = pNode; + mnPara = nPara; + mbDelObject = TRUE; +} + +TextUndoDelPara::~TextUndoDelPara() +{ + if ( mbDelObject ) + delete mpNode; +} + +void __EXPORT TextUndoDelPara::Undo() +{ + GetTextEngine()->InsertContent( mpNode, mnPara ); + mbDelObject = FALSE; // gehoert wieder der Engine + + if ( GetView() ) + { + TextSelection aSel( TextPaM( mnPara, 0 ), TextPaM( mnPara, mpNode->GetText().Len() ) ); + SetSelection( aSel ); + } +} + +void __EXPORT TextUndoDelPara::Redo() +{ + // pNode stimmt nicht mehr, falls zwischendurch Undos, in denen + // Absaetze verschmolzen sind. + mpNode = GetDoc()->GetNodes().GetObject( mnPara ); + + delete GetTEParaPortions()->GetObject( mnPara ); + GetTEParaPortions()->Remove( mnPara ); + + // Node nicht loeschen, haengt im Undo! + GetDoc()->GetNodes().Remove( mnPara ); + GetTextEngine()->ImpParagraphRemoved( mnPara ); + + mbDelObject = TRUE; // gehoert wieder dem Undo + + ULONG nParas = GetDoc()->GetNodes().Count(); + ULONG n = mnPara < nParas ? mnPara : (nParas-1); + TextNode* pN = GetDoc()->GetNodes().GetObject( n ); + TextPaM aPaM( n, pN->GetText().Len() ); + SetSelection( aPaM ); +} + +// ----------------------------------------------------------------------- +// TextUndoConnectParas +// ------------------------------------------------------------------------ +TextUndoConnectParas::TextUndoConnectParas( TextEngine* pTextEngine, ULONG nPara, USHORT nPos ) + : TextUndo( TEXTUNDO_CONNECTPARAS, pTextEngine ) +{ + mnPara = nPara; + mnSepPos = nPos; +} + +TextUndoConnectParas::~TextUndoConnectParas() +{ +} + +void __EXPORT TextUndoConnectParas::Undo() +{ + TextPaM aPaM = GetTextEngine()->SplitContent( mnPara, mnSepPos ); + SetSelection( aPaM ); +} + +void __EXPORT TextUndoConnectParas::Redo() +{ + TextPaM aPaM = GetTextEngine()->ConnectContents( mnPara ); + SetSelection( aPaM ); +} + + +TextUndoSplitPara::TextUndoSplitPara( TextEngine* pTextEngine, ULONG nPara, USHORT nPos ) + : TextUndo( TEXTUNDO_SPLITPARA, pTextEngine ) +{ + mnPara = nPara; + mnSepPos = nPos; +} + +TextUndoSplitPara::~TextUndoSplitPara() +{ +} + +void __EXPORT TextUndoSplitPara::Undo() +{ + TextPaM aPaM = GetTextEngine()->ConnectContents( mnPara ); + SetSelection( aPaM ); +} + +void __EXPORT TextUndoSplitPara::Redo() +{ + TextPaM aPaM = GetTextEngine()->SplitContent( mnPara, mnSepPos ); + SetSelection( aPaM ); +} + + +TextUndoInsertChars::TextUndoInsertChars( TextEngine* pTextEngine, const TextPaM& rTextPaM, const XubString& rStr ) + : TextUndo( TEXTUNDO_INSERTCHARS, pTextEngine ), + maTextPaM( rTextPaM ), maText( rStr ) +{ +} + +void __EXPORT TextUndoInsertChars::Undo() +{ + TextSelection aSel( maTextPaM, maTextPaM ); + aSel.GetEnd().GetIndex() = aSel.GetEnd().GetIndex() + maText.Len(); + TextPaM aPaM = GetTextEngine()->ImpDeleteText( aSel ); + SetSelection( aPaM ); +} + +void __EXPORT TextUndoInsertChars::Redo() +{ + TextSelection aSel( maTextPaM, maTextPaM ); + GetTextEngine()->ImpInsertText( aSel, maText ); + TextPaM aNewPaM( maTextPaM ); + aNewPaM.GetIndex() = aNewPaM.GetIndex() + maText.Len(); + SetSelection( TextSelection( aSel.GetStart(), aNewPaM ) ); +} + +BOOL __EXPORT TextUndoInsertChars::Merge( SfxUndoAction* pNextAction ) +{ + if ( !pNextAction->ISA( TextUndoInsertChars ) ) + return FALSE; + + TextUndoInsertChars* pNext = (TextUndoInsertChars*)pNextAction; + + if ( maTextPaM.GetPara() != pNext->maTextPaM.GetPara() ) + return FALSE; + + if ( ( maTextPaM.GetIndex() + maText.Len() ) == pNext->maTextPaM.GetIndex() ) + { + maText += pNext->maText; + return TRUE; + } + return FALSE; +} + + +TextUndoRemoveChars::TextUndoRemoveChars( TextEngine* pTextEngine, const TextPaM& rTextPaM, const XubString& rStr ) + : TextUndo( TEXTUNDO_REMOVECHARS, pTextEngine ), + maTextPaM( rTextPaM ), maText( rStr ) +{ +} + +void __EXPORT TextUndoRemoveChars::Undo() +{ + TextSelection aSel( maTextPaM, maTextPaM ); + GetTextEngine()->ImpInsertText( aSel, maText ); + aSel.GetEnd().GetIndex() = aSel.GetEnd().GetIndex() + maText.Len(); + SetSelection( aSel ); +} + +void __EXPORT TextUndoRemoveChars::Redo() +{ + TextSelection aSel( maTextPaM, maTextPaM ); + aSel.GetEnd().GetIndex() = aSel.GetEnd().GetIndex() + maText.Len(); + TextPaM aPaM = GetTextEngine()->ImpDeleteText( aSel ); + SetSelection( aPaM ); +} + + +TextUndoSetAttribs::TextUndoSetAttribs( TextEngine* pTextEngine, const TextSelection& rSel ) + : TextUndo( TEXTUNDO_ATTRIBS, pTextEngine ), maSelection( rSel ) +{ + maSelection.Justify(); +// aNewAttribs.Set( rNewItems ); +// mbSetIsRemove = FALSE; +// mnRemoveWhich = 0; +// mnSpecial = 0; +} + +TextUndoSetAttribs::~TextUndoSetAttribs() +{ + // ............... +} + +void __EXPORT TextUndoSetAttribs::Undo() +{ + for ( ULONG nPara = maSelection.GetStart().GetPara(); nPara <= maSelection.GetEnd().GetPara(); nPara++ ) + { +// ContentAttribsInfo* pInf = aPrevAttribs[ (USHORT)(nPara-aESel.nStartPara) ]; +// GetTextEngine()->RemoveCharAttribs( nPara ); +// TextNode* pNode = GetTextEngine()->GetTextDoc().GetObject( nPara ); +// for ( USHORT nAttr = 0; nAttr < pInf->GetPrevCharAttribs().Count(); nAttr++ ) +// { +// GetTextEngine()->GetTextDoc().InsertAttrib( pNode, pX->GetStart(), pX->GetEnd(), *pX->GetItem() ); +// } + } + SetSelection( maSelection ); +} + +void __EXPORT TextUndoSetAttribs::Redo() +{ +// if ( !bSetIsRemove ) +// GetTextEngine()->SetAttribs( aSel, aNewAttribs, nSpecial ); +// else +// GetTextEngine()->RemoveCharAttribs( aSel, bRemoveParaAttribs, nRemoveWhich ); + SetSelection( maSelection ); +} diff --git a/svtools/source/edit/textundo.hxx b/svtools/source/edit/textundo.hxx new file mode 100644 index 000000000000..cc26c0b51ef6 --- /dev/null +++ b/svtools/source/edit/textundo.hxx @@ -0,0 +1,84 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _TEXTUNDO_HXX +#define _TEXTUNDO_HXX + +#include <svl/undo.hxx> + +class TextEngine; + +class TextUndoManager : public SfxUndoManager +{ + TextEngine* mpTextEngine; + +protected: + + void UndoRedoStart(); + void UndoRedoEnd(); + + TextView* GetView() const { return mpTextEngine->GetActiveView(); } + +public: + TextUndoManager( TextEngine* pTextEngine ); + ~TextUndoManager(); + + using SfxUndoManager::Undo; + virtual BOOL Undo( USHORT nCount=1 ); + using SfxUndoManager::Redo; + virtual BOOL Redo( USHORT nCount=1 ); + +}; + +class TextUndo : public SfxUndoAction +{ +private: + USHORT mnId; + TextEngine* mpTextEngine; + +protected: + + TextView* GetView() const { return mpTextEngine->GetActiveView(); } + void SetSelection( const TextSelection& rSel ); + + TextDoc* GetDoc() const { return mpTextEngine->mpDoc; } + TEParaPortions* GetTEParaPortions() const { return mpTextEngine->mpTEParaPortions; } + +public: + TYPEINFO(); + TextUndo( USHORT nId, TextEngine* pTextEngine ); + virtual ~TextUndo(); + + TextEngine* GetTextEngine() const { return mpTextEngine; } + + virtual void Undo() = 0; + virtual void Redo() = 0; + + virtual XubString GetComment() const; + virtual USHORT GetId() const; +}; + +#endif // _TEXTUNDO_HXX diff --git a/svtools/source/edit/textview.cxx b/svtools/source/edit/textview.cxx new file mode 100644 index 000000000000..48cd23bdcc6f --- /dev/null +++ b/svtools/source/edit/textview.cxx @@ -0,0 +1,2470 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" +#include <svtools/textview.hxx> +#include <svtools/texteng.hxx> +#include <textdoc.hxx> +#include <svtools/textdata.hxx> +#include <textdat2.hxx> + +#include <svl/undo.hxx> +#include <vcl/cursor.hxx> +#include <vcl/window.hxx> +#include <vcl/svapp.hxx> +#include <vcl/sound.hxx> +#include <tools/stream.hxx> + +#include <sot/formats.hxx> +#include <svl/urlbmk.hxx> + +#ifndef _COM_SUN_STAR_TEXT_XBREAKITERATOR_HPP_ +#include <com/sun/star/i18n/XBreakIterator.hpp> +#endif + +#ifndef _COM_SUN_STAR_TEXT_CHARACTERITERATORMODE_HPP_ +#include <com/sun/star/i18n/CharacterIteratorMode.hpp> +#endif + +#ifndef _COM_SUN_STAR_TEXT_WORDTYPE_HPP_ +#include <com/sun/star/i18n/WordType.hpp> +#endif +#include <cppuhelper/weak.hxx> +#include <vcl/unohelp.hxx> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp> +#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#ifndef _COM_SUN_STAR_DATATRANSFER_DND_DNDCONSTANS_HPP_ +#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp> +#endif +#include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp> +#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp> + +#include <vcl/edit.hxx> + + +#include <sot/exchange.hxx> +#include <sot/formats.hxx> + +#include <vos/mutex.hxx> + + +using namespace ::com::sun::star; + +class TETextDataObject : public ::com::sun::star::datatransfer::XTransferable, + public ::cppu::OWeakObject + +{ +private: + String maText; + SvMemoryStream maHTMLStream; + +public: + TETextDataObject( const String& rText ); + ~TETextDataObject(); + + String& GetText() { return maText; } + SvMemoryStream& GetHTMLStream() { return maHTMLStream; } + + // ::com::sun::star::uno::XInterface + ::com::sun::star::uno::Any SAL_CALL queryInterface( const ::com::sun::star::uno::Type & rType ) throw(::com::sun::star::uno::RuntimeException); + void SAL_CALL acquire() throw() { OWeakObject::acquire(); } + void SAL_CALL release() throw() { OWeakObject::release(); } + + // ::com::sun::star::datatransfer::XTransferable + ::com::sun::star::uno::Any SAL_CALL getTransferData( const ::com::sun::star::datatransfer::DataFlavor& aFlavor ) throw(::com::sun::star::datatransfer::UnsupportedFlavorException, ::com::sun::star::io::IOException, ::com::sun::star::uno::RuntimeException); + ::com::sun::star::uno::Sequence< ::com::sun::star::datatransfer::DataFlavor > SAL_CALL getTransferDataFlavors( ) throw(::com::sun::star::uno::RuntimeException); + sal_Bool SAL_CALL isDataFlavorSupported( const ::com::sun::star::datatransfer::DataFlavor& aFlavor ) throw(::com::sun::star::uno::RuntimeException); +}; + +TETextDataObject::TETextDataObject( const String& rText ) : maText( rText ) +{ +} + +TETextDataObject::~TETextDataObject() +{ +} + +// uno::XInterface +uno::Any TETextDataObject::queryInterface( const uno::Type & rType ) throw(uno::RuntimeException) +{ + uno::Any aRet = ::cppu::queryInterface( rType, SAL_STATIC_CAST( datatransfer::XTransferable*, this ) ); + return (aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType )); +} + +// datatransfer::XTransferable +uno::Any TETextDataObject::getTransferData( const datatransfer::DataFlavor& rFlavor ) throw(datatransfer::UnsupportedFlavorException, io::IOException, uno::RuntimeException) +{ + uno::Any aAny; + + ULONG nT = SotExchange::GetFormat( rFlavor ); + if ( nT == SOT_FORMAT_STRING ) + { + aAny <<= (::rtl::OUString)GetText(); + } + else if ( nT == SOT_FORMATSTR_ID_HTML ) + { + GetHTMLStream().Seek( STREAM_SEEK_TO_END ); + ULONG nLen = GetHTMLStream().Tell(); + GetHTMLStream().Seek(0); + + uno::Sequence< sal_Int8 > aSeq( nLen ); + memcpy( aSeq.getArray(), GetHTMLStream().GetData(), nLen ); + aAny <<= aSeq; + } + else + { + throw datatransfer::UnsupportedFlavorException(); + } + return aAny; +} + +uno::Sequence< datatransfer::DataFlavor > TETextDataObject::getTransferDataFlavors( ) throw(uno::RuntimeException) +{ + GetHTMLStream().Seek( STREAM_SEEK_TO_END ); + BOOL bHTML = GetHTMLStream().Tell() > 0; + uno::Sequence< datatransfer::DataFlavor > aDataFlavors( bHTML ? 2 : 1 ); + SotExchange::GetFormatDataFlavor( SOT_FORMAT_STRING, aDataFlavors.getArray()[0] ); + if ( bHTML ) + SotExchange::GetFormatDataFlavor( SOT_FORMATSTR_ID_HTML, aDataFlavors.getArray()[1] ); + return aDataFlavors; +} + +sal_Bool TETextDataObject::isDataFlavorSupported( const datatransfer::DataFlavor& rFlavor ) throw(uno::RuntimeException) +{ + ULONG nT = SotExchange::GetFormat( rFlavor ); + return ( nT == SOT_FORMAT_STRING ); +} + +/*-- 24.06.2004 13:54:36--------------------------------------------------- + + -----------------------------------------------------------------------*/ +struct ImpTextView +{ + TextEngine* mpTextEngine; + + Window* mpWindow; + TextSelection maSelection; + Point maStartDocPos; +// TextPaM maMBDownPaM; + + Cursor* mpCursor; + + TextDDInfo* mpDDInfo; + + VirtualDevice* mpVirtDev; + + SelectionEngine* mpSelEngine; + TextSelFunctionSet* mpSelFuncSet; + + ::com::sun::star::uno::Reference< ::com::sun::star::datatransfer::dnd::XDragSourceListener > mxDnDListener; + + USHORT mnTravelXPos; + + BOOL mbAutoScroll : 1; + BOOL mbInsertMode : 1; + BOOL mbReadOnly : 1; + BOOL mbPaintSelection : 1; + BOOL mbAutoIndent : 1; + BOOL mbHighlightSelection : 1; + BOOL mbCursorEnabled : 1; + BOOL mbClickedInSelection : 1; + BOOL mbSupportProtectAttribute : 1; + bool mbCursorAtEndOfLine; +}; + +// ------------------------------------------------------------------------- +// (+) class TextView +// ------------------------------------------------------------------------- +TextView::TextView( TextEngine* pEng, Window* pWindow ) : + mpImpl(new ImpTextView) +{ + pWindow->EnableRTL( FALSE ); + + mpImpl->mpWindow = pWindow; + mpImpl->mpTextEngine = pEng; + mpImpl->mpVirtDev = NULL; + + mpImpl->mbPaintSelection = TRUE; + mpImpl->mbAutoScroll = TRUE; + mpImpl->mbInsertMode = TRUE; + mpImpl->mbReadOnly = FALSE; + mpImpl->mbHighlightSelection = FALSE; + mpImpl->mbAutoIndent = FALSE; + mpImpl->mbCursorEnabled = TRUE; + mpImpl->mbClickedInSelection = FALSE; + mpImpl->mbSupportProtectAttribute = FALSE; + mpImpl->mbCursorAtEndOfLine = false; +// mbInSelection = FALSE; + + mpImpl->mnTravelXPos = TRAVEL_X_DONTKNOW; + + mpImpl->mpSelFuncSet = new TextSelFunctionSet( this ); + mpImpl->mpSelEngine = new SelectionEngine( mpImpl->mpWindow, mpImpl->mpSelFuncSet ); + mpImpl->mpSelEngine->SetSelectionMode( RANGE_SELECTION ); + mpImpl->mpSelEngine->EnableDrag( TRUE ); + + mpImpl->mpCursor = new Cursor; + mpImpl->mpCursor->Show(); + pWindow->SetCursor( mpImpl->mpCursor ); + pWindow->SetInputContext( InputContext( pEng->GetFont(), INPUTCONTEXT_TEXT|INPUTCONTEXT_EXTTEXTINPUT ) ); + + if ( pWindow->GetSettings().GetStyleSettings().GetSelectionOptions() & SELECTION_OPTION_INVERT ) + mpImpl->mbHighlightSelection = TRUE; + + pWindow->SetLineColor(); + + mpImpl->mpDDInfo = NULL; + + if ( pWindow->GetDragGestureRecognizer().is() ) + { + vcl::unohelper::DragAndDropWrapper* pDnDWrapper = new vcl::unohelper::DragAndDropWrapper( this ); + mpImpl->mxDnDListener = pDnDWrapper; + + uno::Reference< datatransfer::dnd::XDragGestureListener> xDGL( mpImpl->mxDnDListener, uno::UNO_QUERY ); + pWindow->GetDragGestureRecognizer()->addDragGestureListener( xDGL ); + uno::Reference< datatransfer::dnd::XDropTargetListener> xDTL( xDGL, uno::UNO_QUERY ); + pWindow->GetDropTarget()->addDropTargetListener( xDTL ); + pWindow->GetDropTarget()->setActive( sal_True ); + pWindow->GetDropTarget()->setDefaultActions( datatransfer::dnd::DNDConstants::ACTION_COPY_OR_MOVE ); + } +} + +TextView::~TextView() +{ + delete mpImpl->mpSelEngine; + delete mpImpl->mpSelFuncSet; + delete mpImpl->mpVirtDev; + + if ( mpImpl->mpWindow->GetCursor() == mpImpl->mpCursor ) + mpImpl->mpWindow->SetCursor( 0 ); + delete mpImpl->mpCursor; + delete mpImpl->mpDDInfo; + delete mpImpl; +} + +void TextView::Invalidate() +{ + mpImpl->mpWindow->Invalidate(); +} + +void TextView::SetSelection( const TextSelection& rTextSel, BOOL bGotoCursor ) +{ + // Falls jemand gerade ein leeres Attribut hinterlassen hat, + // und dann der Outliner die Selektion manipulitert: + if ( !mpImpl->maSelection.HasRange() ) + mpImpl->mpTextEngine->CursorMoved( mpImpl->maSelection.GetStart().GetPara() ); + + // Wenn nach einem KeyInput die Selection manipuliert wird: + mpImpl->mpTextEngine->CheckIdleFormatter(); + + HideSelection(); + TextSelection aNewSel( rTextSel ); + mpImpl->mpTextEngine->ValidateSelection( aNewSel ); + ImpSetSelection( aNewSel ); + ShowSelection(); + ShowCursor( bGotoCursor ); +} + +void TextView::SetSelection( const TextSelection& rTextSel ) +{ + SetSelection( rTextSel, mpImpl->mbAutoScroll ); +} + +const TextSelection& TextView::GetSelection() const +{ + return mpImpl->maSelection; +} +TextSelection& TextView::GetSelection() +{ + return mpImpl->maSelection; +} + +void TextView::DeleteSelected() +{ +// HideSelection(); + + mpImpl->mpTextEngine->UndoActionStart( TEXTUNDO_DELETE ); + TextPaM aPaM = mpImpl->mpTextEngine->ImpDeleteText( mpImpl->maSelection ); + mpImpl->mpTextEngine->UndoActionEnd( TEXTUNDO_DELETE ); + + ImpSetSelection( aPaM ); + mpImpl->mpTextEngine->FormatAndUpdate( this ); + ShowCursor(); +} + +void TextView::ImpPaint( OutputDevice* pOut, const Point& rStartPos, Rectangle const* pPaintArea, TextSelection const* pPaintRange, TextSelection const* pSelection ) +{ + if ( !mpImpl->mbPaintSelection ) + pSelection = NULL; + else + { + // Richtige Hintergrundfarbe einstellen. + // Ich bekomme leider nicht mit, ob sich diese inzwischen geaendert hat. + Font aFont = mpImpl->mpTextEngine->GetFont(); + Color aColor = pOut->GetBackground().GetColor(); + aColor.SetTransparency( 0 ); + if ( aColor != aFont.GetFillColor() ) + { + if( aFont.IsTransparent() ) + aColor = Color( COL_TRANSPARENT ); + aFont.SetFillColor( aColor ); + mpImpl->mpTextEngine->maFont = aFont; + } + } + + mpImpl->mpTextEngine->ImpPaint( pOut, rStartPos, pPaintArea, pPaintRange, pSelection ); +} + +void TextView::Paint( const Rectangle& rRect ) +{ + ImpPaint( rRect, FALSE ); +} + +void TextView::ImpPaint( const Rectangle& rRect, BOOL bUseVirtDev ) +{ + if ( !mpImpl->mpTextEngine->GetUpdateMode() || mpImpl->mpTextEngine->IsInUndo() ) + return; + + TextSelection *pDrawSelection = NULL; + if ( !mpImpl->mbHighlightSelection && mpImpl->maSelection.HasRange() ) + pDrawSelection = &mpImpl->maSelection; + + if ( bUseVirtDev ) + { + VirtualDevice* pVDev = GetVirtualDevice(); + + const Color& rBackgroundColor = mpImpl->mpWindow->GetBackground().GetColor(); + if ( pVDev->GetFillColor() != rBackgroundColor ) + pVDev->SetFillColor( rBackgroundColor ); + if ( pVDev->GetBackground().GetColor() != rBackgroundColor ) + pVDev->SetBackground( rBackgroundColor ); + + BOOL bVDevValid = TRUE; + Size aOutSz( pVDev->GetOutputSizePixel() ); + if ( ( aOutSz.Width() < rRect.GetWidth() ) || + ( aOutSz.Height() < rRect.GetHeight() ) ) + { + bVDevValid = pVDev->SetOutputSizePixel( rRect.GetSize() ); + } + else + { + // Das VirtDev kann bei einem Resize sehr gross werden => + // irgendwann mal kleiner machen! + if ( ( aOutSz.Height() > ( rRect.GetHeight() + 20 ) ) || + ( aOutSz.Width() > ( rRect.GetWidth() + 20 ) ) ) + { + bVDevValid = pVDev->SetOutputSizePixel( rRect.GetSize() ); + } + else + { + pVDev->Erase(); + } + } + if ( !bVDevValid ) + { + ImpPaint( rRect, FALSE /* ohne VDev */ ); + return; + } + + Rectangle aTmpRec( Point( 0, 0 ), rRect.GetSize() ); + + Point aDocPos( mpImpl->maStartDocPos.X(), mpImpl->maStartDocPos.Y() + rRect.Top() ); + Point aStartPos = ImpGetOutputStartPos( aDocPos ); + ImpPaint( pVDev, aStartPos, &aTmpRec, NULL, pDrawSelection ); + mpImpl->mpWindow->DrawOutDev( rRect.TopLeft(), rRect.GetSize(), + Point(0,0), rRect.GetSize(), *pVDev ); +// ShowSelection(); + if ( mpImpl->mbHighlightSelection ) + ImpHighlight( mpImpl->maSelection ); + } + else + { + Point aStartPos = ImpGetOutputStartPos( mpImpl->maStartDocPos ); + ImpPaint( mpImpl->mpWindow, aStartPos, &rRect, NULL, pDrawSelection ); + +// ShowSelection(); + if ( mpImpl->mbHighlightSelection ) + ImpHighlight( mpImpl->maSelection ); + } +} + +void TextView::ImpHighlight( const TextSelection& rSel ) +{ + TextSelection aSel( rSel ); + aSel.Justify(); + if ( aSel.HasRange() && !mpImpl->mpTextEngine->IsInUndo() && mpImpl->mpTextEngine->GetUpdateMode() ) + { + mpImpl->mpCursor->Hide(); + + DBG_ASSERT( !mpImpl->mpTextEngine->mpIdleFormatter->IsActive(), "ImpHighlight: Not formatted!" ); + + Rectangle aVisArea( mpImpl->maStartDocPos, mpImpl->mpWindow->GetOutputSizePixel() ); + long nY = 0; + ULONG nStartPara = aSel.GetStart().GetPara(); + ULONG nEndPara = aSel.GetEnd().GetPara(); + for ( ULONG nPara = 0; nPara <= nEndPara; nPara++ ) + { + long nParaHeight = (long)mpImpl->mpTextEngine->CalcParaHeight( nPara ); + if ( ( nPara >= nStartPara ) && ( ( nY + nParaHeight ) > aVisArea.Top() ) ) + { + TEParaPortion* pTEParaPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( nPara ); + USHORT nStartLine = 0; + USHORT nEndLine = pTEParaPortion->GetLines().Count() -1; + if ( nPara == nStartPara ) + nStartLine = pTEParaPortion->GetLineNumber( aSel.GetStart().GetIndex(), FALSE ); + if ( nPara == nEndPara ) + nEndLine = pTEParaPortion->GetLineNumber( aSel.GetEnd().GetIndex(), TRUE ); + + // ueber die Zeilen iterieren.... + for ( USHORT nLine = nStartLine; nLine <= nEndLine; nLine++ ) + { + TextLine* pLine = pTEParaPortion->GetLines().GetObject( nLine ); + USHORT nStartIndex = pLine->GetStart(); + USHORT nEndIndex = pLine->GetEnd(); + if ( ( nPara == nStartPara ) && ( nLine == nStartLine ) ) + nStartIndex = aSel.GetStart().GetIndex(); + if ( ( nPara == nEndPara ) && ( nLine == nEndLine ) ) + nEndIndex = aSel.GetEnd().GetIndex(); + + // Kann passieren, wenn am Anfang einer umgebrochenen Zeile. + if ( nEndIndex < nStartIndex ) + nEndIndex = nStartIndex; + + Rectangle aTmpRec( mpImpl->mpTextEngine->GetEditCursor( TextPaM( nPara, nStartIndex ), FALSE ) ); + aTmpRec.Top() += nY; + aTmpRec.Bottom() += nY; + Point aTopLeft( aTmpRec.TopLeft() ); + + aTmpRec = mpImpl->mpTextEngine->GetEditCursor( TextPaM( nPara, nEndIndex ), TRUE ); + aTmpRec.Top() += nY; + aTmpRec.Bottom() += nY; + Point aBottomRight( aTmpRec.BottomRight() ); + aBottomRight.X()--; + + // Nur Painten, wenn im sichtbaren Bereich... + if ( ( aTopLeft.X() < aBottomRight.X() ) && ( aBottomRight.Y() >= aVisArea.Top() ) ) + { + Point aPnt1( GetWindowPos( aTopLeft ) ); + Point aPnt2( GetWindowPos( aBottomRight ) ); + + Rectangle aRect( aPnt1, aPnt2 ); + mpImpl->mpWindow->Invert( aRect ); + } + } + } + nY += nParaHeight; + + if ( nY >= aVisArea.Bottom() ) + break; + } + } +} + +void TextView::ImpSetSelection( const TextSelection& rSelection ) +{ + if ( rSelection != mpImpl->maSelection ) + { + mpImpl->maSelection = rSelection; + mpImpl->mpTextEngine->Broadcast( TextHint( TEXT_HINT_VIEWSELECTIONCHANGED ) ); + } +} + +void TextView::ShowSelection() +{ + ImpShowHideSelection( TRUE ); +} + +void TextView::HideSelection() +{ + ImpShowHideSelection( FALSE ); +} + +void TextView::ShowSelection( const TextSelection& rRange ) +{ + ImpShowHideSelection( TRUE, &rRange ); +} + +void TextView::ImpShowHideSelection( BOOL bShow, const TextSelection* pRange ) +{ + const TextSelection* pRangeOrSelection = pRange ? pRange : &mpImpl->maSelection; + + if ( pRangeOrSelection->HasRange() ) + { + if ( mpImpl->mbHighlightSelection ) + { + ImpHighlight( *pRangeOrSelection ); + } + else + { + if( mpImpl->mpWindow->IsPaintTransparent() ) + mpImpl->mpWindow->Invalidate(); + else + { + Rectangle aOutArea( Point( 0, 0 ), mpImpl->mpWindow->GetOutputSizePixel() ); + Point aStartPos( ImpGetOutputStartPos( mpImpl->maStartDocPos ) ); + TextSelection aRange( *pRangeOrSelection ); + aRange.Justify(); + BOOL bVisCursor = mpImpl->mpCursor->IsVisible(); + mpImpl->mpCursor->Hide(); + ImpPaint( mpImpl->mpWindow, aStartPos, &aOutArea, &aRange, bShow ? &mpImpl->maSelection : NULL ); + if ( bVisCursor ) + mpImpl->mpCursor->Show(); + } + } + } +} + +VirtualDevice* TextView::GetVirtualDevice() +{ + if ( !mpImpl->mpVirtDev ) + { + mpImpl->mpVirtDev = new VirtualDevice; + mpImpl->mpVirtDev->SetLineColor(); + } + return mpImpl->mpVirtDev; +} + +void TextView::EraseVirtualDevice() +{ + delete mpImpl->mpVirtDev; + mpImpl->mpVirtDev = 0; +} + +BOOL TextView::KeyInput( const KeyEvent& rKeyEvent ) +{ + BOOL bDone = TRUE; + BOOL bModified = FALSE; + BOOL bMoved = FALSE; + BOOL bEndKey = FALSE; // spezielle CursorPosition + BOOL bAllowIdle = TRUE; + + // Um zu pruefen ob durch irgendeine Aktion mModified, das lokale + // bModified wird z.B. bei Cut/Paste nicht gesetzt, weil dort an anderen + // Stellen das updaten erfolgt. + BOOL bWasModified = mpImpl->mpTextEngine->IsModified(); + mpImpl->mpTextEngine->SetModified( FALSE ); + + TextSelection aCurSel( mpImpl->maSelection ); + TextSelection aOldSel( aCurSel ); + + USHORT nCode = rKeyEvent.GetKeyCode().GetCode(); + KeyFuncType eFunc = rKeyEvent.GetKeyCode().GetFunction(); + if ( eFunc != KEYFUNC_DONTKNOW ) + { + switch ( eFunc ) + { + case KEYFUNC_CUT: + { + if ( !mpImpl->mbReadOnly ) + Cut(); + } + break; + case KEYFUNC_COPY: + { + Copy(); + } + break; + case KEYFUNC_PASTE: + { + if ( !mpImpl->mbReadOnly ) + Paste(); + } + break; + case KEYFUNC_UNDO: + { + if ( !mpImpl->mbReadOnly ) + Undo(); + } + break; + case KEYFUNC_REDO: + { + if ( !mpImpl->mbReadOnly ) + Redo(); + } + break; + + default: // wird dann evtl. unten bearbeitet. + eFunc = KEYFUNC_DONTKNOW; + } + } + if ( eFunc == KEYFUNC_DONTKNOW ) + { + switch ( nCode ) + { + case KEY_UP: + case KEY_DOWN: + case KEY_LEFT: + case KEY_RIGHT: + case KEY_HOME: + case KEY_END: + case KEY_PAGEUP: + case KEY_PAGEDOWN: + case com::sun::star::awt::Key::MOVE_WORD_FORWARD: + case com::sun::star::awt::Key::SELECT_WORD_FORWARD: + case com::sun::star::awt::Key::MOVE_WORD_BACKWARD: + case com::sun::star::awt::Key::SELECT_WORD_BACKWARD: + case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_LINE: + case com::sun::star::awt::Key::MOVE_TO_END_OF_LINE: + case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_LINE: + case com::sun::star::awt::Key::SELECT_TO_END_OF_LINE: + case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH: + case com::sun::star::awt::Key::MOVE_TO_END_OF_PARAGRAPH: + case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH: + case com::sun::star::awt::Key::SELECT_TO_END_OF_PARAGRAPH: + case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT: + case com::sun::star::awt::Key::MOVE_TO_END_OF_DOCUMENT: + case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT: + case com::sun::star::awt::Key::SELECT_TO_END_OF_DOCUMENT: + { + if ( ( !rKeyEvent.GetKeyCode().IsMod2() || ( nCode == KEY_LEFT ) || ( nCode == KEY_RIGHT ) ) + && !( rKeyEvent.GetKeyCode().IsMod1() && ( nCode == KEY_PAGEUP || nCode == KEY_PAGEDOWN ) ) ) + { + aCurSel = ImpMoveCursor( rKeyEvent ); + if ( aCurSel.HasRange() ) { + uno::Reference<datatransfer::clipboard::XClipboard> aSelection(GetWindow()->GetPrimarySelection()); + Copy( aSelection ); + } + bMoved = TRUE; + if ( nCode == KEY_END ) + bEndKey = TRUE; + } + else + bDone = FALSE; + } + break; + case KEY_BACKSPACE: + case KEY_DELETE: + case com::sun::star::awt::Key::DELETE_WORD_BACKWARD: + case com::sun::star::awt::Key::DELETE_WORD_FORWARD: + case com::sun::star::awt::Key::DELETE_TO_BEGIN_OF_LINE: + case com::sun::star::awt::Key::DELETE_TO_END_OF_LINE: + { + if ( !mpImpl->mbReadOnly && !rKeyEvent.GetKeyCode().IsMod2() ) + { + BYTE nDel = ( nCode == KEY_DELETE ) ? DEL_RIGHT : DEL_LEFT; + BYTE nMode = rKeyEvent.GetKeyCode().IsMod1() ? DELMODE_RESTOFWORD : DELMODE_SIMPLE; + if ( ( nMode == DELMODE_RESTOFWORD ) && rKeyEvent.GetKeyCode().IsShift() ) + nMode = DELMODE_RESTOFCONTENT; + + switch( nCode ) + { + case com::sun::star::awt::Key::DELETE_WORD_BACKWARD: + nDel = DEL_LEFT; + nMode = DELMODE_RESTOFWORD; + break; + case com::sun::star::awt::Key::DELETE_WORD_FORWARD: + nDel = DEL_RIGHT; + nMode = DELMODE_RESTOFWORD; + break; + case com::sun::star::awt::Key::DELETE_TO_BEGIN_OF_LINE: + nDel = DEL_LEFT; + nMode = DELMODE_RESTOFCONTENT; + break; + case com::sun::star::awt::Key::DELETE_TO_END_OF_LINE: + nDel = DEL_RIGHT; + nMode = DELMODE_RESTOFCONTENT; + break; + default: break; + } + + mpImpl->mpTextEngine->UndoActionStart( TEXTUNDO_DELETE ); + if(mpImpl->mbSupportProtectAttribute) + { + //expand selection to include all protected content - if there is any + const TextCharAttrib* pStartAttr = mpImpl->mpTextEngine->FindCharAttrib( + TextPaM(mpImpl->maSelection.GetStart().GetPara(), + mpImpl->maSelection.GetStart().GetIndex()), + TEXTATTR_PROTECTED ); + const TextCharAttrib* pEndAttr = mpImpl->mpTextEngine->FindCharAttrib( + TextPaM(mpImpl->maSelection.GetEnd().GetPara(), + mpImpl->maSelection.GetEnd().GetIndex()), + TEXTATTR_PROTECTED ); + if(pStartAttr && pStartAttr->GetStart() < mpImpl->maSelection.GetStart().GetIndex()) + { + mpImpl->maSelection.GetStart().GetIndex() = pStartAttr->GetStart(); + } + if(pEndAttr && pEndAttr->GetEnd() > mpImpl->maSelection.GetEnd().GetIndex()) + { + mpImpl->maSelection.GetEnd().GetIndex() = pEndAttr->GetEnd(); + } + } + aCurSel = ImpDelete( nDel, nMode ); + mpImpl->mpTextEngine->UndoActionEnd( TEXTUNDO_DELETE ); + bModified = TRUE; + bAllowIdle = FALSE; + } + else + bDone = FALSE; + } + break; + case KEY_TAB: + { + if ( !mpImpl->mbReadOnly && !rKeyEvent.GetKeyCode().IsShift() && + !rKeyEvent.GetKeyCode().IsMod1() && !rKeyEvent.GetKeyCode().IsMod2() && + ImplCheckTextLen( 'x' ) ) + { + aCurSel = mpImpl->mpTextEngine->ImpInsertText( aCurSel, '\t', !IsInsertMode() ); + bModified = TRUE; + } + else + bDone = FALSE; + } + break; + case KEY_RETURN: + { + // Shift-RETURN darf nicht geschluckt werden, weil dann keine + // mehrzeilige Eingabe in Dialogen/Property-Editor moeglich. + if ( !mpImpl->mbReadOnly && !rKeyEvent.GetKeyCode().IsMod1() && + !rKeyEvent.GetKeyCode().IsMod2() && ImplCheckTextLen( 'x' ) ) + { + mpImpl->mpTextEngine->UndoActionStart( TEXTUNDO_INSERT ); + aCurSel = mpImpl->mpTextEngine->ImpInsertParaBreak( aCurSel ); + if ( mpImpl->mbAutoIndent ) + { + TextNode* pPrev = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aCurSel.GetEnd().GetPara() - 1 ); + USHORT n = 0; + while ( ( n < pPrev->GetText().Len() ) && ( + ( pPrev->GetText().GetChar( n ) == ' ' ) || + ( pPrev->GetText().GetChar( n ) == '\t' ) ) ) + { + n++; + } + if ( n ) + aCurSel = mpImpl->mpTextEngine->ImpInsertText( aCurSel, pPrev->GetText().Copy( 0, n ) ); + } + mpImpl->mpTextEngine->UndoActionEnd( TEXTUNDO_INSERT ); + bModified = TRUE; + } + else + bDone = FALSE; + } + break; + case KEY_INSERT: + { + if ( !mpImpl->mbReadOnly ) + SetInsertMode( !IsInsertMode() ); + } + break; + default: + { + if ( TextEngine::IsSimpleCharInput( rKeyEvent ) ) + { + xub_Unicode nCharCode = rKeyEvent.GetCharCode(); + if ( !mpImpl->mbReadOnly && ImplCheckTextLen( nCharCode ) ) // sonst trotzdem das Zeichen schlucken... + { + aCurSel = mpImpl->mpTextEngine->ImpInsertText( nCharCode, aCurSel, !IsInsertMode(), sal_True ); + bModified = TRUE; + } + } + else + bDone = FALSE; + } + } + } + + if ( aCurSel != aOldSel ) // Check if changed, maybe other method already changed mpImpl->maSelection, don't overwrite that! + ImpSetSelection( aCurSel ); + + mpImpl->mpTextEngine->UpdateSelections(); + + if ( ( nCode != KEY_UP ) && ( nCode != KEY_DOWN ) ) + mpImpl->mnTravelXPos = TRAVEL_X_DONTKNOW; + + if ( bModified ) + { + // Idle-Formatter nur, wenn AnyInput. + if ( bAllowIdle && Application::AnyInput( INPUT_KEYBOARD) ) + mpImpl->mpTextEngine->IdleFormatAndUpdate( this ); + else + mpImpl->mpTextEngine->FormatAndUpdate( this); + } + else if ( bMoved ) + { + // Selection wird jetzt gezielt in ImpMoveCursor gemalt. + ImpShowCursor( mpImpl->mbAutoScroll, TRUE, bEndKey ); + } + + if ( mpImpl->mpTextEngine->IsModified() ) + mpImpl->mpTextEngine->Broadcast( TextHint( TEXT_HINT_MODIFIED ) ); + else if ( bWasModified ) + mpImpl->mpTextEngine->SetModified( TRUE ); + + return bDone; +} + +void TextView::MouseButtonUp( const MouseEvent& rMouseEvent ) +{ + mpImpl->mbClickedInSelection = FALSE; + mpImpl->mnTravelXPos = TRAVEL_X_DONTKNOW; + mpImpl->mpSelEngine->SelMouseButtonUp( rMouseEvent ); + if ( rMouseEvent.IsMiddle() && !IsReadOnly() && + ( GetWindow()->GetSettings().GetMouseSettings().GetMiddleButtonAction() == MOUSE_MIDDLE_PASTESELECTION ) ) + { + uno::Reference<datatransfer::clipboard::XClipboard> aSelection(GetWindow()->GetPrimarySelection()); + Paste( aSelection ); + if ( mpImpl->mpTextEngine->IsModified() ) + mpImpl->mpTextEngine->Broadcast( TextHint( TEXT_HINT_MODIFIED ) ); + } + else if ( rMouseEvent.IsLeft() && GetSelection().HasRange() ) + { + uno::Reference<datatransfer::clipboard::XClipboard> aSelection(GetWindow()->GetPrimarySelection()); + Copy( aSelection ); + } +} + +void TextView::MouseButtonDown( const MouseEvent& rMouseEvent ) +{ + mpImpl->mpTextEngine->CheckIdleFormatter(); // Falls schnelles Tippen und MouseButtonDown + mpImpl->mnTravelXPos = TRAVEL_X_DONTKNOW; + mpImpl->mbClickedInSelection = IsSelectionAtPoint( rMouseEvent.GetPosPixel() ); + + mpImpl->mpTextEngine->SetActiveView( this ); + + mpImpl->mpSelEngine->SelMouseButtonDown( rMouseEvent ); + + // mbu 20.01.2005 - SelMouseButtonDown() possibly triggers a 'selection changed' + // notification. The appropriate handler could change the current selection, + // which is the case in the MailMerge address block control. To enable select'n'drag + // we need to reevaluate the selection after the notification has been fired. + mpImpl->mbClickedInSelection = IsSelectionAtPoint( rMouseEvent.GetPosPixel() ); + + // Sonderbehandlungen + if ( !rMouseEvent.IsShift() && ( rMouseEvent.GetClicks() >= 2 ) ) + { + if ( rMouseEvent.IsMod2() ) + { + HideSelection(); + ImpSetSelection( mpImpl->maSelection.GetEnd() ); + SetCursorAtPoint( rMouseEvent.GetPosPixel() ); // Wird von SelectionEngine bei MOD2 nicht gesetzt + } + + if ( rMouseEvent.GetClicks() == 2 ) + { + // Wort selektieren + if ( mpImpl->maSelection.GetEnd().GetIndex() < mpImpl->mpTextEngine->GetTextLen( mpImpl->maSelection.GetEnd().GetPara() ) ) + { + HideSelection(); + TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( mpImpl->maSelection.GetEnd().GetPara() ); + uno::Reference < i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator(); + i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), mpImpl->maSelection.GetEnd().GetIndex(), mpImpl->mpTextEngine->GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True ); + TextSelection aNewSel( mpImpl->maSelection ); + aNewSel.GetStart().GetIndex() = (USHORT)aBoundary.startPos; + aNewSel.GetEnd().GetIndex() = (USHORT)aBoundary.endPos; + if(mpImpl->mbSupportProtectAttribute) + { + //expand selection to include all protected content - if there is any + const TextCharAttrib* pStartAttr = mpImpl->mpTextEngine->FindCharAttrib( + TextPaM(aNewSel.GetStart().GetPara(), + (USHORT)aBoundary.startPos), + TEXTATTR_PROTECTED ); + const TextCharAttrib* pEndAttr = mpImpl->mpTextEngine->FindCharAttrib( + TextPaM(aNewSel.GetEnd().GetPara(), + (USHORT)aBoundary.endPos), + TEXTATTR_PROTECTED ); + if(pStartAttr && pStartAttr->GetStart() < aNewSel.GetStart().GetIndex()) + { + aNewSel.GetStart().GetIndex() = pStartAttr->GetStart(); + } + if(pEndAttr && pEndAttr->GetEnd() > aNewSel.GetEnd().GetIndex()) + { + aNewSel.GetEnd().GetIndex() = pEndAttr->GetEnd(); + } + } + ImpSetSelection( aNewSel ); + ShowSelection(); + ShowCursor( TRUE, TRUE ); + } + } + else if ( rMouseEvent.GetClicks() == 3 ) + { + // Absatz selektieren + if ( mpImpl->maSelection.GetStart().GetIndex() || ( mpImpl->maSelection.GetEnd().GetIndex() < mpImpl->mpTextEngine->GetTextLen( mpImpl->maSelection.GetEnd().GetPara() ) ) ) + { + HideSelection(); + TextSelection aNewSel( mpImpl->maSelection ); + aNewSel.GetStart().GetIndex() = 0; + aNewSel.GetEnd().GetIndex() = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( mpImpl->maSelection.GetEnd().GetPara() )->GetText().Len(); + ImpSetSelection( aNewSel ); + ShowSelection(); + ShowCursor( TRUE, TRUE ); + } + } + } +} + + +void TextView::MouseMove( const MouseEvent& rMouseEvent ) +{ + mpImpl->mnTravelXPos = TRAVEL_X_DONTKNOW; + mpImpl->mpSelEngine->SelMouseMove( rMouseEvent ); +} + +void TextView::Command( const CommandEvent& rCEvt ) +{ + mpImpl->mpTextEngine->CheckIdleFormatter(); // Falls schnelles Tippen und MouseButtonDown + mpImpl->mpTextEngine->SetActiveView( this ); + + if ( rCEvt.GetCommand() == COMMAND_STARTEXTTEXTINPUT ) + { + DeleteSelected(); + delete mpImpl->mpTextEngine->mpIMEInfos; + TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( GetSelection().GetEnd().GetPara() ); + mpImpl->mpTextEngine->mpIMEInfos = new TEIMEInfos( GetSelection().GetEnd(), pNode->GetText().Copy( GetSelection().GetEnd().GetIndex() ) ); + mpImpl->mpTextEngine->mpIMEInfos->bWasCursorOverwrite = !IsInsertMode(); + } + else if ( rCEvt.GetCommand() == COMMAND_ENDEXTTEXTINPUT ) + { + DBG_ASSERT( mpImpl->mpTextEngine->mpIMEInfos, "COMMAND_ENDEXTTEXTINPUT => Kein Start ?" ); + if( mpImpl->mpTextEngine->mpIMEInfos ) + { + TEParaPortion* pPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetPara() ); + pPortion->MarkSelectionInvalid( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetIndex(), 0 ); + + BOOL bInsertMode = !mpImpl->mpTextEngine->mpIMEInfos->bWasCursorOverwrite; + + delete mpImpl->mpTextEngine->mpIMEInfos; + mpImpl->mpTextEngine->mpIMEInfos = NULL; + + mpImpl->mpTextEngine->FormatAndUpdate( this ); + + SetInsertMode( bInsertMode ); + + if ( mpImpl->mpTextEngine->IsModified() ) + mpImpl->mpTextEngine->Broadcast( TextHint( TEXT_HINT_MODIFIED ) ); + } + } + else if ( rCEvt.GetCommand() == COMMAND_EXTTEXTINPUT ) + { + DBG_ASSERT( mpImpl->mpTextEngine->mpIMEInfos, "COMMAND_EXTTEXTINPUT => Kein Start ?" ); + if( mpImpl->mpTextEngine->mpIMEInfos ) + { + const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData(); + + if ( !pData->IsOnlyCursorChanged() ) + { + TextSelection aSelect( mpImpl->mpTextEngine->mpIMEInfos->aPos ); + aSelect.GetEnd().GetIndex() = aSelect.GetEnd().GetIndex() + mpImpl->mpTextEngine->mpIMEInfos->nLen; + aSelect = mpImpl->mpTextEngine->ImpDeleteText( aSelect ); + aSelect = mpImpl->mpTextEngine->ImpInsertText( aSelect, pData->GetText() ); + + if ( mpImpl->mpTextEngine->mpIMEInfos->bWasCursorOverwrite ) + { + USHORT nOldIMETextLen = mpImpl->mpTextEngine->mpIMEInfos->nLen; + USHORT nNewIMETextLen = pData->GetText().Len(); + + if ( ( nOldIMETextLen > nNewIMETextLen ) && + ( nNewIMETextLen < mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.Len() ) ) + { + // restore old characters + USHORT nRestore = nOldIMETextLen - nNewIMETextLen; + TextPaM aPaM( mpImpl->mpTextEngine->mpIMEInfos->aPos ); + aPaM.GetIndex() = aPaM.GetIndex() + nNewIMETextLen; + mpImpl->mpTextEngine->ImpInsertText( aPaM, mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.Copy( nNewIMETextLen, nRestore ) ); + } + else if ( ( nOldIMETextLen < nNewIMETextLen ) && + ( nOldIMETextLen < mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.Len() ) ) + { + // overwrite + USHORT nOverwrite = nNewIMETextLen - nOldIMETextLen; + if ( ( nOldIMETextLen + nOverwrite ) > mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.Len() ) + nOverwrite = mpImpl->mpTextEngine->mpIMEInfos->aOldTextAfterStartPos.Len() - nOldIMETextLen; + DBG_ASSERT( nOverwrite && (nOverwrite < 0xFF00), "IME Overwrite?!" ); + TextPaM aPaM( mpImpl->mpTextEngine->mpIMEInfos->aPos ); + aPaM.GetIndex() = aPaM.GetIndex() + nNewIMETextLen; + TextSelection aSel( aPaM ); + aSel.GetEnd().GetIndex() = + aSel.GetEnd().GetIndex() + nOverwrite; + mpImpl->mpTextEngine->ImpDeleteText( aSel ); + } + } + + if ( pData->GetTextAttr() ) + { + mpImpl->mpTextEngine->mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().Len() ); + mpImpl->mpTextEngine->mpIMEInfos->bCursor = pData->IsCursorVisible(); + } + else + { + mpImpl->mpTextEngine->mpIMEInfos->DestroyAttribs(); + } + + TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetPara() ); + pPPortion->MarkSelectionInvalid( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetIndex(), 0 ); + mpImpl->mpTextEngine->FormatAndUpdate( this ); + } + + TextSelection aNewSel = TextPaM( mpImpl->mpTextEngine->mpIMEInfos->aPos.GetPara(), mpImpl->mpTextEngine->mpIMEInfos->aPos.GetIndex()+pData->GetCursorPos() ); + SetSelection( aNewSel ); + SetInsertMode( !pData->IsCursorOverwrite() ); + + if ( pData->IsCursorVisible() ) + ShowCursor(); + else + HideCursor(); + } + } + else if ( rCEvt.GetCommand() == COMMAND_CURSORPOS ) + { + if ( mpImpl->mpTextEngine->mpIMEInfos && mpImpl->mpTextEngine->mpIMEInfos->nLen ) + { + TextPaM aPaM( GetSelection().GetEnd() ); + Rectangle aR1 = mpImpl->mpTextEngine->PaMtoEditCursor( aPaM ); + + USHORT nInputEnd = mpImpl->mpTextEngine->mpIMEInfos->aPos.GetIndex() + mpImpl->mpTextEngine->mpIMEInfos->nLen; + + if ( !mpImpl->mpTextEngine->IsFormatted() ) + mpImpl->mpTextEngine->FormatDoc(); + + TEParaPortion* pParaPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() ); + USHORT nLine = pParaPortion->GetLineNumber( aPaM.GetIndex(), sal_True ); + TextLine* pLine = pParaPortion->GetLines().GetObject( nLine ); + if ( pLine && ( nInputEnd > pLine->GetEnd() ) ) + nInputEnd = pLine->GetEnd(); + Rectangle aR2 = mpImpl->mpTextEngine->PaMtoEditCursor( TextPaM( aPaM.GetPara(), nInputEnd ) ); + + long nWidth = aR2.Left()-aR1.Right(); + aR1.Move( -GetStartDocPos().X(), -GetStartDocPos().Y() ); + GetWindow()->SetCursorRect( &aR1, nWidth ); + } + else + { + GetWindow()->SetCursorRect(); + } + } + else + { + mpImpl->mpSelEngine->Command( rCEvt ); + } +} + +void TextView::ShowCursor( BOOL bGotoCursor, BOOL bForceVisCursor ) +{ + // Die Einstellung hat mehr Gewicht: + if ( !mpImpl->mbAutoScroll ) + bGotoCursor = FALSE; + ImpShowCursor( bGotoCursor, bForceVisCursor, FALSE ); +} + +void TextView::HideCursor() +{ + mpImpl->mpCursor->Hide(); +} + +void TextView::Scroll( long ndX, long ndY ) +{ + DBG_ASSERT( mpImpl->mpTextEngine->IsFormatted(), "Scroll: Nicht formatiert!" ); + + if ( !ndX && !ndY ) + return; + + Point aNewStartPos( mpImpl->maStartDocPos ); + + // Vertical: + aNewStartPos.Y() -= ndY; + if ( aNewStartPos.Y() < 0 ) + aNewStartPos.Y() = 0; + + // Horizontal: + aNewStartPos.X() -= ndX; + if ( aNewStartPos.X() < 0 ) + aNewStartPos.X() = 0; + + long nDiffX = mpImpl->maStartDocPos.X() - aNewStartPos.X(); + long nDiffY = mpImpl->maStartDocPos.Y() - aNewStartPos.Y(); + + if ( nDiffX || nDiffY ) + { + BOOL bVisCursor = mpImpl->mpCursor->IsVisible(); + mpImpl->mpCursor->Hide(); + mpImpl->mpWindow->Update(); + mpImpl->maStartDocPos = aNewStartPos; + + if ( mpImpl->mpTextEngine->IsRightToLeft() ) + nDiffX = -nDiffX; + mpImpl->mpWindow->Scroll( nDiffX, nDiffY ); + mpImpl->mpWindow->Update(); + mpImpl->mpCursor->SetPos( mpImpl->mpCursor->GetPos() + Point( nDiffX, nDiffY ) ); + if ( bVisCursor && !mpImpl->mbReadOnly ) + mpImpl->mpCursor->Show(); + } + + mpImpl->mpTextEngine->Broadcast( TextHint( TEXT_HINT_VIEWSCROLLED ) ); +} + +void TextView::Undo() +{ + mpImpl->mpTextEngine->SetActiveView( this ); + mpImpl->mpTextEngine->GetUndoManager().Undo( 1 ); +} + +void TextView::Redo() +{ + mpImpl->mpTextEngine->SetActiveView( this ); + mpImpl->mpTextEngine->GetUndoManager().Redo( 0 ); +} + +void TextView::Cut() +{ + mpImpl->mpTextEngine->UndoActionStart( TEXTUNDO_CUT ); + Copy(); + DeleteSelected(); + mpImpl->mpTextEngine->UndoActionEnd( TEXTUNDO_CUT ); +} + +void TextView::Copy( uno::Reference< datatransfer::clipboard::XClipboard >& rxClipboard ) +{ + if ( rxClipboard.is() ) + { + TETextDataObject* pDataObj = new TETextDataObject( GetSelected() ); + + if ( mpImpl->mpTextEngine->HasAttrib( TEXTATTR_HYPERLINK ) ) // Dann auch als HTML + mpImpl->mpTextEngine->Write( pDataObj->GetHTMLStream(), &mpImpl->maSelection, TRUE ); + + const sal_uInt32 nRef = Application::ReleaseSolarMutex(); + + try + { + rxClipboard->setContents( pDataObj, NULL ); + + uno::Reference< datatransfer::clipboard::XFlushableClipboard > xFlushableClipboard( rxClipboard, uno::UNO_QUERY ); + if( xFlushableClipboard.is() ) + xFlushableClipboard->flushClipboard(); + } + catch( const ::com::sun::star::uno::Exception& ) + { + } + + Application::AcquireSolarMutex( nRef ); + } +} + +void TextView::Copy() +{ + uno::Reference<datatransfer::clipboard::XClipboard> aClipboard(GetWindow()->GetClipboard()); + Copy( aClipboard ); +} + +void TextView::Paste( uno::Reference< datatransfer::clipboard::XClipboard >& rxClipboard ) +{ + if ( rxClipboard.is() ) + { + uno::Reference< datatransfer::XTransferable > xDataObj; + + const sal_uInt32 nRef = Application::ReleaseSolarMutex(); + + try + { + xDataObj = rxClipboard->getContents(); + } + catch( const ::com::sun::star::uno::Exception& ) + { + } + + Application::AcquireSolarMutex( nRef ); + + if ( xDataObj.is() ) + { + datatransfer::DataFlavor aFlavor; + SotExchange::GetFormatDataFlavor( SOT_FORMAT_STRING, aFlavor ); + if ( xDataObj->isDataFlavorSupported( aFlavor ) ) + { + try + { + uno::Any aData = xDataObj->getTransferData( aFlavor ); + ::rtl::OUString aText; + aData >>= aText; + bool bWasTruncated = false; + if( mpImpl->mpTextEngine->GetMaxTextLen() != 0 ) + bWasTruncated = ImplTruncateNewText( aText ); + InsertNewText( aText, FALSE ); + mpImpl->mpTextEngine->Broadcast( TextHint( TEXT_HINT_MODIFIED ) ); + + if( bWasTruncated ) + Edit::ShowTruncationWarning( mpImpl->mpWindow ); + } + catch( const ::com::sun::star::datatransfer::UnsupportedFlavorException& ) + { + } + } + } + } +} + +void TextView::Paste() +{ + uno::Reference<datatransfer::clipboard::XClipboard> aClipboard(GetWindow()->GetClipboard()); + Paste( aClipboard ); +} + +String TextView::GetSelected() +{ + return GetSelected( GetSystemLineEnd() ); +} + +String TextView::GetSelected( LineEnd aSeparator ) +{ + return mpImpl->mpTextEngine->GetText( mpImpl->maSelection, aSeparator ); +} + +void TextView::SetInsertMode( BOOL bInsert ) +{ + if ( mpImpl->mbInsertMode != bInsert ) + { + mpImpl->mbInsertMode = bInsert; + ShowCursor( mpImpl->mbAutoScroll, FALSE ); + } +} + +void TextView::SetReadOnly( BOOL bReadOnly ) +{ + if ( mpImpl->mbReadOnly != bReadOnly ) + { + mpImpl->mbReadOnly = bReadOnly; + if ( !mpImpl->mbReadOnly ) + ShowCursor( mpImpl->mbAutoScroll, FALSE ); + else + HideCursor(); + + GetWindow()->SetInputContext( InputContext( mpImpl->mpTextEngine->GetFont(), bReadOnly ? INPUTCONTEXT_TEXT|INPUTCONTEXT_EXTTEXTINPUT : 0 ) ); + } +} + +TextSelection TextView::ImpMoveCursor( const KeyEvent& rKeyEvent ) +{ + // Eigentlich nur bei Up/Down noetig, aber was solls. + mpImpl->mpTextEngine->CheckIdleFormatter(); + + TextPaM aPaM( mpImpl->maSelection.GetEnd() ); + TextPaM aOldEnd( aPaM ); + + TextDirectionality eTextDirection = TextDirectionality_LeftToRight_TopToBottom; + if ( mpImpl->mpTextEngine->IsRightToLeft() ) + eTextDirection = TextDirectionality_RightToLeft_TopToBottom; + + KeyEvent aTranslatedKeyEvent = rKeyEvent.LogicalTextDirectionality( eTextDirection ); + + BOOL bCtrl = aTranslatedKeyEvent.GetKeyCode().IsMod1() ? TRUE : FALSE; + USHORT nCode = aTranslatedKeyEvent.GetKeyCode().GetCode(); + + bool bSelect = aTranslatedKeyEvent.GetKeyCode().IsShift(); + switch ( nCode ) + { + case KEY_UP: aPaM = CursorUp( aPaM ); + break; + case KEY_DOWN: aPaM = CursorDown( aPaM ); + break; + case KEY_HOME: aPaM = bCtrl ? CursorStartOfDoc() : CursorStartOfLine( aPaM ); + break; + case KEY_END: aPaM = bCtrl ? CursorEndOfDoc() : CursorEndOfLine( aPaM ); + break; + case KEY_PAGEUP: aPaM = bCtrl ? CursorStartOfDoc() : PageUp( aPaM ); + break; + case KEY_PAGEDOWN: aPaM = bCtrl ? CursorEndOfDoc() : PageDown( aPaM ); + break; + case KEY_LEFT: aPaM = bCtrl ? CursorWordLeft( aPaM ) : CursorLeft( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? (USHORT)i18n::CharacterIteratorMode::SKIPCHARACTER : (USHORT)i18n::CharacterIteratorMode::SKIPCELL ); + break; + case KEY_RIGHT: aPaM = bCtrl ? CursorWordRight( aPaM ) : CursorRight( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? (USHORT)i18n::CharacterIteratorMode::SKIPCHARACTER : (USHORT)i18n::CharacterIteratorMode::SKIPCELL ); + break; + case com::sun::star::awt::Key::SELECT_WORD_FORWARD: + bSelect = true; // fallthrough intentional + case com::sun::star::awt::Key::MOVE_WORD_FORWARD: + aPaM = CursorWordRight( aPaM ); + break; + case com::sun::star::awt::Key::SELECT_WORD_BACKWARD: + bSelect = true; // fallthrough intentional + case com::sun::star::awt::Key::MOVE_WORD_BACKWARD: + aPaM = CursorWordLeft( aPaM ); + break; + case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_LINE: + bSelect = true; // fallthrough intentional + case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_LINE: + aPaM = CursorStartOfLine( aPaM ); + break; + case com::sun::star::awt::Key::SELECT_TO_END_OF_LINE: + bSelect = true; // fallthrough intentional + case com::sun::star::awt::Key::MOVE_TO_END_OF_LINE: + aPaM = CursorEndOfLine( aPaM ); + break; + case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH: + bSelect = true; // falltthrough intentional + case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH: + aPaM = CursorStartOfParagraph( aPaM ); + break; + case com::sun::star::awt::Key::SELECT_TO_END_OF_PARAGRAPH: + bSelect = true; // falltthrough intentional + case com::sun::star::awt::Key::MOVE_TO_END_OF_PARAGRAPH: + aPaM = CursorEndOfParagraph( aPaM ); + break; + case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT: + bSelect = true; // falltthrough intentional + case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT: + aPaM = CursorStartOfDoc(); + break; + case com::sun::star::awt::Key::SELECT_TO_END_OF_DOCUMENT: + bSelect = true; // falltthrough intentional + case com::sun::star::awt::Key::MOVE_TO_END_OF_DOCUMENT: + aPaM = CursorEndOfDoc(); + break; + } + + // Bewirkt evtl. ein CreateAnchor oder Deselection all + mpImpl->mpSelEngine->CursorPosChanging( bSelect, aTranslatedKeyEvent.GetKeyCode().IsMod1() ); + + if ( aOldEnd != aPaM ) + { + mpImpl->mpTextEngine->CursorMoved( aOldEnd.GetPara() ); + + + TextSelection aOldSelection( mpImpl->maSelection ); + TextSelection aNewSelection( mpImpl->maSelection ); + aNewSelection.GetEnd() = aPaM; + if ( bSelect ) + { + // Dann wird die Selektion erweitert... + ImpSetSelection( aNewSelection ); + ShowSelection( TextSelection( aOldEnd, aPaM ) ); + } + else + { + aNewSelection.GetStart() = aPaM; + ImpSetSelection( aNewSelection ); + } + } + + return mpImpl->maSelection; +} + +void TextView::InsertText( const XubString& rStr, BOOL bSelect ) +{ + InsertNewText( rStr, bSelect ); +} + +void TextView::InsertNewText( const rtl::OUString& rStr, BOOL bSelect ) +{ +// HideSelection(); + mpImpl->mpTextEngine->UndoActionStart( TEXTUNDO_INSERT ); + + /* #i87633# + break inserted text into chunks that fit into the underlying String + based API (which has a maximum length of 65534 elements + + note: this will of course still cause problems for lines longer than those + 65534 elements, but those cases will hopefully be few. + In the long run someone should switch the TextEngine to OUString instead of String + */ + sal_Int32 nLen = rStr.getLength(); + sal_Int32 nPos = 0; + while( nLen ) + { + sal_Int32 nChunkLen = nLen > 65534 ? 65534 : nLen; + String aChunk( rStr.copy( nPos, nChunkLen ) ); + + TextSelection aNewSel( mpImpl->maSelection ); + + TextPaM aPaM = mpImpl->mpTextEngine->ImpInsertText( mpImpl->maSelection, aChunk ); + + if ( bSelect ) + { + aNewSel.Justify(); + aNewSel.GetEnd() = aPaM; + } + else + { + aNewSel = aPaM; + } + + ImpSetSelection( aNewSel ); + nLen -= nChunkLen; + nPos += nChunkLen; + } + mpImpl->mpTextEngine->UndoActionEnd( TEXTUNDO_INSERT ); + + mpImpl->mpTextEngine->FormatAndUpdate( this ); +} + +/* +void TextView::InsertText( const XubString& rStr, BOOL bSelect ) +{ +// HideSelection(); + + TextSelection aNewSel( mpImpl->maSelection ); + + mpImpl->mpTextEngine->UndoActionStart( TEXTUNDO_INSERT ); + TextPaM aPaM = mpImpl->mpTextEngine->ImpInsertText( mpImpl->maSelection, rStr ); + mpImpl->mpTextEngine->UndoActionEnd( TEXTUNDO_INSERT ); + + if ( bSelect ) + { + aNewSel.Justify(); + aNewSel.GetEnd() = aPaM; + } + else + { + aNewSel = aPaM; + } + + ImpSetSelection( aNewSel ); + + mpImpl->mpTextEngine->FormatAndUpdate( this ); +} +*/ + +// OLD +TextPaM TextView::CursorLeft( const TextPaM& rPaM, BOOL bWordMode ) +{ + return bWordMode ? CursorWordLeft( rPaM ) : CursorLeft( rPaM, (USHORT)i18n::CharacterIteratorMode::SKIPCELL ); + + // Remove (USHORT) typecasts in this file when removing this method! +} + +// OLD +TextPaM TextView::CursorRight( const TextPaM& rPaM, BOOL bWordMode ) +{ + return bWordMode ? CursorWordRight( rPaM ) : CursorRight( rPaM, (USHORT)i18n::CharacterIteratorMode::SKIPCELL ); + + // Remove (USHORT) typecasts in this file when removing this method! +} + +TextPaM TextView::CursorLeft( const TextPaM& rPaM, USHORT nCharacterIteratorMode ) +{ + TextPaM aPaM( rPaM ); + + if ( aPaM.GetIndex() ) + { + TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aPaM.GetPara() ); + uno::Reference < i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator(); + sal_Int32 nCount = 1; + aPaM.GetIndex() = (USHORT)xBI->previousCharacters( pNode->GetText(), aPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), nCharacterIteratorMode, nCount, nCount ); + } + else if ( aPaM.GetPara() ) + { + aPaM.GetPara()--; + TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aPaM.GetPara() ); + aPaM.GetIndex() = pNode->GetText().Len(); + } + return aPaM; +} + +TextPaM TextView::CursorRight( const TextPaM& rPaM, USHORT nCharacterIteratorMode ) +{ + TextPaM aPaM( rPaM ); + + TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aPaM.GetPara() ); + if ( aPaM.GetIndex() < pNode->GetText().Len() ) + { + uno::Reference < i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator(); + sal_Int32 nCount = 1; + aPaM.GetIndex() = (USHORT)xBI->nextCharacters( pNode->GetText(), aPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), nCharacterIteratorMode, nCount, nCount ); + } + else if ( aPaM.GetPara() < ( mpImpl->mpTextEngine->mpDoc->GetNodes().Count()-1) ) + { + aPaM.GetPara()++; + aPaM.GetIndex() = 0; + } + + return aPaM; +} + + +TextPaM TextView::CursorWordLeft( const TextPaM& rPaM ) +{ + TextPaM aPaM( rPaM ); + + if ( aPaM.GetIndex() ) + { + TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aPaM.GetPara() ); + uno::Reference < i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator(); + i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), rPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True ); + if ( aBoundary.startPos >= rPaM.GetIndex() ) + aBoundary = xBI->previousWord( pNode->GetText(), rPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES ); + aPaM.GetIndex() = ( aBoundary.startPos != -1 ) ? (USHORT)aBoundary.startPos : 0; + } + else if ( aPaM.GetPara() ) + { + aPaM.GetPara()--; + TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aPaM.GetPara() ); + aPaM.GetIndex() = pNode->GetText().Len(); + } + return aPaM; +} + + +TextPaM TextView::CursorWordRight( const TextPaM& rPaM ) +{ + TextPaM aPaM( rPaM ); + + TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aPaM.GetPara() ); + if ( aPaM.GetIndex() < pNode->GetText().Len() ) + { + uno::Reference < i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator(); + i18n::Boundary aBoundary = xBI->nextWord( pNode->GetText(), aPaM.GetIndex(), mpImpl->mpTextEngine->GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES ); + aPaM.GetIndex() = (USHORT)aBoundary.startPos; + } + else if ( aPaM.GetPara() < ( mpImpl->mpTextEngine->mpDoc->GetNodes().Count()-1) ) + { + aPaM.GetPara()++; + aPaM.GetIndex() = 0; + } + + return aPaM; +} + +TextPaM TextView::ImpDelete( BYTE nMode, BYTE nDelMode ) +{ + if ( mpImpl->maSelection.HasRange() ) // dann nur Sel. loeschen + return mpImpl->mpTextEngine->ImpDeleteText( mpImpl->maSelection ); + + TextPaM aStartPaM = mpImpl->maSelection.GetStart(); + TextPaM aEndPaM = aStartPaM; + if ( nMode == DEL_LEFT ) + { + if ( nDelMode == DELMODE_SIMPLE ) + { + aEndPaM = CursorLeft( aEndPaM, (USHORT)i18n::CharacterIteratorMode::SKIPCHARACTER ); + } + else if ( nDelMode == DELMODE_RESTOFWORD ) + { + TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aEndPaM.GetPara() ); + uno::Reference < i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator(); + i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), mpImpl->maSelection.GetEnd().GetIndex(), mpImpl->mpTextEngine->GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True ); + if ( aBoundary.startPos == mpImpl->maSelection.GetEnd().GetIndex() ) + aBoundary = xBI->previousWord( pNode->GetText(), mpImpl->maSelection.GetEnd().GetIndex(), mpImpl->mpTextEngine->GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES ); + // #i63506# startPos is -1 when the paragraph starts with a tab + aEndPaM.GetIndex() = (aBoundary.startPos >= 0) ? (USHORT)aBoundary.startPos : 0; + } + else // DELMODE_RESTOFCONTENT + { + if ( aEndPaM.GetIndex() != 0 ) + aEndPaM.GetIndex() = 0; + else if ( aEndPaM.GetPara() ) + { + // Absatz davor + aEndPaM.GetPara()--; + aEndPaM.GetIndex() = 0; + } + } + } + else + { + if ( nDelMode == DELMODE_SIMPLE ) + { + aEndPaM = CursorRight( aEndPaM, (USHORT)i18n::CharacterIteratorMode::SKIPCELL ); + } + else if ( nDelMode == DELMODE_RESTOFWORD ) + { + TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aEndPaM.GetPara() ); + uno::Reference < i18n::XBreakIterator > xBI = mpImpl->mpTextEngine->GetBreakIterator(); + i18n::Boundary aBoundary = xBI->nextWord( pNode->GetText(), mpImpl->maSelection.GetEnd().GetIndex(), mpImpl->mpTextEngine->GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES ); + aEndPaM.GetIndex() = (USHORT)aBoundary.startPos; + } + else // DELMODE_RESTOFCONTENT + { + TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aEndPaM.GetPara() ); + if ( aEndPaM.GetIndex() < pNode->GetText().Len() ) + aEndPaM.GetIndex() = pNode->GetText().Len(); + else if ( aEndPaM.GetPara() < ( mpImpl->mpTextEngine->mpDoc->GetNodes().Count() - 1 ) ) + { + // Absatz danach + aEndPaM.GetPara()++; + TextNode* pNextNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aEndPaM.GetPara() ); + aEndPaM.GetIndex() = pNextNode->GetText().Len(); + } + } + } + + return mpImpl->mpTextEngine->ImpDeleteText( TextSelection( aStartPaM, aEndPaM ) ); +} + + + +TextPaM TextView::CursorUp( const TextPaM& rPaM ) +{ + TextPaM aPaM( rPaM ); + + long nX; + if ( mpImpl->mnTravelXPos == TRAVEL_X_DONTKNOW ) + { + nX = mpImpl->mpTextEngine->GetEditCursor( rPaM, FALSE ).Left(); + mpImpl->mnTravelXPos = (USHORT)nX+1; + } + else + nX = mpImpl->mnTravelXPos; + + TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( rPaM.GetPara() ); + USHORT nLine = pPPortion->GetLineNumber( rPaM.GetIndex(), FALSE ); + if ( nLine ) // gleicher Absatz + { + USHORT nCharPos = mpImpl->mpTextEngine->GetCharPos( rPaM.GetPara(), nLine-1, nX ); + aPaM.GetIndex() = nCharPos; + // Wenn davor eine autom.Umgebrochene Zeile, und ich muss genau an das + // Ende dieser Zeile, landet der Cursor in der aktuellen Zeile am Anfang + // Siehe Problem: Letztes Zeichen einer autom.umgebr. Zeile = Cursor + TextLine* pLine = pPPortion->GetLines().GetObject( nLine - 1 ); + if ( aPaM.GetIndex() && ( aPaM.GetIndex() == pLine->GetEnd() ) ) + aPaM.GetIndex()--; + } + else if ( rPaM.GetPara() ) // vorheriger Absatz + { + aPaM.GetPara()--; + pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() ); + USHORT nL = pPPortion->GetLines().Count() - 1; + USHORT nCharPos = mpImpl->mpTextEngine->GetCharPos( aPaM.GetPara(), nL, nX+1 ); + aPaM.GetIndex() = nCharPos; + } + + return aPaM; +} + +TextPaM TextView::CursorDown( const TextPaM& rPaM ) +{ + TextPaM aPaM( rPaM ); + + long nX; + if ( mpImpl->mnTravelXPos == TRAVEL_X_DONTKNOW ) + { + nX = mpImpl->mpTextEngine->GetEditCursor( rPaM, FALSE ).Left(); + mpImpl->mnTravelXPos = (USHORT)nX+1; + } + else + nX = mpImpl->mnTravelXPos; + + TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( rPaM.GetPara() ); + USHORT nLine = pPPortion->GetLineNumber( rPaM.GetIndex(), FALSE ); + if ( nLine < ( pPPortion->GetLines().Count() - 1 ) ) + { + USHORT nCharPos = mpImpl->mpTextEngine->GetCharPos( rPaM.GetPara(), nLine+1, nX ); + aPaM.GetIndex() = nCharPos; + + // Sonderbehandlung siehe CursorUp... + TextLine* pLine = pPPortion->GetLines().GetObject( nLine + 1 ); + if ( ( aPaM.GetIndex() == pLine->GetEnd() ) && ( aPaM.GetIndex() > pLine->GetStart() ) && aPaM.GetIndex() < pPPortion->GetNode()->GetText().Len() ) + aPaM.GetIndex()--; + } + else if ( rPaM.GetPara() < ( mpImpl->mpTextEngine->mpDoc->GetNodes().Count() - 1 ) ) // naechster Absatz + { + aPaM.GetPara()++; + pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() ); + USHORT nCharPos = mpImpl->mpTextEngine->GetCharPos( aPaM.GetPara(), 0, nX+1 ); + aPaM.GetIndex() = nCharPos; + TextLine* pLine = pPPortion->GetLines().GetObject( 0 ); + if ( ( aPaM.GetIndex() == pLine->GetEnd() ) && ( aPaM.GetIndex() > pLine->GetStart() ) && ( pPPortion->GetLines().Count() > 1 ) ) + aPaM.GetIndex()--; + } + + return aPaM; +} + +TextPaM TextView::CursorStartOfLine( const TextPaM& rPaM ) +{ + TextPaM aPaM( rPaM ); + + TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( rPaM.GetPara() ); + USHORT nLine = pPPortion->GetLineNumber( aPaM.GetIndex(), FALSE ); + TextLine* pLine = pPPortion->GetLines().GetObject( nLine ); + aPaM.GetIndex() = pLine->GetStart(); + + return aPaM; +} + +TextPaM TextView::CursorEndOfLine( const TextPaM& rPaM ) +{ + TextPaM aPaM( rPaM ); + + TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( rPaM.GetPara() ); + USHORT nLine = pPPortion->GetLineNumber( aPaM.GetIndex(), FALSE ); + TextLine* pLine = pPPortion->GetLines().GetObject( nLine ); + aPaM.GetIndex() = pLine->GetEnd(); + + if ( pLine->GetEnd() > pLine->GetStart() ) // Leerzeile + { + xub_Unicode cLastChar = pPPortion->GetNode()->GetText().GetChar((USHORT)(aPaM.GetIndex()-1) ); + if ( ( cLastChar == ' ' ) && ( aPaM.GetIndex() != pPPortion->GetNode()->GetText().Len() ) ) + { + // Bei einem Blank in einer autom. umgebrochenen Zeile macht es Sinn, + // davor zu stehen, da der Anwender hinter das Wort will. + // Wenn diese geaendert wird, Sonderbehandlung fuer Pos1 nach End! + aPaM.GetIndex()--; + } + } + return aPaM; +} + +TextPaM TextView::CursorStartOfParagraph( const TextPaM& rPaM ) +{ + TextPaM aPaM( rPaM ); + aPaM.GetIndex() = 0; + return aPaM; +} + +TextPaM TextView::CursorEndOfParagraph( const TextPaM& rPaM ) +{ + TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( rPaM.GetPara() ); + TextPaM aPaM( rPaM ); + aPaM.GetIndex() = pNode->GetText().Len(); + return aPaM; +} + +TextPaM TextView::CursorStartOfDoc() +{ + TextPaM aPaM( 0, 0 ); + return aPaM; +} + +TextPaM TextView::CursorEndOfDoc() +{ + ULONG nNode = mpImpl->mpTextEngine->mpDoc->GetNodes().Count() - 1; + TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( nNode ); + TextPaM aPaM( nNode, pNode->GetText().Len() ); + return aPaM; +} + +TextPaM TextView::PageUp( const TextPaM& rPaM ) +{ + Rectangle aRec = mpImpl->mpTextEngine->PaMtoEditCursor( rPaM ); + Point aTopLeft = aRec.TopLeft(); + aTopLeft.Y() -= mpImpl->mpWindow->GetOutputSizePixel().Height() * 9/10; + aTopLeft.X() += 1; + if ( aTopLeft.Y() < 0 ) + aTopLeft.Y() = 0; + + TextPaM aPaM = mpImpl->mpTextEngine->GetPaM( aTopLeft ); + return aPaM; +} + +TextPaM TextView::PageDown( const TextPaM& rPaM ) +{ + Rectangle aRec = mpImpl->mpTextEngine->PaMtoEditCursor( rPaM ); + Point aBottomRight = aRec.BottomRight(); + aBottomRight.Y() += mpImpl->mpWindow->GetOutputSizePixel().Height() * 9/10; + aBottomRight.X() += 1; + long nHeight = mpImpl->mpTextEngine->GetTextHeight(); + if ( aBottomRight.Y() > nHeight ) + aBottomRight.Y() = nHeight-1; + + TextPaM aPaM = mpImpl->mpTextEngine->GetPaM( aBottomRight ); + return aPaM; +} + +void TextView::ImpShowCursor( BOOL bGotoCursor, BOOL bForceVisCursor, BOOL bSpecial ) +{ + if ( mpImpl->mpTextEngine->IsFormatting() ) + return; + if ( mpImpl->mpTextEngine->GetUpdateMode() == FALSE ) + return; + if ( mpImpl->mpTextEngine->IsInUndo() ) + return; + + mpImpl->mpTextEngine->CheckIdleFormatter(); + if ( !mpImpl->mpTextEngine->IsFormatted() ) + mpImpl->mpTextEngine->FormatAndUpdate( this ); + + + TextPaM aPaM( mpImpl->maSelection.GetEnd() ); + Rectangle aEditCursor = mpImpl->mpTextEngine->PaMtoEditCursor( aPaM, bSpecial ); + + // Remember that we placed the cursor behind the last character of a line + mpImpl->mbCursorAtEndOfLine = false; + if( bSpecial ) + { + TEParaPortion* pParaPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() ); + mpImpl->mbCursorAtEndOfLine = + pParaPortion->GetLineNumber( aPaM.GetIndex(), TRUE ) != pParaPortion->GetLineNumber( aPaM.GetIndex(), FALSE ); + } + + if ( !IsInsertMode() && !mpImpl->maSelection.HasRange() ) + { + TextNode* pNode = mpImpl->mpTextEngine->mpDoc->GetNodes().GetObject( aPaM.GetPara() ); + if ( pNode->GetText().Len() && ( aPaM.GetIndex() < pNode->GetText().Len() ) ) + { + // If we are behind a portion, and the next portion has other direction, we must change position... + aEditCursor.Left() = aEditCursor.Right() = mpImpl->mpTextEngine->GetEditCursor( aPaM, FALSE, TRUE ).Left(); + + TEParaPortion* pParaPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() ); + + USHORT nTextPortionStart = 0; + USHORT nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nTextPortionStart, TRUE ); + TETextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject( nTextPortion ); + if ( pTextPortion->GetKind() == PORTIONKIND_TAB ) + { + if ( mpImpl->mpTextEngine->IsRightToLeft() ) + { + + } + aEditCursor.Right() += pTextPortion->GetWidth(); + } + else + { + TextPaM aNext = CursorRight( TextPaM( aPaM.GetPara(), aPaM.GetIndex() ), (USHORT)i18n::CharacterIteratorMode::SKIPCELL ); + aEditCursor.Right() = mpImpl->mpTextEngine->GetEditCursor( aNext, TRUE ).Left(); + } + } + } + + Size aOutSz = mpImpl->mpWindow->GetOutputSizePixel(); + if ( aEditCursor.GetHeight() > aOutSz.Height() ) + aEditCursor.Bottom() = aEditCursor.Top() + aOutSz.Height() - 1; + + aEditCursor.Left() -= 1; + + if ( bGotoCursor + // #i81283# protext maStartDocPos against initialization problems + && aOutSz.Width() && aOutSz.Height() + ) + { + long nVisStartY = mpImpl->maStartDocPos.Y(); + long nVisEndY = mpImpl->maStartDocPos.Y() + aOutSz.Height(); + long nVisStartX = mpImpl->maStartDocPos.X(); + long nVisEndX = mpImpl->maStartDocPos.X() + aOutSz.Width(); + long nMoreX = aOutSz.Width() / 4; + + Point aNewStartPos( mpImpl->maStartDocPos ); + + if ( aEditCursor.Bottom() > nVisEndY ) + { + aNewStartPos.Y() += ( aEditCursor.Bottom() - nVisEndY ); + } + else if ( aEditCursor.Top() < nVisStartY ) + { + aNewStartPos.Y() -= ( nVisStartY - aEditCursor.Top() ); + } + + if ( aEditCursor.Right() >= nVisEndX ) + { + aNewStartPos.X() += ( aEditCursor.Right() - nVisEndX ); + + // Darfs ein bischen mehr sein? + aNewStartPos.X() += nMoreX; + } + else if ( aEditCursor.Left() <= nVisStartX ) + { + aNewStartPos.X() -= ( nVisStartX - aEditCursor.Left() ); + + // Darfs ein bischen mehr sein? + aNewStartPos.X() -= nMoreX; + } + + // X kann durch das 'bischen mehr' falsch sein: +// ULONG nMaxTextWidth = mpImpl->mpTextEngine->GetMaxTextWidth(); +// if ( !nMaxTextWidth || ( nMaxTextWidth > 0x7FFFFFFF ) ) +// nMaxTextWidth = 0x7FFFFFFF; +// long nMaxX = (long)nMaxTextWidth - aOutSz.Width(); + long nMaxX = mpImpl->mpTextEngine->CalcTextWidth() - aOutSz.Width(); + if ( nMaxX < 0 ) + nMaxX = 0; + + if ( aNewStartPos.X() < 0 ) + aNewStartPos.X() = 0; + else if ( aNewStartPos.X() > nMaxX ) + aNewStartPos.X() = nMaxX; + + // Y sollte nicht weiter unten als noetig liegen: + long nYMax = mpImpl->mpTextEngine->GetTextHeight() - aOutSz.Height(); + if ( nYMax < 0 ) + nYMax = 0; + if ( aNewStartPos.Y() > nYMax ) + aNewStartPos.Y() = nYMax; + + if ( aNewStartPos != mpImpl->maStartDocPos ) + Scroll( -(aNewStartPos.X() - mpImpl->maStartDocPos.X()), -(aNewStartPos.Y() - mpImpl->maStartDocPos.Y()) ); + } + + if ( aEditCursor.Right() < aEditCursor.Left() ) + { + long n = aEditCursor.Left(); + aEditCursor.Left() = aEditCursor.Right(); + aEditCursor.Right() = n; + } + + Point aPoint( GetWindowPos( !mpImpl->mpTextEngine->IsRightToLeft() ? aEditCursor.TopLeft() : aEditCursor.TopRight() ) ); + mpImpl->mpCursor->SetPos( aPoint ); + mpImpl->mpCursor->SetSize( aEditCursor.GetSize() ); + if ( bForceVisCursor && mpImpl->mbCursorEnabled ) + mpImpl->mpCursor->Show(); +} + +BOOL TextView::SetCursorAtPoint( const Point& rPosPixel ) +{ + mpImpl->mpTextEngine->CheckIdleFormatter(); + + Point aDocPos = GetDocPos( rPosPixel ); + + TextPaM aPaM = mpImpl->mpTextEngine->GetPaM( aDocPos ); + + // aTmpNewSel: Diff zwischen alt und neu, nicht die neue Selektion + TextSelection aTmpNewSel( mpImpl->maSelection.GetEnd(), aPaM ); + TextSelection aNewSel( mpImpl->maSelection ); + aNewSel.GetEnd() = aPaM; + + if ( !mpImpl->mpSelEngine->HasAnchor() ) + { + if ( mpImpl->maSelection.GetStart() != aPaM ) + mpImpl->mpTextEngine->CursorMoved( mpImpl->maSelection.GetStart().GetPara() ); + aNewSel.GetStart() = aPaM; + ImpSetSelection( aNewSel ); + } + else + { + ImpSetSelection( aNewSel ); + ShowSelection( aTmpNewSel ); + } + + BOOL bForceCursor = mpImpl->mpDDInfo ? FALSE : TRUE; // && !mbInSelection + ImpShowCursor( mpImpl->mbAutoScroll, bForceCursor, FALSE ); + return TRUE; +} + +BOOL TextView::IsSelectionAtPoint( const Point& rPosPixel ) +{ +// if ( !Rectangle( Point(), mpImpl->mpWindow->GetOutputSizePixel() ).IsInside( rPosPixel ) && !mbInSelection ) +// return FALSE; + + Point aDocPos = GetDocPos( rPosPixel ); + TextPaM aPaM = mpImpl->mpTextEngine->GetPaM( aDocPos, FALSE ); + // Bei Hyperlinks D&D auch ohne Selektion starten. + // BeginDrag wird aber nur gerufen, wenn IsSelectionAtPoint() + // Problem: IsSelectionAtPoint wird bei Command() nicht gerufen, + // wenn vorher im MBDown schon FALSE returnt wurde. + return ( IsInSelection( aPaM ) || + ( /* mpImpl->mpSelEngine->IsInCommand() && */ mpImpl->mpTextEngine->FindAttrib( aPaM, TEXTATTR_HYPERLINK ) ) ); +} + +BOOL TextView::IsInSelection( const TextPaM& rPaM ) +{ + TextSelection aSel = mpImpl->maSelection; + aSel.Justify(); + + ULONG nStartNode = aSel.GetStart().GetPara(); + ULONG nEndNode = aSel.GetEnd().GetPara(); + ULONG nCurNode = rPaM.GetPara(); + + if ( ( nCurNode > nStartNode ) && ( nCurNode < nEndNode ) ) + return TRUE; + + if ( nStartNode == nEndNode ) + { + if ( nCurNode == nStartNode ) + if ( ( rPaM.GetIndex() >= aSel.GetStart().GetIndex() ) && ( rPaM.GetIndex() < aSel.GetEnd().GetIndex() ) ) + return TRUE; + } + else if ( ( nCurNode == nStartNode ) && ( rPaM.GetIndex() >= aSel.GetStart().GetIndex() ) ) + return TRUE; + else if ( ( nCurNode == nEndNode ) && ( rPaM.GetIndex() < aSel.GetEnd().GetIndex() ) ) + return TRUE; + + return FALSE; +} + +void TextView::ImpHideDDCursor() +{ + if ( mpImpl->mpDDInfo && mpImpl->mpDDInfo->mbVisCursor ) + { + mpImpl->mpDDInfo->maCursor.Hide(); + mpImpl->mpDDInfo->mbVisCursor = FALSE; + } +} + +void TextView::ImpShowDDCursor() +{ + if ( !mpImpl->mpDDInfo->mbVisCursor ) + { + Rectangle aCursor = mpImpl->mpTextEngine->PaMtoEditCursor( mpImpl->mpDDInfo->maDropPos, TRUE ); + aCursor.Right()++; + aCursor.SetPos( GetWindowPos( aCursor.TopLeft() ) ); + + mpImpl->mpDDInfo->maCursor.SetWindow( mpImpl->mpWindow ); + mpImpl->mpDDInfo->maCursor.SetPos( aCursor.TopLeft() ); + mpImpl->mpDDInfo->maCursor.SetSize( aCursor.GetSize() ); + mpImpl->mpDDInfo->maCursor.Show(); + mpImpl->mpDDInfo->mbVisCursor = TRUE; + } +} + +void TextView::SetPaintSelection( BOOL bPaint ) +{ + if ( bPaint != mpImpl->mbPaintSelection ) + { + mpImpl->mbPaintSelection = bPaint; + ShowSelection( mpImpl->maSelection ); + } +} + +void TextView::SetHighlightSelection( BOOL bSelectByHighlight ) +{ + if ( bSelectByHighlight != mpImpl->mbHighlightSelection ) + { + // Falls umschalten zwischendurch moeglich... + mpImpl->mbHighlightSelection = bSelectByHighlight; + } +} + +BOOL TextView::Read( SvStream& rInput ) +{ + BOOL bDone = mpImpl->mpTextEngine->Read( rInput, &mpImpl->maSelection ); + ShowCursor(); + return bDone; +} + +BOOL TextView::Write( SvStream& rOutput ) +{ + return mpImpl->mpTextEngine->Read( rOutput, &mpImpl->maSelection ); +} + +bool TextView::ImplTruncateNewText( rtl::OUString& rNewText ) const +{ + bool bTruncated = false; + + if( rNewText.getLength() > 65534 ) // limit to String API + { + rNewText = rNewText.copy( 0, 65534 ); + bTruncated = true; + } + + ULONG nMaxLen = mpImpl->mpTextEngine->GetMaxTextLen(); + // 0 means unlimited, there is just the String API limit handled above + if( nMaxLen != 0 ) + { + ULONG nCurLen = mpImpl->mpTextEngine->GetTextLen(); + + sal_uInt32 nNewLen = rNewText.getLength(); + if ( nCurLen + nNewLen > nMaxLen ) + { + // see how much text will be replaced + ULONG nSelLen = mpImpl->mpTextEngine->GetTextLen( mpImpl->maSelection ); + if ( nCurLen + nNewLen - nSelLen > nMaxLen ) + { + sal_uInt32 nTruncatedLen = static_cast<sal_uInt32>(nMaxLen - (nCurLen - nSelLen)); + rNewText = rNewText.copy( 0, nTruncatedLen ); + bTruncated = true; + } + } + } + return bTruncated; +} + +BOOL TextView::ImplCheckTextLen( const String& rNewText ) +{ + BOOL bOK = TRUE; + if ( mpImpl->mpTextEngine->GetMaxTextLen() ) + { + ULONG n = mpImpl->mpTextEngine->GetTextLen(); + n += rNewText.Len(); + if ( n > mpImpl->mpTextEngine->GetMaxTextLen() ) + { + // nur dann noch ermitteln, wie viel Text geloescht wird + n -= mpImpl->mpTextEngine->GetTextLen( mpImpl->maSelection ); + if ( n > mpImpl->mpTextEngine->GetMaxTextLen() ) + { + // Beep hat hier eigentlich nichts verloren, sondern lieber ein Hdl, + // aber so funktioniert es wenigstens in ME, BasicIDE, SourceView + Sound::Beep(); + bOK = FALSE; + } + } + } + return bOK; +} + +void TextView::dragGestureRecognized( const ::com::sun::star::datatransfer::dnd::DragGestureEvent& rDGE ) throw (::com::sun::star::uno::RuntimeException) +{ + if ( mpImpl->mbClickedInSelection ) + { + vos::OGuard aVclGuard( Application::GetSolarMutex() ); + + DBG_ASSERT( mpImpl->maSelection.HasRange(), "TextView::dragGestureRecognized: mpImpl->mbClickedInSelection, but no selection?" ); + + delete mpImpl->mpDDInfo; + mpImpl->mpDDInfo = new TextDDInfo; + mpImpl->mpDDInfo->mbStarterOfDD = TRUE; + + TETextDataObject* pDataObj = new TETextDataObject( GetSelected() ); + + if ( mpImpl->mpTextEngine->HasAttrib( TEXTATTR_HYPERLINK ) ) // Dann auch als HTML + mpImpl->mpTextEngine->Write( pDataObj->GetHTMLStream(), &mpImpl->maSelection, TRUE ); + + + /* + // D&D eines Hyperlinks. + // Besser waere es im MBDown sich den MBDownPaM zu merken, + // ist dann aber inkompatibel => spaeter mal umstellen. + TextPaM aPaM( mpImpl->mpTextEngine->GetPaM( GetDocPos( GetWindow()->GetPointerPosPixel() ) ) ); + const TextCharAttrib* pAttr = mpImpl->mpTextEngine->FindCharAttrib( aPaM, TEXTATTR_HYPERLINK ); + if ( pAttr ) + { + aSel = aPaM; + aSel.GetStart().GetIndex() = pAttr->GetStart(); + aSel.GetEnd().GetIndex() = pAttr->GetEnd(); + + const TextAttribHyperLink& rLink = (const TextAttribHyperLink&)pAttr->GetAttr(); + String aText( rLink.GetDescription() ); + if ( !aText.Len() ) + aText = mpImpl->mpTextEngine->GetText( aSel ); + INetBookmark aBookmark( rLink.GetURL(), aText ); + aBookmark.CopyDragServer(); + } + */ + + mpImpl->mpCursor->Hide(); + + sal_Int8 nActions = datatransfer::dnd::DNDConstants::ACTION_COPY; + if ( !IsReadOnly() ) + nActions |= datatransfer::dnd::DNDConstants::ACTION_MOVE; + rDGE.DragSource->startDrag( rDGE, nActions, 0 /*cursor*/, 0 /*image*/, pDataObj, mpImpl->mxDnDListener ); + } +} + +void TextView::dragDropEnd( const ::com::sun::star::datatransfer::dnd::DragSourceDropEvent& ) throw (::com::sun::star::uno::RuntimeException) +{ + ImpHideDDCursor(); + delete mpImpl->mpDDInfo; + mpImpl->mpDDInfo = NULL; +} + +void TextView::drop( const ::com::sun::star::datatransfer::dnd::DropTargetDropEvent& rDTDE ) throw (::com::sun::star::uno::RuntimeException) +{ + vos::OGuard aVclGuard( Application::GetSolarMutex() ); + + BOOL bChanges = FALSE; + if ( !mpImpl->mbReadOnly && mpImpl->mpDDInfo ) + { + ImpHideDDCursor(); + + // Daten fuer das loeschen nach einem DROP_MOVE: + TextSelection aPrevSel( mpImpl->maSelection ); + aPrevSel.Justify(); + ULONG nPrevParaCount = mpImpl->mpTextEngine->GetParagraphCount(); + USHORT nPrevStartParaLen = mpImpl->mpTextEngine->GetTextLen( aPrevSel.GetStart().GetPara() ); + + BOOL bStarterOfDD = FALSE; + for ( USHORT nView = mpImpl->mpTextEngine->GetViewCount(); nView && !bStarterOfDD; ) + bStarterOfDD = mpImpl->mpTextEngine->GetView( --nView )->mpImpl->mpDDInfo ? mpImpl->mpTextEngine->GetView( nView )->mpImpl->mpDDInfo->mbStarterOfDD : FALSE; + + HideSelection(); + ImpSetSelection( mpImpl->mpDDInfo->maDropPos ); + + mpImpl->mpTextEngine->UndoActionStart( TEXTUNDO_DRAGANDDROP ); + + String aText; + uno::Reference< datatransfer::XTransferable > xDataObj = rDTDE.Transferable; + if ( xDataObj.is() ) + { + datatransfer::DataFlavor aFlavor; + SotExchange::GetFormatDataFlavor( SOT_FORMAT_STRING, aFlavor ); + if ( xDataObj->isDataFlavorSupported( aFlavor ) ) + { + uno::Any aData = xDataObj->getTransferData( aFlavor ); + ::rtl::OUString aOUString; + aData >>= aOUString; + aText = aOUString; + aText.ConvertLineEnd( LINEEND_LF ); + } + } + + if ( aText.Len() && ( aText.GetChar( aText.Len()-1 ) == LINE_SEP ) ) + aText.Erase( aText.Len()-1 ); + + TextPaM aTempStart = mpImpl->maSelection.GetStart(); + if ( ImplCheckTextLen( aText ) ) + ImpSetSelection( mpImpl->mpTextEngine->ImpInsertText( mpImpl->mpDDInfo->maDropPos, aText ) ); + if(mpImpl->mbSupportProtectAttribute) + { + mpImpl->mpTextEngine->SetAttrib( TextAttribProtect(), + aTempStart.GetPara(), + aTempStart.GetIndex(), + mpImpl->maSelection.GetEnd().GetIndex(), FALSE ); + } + + if ( aPrevSel.HasRange() && + !mpImpl->mbSupportProtectAttribute && // don't remove currently selected element + (( rDTDE.DropAction & datatransfer::dnd::DNDConstants::ACTION_MOVE ) || !bStarterOfDD) ) + { + // ggf. Selection anpasssen: + if ( ( mpImpl->mpDDInfo->maDropPos.GetPara() < aPrevSel.GetStart().GetPara() ) || + ( ( mpImpl->mpDDInfo->maDropPos.GetPara() == aPrevSel.GetStart().GetPara() ) + && ( mpImpl->mpDDInfo->maDropPos.GetIndex() < aPrevSel.GetStart().GetIndex() ) ) ) + { + ULONG nNewParasBeforeSelection = + mpImpl->mpTextEngine->GetParagraphCount() - nPrevParaCount; + + aPrevSel.GetStart().GetPara() += nNewParasBeforeSelection; + aPrevSel.GetEnd().GetPara() += nNewParasBeforeSelection; + + if ( mpImpl->mpDDInfo->maDropPos.GetPara() == aPrevSel.GetStart().GetPara() ) + { + USHORT nNewChars = + mpImpl->mpTextEngine->GetTextLen( aPrevSel.GetStart().GetPara() ) - nPrevStartParaLen; + + aPrevSel.GetStart().GetIndex() = + aPrevSel.GetStart().GetIndex() + nNewChars; + if ( aPrevSel.GetStart().GetPara() == aPrevSel.GetEnd().GetPara() ) + aPrevSel.GetEnd().GetIndex() = + aPrevSel.GetEnd().GetIndex() + nNewChars; + } + } + else + { + // aktuelle Selektion anpassen + TextPaM aPaM = mpImpl->maSelection.GetStart(); + aPaM.GetPara() -= ( aPrevSel.GetEnd().GetPara() - aPrevSel.GetStart().GetPara() ); + if ( aPrevSel.GetEnd().GetPara() == mpImpl->mpDDInfo->maDropPos.GetPara() ) + { + aPaM.GetIndex() = + aPaM.GetIndex() - aPrevSel.GetEnd().GetIndex(); + if ( aPrevSel.GetStart().GetPara() == mpImpl->mpDDInfo->maDropPos.GetPara() ) + aPaM.GetIndex() = + aPaM.GetIndex() + aPrevSel.GetStart().GetIndex(); + } + ImpSetSelection( aPaM ); + + } + mpImpl->mpTextEngine->ImpDeleteText( aPrevSel ); + } + + mpImpl->mpTextEngine->UndoActionEnd( TEXTUNDO_DRAGANDDROP ); + + delete mpImpl->mpDDInfo; + mpImpl->mpDDInfo = 0; + + mpImpl->mpTextEngine->FormatAndUpdate( this ); + + mpImpl->mpTextEngine->Broadcast( TextHint( TEXT_HINT_MODIFIED ) ); + } + rDTDE.Context->dropComplete( bChanges ); +} + +void TextView::dragEnter( const ::com::sun::star::datatransfer::dnd::DropTargetDragEnterEvent& ) throw (::com::sun::star::uno::RuntimeException) +{ +} + +void TextView::dragExit( const ::com::sun::star::datatransfer::dnd::DropTargetEvent& ) throw (::com::sun::star::uno::RuntimeException) +{ + vos::OGuard aVclGuard( Application::GetSolarMutex() ); + ImpHideDDCursor(); +} + +void TextView::dragOver( const ::com::sun::star::datatransfer::dnd::DropTargetDragEvent& rDTDE ) throw (::com::sun::star::uno::RuntimeException) +{ + vos::OGuard aVclGuard( Application::GetSolarMutex() ); + + if ( !mpImpl->mpDDInfo ) + mpImpl->mpDDInfo = new TextDDInfo; + + TextPaM aPrevDropPos = mpImpl->mpDDInfo->maDropPos; + Point aMousePos( rDTDE.LocationX, rDTDE.LocationY ); + Point aDocPos = GetDocPos( aMousePos ); + mpImpl->mpDDInfo->maDropPos = mpImpl->mpTextEngine->GetPaM( aDocPos ); + +/* + Size aOutSize = mpImpl->mpWindow->GetOutputSizePixel(); + if ( ( aMousePos.X() < 0 ) || ( aMousePos.X() > aOutSize.Width() ) || + ( aMousePos.Y() < 0 ) || ( aMousePos.Y() > aOutSize.Height() ) ) + { + // Scroll? + // No, I will not receive events for this... + } +*/ + + sal_Bool bProtected = sal_False; + if(mpImpl->mbSupportProtectAttribute) + { + const TextCharAttrib* pStartAttr = mpImpl->mpTextEngine->FindCharAttrib( + mpImpl->mpDDInfo->maDropPos, + TEXTATTR_PROTECTED ); + bProtected = pStartAttr != 0 && + pStartAttr->GetStart() != mpImpl->mpDDInfo->maDropPos.GetIndex() && + pStartAttr->GetEnd() != mpImpl->mpDDInfo->maDropPos.GetIndex(); + } + // Don't drop in selection or in read only engine + if ( IsReadOnly() || IsInSelection( mpImpl->mpDDInfo->maDropPos ) || bProtected) + { + ImpHideDDCursor(); + rDTDE.Context->rejectDrag(); + } + else + { + // Alten Cursor wegzeichnen... + if ( !mpImpl->mpDDInfo->mbVisCursor || ( aPrevDropPos != mpImpl->mpDDInfo->maDropPos ) ) + { + ImpHideDDCursor(); + ImpShowDDCursor(); + } + rDTDE.Context->acceptDrag( rDTDE.DropAction ); + } +} + +Point TextView::ImpGetOutputStartPos( const Point& rStartDocPos ) const +{ + Point aStartPos( -rStartDocPos.X(), -rStartDocPos.Y() ); + if ( mpImpl->mpTextEngine->IsRightToLeft() ) + { + Size aSz = mpImpl->mpWindow->GetOutputSizePixel(); + aStartPos.X() = rStartDocPos.X() + aSz.Width() - 1; // -1: Start is 0 + } + return aStartPos; +} + +Point TextView::GetDocPos( const Point& rWindowPos ) const +{ + // Fensterposition => Dokumentposition + + Point aPoint; + + aPoint.Y() = rWindowPos.Y() + mpImpl->maStartDocPos.Y(); + + if ( !mpImpl->mpTextEngine->IsRightToLeft() ) + { + aPoint.X() = rWindowPos.X() + mpImpl->maStartDocPos.X(); + } + else + { + Size aSz = mpImpl->mpWindow->GetOutputSizePixel(); + aPoint.X() = ( aSz.Width() - 1 ) - rWindowPos.X() + mpImpl->maStartDocPos.X(); + } + + return aPoint; +} + +Point TextView::GetWindowPos( const Point& rDocPos ) const +{ + // Dokumentposition => Fensterposition + + Point aPoint; + + aPoint.Y() = rDocPos.Y() - mpImpl->maStartDocPos.Y(); + + if ( !mpImpl->mpTextEngine->IsRightToLeft() ) + { + aPoint.X() = rDocPos.X() - mpImpl->maStartDocPos.X(); + } + else + { + Size aSz = mpImpl->mpWindow->GetOutputSizePixel(); + aPoint.X() = ( aSz.Width() - 1 ) - ( rDocPos.X() - mpImpl->maStartDocPos.X() ); + } + + return aPoint; +} + +sal_Int32 TextView::GetLineNumberOfCursorInSelection() const +{ + // PROGRESS + sal_Int32 nLineNo = -1; + if( mpImpl->mbCursorEnabled ) + { + TextPaM aPaM = GetSelection().GetEnd(); + TEParaPortion* pPPortion = mpImpl->mpTextEngine->mpTEParaPortions->GetObject( aPaM.GetPara() ); + nLineNo = pPPortion->GetLineNumber( aPaM.GetIndex(), FALSE ); + if( mpImpl->mbCursorAtEndOfLine ) + --nLineNo; + } + return nLineNo; +} + + +// ------------------------------------------------------------------------- +// (+) class TextSelFunctionSet +// ------------------------------------------------------------------------- +TextSelFunctionSet::TextSelFunctionSet( TextView* pView ) +{ + mpView = pView; +} + +void __EXPORT TextSelFunctionSet::BeginDrag() +{ +} + +void __EXPORT TextSelFunctionSet::CreateAnchor() +{ +// TextSelection aSel( mpView->GetSelection() ); +// aSel.GetStart() = aSel.GetEnd(); +// mpView->SetSelection( aSel ); + + // Es darf kein ShowCursor folgen: + mpView->HideSelection(); + mpView->ImpSetSelection( mpView->mpImpl->maSelection.GetEnd() ); +} + +BOOL __EXPORT TextSelFunctionSet::SetCursorAtPoint( const Point& rPointPixel, BOOL ) +{ + return mpView->SetCursorAtPoint( rPointPixel ); +} + +BOOL __EXPORT TextSelFunctionSet::IsSelectionAtPoint( const Point& rPointPixel ) +{ + return mpView->IsSelectionAtPoint( rPointPixel ); +} + +void __EXPORT TextSelFunctionSet::DeselectAll() +{ + CreateAnchor(); +} + +void __EXPORT TextSelFunctionSet::DeselectAtPoint( const Point& ) +{ + // Nur bei Mehrfachselektion +} + +void __EXPORT TextSelFunctionSet::DestroyAnchor() +{ + // Nur bei Mehrfachselektion +} +TextEngine* TextView::GetTextEngine() const +{ return mpImpl->mpTextEngine; } +Window* TextView::GetWindow() const +{ return mpImpl->mpWindow; } +void TextView::EnableCursor( BOOL bEnable ) +{ mpImpl->mbCursorEnabled = bEnable; } +BOOL TextView::IsCursorEnabled() const +{ return mpImpl->mbCursorEnabled; } +void TextView::SetStartDocPos( const Point& rPos ) +{ mpImpl->maStartDocPos = rPos; } +const Point& TextView::GetStartDocPos() const +{ return mpImpl->maStartDocPos; } +void TextView::SetAutoIndentMode( BOOL bAutoIndent ) +{ mpImpl->mbAutoIndent = bAutoIndent; } +BOOL TextView::IsAutoIndentMode() const +{ return mpImpl->mbAutoIndent; } +BOOL TextView::IsReadOnly() const +{ return mpImpl->mbReadOnly; } +void TextView::SetAutoScroll( BOOL bAutoScroll ) +{ mpImpl->mbAutoScroll = bAutoScroll; } +BOOL TextView::IsAutoScroll() const +{ return mpImpl->mbAutoScroll; } +BOOL TextView::IsPaintSelection() const +{ return mpImpl->mbPaintSelection; } +BOOL TextView::IsHighlightSelection() const +{ return mpImpl->mbHighlightSelection; } +BOOL TextView::HasSelection() const +{ return mpImpl->maSelection.HasRange(); } +BOOL TextView::IsInsertMode() const +{ return mpImpl->mbInsertMode; } +void TextView::SupportProtectAttribute(sal_Bool bSupport) +{ mpImpl->mbSupportProtectAttribute = bSupport;} + diff --git a/svtools/source/edit/textwindowpeer.cxx b/svtools/source/edit/textwindowpeer.cxx new file mode 100644 index 000000000000..7f31e95c75a7 --- /dev/null +++ b/svtools/source/edit/textwindowpeer.cxx @@ -0,0 +1,59 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + +#include <svtools/textwindowpeer.hxx> +#include <svtools/textview.hxx> +#include "svtaccessiblefactory.hxx" + +namespace css = ::com::sun::star; + +namespace svt +{ + TextWindowPeer::TextWindowPeer(::TextView & rView, bool bCompoundControlChild): + m_rEngine(*rView.GetTextEngine()), m_rView(rView), m_bCompoundControlChild(bCompoundControlChild) + { + SetWindow(rView.GetWindow()); + m_pFactoryAccess.reset( new AccessibleFactoryAccess ); + } + + // virtual + TextWindowPeer::~TextWindowPeer() + { + } + + ::css::uno::Reference< ::css::accessibility::XAccessibleContext > TextWindowPeer::CreateAccessibleContext() + { + return m_pFactoryAccess->getFactory().createAccessibleTextWindowContext( + this, m_rEngine, m_rView, m_bCompoundControlChild + ); + } +} diff --git a/svtools/source/edit/txtattr.cxx b/svtools/source/edit/txtattr.cxx new file mode 100644 index 000000000000..e7466cbe1d85 --- /dev/null +++ b/svtools/source/edit/txtattr.cxx @@ -0,0 +1,197 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + +#include <txtattr.hxx> +#include <vcl/font.hxx> + + + + +TextAttrib::~TextAttrib() +{ +} + +void TextAttrib::SetFont( Font& ) const +{ +} + +TextAttrib* TextAttrib::Clone() const +{ + return NULL; +} + +int TextAttrib::operator==( const TextAttrib& rAttr ) const +{ + return mnWhich == rAttr.mnWhich; +} + + +TextAttribFontColor::TextAttribFontColor( const Color& rColor ) + : TextAttrib( TEXTATTR_FONTCOLOR ), maColor( rColor ) +{ +} + +TextAttribFontColor::TextAttribFontColor( const TextAttribFontColor& rAttr ) + : TextAttrib( rAttr ), maColor( rAttr.maColor ) +{ +} + +TextAttribFontColor::~TextAttribFontColor() +{ +} + +void TextAttribFontColor::SetFont( Font& rFont ) const +{ + rFont.SetColor( maColor ); +} + +TextAttrib* TextAttribFontColor::Clone() const +{ + return new TextAttribFontColor( *this ); +} + +int TextAttribFontColor::operator==( const TextAttrib& rAttr ) const +{ + return ( ( TextAttrib::operator==(rAttr ) ) && + ( maColor == ((const TextAttribFontColor&)rAttr).maColor ) ); +} + +TextAttribFontWeight::TextAttribFontWeight( FontWeight eWeight ) + : TextAttrib( TEXTATTR_FONTWEIGHT ), meWeight( eWeight ) +{ +} + +TextAttribFontWeight::TextAttribFontWeight( const TextAttribFontWeight& rAttr ) + : TextAttrib( rAttr ), meWeight( rAttr.meWeight ) +{ +} + +TextAttribFontWeight::~TextAttribFontWeight() +{ +} + +void TextAttribFontWeight::SetFont( Font& rFont ) const +{ + rFont.SetWeight( meWeight ); +} + +TextAttrib* TextAttribFontWeight::Clone() const +{ + return new TextAttribFontWeight( *this ); +} + +int TextAttribFontWeight::operator==( const TextAttrib& rAttr ) const +{ + return ( ( TextAttrib::operator==(rAttr ) ) && + ( meWeight == ((const TextAttribFontWeight&)rAttr).meWeight ) ); +} + + +TextAttribHyperLink::TextAttribHyperLink( const XubString& rURL ) + : TextAttrib( TEXTATTR_HYPERLINK ), maURL( rURL ) +{ + maColor = COL_BLUE; +} + +TextAttribHyperLink::TextAttribHyperLink( const XubString& rURL, const XubString& rDescription ) + : TextAttrib( TEXTATTR_HYPERLINK ), maURL( rURL ), maDescription( rDescription ) +{ + maColor = COL_BLUE; +} + +TextAttribHyperLink::TextAttribHyperLink( const TextAttribHyperLink& rAttr ) + : TextAttrib( rAttr ), maURL( rAttr.maURL ), maDescription( rAttr.maDescription ) +{ + maColor = rAttr.maColor; +} + +TextAttribHyperLink::~TextAttribHyperLink() +{ +} + +void TextAttribHyperLink::SetFont( Font& rFont ) const +{ + rFont.SetColor( maColor ); + rFont.SetUnderline( UNDERLINE_SINGLE ); +} + +TextAttrib* TextAttribHyperLink::Clone() const +{ + return new TextAttribHyperLink( *this ); +} + +int TextAttribHyperLink::operator==( const TextAttrib& rAttr ) const +{ + return ( ( TextAttrib::operator==(rAttr ) ) && + ( maURL == ((const TextAttribHyperLink&)rAttr).maURL ) && + ( maDescription == ((const TextAttribHyperLink&)rAttr).maDescription ) && + ( maColor == ((const TextAttribHyperLink&)rAttr).maColor ) ); +} + +/*-- 24.06.2004 14:49:44--------------------------------------------------- + + -----------------------------------------------------------------------*/ +TextAttribProtect::TextAttribProtect() : + TextAttrib( TEXTATTR_PROTECTED ) +{ +} +/*-- 24.06.2004 14:49:44--------------------------------------------------- + + -----------------------------------------------------------------------*/ +TextAttribProtect::TextAttribProtect( const TextAttribProtect&) : + TextAttrib( TEXTATTR_PROTECTED ) +{ +} +/*-- 24.06.2004 14:49:44--------------------------------------------------- + + -----------------------------------------------------------------------*/ +TextAttribProtect::~TextAttribProtect() +{ +} +/*-- 24.06.2004 14:49:44--------------------------------------------------- + + -----------------------------------------------------------------------*/ +void TextAttribProtect::SetFont( Font& ) const +{ +} +/*-- 24.06.2004 14:49:44--------------------------------------------------- + + -----------------------------------------------------------------------*/ +TextAttrib* TextAttribProtect::Clone() const +{ + return new TextAttribProtect(); +} +/*-- 24.06.2004 14:49:45--------------------------------------------------- + + -----------------------------------------------------------------------*/ +int TextAttribProtect::operator==( const TextAttrib& rAttr ) const +{ + return ( TextAttrib::operator==(rAttr ) ); +} diff --git a/svtools/source/edit/xtextedt.cxx b/svtools/source/edit/xtextedt.cxx new file mode 100644 index 000000000000..0a4907edcadd --- /dev/null +++ b/svtools/source/edit/xtextedt.cxx @@ -0,0 +1,421 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svtools.hxx" + +#include <svtools/xtextedt.hxx> +#include <vcl/svapp.hxx> // International +#include <unotools/textsearch.hxx> +#include <com/sun/star/util/SearchOptions.hpp> +#include <com/sun/star/util/SearchFlags.hpp> + +using namespace ::com::sun::star; + + + +// ------------------------------------------------------------------------- +// class ExtTextEngine +// ------------------------------------------------------------------------- +ExtTextEngine::ExtTextEngine() : maGroupChars( String::CreateFromAscii( "(){}[]", 6 ) ) +{ +} + +ExtTextEngine::~ExtTextEngine() +{ +} + +TextSelection ExtTextEngine::MatchGroup( const TextPaM& rCursor ) const +{ + TextSelection aSel( rCursor ); + USHORT nPos = rCursor.GetIndex(); + ULONG nPara = rCursor.GetPara(); + ULONG nParas = GetParagraphCount(); + if ( ( nPara < nParas ) && ( nPos < GetTextLen( nPara ) ) ) + { + USHORT nMatchChar = maGroupChars.Search( GetText( rCursor.GetPara() ).GetChar( nPos ) ); + if ( nMatchChar != STRING_NOTFOUND ) + { + if ( ( nMatchChar % 2 ) == 0 ) + { + // Vorwaerts suchen... + sal_Unicode nSC = maGroupChars.GetChar( nMatchChar ); + sal_Unicode nEC = maGroupChars.GetChar( nMatchChar+1 ); + + USHORT nCur = nPos+1; + USHORT nLevel = 1; + while ( nLevel && ( nPara < nParas ) ) + { + XubString aStr = GetText( nPara ); + while ( nCur < aStr.Len() ) + { + if ( aStr.GetChar( nCur ) == nSC ) + nLevel++; + else if ( aStr.GetChar( nCur ) == nEC ) + { + nLevel--; + if ( !nLevel ) + break; // while nCur... + } + nCur++; + } + + if ( nLevel ) + { + nPara++; + nCur = 0; + } + } + if ( nLevel == 0 ) // gefunden + { + aSel.GetStart() = rCursor; + aSel.GetEnd() = TextPaM( nPara, nCur+1 ); + } + } + else + { + // Rueckwaerts suchen... + xub_Unicode nEC = maGroupChars.GetChar( nMatchChar ); + xub_Unicode nSC = maGroupChars.GetChar( nMatchChar-1 ); + + USHORT nCur = rCursor.GetIndex()-1; + USHORT nLevel = 1; + while ( nLevel ) + { + if ( GetTextLen( nPara ) ) + { + XubString aStr = GetText( nPara ); + while ( nCur ) + { + if ( aStr.GetChar( nCur ) == nSC ) + { + nLevel--; + if ( !nLevel ) + break; // while nCur... + } + else if ( aStr.GetChar( nCur ) == nEC ) + nLevel++; + + nCur--; + } + } + + if ( nLevel ) + { + if ( nPara ) + { + nPara--; + nCur = GetTextLen( nPara )-1; // egal ob negativ, weil if Len() + } + else + break; + } + } + + if ( nLevel == 0 ) // gefunden + { + aSel.GetStart() = rCursor; + aSel.GetStart().GetIndex()++; // hinter das Zeichen + aSel.GetEnd() = TextPaM( nPara, nCur ); + } + } + } + } + return aSel; +} + +BOOL ExtTextEngine::Search( TextSelection& rSel, const util::SearchOptions& rSearchOptions, BOOL bForward ) +{ + TextSelection aSel( rSel ); + aSel.Justify(); + + BOOL bSearchInSelection = (0 != (rSearchOptions.searchFlag & util::SearchFlags::REG_NOT_BEGINOFLINE) ); + + TextPaM aStartPaM( aSel.GetEnd() ); + if ( aSel.HasRange() && ( ( bSearchInSelection && bForward ) || ( !bSearchInSelection && !bForward ) ) ) + { + aStartPaM = aSel.GetStart(); + } + + bool bFound = false; + ULONG nStartNode, nEndNode; + + if ( bSearchInSelection ) + nEndNode = bForward ? aSel.GetEnd().GetPara() : aSel.GetStart().GetPara(); + else + nEndNode = bForward ? (GetParagraphCount()-1) : 0; + + nStartNode = aStartPaM.GetPara(); + + util::SearchOptions aOptions( rSearchOptions ); + aOptions.Locale = Application::GetSettings().GetLocale(); + utl::TextSearch aSearcher( rSearchOptions ); + + // ueber die Absaetze iterieren... + for ( ULONG nNode = nStartNode; + bForward ? ( nNode <= nEndNode) : ( nNode >= nEndNode ); + bForward ? nNode++ : nNode-- ) + { + String aText = GetText( nNode ); + USHORT nStartPos = 0; + USHORT nEndPos = aText.Len(); + if ( nNode == nStartNode ) + { + if ( bForward ) + nStartPos = aStartPaM.GetIndex(); + else + nEndPos = aStartPaM.GetIndex(); + } + if ( ( nNode == nEndNode ) && bSearchInSelection ) + { + if ( bForward ) + nEndPos = aSel.GetEnd().GetIndex(); + else + nStartPos = aSel.GetStart().GetIndex(); + } + + if ( bForward ) + bFound = aSearcher.SearchFrwrd( aText, &nStartPos, &nEndPos ); + else + bFound = aSearcher.SearchBkwrd( aText, &nEndPos, &nStartPos ); + + if ( bFound ) + { + rSel.GetStart().GetPara() = nNode; + rSel.GetStart().GetIndex() = nStartPos; + rSel.GetEnd().GetPara() = nNode; + rSel.GetEnd().GetIndex() = nEndPos; + // Ueber den Absatz selektieren? + // Select over the paragraph? + // FIXME This should be max long... + if( nEndPos == sal::static_int_cast<USHORT>(-1) ) // USHORT for 0 and -1 ! + { + if ( (rSel.GetEnd().GetPara()+1) < GetParagraphCount() ) + { + rSel.GetEnd().GetPara()++; + rSel.GetEnd().GetIndex() = 0; + } + else + { + rSel.GetEnd().GetIndex() = nStartPos; + bFound = false; + } + } + + break; + } + + if ( !bForward && !nNode ) // Bei rueckwaertsuche, wenn nEndNode = 0: + break; + } + + return bFound; +} + + +// ------------------------------------------------------------------------- +// class ExtTextView +// ------------------------------------------------------------------------- +ExtTextView::ExtTextView( ExtTextEngine* pEng, Window* pWindow ) + : TextView( pEng, pWindow ) +{ +} + +ExtTextView::~ExtTextView() +{ +} + +BOOL ExtTextView::MatchGroup() +{ + TextSelection aTmpSel( GetSelection() ); + aTmpSel.Justify(); + if ( ( aTmpSel.GetStart().GetPara() != aTmpSel.GetEnd().GetPara() ) || + ( ( aTmpSel.GetEnd().GetIndex() - aTmpSel.GetStart().GetIndex() ) > 1 ) ) + { + return FALSE; + } + + TextSelection aMatchSel = ((ExtTextEngine*)GetTextEngine())->MatchGroup( aTmpSel.GetStart() ); + if ( aMatchSel.HasRange() ) + SetSelection( aMatchSel ); + + return aMatchSel.HasRange() ? TRUE : FALSE; +} + +BOOL ExtTextView::Search( const util::SearchOptions& rSearchOptions, BOOL bForward ) +{ + BOOL bFound = FALSE; + TextSelection aSel( GetSelection() ); + if ( ((ExtTextEngine*)GetTextEngine())->Search( aSel, rSearchOptions, bForward ) ) + { + bFound = TRUE; + // Erstmal den Anfang des Wortes als Selektion einstellen, + // damit das ganze Wort in den sichtbaren Bereich kommt. + SetSelection( aSel.GetStart() ); + ShowCursor( TRUE, FALSE ); + } + else + { + aSel = GetSelection().GetEnd(); + } + + SetSelection( aSel ); + ShowCursor(); + + return bFound; +} + +USHORT ExtTextView::Replace( const util::SearchOptions& rSearchOptions, BOOL bAll, BOOL bForward ) +{ + USHORT nFound = 0; + + if ( !bAll ) + { + if ( GetSelection().HasRange() ) + { + InsertText( rSearchOptions.replaceString ); + nFound = 1; + Search( rSearchOptions, bForward ); // gleich zum naechsten + } + else + { + if( Search( rSearchOptions, bForward ) ) + nFound = 1; + } + } + else + { + // Der Writer ersetzt alle, vom Anfang bis Ende... + + ExtTextEngine* pTextEngine = (ExtTextEngine*)GetTextEngine(); + + // HideSelection(); + TextSelection aSel; + + BOOL bSearchInSelection = (0 != (rSearchOptions.searchFlag & util::SearchFlags::REG_NOT_BEGINOFLINE) ); + if ( bSearchInSelection ) + { + aSel = GetSelection(); + aSel.Justify(); + } + + TextSelection aSearchSel( aSel ); + + BOOL bFound = pTextEngine->Search( aSel, rSearchOptions, TRUE ); + if ( bFound ) + pTextEngine->UndoActionStart( XTEXTUNDO_REPLACEALL ); + while ( bFound ) + { + nFound++; + + TextPaM aNewStart = pTextEngine->ImpInsertText( aSel, rSearchOptions.replaceString ); + aSel = aSearchSel; + aSel.GetStart() = aNewStart; + bFound = pTextEngine->Search( aSel, rSearchOptions, TRUE ); + } + if ( nFound ) + { + SetSelection( aSel.GetStart() ); + pTextEngine->FormatAndUpdate( this ); + pTextEngine->UndoActionEnd( XTEXTUNDO_REPLACEALL ); + } + } + return nFound; +} + +BOOL ExtTextView::ImpIndentBlock( BOOL bRight ) +{ + BOOL bDone = FALSE; + + TextSelection aSel = GetSelection(); + aSel.Justify(); + + HideSelection(); + GetTextEngine()->UndoActionStart( bRight ? XTEXTUNDO_INDENTBLOCK : XTEXTUNDO_UNINDENTBLOCK ); + + ULONG nStartPara = aSel.GetStart().GetPara(); + ULONG nEndPara = aSel.GetEnd().GetPara(); + if ( aSel.HasRange() && !aSel.GetEnd().GetIndex() ) + { + nEndPara--; // den dann nicht einruecken... + } + + for ( ULONG nPara = nStartPara; nPara <= nEndPara; nPara++ ) + { + if ( bRight ) + { + // Tabs hinzufuegen + GetTextEngine()->ImpInsertText( TextPaM( nPara, 0 ), '\t' ); + bDone = TRUE; + } + else + { + // Tabs/Blanks entfernen + String aText = GetTextEngine()->GetText( nPara ); + if ( aText.Len() && ( + ( aText.GetChar( 0 ) == '\t' ) || + ( aText.GetChar( 0 ) == ' ' ) ) ) + { + GetTextEngine()->ImpDeleteText( TextSelection( TextPaM( nPara, 0 ), TextPaM( nPara, 1 ) ) ); + bDone = TRUE; + } + } + } + + GetTextEngine()->UndoActionEnd( bRight ? XTEXTUNDO_INDENTBLOCK : XTEXTUNDO_UNINDENTBLOCK ); + + BOOL bRange = aSel.HasRange(); + if ( bRight ) + { + aSel.GetStart().GetIndex()++; + if ( bRange && ( aSel.GetEnd().GetPara() == nEndPara ) ) + aSel.GetEnd().GetIndex()++; + } + else + { + if ( aSel.GetStart().GetIndex() ) + aSel.GetStart().GetIndex()--; + if ( bRange && aSel.GetEnd().GetIndex() ) + aSel.GetEnd().GetIndex()--; + } + + ImpSetSelection( aSel ); + GetTextEngine()->FormatAndUpdate( this ); + + return bDone; +} + +BOOL ExtTextView::IndentBlock() +{ + return ImpIndentBlock( TRUE ); +} + +BOOL ExtTextView::UnindentBlock() +{ + return ImpIndentBlock( FALSE ); +} + |