/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * 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_sc.hxx"

#include <algorithm>

#include "scitems.hxx"
#include <editeng/eeitem.hxx>

#include <sfx2/app.hxx>
#include <editeng/adjitem.hxx>
#include <editeng/editview.hxx>
#include <editeng/editstat.hxx>
#include <editeng/frmdiritem.hxx>
#include <editeng/lspcitem.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/event.hxx>
#include <sfx2/imgmgr.hxx>
#include <stdlib.h>		// qsort
#include <editeng/scriptspaceitem.hxx>
#include <editeng/scripttypeitem.hxx>
#include <vcl/cursor.hxx>
#include <vcl/help.hxx>
#include <svl/stritem.hxx>

#include "inputwin.hxx"
#include "scmod.hxx"
#include "uiitems.hxx"
#include "global.hxx"
#include "scresid.hxx"
#include "sc.hrc"
#include "globstr.hrc"
#include "editutil.hxx"
#include "inputhdl.hxx"
#include "tabvwsh.hxx"
#include "document.hxx"
#include "docsh.hxx"
#include "appoptio.hxx"
#include "rangenam.hxx"
#include <formula/compiler.hrc>
#include "dbcolect.hxx"
#include "rangeutl.hxx"
#include "docfunc.hxx"
#include "funcdesc.hxx"
#include <editeng/fontitem.hxx>
#include <com/sun/star/accessibility/XAccessible.hpp>
#include "AccessibleEditObject.hxx"
#include "AccessibleText.hxx"

#define TEXT_STARTPOS		3
#define THESIZE				1000000	//!!! langt... :-)
#define TBX_WINDOW_HEIGHT 	22 // in Pixeln - fuer alle Systeme gleich?

enum ScNameInputType
{
    SC_NAME_INPUT_CELL,
    SC_NAME_INPUT_RANGE,
    SC_NAME_INPUT_NAMEDRANGE,
    SC_NAME_INPUT_DATABASE,
    SC_NAME_INPUT_ROW,
    SC_NAME_INPUT_SHEET,
    SC_NAME_INPUT_DEFINE,
    SC_NAME_INPUT_BAD_NAME,
    SC_NAME_INPUT_BAD_SELECTION
};


//==================================================================
//	class ScInputWindowWrapper
//==================================================================

SFX_IMPL_CHILDWINDOW(ScInputWindowWrapper,FID_INPUTLINE_STATUS)

ScInputWindowWrapper::ScInputWindowWrapper( Window*          pParentP,
                                            sal_uInt16			 nId,
                                            SfxBindings*	 pBindings,
                                            SfxChildWinInfo* /* pInfo */ )
    :   SfxChildWindow( pParentP, nId )
{
    ScInputWindow* pWin=new ScInputWindow( pParentP, pBindings );
    pWindow = pWin;

    pWin->Show();

    pWin->SetSizePixel( pWin->CalcWindowSizePixel() );

    eChildAlignment = SFX_ALIGN_LOWESTTOP;
    pBindings->Invalidate( FID_TOGGLEINPUTLINE );
}

//	GetInfo fliegt wieder raus, wenn es ein SFX_IMPL_TOOLBOX gibt !!!!

SfxChildWinInfo ScInputWindowWrapper::GetInfo() const
{
    SfxChildWinInfo aInfo = SfxChildWindow::GetInfo();
    return aInfo;
}

//==================================================================

#define IMAGE(id) pImgMgr->SeekImage(id)

//==================================================================
//	class ScInputWindow
//==================================================================

ScInputWindow::ScInputWindow( Window* pParent, SfxBindings* pBind ) :
#ifdef OS2
// ohne WB_CLIPCHILDREN wg. os/2 Paintproblem
        ToolBox         ( pParent, WinBits(WB_BORDER|WB_3DLOOK) ),
#else
// mit WB_CLIPCHILDREN, sonst Flicker
        ToolBox         ( pParent, WinBits(WB_BORDER|WB_3DLOOK|WB_CLIPCHILDREN) ),
#endif
        aWndPos         ( this ),
        aTextWindow     ( this ),
        pInputHdl		( NULL ),
        pBindings       ( pBind ),
        aTextOk			( ScResId( SCSTR_QHELP_BTNOK ) ),		// nicht immer neu aus Resource
        aTextCancel		( ScResId( SCSTR_QHELP_BTNCANCEL ) ),
        aTextSum		( ScResId( SCSTR_QHELP_BTNSUM ) ),
        aTextEqual		( ScResId( SCSTR_QHELP_BTNEQUAL ) ),
        bIsOkCancelMode ( false )
{
    ScModule*		 pScMod  = SC_MOD();
    SfxImageManager* pImgMgr = SfxImageManager::GetImageManager( pScMod );

    // #i73615# don't rely on SfxViewShell::Current while constructing the input line
    // (also for GetInputHdl below)
    ScTabViewShell* pViewSh = NULL;
    SfxDispatcher* pDisp = pBind->GetDispatcher();
    if ( pDisp )
    {
        SfxViewFrame* pViewFrm = pDisp->GetFrame();
        if ( pViewFrm )
            pViewSh = PTR_CAST( ScTabViewShell, pViewFrm->GetViewShell() );
    }
    DBG_ASSERT( pViewSh, "no view shell for input window" );

    // Positionsfenster, 3 Buttons, Eingabefenster
    InsertWindow    ( 1, &aWndPos, 0,								      0 );
    InsertSeparator ( 												   	  1 );
    InsertItem      ( SID_INPUT_FUNCTION, IMAGE( SID_INPUT_FUNCTION ), 0, 2 );
    InsertItem      ( SID_INPUT_SUM, 	  IMAGE( SID_INPUT_SUM ), 0,      3 );
    InsertItem      ( SID_INPUT_EQUAL,	  IMAGE( SID_INPUT_EQUAL ), 0,    4 );
    InsertSeparator ( 												      5 );
    InsertWindow    ( 7, &aTextWindow, 0,                                 6 );

    aWndPos	   .SetQuickHelpText( ScResId( SCSTR_QHELP_POSWND ) );
    aWndPos    .SetHelpId		( HID_INSWIN_POS );
    aTextWindow.SetQuickHelpText( ScResId( SCSTR_QHELP_INPUTWND ) );
    aTextWindow.SetHelpId		( HID_INSWIN_INPUT );

    //	kein SetHelpText, die Hilfetexte kommen aus der Hilfe

    SetItemText ( SID_INPUT_FUNCTION, ScResId( SCSTR_QHELP_BTNCALC ) );
    SetHelpId	( SID_INPUT_FUNCTION, HID_INSWIN_CALC );

    SetItemText ( SID_INPUT_SUM, aTextSum );
    SetHelpId	( SID_INPUT_SUM, HID_INSWIN_SUMME );

    SetItemText ( SID_INPUT_EQUAL, aTextEqual );
    SetHelpId	( SID_INPUT_EQUAL, HID_INSWIN_FUNC );

    SetHelpId( HID_SC_INPUTWIN );	// fuer die ganze Eingabezeile

    aWndPos		.Show();
    aTextWindow	.Show();

    pInputHdl = SC_MOD()->GetInputHdl( pViewSh, false );    // use own handler even if ref-handler is set
    if (pInputHdl)
        pInputHdl->SetInputWindow( this );

    if ( pInputHdl && pInputHdl->GetFormString().Len() )
    {
        //	Umschalten waehrend der Funktionsautopilot aktiv ist
        //	-> Inhalt des Funktionsautopiloten wieder anzeigen
        //!	auch Selektion (am InputHdl gemerkt) wieder anzeigen

        aTextWindow.SetTextString( pInputHdl->GetFormString() );
    }
    else if ( pInputHdl && pInputHdl->IsInputMode() )
    {
        //	wenn waehrend des Editierens die Eingabezeile weg war
        //	(Editieren einer Formel, dann umschalten zu fremdem Dokument/Hilfe),
        //	wieder den gerade editierten Text aus dem InputHandler anzeigen

        aTextWindow.SetTextString( pInputHdl->GetEditString() );	// Text anzeigen
        if ( pInputHdl->IsTopMode() )
            pInputHdl->SetMode( SC_INPUT_TABLE );		// Focus kommt eh nach unten
    }
    else if ( pViewSh )
        pViewSh->UpdateInputHandler( sal_True ); // unbedingtes Update

    pImgMgr->RegisterToolBox( this );
    SetAccessibleName(ScResId(STR_ACC_TOOLBAR_FORMULA));
}

ScInputWindow::~ScInputWindow()
{
    sal_Bool bDown = ( ScGlobal::pSysLocale == NULL );    // after Clear?

    //	if any view's input handler has a pointer to this input window, reset it
    //	(may be several ones, #74522#)
    //	member pInputHdl is not used here

    if ( !bDown )
    {
        TypeId aScType = TYPE(ScTabViewShell);
        SfxViewShell* pSh = SfxViewShell::GetFirst( &aScType );
        while ( pSh )
        {
            ScInputHandler* pHdl = ((ScTabViewShell*)pSh)->GetInputHandler();
            if ( pHdl && pHdl->GetInputWindow() == this )
            {
                pHdl->SetInputWindow( NULL );
                pHdl->StopInputWinEngine( false );  // reset pTopView pointer
            }
            pSh = SfxViewShell::GetNext( *pSh, &aScType );
        }
    }

    SfxImageManager::GetImageManager( SC_MOD() )->ReleaseToolBox( this );
}

void ScInputWindow::SetInputHandler( ScInputHandler* pNew )
{
    //	wird im Activate der View gerufen...

    if ( pNew != pInputHdl )
    {
        //	Bei Reload (letzte Version) ist pInputHdl der Input-Handler der alten,
        //	geloeschten ViewShell, darum hier auf keinen Fall anfassen!

        pInputHdl = pNew;
        if (pInputHdl)
            pInputHdl->SetInputWindow( this );
    }
}

sal_Bool ScInputWindow::UseSubTotal(ScRangeList* pRangeList) const
{
    sal_Bool bSubTotal(false);
    ScTabViewShell* pViewSh = PTR_CAST( ScTabViewShell, SfxViewShell::Current() );
    if ( pViewSh )
    {
        ScDocument* pDoc = pViewSh->GetViewData()->GetDocument();
        size_t nRangeCount (pRangeList->size());
        size_t nRangeIndex (0);
        while (!bSubTotal && nRangeIndex < nRangeCount)
        {
            const ScRange* pRange = (*pRangeList)[nRangeIndex];
            if( pRange )
            {
                SCTAB nTabEnd(pRange->aEnd.Tab());
                SCTAB nTab(pRange->aStart.Tab());
                while (!bSubTotal && nTab <= nTabEnd)
                {
                    SCROW nRowEnd(pRange->aEnd.Row());
                    SCROW nRow(pRange->aStart.Row());
                    while (!bSubTotal && nRow <= nRowEnd)
                    {
                        if (pDoc->RowFiltered(nRow, nTab))
                            bSubTotal = sal_True;
                        else
                            ++nRow;
                    }
                    ++nTab;
                }
            }
            ++nRangeIndex;
        }

        ScDBCollection* pDBCollection = pDoc->GetDBCollection();
        sal_uInt16 nDBCount (pDBCollection->GetCount());
        sal_uInt16 nDBIndex (0);
        while (!bSubTotal && nDBIndex < nDBCount)
        {
            ScDBData* pDB = (*pDBCollection)[nDBIndex];
            if (pDB && pDB->HasAutoFilter())
            {
                nRangeIndex = 0;
                while (!bSubTotal && nRangeIndex < nRangeCount)
                {
                    const ScRange* pRange = (*pRangeList)[nRangeIndex];
                    if( pRange )
                    {
                        ScRange aDBArea;
                        pDB->GetArea(aDBArea);
                        if (aDBArea.Intersects(*pRange))
                            bSubTotal = sal_True;
                    }
                    ++nRangeIndex;
                }
            }
            ++nDBIndex;
        }
    }
    return bSubTotal;
}

void ScInputWindow::Select()
{
    ScModule* pScMod = SC_MOD();
    ToolBox::Select();

    switch ( GetCurItemId() )
    {
        case SID_INPUT_FUNCTION:
            {
                //!	new method at ScModule to query if function autopilot is open
                SfxViewFrame* pViewFrm = SfxViewFrame::Current();
                if ( pViewFrm && !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) )
                {
                    pViewFrm->GetDispatcher()->Execute( SID_OPENDLG_FUNCTION,
                                              SFX_CALLMODE_SYNCHRON | SFX_CALLMODE_RECORD );

                    //	die Toolbox wird sowieso disabled, also braucht auch nicht umgeschaltet
                    //	zu werden, egal ob's geklappt hat oder nicht
//					SetOkCancelMode();
                }
            }
            break;

        case SID_INPUT_CANCEL:
            pScMod->InputCancelHandler();
            SetSumAssignMode();
            break;

        case SID_INPUT_OK:
            pScMod->InputEnterHandler();
            SetSumAssignMode();
            aTextWindow.Invalidate();		// sonst bleibt Selektion stehen
            break;

        case SID_INPUT_SUM:
            {
                ScTabViewShell* pViewSh = PTR_CAST( ScTabViewShell, SfxViewShell::Current() );
                if ( pViewSh )
                {
                    const ScMarkData& rMark = pViewSh->GetViewData()->GetMarkData();
                    if ( rMark.IsMarked() || rMark.IsMultiMarked() )
                    {
                        ScRangeList aMarkRangeList;
                        rMark.FillRangeListWithMarks( &aMarkRangeList, false );
                        ScDocument* pDoc = pViewSh->GetViewData()->GetDocument();

                        // check if one of the marked ranges is empty
                        bool bEmpty = false;
                        const size_t nCount = aMarkRangeList.size();
                        for ( size_t i = 0; i < nCount; ++i )
                        {
                            const ScRange aRange( *aMarkRangeList[i] );
                            if ( pDoc->IsBlockEmpty( aRange.aStart.Tab(),
                                    aRange.aStart.Col(), aRange.aStart.Row(),
                                    aRange.aEnd.Col(), aRange.aEnd.Row() ) )
                            {
                                bEmpty = true;
                                break;
                            }
                        }

                        if ( bEmpty )
                        {
                            ScRangeList aRangeList;
                            const sal_Bool bDataFound = pViewSh->GetAutoSumArea( aRangeList );
                            if ( bDataFound )
                            {
                                ScAddress aAddr = aRangeList.back()->aEnd;
                                aAddr.IncRow();
                                const sal_Bool bSubTotal( UseSubTotal( &aRangeList ) );
                                pViewSh->EnterAutoSum( aRangeList, bSubTotal, aAddr );
                            }
                        }
                        else
                        {
                            const sal_Bool bSubTotal( UseSubTotal( &aMarkRangeList ) );
                            for ( size_t i = 0; i < nCount; ++i )
                            {
                                const ScRange aRange( *aMarkRangeList[i] );
                                const bool bSetCursor = ( i == nCount - 1 ? true : false );
                                const bool bContinue = ( i != 0  ? true : false );
                                if ( !pViewSh->AutoSum( aRange, bSubTotal, bSetCursor, bContinue ) )
                                {
                                    pViewSh->MarkRange( aRange, false, false );
                                    pViewSh->SetCursor( aRange.aEnd.Col(), aRange.aEnd.Row() );
                                    const ScRangeList aRangeList;
                                    ScAddress aAddr = aRange.aEnd;
                                    aAddr.IncRow();
                                    const String aFormula = pViewSh->GetAutoSumFormula(
                                        aRangeList, bSubTotal, aAddr );
                                    SetFuncString( aFormula );
                                    break;
                                }
                            }
                        }
                    }
                    else									// nur in Eingabezeile einfuegen
                    {
                        ScRangeList aRangeList;
                        const sal_Bool bDataFound = pViewSh->GetAutoSumArea( aRangeList );
                        const sal_Bool bSubTotal( UseSubTotal( &aRangeList ) );
                        ScAddress aAddr = pViewSh->GetViewData()->GetCurPos();
                        const String aFormula = pViewSh->GetAutoSumFormula( aRangeList, bSubTotal, aAddr );
                        SetFuncString( aFormula );

                        if ( bDataFound && pScMod->IsEditMode() )
                        {
                            ScInputHandler* pHdl = pScMod->GetInputHdl( pViewSh );
                            if ( pHdl )
                            {
                                pHdl->InitRangeFinder( aFormula );

                                //!	SetSelection am InputHandler ???
                                //!	bSelIsRef setzen ???
                                const xub_StrLen nOpen = aFormula.Search('(');
                                const xub_StrLen nLen = aFormula.Len();
                                if ( nOpen != STRING_NOTFOUND && nLen > nOpen )
                                {
                                    sal_uInt8 nAdd(1);
                                    if (bSubTotal)
                                        nAdd = 3;
                                    ESelection aSel(0,nOpen+nAdd,0,nLen-1);
                                    EditView* pTableView = pHdl->GetTableView();
                                    if (pTableView)
                                        pTableView->SetSelection(aSel);
                                    EditView* pTopView = pHdl->GetTopView();
                                    if (pTopView)
                                        pTopView->SetSelection(aSel);
                                }
                            }
                        }
                    }
                }
            }
            break;

        case SID_INPUT_EQUAL:
        {
            aTextWindow.StartEditEngine();
            if ( pScMod->IsEditMode() )			// nicht, wenn z.B. geschuetzt
            {
                aTextWindow.GrabFocus();
                aTextWindow.SetTextString( '=' );

                EditView* pView = aTextWindow.GetEditView();
                if (pView)
                {
                    pView->SetSelection( ESelection(0,1, 0,1) );
                    pScMod->InputChanged(pView);
                    SetOkCancelMode();
                    pView->SetEditEngineUpdateMode(sal_True);
                }
            }
            break;
        }
    }
}

void ScInputWindow::Resize()
{
    ToolBox::Resize();

    long nWidth = GetSizePixel().Width();
    long nLeft  = aTextWindow.GetPosPixel().X();
    Size aSize  = aTextWindow.GetSizePixel();

    aSize.Width() = Max( ((long)(nWidth - nLeft - 5)), (long)0 );
    aTextWindow.SetSizePixel( aSize );
    aTextWindow.Invalidate();
}

void ScInputWindow::SetFuncString( const String& rString, sal_Bool bDoEdit )
{
    //!	new method at ScModule to query if function autopilot is open
    SfxViewFrame* pViewFrm = SfxViewFrame::Current();
    EnableButtons( pViewFrm && !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) );
    aTextWindow.StartEditEngine();

    ScModule* pScMod = SC_MOD();
    if ( pScMod->IsEditMode() )
    {
        if ( bDoEdit )
            aTextWindow.GrabFocus();
        aTextWindow.SetTextString( rString );
        EditView* pView = aTextWindow.GetEditView();
        if (pView)
        {
            xub_StrLen nLen = rString.Len();

            if ( nLen > 0 )
            {
                nLen--;
                pView->SetSelection( ESelection( 0, nLen, 0, nLen ) );
            }

            pScMod->InputChanged(pView);
            if ( bDoEdit )
                SetOkCancelMode();			// nicht, wenn gleich hinterher Enter/Cancel

            pView->SetEditEngineUpdateMode(sal_True);
        }
    }
}

void ScInputWindow::SetPosString( const String& rStr )
{
    aWndPos.SetPos( rStr );
}

void ScInputWindow::SetTextString( const String& rString )
{
    if (rString.Len() <= 32767)
        aTextWindow.SetTextString(rString);
    else
    {
        String aNew = rString;
        aNew.Erase(32767);
        aTextWindow.SetTextString(aNew);
    }
}

void ScInputWindow::SetOkCancelMode()
{
    //!	new method at ScModule to query if function autopilot is open
    SfxViewFrame* pViewFrm = SfxViewFrame::Current();
    EnableButtons( pViewFrm && !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) );

    ScModule* pScMod = SC_MOD();
    SfxImageManager* pImgMgr = SfxImageManager::GetImageManager( pScMod );
    if (!bIsOkCancelMode)
    {
        RemoveItem( 3 ); // SID_INPUT_SUM und SID_INPUT_EQUAL entfernen
        RemoveItem( 3 );
        InsertItem( SID_INPUT_CANCEL, IMAGE( SID_INPUT_CANCEL ), 0, 3 );
        InsertItem( SID_INPUT_OK,	  IMAGE( SID_INPUT_OK ),	 0, 4 );
        SetItemText	( SID_INPUT_CANCEL, aTextCancel );
        SetHelpId	( SID_INPUT_CANCEL, HID_INSWIN_CANCEL );
        SetItemText	( SID_INPUT_OK,		aTextOk );
        SetHelpId	( SID_INPUT_OK,		HID_INSWIN_OK );
        bIsOkCancelMode = sal_True;
    }
}

void ScInputWindow::SetSumAssignMode()
{
    //!	new method at ScModule to query if function autopilot is open
    SfxViewFrame* pViewFrm = SfxViewFrame::Current();
    EnableButtons( pViewFrm && !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) );

    ScModule* pScMod = SC_MOD();
    SfxImageManager* pImgMgr = SfxImageManager::GetImageManager( pScMod );
    if (bIsOkCancelMode)
    {
        // SID_INPUT_CANCEL, und SID_INPUT_OK entfernen
        RemoveItem( 3 );
        RemoveItem( 3 );
        InsertItem( SID_INPUT_SUM, 	 IMAGE( SID_INPUT_SUM ), 	 0, 3 );
        InsertItem( SID_INPUT_EQUAL, IMAGE( SID_INPUT_EQUAL ),	 0, 4 );
        SetItemText	( SID_INPUT_SUM,   aTextSum );
        SetHelpId	( SID_INPUT_SUM,   HID_INSWIN_SUMME );
        SetItemText	( SID_INPUT_EQUAL, aTextEqual );
        SetHelpId	( SID_INPUT_EQUAL, HID_INSWIN_FUNC );
        bIsOkCancelMode = false;

        SetFormulaMode(false);		// kein editieren -> keine Formel
    }
}

void ScInputWindow::SetFormulaMode( sal_Bool bSet )
{
    aWndPos.SetFormulaMode(bSet);
    aTextWindow.SetFormulaMode(bSet);
}

void ScInputWindow::SetText( const String& rString )
{
    ToolBox::SetText(rString);
}

String ScInputWindow::GetText() const
{
    return ToolBox::GetText();
}

sal_Bool ScInputWindow::IsInputActive()
{
    return aTextWindow.IsInputActive();
}

EditView* ScInputWindow::GetEditView()
{
    return aTextWindow.GetEditView();
}

void ScInputWindow::MakeDialogEditView()
{
    aTextWindow.MakeDialogEditView();
}

void ScInputWindow::StopEditEngine( sal_Bool bAll )
{
    aTextWindow.StopEditEngine( bAll );
}

void ScInputWindow::TextGrabFocus()
{
    aTextWindow.GrabFocus();
}

void ScInputWindow::TextInvalidate()
{
    aTextWindow.Invalidate();
}

void ScInputWindow::SwitchToTextWin()
{
    // used for shift-ctrl-F2

    aTextWindow.StartEditEngine();
    if ( SC_MOD()->IsEditMode() )
    {
        aTextWindow.GrabFocus();
        EditView* pView = aTextWindow.GetEditView();
        if (pView)
        {
            xub_StrLen nLen = pView->GetEditEngine()->GetTextLen(0);
            ESelection aSel( 0, nLen, 0, nLen );
            pView->SetSelection( aSel );				// set cursor to end of text
        }
    }
}

void ScInputWindow::PosGrabFocus()
{
    aWndPos.GrabFocus();
}

void ScInputWindow::EnableButtons( sal_Bool bEnable )
{
    //	when enabling buttons, always also enable the input window itself
    if ( bEnable && !IsEnabled() )
        Enable();

    EnableItem( SID_INPUT_FUNCTION,									  bEnable );
    EnableItem( bIsOkCancelMode ? SID_INPUT_CANCEL : SID_INPUT_SUM,   bEnable );
    EnableItem( bIsOkCancelMode ? SID_INPUT_OK     : SID_INPUT_EQUAL, bEnable );
//	Invalidate();
}

void ScInputWindow::StateChanged( StateChangedType nType )
{
    ToolBox::StateChanged( nType );

    if ( nType == STATE_CHANGE_INITSHOW ) Resize();
}

void ScInputWindow::DataChanged( const DataChangedEvent& rDCEvt )
{
    if ( rDCEvt.GetType() == DATACHANGED_SETTINGS && (rDCEvt.GetFlags() & SETTINGS_STYLE) )
    {
        //	update item images
        ScModule*		 pScMod  = SC_MOD();
        SfxImageManager* pImgMgr = SfxImageManager::GetImageManager( pScMod );
        // IMAGE macro uses pScMod, pImgMg

        SetItemImage( SID_INPUT_FUNCTION, IMAGE( SID_INPUT_FUNCTION ) );
        if ( bIsOkCancelMode )
        {
            SetItemImage( SID_INPUT_CANCEL, IMAGE( SID_INPUT_CANCEL ) );
            SetItemImage( SID_INPUT_OK,     IMAGE( SID_INPUT_OK ) );
        }
        else
        {
            SetItemImage( SID_INPUT_SUM,   IMAGE( SID_INPUT_SUM ) );
            SetItemImage( SID_INPUT_EQUAL, IMAGE( SID_INPUT_EQUAL ) );
        }
    }

    ToolBox::DataChanged( rDCEvt );
}

//========================================================================
// 							Eingabefenster
//========================================================================

ScTextWnd::ScTextWnd( Window* pParent )
    :	Window		 ( pParent, WinBits(WB_HIDE | WB_BORDER) ),
        DragSourceHelper( this ),
        pEditEngine	 ( NULL ),
        pEditView	 ( NULL ),
        bIsInsertMode( sal_True ),
        bFormulaMode ( false ),
        bInputMode   ( false )
{
    EnableRTL( false );		// EditEngine can't be used with VCL EnableRTL

    bIsRTL = GetSettings().GetLayoutRTL();

    //	always use application font, so a font with cjk chars can be installed
    Font aAppFont = GetFont();
    aTextFont = aAppFont;
    aTextFont.SetSize( PixelToLogic( aAppFont.GetSize(), MAP_TWIP ) );	// AppFont ist in Pixeln

    const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();

    Color aBgColor= rStyleSettings.GetWindowColor();
    Color aTxtColor= rStyleSettings.GetWindowTextColor();

    aTextFont.SetTransparent ( sal_True );
    aTextFont.SetFillColor   ( aBgColor );
    //aTextFont.SetColor		 ( COL_FIELDTEXT );
    aTextFont.SetColor		 (aTxtColor);
    aTextFont.SetWeight		 ( WEIGHT_NORMAL );

    Size aSize(1,TBX_WINDOW_HEIGHT);
    Size aMinEditSize( Edit::GetMinimumEditSize() );
    if( aMinEditSize.Height() > aSize.Height() )
        aSize.Height() = aMinEditSize.Height();
    SetSizePixel		( aSize );
    SetBackground		( aBgColor );
    SetLineColor		( COL_BLACK );
    SetMapMode		    ( MAP_TWIP );
    SetPointer		    ( POINTER_TEXT );
}

ScTextWnd::~ScTextWnd()
{
    while (!maAccTextDatas.empty()) {
        maAccTextDatas.back()->Dispose();
    }
    delete pEditView;
    delete pEditEngine;
}

void ScTextWnd::Paint( const Rectangle& rRec )
{
    if (pEditView)
        pEditView->Paint( rRec );
    else
    {
        SetFont( aTextFont );

        long nDiff =  GetOutputSizePixel().Height()
                    - LogicToPixel( Size( 0, GetTextHeight() ) ).Height();
//		if (nDiff<2) nDiff=2;		// mind. 1 Pixel

        long nStartPos = TEXT_STARTPOS;
        if ( bIsRTL )
        {
            //	right-align
            nStartPos += GetOutputSizePixel().Width() - 2*TEXT_STARTPOS -
                        LogicToPixel( Size( GetTextWidth( aString ), 0 ) ).Width();

            //	LayoutMode isn't changed as long as ModifyRTLDefaults doesn't include SvxFrameDirectionItem
        }

        DrawText( PixelToLogic( Point( nStartPos, nDiff/2 ) ), aString );
    }
}

void ScTextWnd::Resize()
{
    if (pEditView)
    {
        Size aSize = GetOutputSizePixel();
        long nDiff =  aSize.Height()
                    - LogicToPixel( Size( 0, GetTextHeight() ) ).Height();

        aSize.Width() -= 2 * TEXT_STARTPOS - 1;

        pEditView->SetOutputArea(
            PixelToLogic( Rectangle( Point( TEXT_STARTPOS, (nDiff > 0) ? nDiff/2 : 1 ),
                                     aSize ) ) );
    }
}

void ScTextWnd::MouseMove( const MouseEvent& rMEvt )
{
    if (pEditView)
        pEditView->MouseMove( rMEvt );
}

void ScTextWnd::MouseButtonDown( const MouseEvent& rMEvt )
{
    if (!HasFocus())
    {
        StartEditEngine();
        if ( SC_MOD()->IsEditMode() )
            GrabFocus();
    }

    if (pEditView)
    {
        pEditView->SetEditEngineUpdateMode( sal_True );
        pEditView->MouseButtonDown( rMEvt );
    }
}

void ScTextWnd::MouseButtonUp( const MouseEvent& rMEvt )
{
    if (pEditView)
        if (pEditView->MouseButtonUp( rMEvt ))
        {
            if ( rMEvt.IsMiddle() &&
                     GetSettings().GetMouseSettings().GetMiddleButtonAction() == MOUSE_MIDDLE_PASTESELECTION )
            {
                //	EditView may have pasted from selection
                SC_MOD()->InputChanged( pEditView );
            }
            else
                SC_MOD()->InputSelection( pEditView );
        }
}

void ScTextWnd::Command( const CommandEvent& rCEvt )
{
    bInputMode = sal_True;
    sal_uInt16 nCommand = rCEvt.GetCommand();
    if ( pEditView /* && ( nCommand == COMMAND_STARTDRAG || nCommand == COMMAND_VOICE ) */ )
    {
        ScModule* pScMod = SC_MOD();
        ScTabViewShell* pStartViewSh = ScTabViewShell::GetActiveViewShell();

        // don't modify the font defaults here - the right defaults are
        // already set in StartEditEngine when the EditEngine is created

        // verhindern, dass die EditView beim View-Umschalten wegkommt
        pScMod->SetInEditCommand( true );
        pEditView->Command( rCEvt );
        pScMod->SetInEditCommand( false );

        //	COMMAND_STARTDRAG heiss noch lange nicht, dass der Inhalt geaendert wurde
        //	darum in dem Fall kein InputChanged
        //!	erkennen, ob mit Move gedraggt wurde, oder Drag&Move irgendwie verbieten

        if ( nCommand == COMMAND_STARTDRAG )
        {
            //	ist auf eine andere View gedraggt worden?
            ScTabViewShell* pEndViewSh = ScTabViewShell::GetActiveViewShell();
            if ( pEndViewSh != pStartViewSh && pStartViewSh != NULL )
            {
                ScViewData* pViewData = pStartViewSh->GetViewData();
                ScInputHandler* pHdl = pScMod->GetInputHdl( pStartViewSh );
                if ( pHdl && pViewData->HasEditView( pViewData->GetActivePart() ) )
                {
                    pHdl->CancelHandler();
                    pViewData->GetView()->ShowCursor();		// fehlt bei KillEditView, weil nicht aktiv
                }
            }
        }
        else if ( nCommand == COMMAND_CURSORPOS )
        {
            //	don't call InputChanged for COMMAND_CURSORPOS
        }
        else if ( nCommand == COMMAND_INPUTLANGUAGECHANGE )
        {
            // #i55929# Font and font size state depends on input language if nothing is selected,
            // so the slots have to be invalidated when the input language is changed.

            SfxViewFrame* pViewFrm = SfxViewFrame::Current();
            if (pViewFrm)
            {
                SfxBindings& rBindings = pViewFrm->GetBindings();
                rBindings.Invalidate( SID_ATTR_CHAR_FONT );
                rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
            }
        }
        else
            SC_MOD()->InputChanged( pEditView );
    }
    else
        Window::Command(rCEvt);		//	sonst soll sich die Basisklasse drum kuemmern...

    bInputMode = false;
}

void ScTextWnd::StartDrag( sal_Int8 /* nAction */, const Point& rPosPixel )
{
    if ( pEditView )
    {
        CommandEvent aDragEvent( rPosPixel, COMMAND_STARTDRAG, sal_True );
        pEditView->Command( aDragEvent );

        //	handling of d&d to different view (CancelHandler) can't be done here,
        //	because the call returns before d&d is complete.
    }
}

void ScTextWnd::KeyInput(const KeyEvent& rKEvt)
{
    bInputMode = sal_True;
    if (!SC_MOD()->InputKeyEvent( rKEvt ))
    {
        sal_Bool bUsed = false;
        ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
        if ( pViewSh )
            bUsed = pViewSh->SfxKeyInput(rKEvt);	// nur Acceleratoren, keine Eingabe
        if (!bUsed)
            Window::KeyInput( rKEvt );
    }
    bInputMode = false;
}

void ScTextWnd::GetFocus()
{
    ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
    if ( pViewSh )
        pViewSh->SetFormShellAtTop( false );     // focus in input line -> FormShell no longer on top
}

void ScTextWnd::LoseFocus()
{
}

String ScTextWnd::GetText() const
{
    //	ueberladen, um per Testtool an den Text heranzukommen

    if ( pEditEngine )
        return pEditEngine->GetText();
    else
        return GetTextString();
}

void ScTextWnd::SetFormulaMode( sal_Bool bSet )
{
    if ( bSet != bFormulaMode )
    {
        bFormulaMode = bSet;
        UpdateAutoCorrFlag();
    }
}

void ScTextWnd::UpdateAutoCorrFlag()
{
    if ( pEditEngine )
    {
        sal_uLong nControl = pEditEngine->GetControlWord();
        sal_uLong nOld = nControl;
        if ( bFormulaMode )
            nControl &= ~EE_CNTRL_AUTOCORRECT;		// keine Autokorrektur in Formeln
        else
            nControl |= EE_CNTRL_AUTOCORRECT;		// sonst schon
        if ( nControl != nOld )
            pEditEngine->SetControlWord( nControl );
    }
}

void lcl_ExtendEditFontAttribs( SfxItemSet& rSet )
{
    const SfxPoolItem& rFontItem = rSet.Get( EE_CHAR_FONTINFO );
    rSet.Put( rFontItem, EE_CHAR_FONTINFO_CJK );
    rSet.Put( rFontItem, EE_CHAR_FONTINFO_CTL );
    const SfxPoolItem& rHeightItem = rSet.Get( EE_CHAR_FONTHEIGHT );
    rSet.Put( rHeightItem, EE_CHAR_FONTHEIGHT_CJK );
    rSet.Put( rHeightItem, EE_CHAR_FONTHEIGHT_CTL );
    const SfxPoolItem& rWeightItem = rSet.Get( EE_CHAR_WEIGHT );
    rSet.Put( rWeightItem, EE_CHAR_WEIGHT_CJK );
    rSet.Put( rWeightItem, EE_CHAR_WEIGHT_CTL );
    const SfxPoolItem& rItalicItem = rSet.Get( EE_CHAR_ITALIC );
    rSet.Put( rItalicItem, EE_CHAR_ITALIC_CJK );
    rSet.Put( rItalicItem, EE_CHAR_ITALIC_CTL );
    const SfxPoolItem& rLangItem = rSet.Get( EE_CHAR_LANGUAGE );
    rSet.Put( rLangItem, EE_CHAR_LANGUAGE_CJK );
    rSet.Put( rLangItem, EE_CHAR_LANGUAGE_CTL );
}

void lcl_ModifyRTLDefaults( SfxItemSet& rSet )
{
    rSet.Put( SvxAdjustItem( SVX_ADJUST_RIGHT, EE_PARA_JUST ) );

    //	always using rtl writing direction would break formulas
    //rSet.Put( SvxFrameDirectionItem( FRMDIR_HORI_RIGHT_TOP, EE_PARA_WRITINGDIR ) );

    //	PaperSize width is limited to USHRT_MAX in RTL mode (because of EditEngine's
    //	sal_uInt16 values in EditLine), so the text may be wrapped and line spacing must be
    //	increased to not see the beginning of the next line.
    SvxLineSpacingItem aItem( SVX_LINESPACE_TWO_LINES, EE_PARA_SBL );
    aItem.SetPropLineSpace( 200 );
    rSet.Put( aItem );
}

void lcl_ModifyRTLVisArea( EditView* pEditView )
{
    Rectangle aVisArea = pEditView->GetVisArea();
    Size aPaper = pEditView->GetEditEngine()->GetPaperSize();
    long nDiff = aPaper.Width() - aVisArea.Right();
    aVisArea.Left()  += nDiff;
    aVisArea.Right() += nDiff;
    pEditView->SetVisArea(aVisArea);
}

void ScTextWnd::StartEditEngine()
{
    //	Bei "eigener Modalitaet" (Doc-modale Dialoge) nicht aktivieren
    SfxObjectShell* pObjSh = SfxObjectShell::Current();
    if ( pObjSh && pObjSh->IsInModalMode() )
        return;

    if ( !pEditView || !pEditEngine )
    {
        ScFieldEditEngine* pNew;
        ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
        if ( pViewSh )
        {
            const ScDocument* pDoc = pViewSh->GetViewData()->GetDocument();
            pNew = new ScFieldEditEngine( pDoc->GetEnginePool(), pDoc->GetEditPool() );
        }
        else
            pNew = new ScFieldEditEngine( EditEngine::CreatePool(),	NULL, sal_True );
        pNew->SetExecuteURL( false );
        pEditEngine = pNew;

        pEditEngine->SetUpdateMode( false );
        pEditEngine->SetPaperSize( Size( bIsRTL ? USHRT_MAX : THESIZE, 300 ) );
        pEditEngine->SetWordDelimiters(
                        ScEditUtil::ModifyDelimiters( pEditEngine->GetWordDelimiters() ) );

        UpdateAutoCorrFlag();

        {
            SfxItemSet* pSet = new SfxItemSet( pEditEngine->GetEmptyItemSet() );
            pEditEngine->SetFontInfoInItemSet( *pSet, aTextFont );
            lcl_ExtendEditFontAttribs( *pSet );
            // turn off script spacing to match DrawText output
            pSet->Put( SvxScriptSpaceItem( false, EE_PARA_ASIANCJKSPACING ) );
            if ( bIsRTL )
                lcl_ModifyRTLDefaults( *pSet );
            pEditEngine->SetDefaults( pSet );
        }

        //	Wenn in der Zelle URL-Felder enthalten sind, muessen die auch in
        //	die Eingabezeile uebernommen werden, weil sonst die Positionen nicht stimmen.

        sal_Bool bFilled = false;
        ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
        if ( pHdl )			//!	Testen, ob's der richtige InputHdl ist?
            bFilled = pHdl->GetTextAndFields( *pEditEngine );

        pEditEngine->SetUpdateMode( sal_True );

        //	aString ist die Wahrheit...
        if ( bFilled && pEditEngine->GetText() == aString )
            Invalidate();						// Repaint fuer (hinterlegte) Felder
        else
            pEditEngine->SetText(aString);		// dann wenigstens den richtigen Text

        pEditView = new EditView( pEditEngine, this );
        pEditView->SetInsertMode(bIsInsertMode);

        // Text aus Clipboard wird als ASCII einzeilig uebernommen
        sal_uLong n = pEditView->GetControlWord();
        pEditView->SetControlWord( n | EV_CNTRL_SINGLELINEPASTE	);

        pEditEngine->InsertView( pEditView, EE_APPEND );

        Resize();

        if ( bIsRTL )
            lcl_ModifyRTLVisArea( pEditView );

        pEditEngine->SetModifyHdl(LINK(this, ScTextWnd, NotifyHdl));

        if (!maAccTextDatas.empty())
            maAccTextDatas.back()->StartEdit();

        //	as long as EditEngine and DrawText sometimes differ for CTL text,
        //	repaint now to have the EditEngine's version visible
//        SfxObjectShell* pObjSh = SfxObjectShell::Current();
        if ( pObjSh && pObjSh->ISA(ScDocShell) )
        {
            ScDocument* pDoc = ((ScDocShell*)pObjSh)->GetDocument();	// any document
            sal_uInt8 nScript = pDoc->GetStringScriptType( aString );
            if ( nScript & SCRIPTTYPE_COMPLEX )
                Invalidate();
        }
    }

    SC_MOD()->SetInputMode( SC_INPUT_TOP );

    SfxViewFrame* pViewFrm = SfxViewFrame::Current();
    if (pViewFrm)
        pViewFrm->GetBindings().Invalidate( SID_ATTR_INSERT );
}

IMPL_LINK(ScTextWnd, NotifyHdl, EENotify*, EMPTYARG)
{
    if (pEditView && !bInputMode)
    {
        ScInputHandler* pHdl = SC_MOD()->GetInputHdl();

        //	Use the InputHandler's InOwnChange flag to prevent calling InputChanged
        //	while an InputHandler method is modifying the EditEngine content

        if ( pHdl && !pHdl->IsInOwnChange() )
            pHdl->InputChanged( pEditView, sal_True );	// #i20282# InputChanged must know if called from modify handler
    }

    return 0;
}

void ScTextWnd::StopEditEngine( sal_Bool bAll )
{
    if (pEditView)
    {
        if (!maAccTextDatas.empty())
            maAccTextDatas.back()->EndEdit();

        ScModule* pScMod = SC_MOD();

        if (!bAll)
            pScMod->InputSelection( pEditView );
        aString = pEditEngine->GetText();
        bIsInsertMode = pEditView->IsInsertMode();
        sal_Bool bSelection = pEditView->HasSelection();
        pEditEngine->SetModifyHdl(Link());
        DELETEZ(pEditView);
        DELETEZ(pEditEngine);

        if ( pScMod->IsEditMode() && !bAll )
            pScMod->SetInputMode(SC_INPUT_TABLE);

        SfxViewFrame* pViewFrm = SfxViewFrame::Current();
        if (pViewFrm)
            pViewFrm->GetBindings().Invalidate( SID_ATTR_INSERT );

        if (bSelection)
            Invalidate();			// damit Selektion nicht stehenbleibt
    }
}

void ScTextWnd::SetTextString( const String& rNewString )
{
    if ( rNewString != aString )
    {
        bInputMode = sal_True;

        //	Position der Aenderung suchen, nur Rest painten

        if (!pEditEngine)
        {
            sal_Bool bPaintAll;
            if ( bIsRTL )
                bPaintAll = sal_True;
            else
            {
                //	test if CTL script type is involved
                sal_uInt8 nOldScript = 0;
                sal_uInt8 nNewScript = 0;
                SfxObjectShell* pObjSh = SfxObjectShell::Current();
                if ( pObjSh && pObjSh->ISA(ScDocShell) )
                {
                    //	any document can be used (used only for its break iterator)
                    ScDocument* pDoc = ((ScDocShell*)pObjSh)->GetDocument();
                    nOldScript = pDoc->GetStringScriptType( aString );
                    nNewScript = pDoc->GetStringScriptType( rNewString );
                }
                bPaintAll = ( nOldScript & SCRIPTTYPE_COMPLEX ) || ( nNewScript & SCRIPTTYPE_COMPLEX );
            }

            if ( bPaintAll )
            {
                // if CTL is involved, the whole text has to be redrawn
                Invalidate();
            }
            else
            {
                long nTextSize = 0;
                xub_StrLen nDifPos;
                if (rNewString.Len() > aString.Len())
                    nDifPos = rNewString.Match(aString);
                else
                    nDifPos = aString.Match(rNewString);

                long nSize1 = GetTextWidth(aString);
                long nSize2 = GetTextWidth(rNewString);
                if ( nSize1>0 && nSize2>0 )
                    nTextSize = Max( nSize1, nSize2 );
                else
                    nTextSize = GetOutputSize().Width();		// Ueberlauf

                if (nDifPos == STRING_MATCH)
                    nDifPos = 0;

                                                // -1 wegen Rundung und "A"
                Point aLogicStart = PixelToLogic(Point(TEXT_STARTPOS-1,0));
                long nStartPos = aLogicStart.X();
                long nInvPos = nStartPos;
                if (nDifPos)
                    nInvPos += GetTextWidth(aString,0,nDifPos);

                sal_uInt16 nFlags = 0;
                if ( nDifPos == aString.Len() )			// only new characters appended
                    nFlags = INVALIDATE_NOERASE;		// then background is already clear

                Invalidate( Rectangle( nInvPos, 0,
                                        nStartPos+nTextSize, GetOutputSize().Height()-1 ),
                            nFlags );
            }
        }
        else
        {
            pEditEngine->SetText(rNewString);
        }

        aString = rNewString;

        if (!maAccTextDatas.empty())
            maAccTextDatas.back()->TextChanged();

        bInputMode = false;
    }
}

const String& ScTextWnd::GetTextString() const
{
    return aString;
}

sal_Bool ScTextWnd::IsInputActive()
{
    return HasFocus();
}

EditView* ScTextWnd::GetEditView()
{
    return pEditView;
}

void ScTextWnd::MakeDialogEditView()
{
    if ( pEditView ) return;

    ScFieldEditEngine* pNew;
    ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
    if ( pViewSh )
    {
        const ScDocument* pDoc = pViewSh->GetViewData()->GetDocument();
        pNew = new ScFieldEditEngine( pDoc->GetEnginePool(), pDoc->GetEditPool() );
    }
    else
        pNew = new ScFieldEditEngine( EditEngine::CreatePool(),	NULL, sal_True );
    pNew->SetExecuteURL( false );
    pEditEngine = pNew;

    pEditEngine->SetUpdateMode( false );
    pEditEngine->SetWordDelimiters( pEditEngine->GetWordDelimiters() += '=' );
    pEditEngine->SetPaperSize( Size( bIsRTL ? USHRT_MAX : THESIZE, 300 ) );

    SfxItemSet* pSet = new SfxItemSet( pEditEngine->GetEmptyItemSet() );
    pEditEngine->SetFontInfoInItemSet( *pSet, aTextFont );
    lcl_ExtendEditFontAttribs( *pSet );
    if ( bIsRTL )
        lcl_ModifyRTLDefaults( *pSet );
    pEditEngine->SetDefaults( pSet );
    pEditEngine->SetUpdateMode( sal_True );

    pEditView	= new EditView( pEditEngine, this );
    pEditEngine->InsertView( pEditView, EE_APPEND );

    Resize();

    if ( bIsRTL )
        lcl_ModifyRTLVisArea( pEditView );

    if (!maAccTextDatas.empty())
        maAccTextDatas.back()->StartEdit();
}

void ScTextWnd::ImplInitSettings()
{
    bIsRTL = GetSettings().GetLayoutRTL();

    const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();

    Color aBgColor= rStyleSettings.GetWindowColor();
    Color aTxtColor= rStyleSettings.GetWindowTextColor();

    aTextFont.SetFillColor   ( aBgColor );
    aTextFont.SetColor		 (aTxtColor);
    SetBackground			( aBgColor );
    Invalidate();
}

::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > ScTextWnd::CreateAccessible()
{
    return new ScAccessibleEditObject(GetAccessibleParentWindow()->GetAccessible(), NULL, this,
        rtl::OUString(String(ScResId(STR_ACC_EDITLINE_NAME))),
        rtl::OUString(String(ScResId(STR_ACC_EDITLINE_DESCR))), EditLine);
}

void ScTextWnd::InsertAccessibleTextData( ScAccessibleEditLineTextData& rTextData )
{
    OSL_ENSURE( ::std::find( maAccTextDatas.begin(), maAccTextDatas.end(), &rTextData ) == maAccTextDatas.end(),
        "ScTextWnd::InsertAccessibleTextData - passed object already registered" );
    maAccTextDatas.push_back( &rTextData );
}

void ScTextWnd::RemoveAccessibleTextData( ScAccessibleEditLineTextData& rTextData )
{
    AccTextDataVector::iterator aEnd = maAccTextDatas.end();
    AccTextDataVector::iterator aIt = ::std::find( maAccTextDatas.begin(), aEnd, &rTextData );
    OSL_ENSURE( aIt != aEnd, "ScTextWnd::RemoveAccessibleTextData - passed object not registered" );
    if( aIt != aEnd )
        maAccTextDatas.erase( aIt );
}

// -----------------------------------------------------------------------

void ScTextWnd::DataChanged( const DataChangedEvent& rDCEvt )
{
    if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS) &&
         (rDCEvt.GetFlags() & SETTINGS_STYLE) )
    {
        ImplInitSettings();
        Invalidate();
    }
    else
        Window::DataChanged( rDCEvt );
}


//========================================================================
// 							Positionsfenster
//========================================================================

ScPosWnd::ScPosWnd( Window* pParent ) :
    ComboBox	( pParent, WinBits(WB_HIDE | WB_DROPDOWN) ),
    pAccel		( NULL ),
    nTipVisible ( 0 ),
    bFormulaMode( false )
{
    Size aSize( GetTextWidth( String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("GW99999:GW99999")) ),
                GetTextHeight() );
    aSize.Width() += 25;	// ??
    aSize.Height() = CalcWindowSizePixel(11);		// Funktionen: 10 MRU + "andere..."
    SetSizePixel( aSize );

    FillRangeNames();

    StartListening( *SFX_APP() );		// fuer Navigator-Bereichsnamen-Updates
}

ScPosWnd::~ScPosWnd()
{
    EndListening( *SFX_APP() );

    HideTip();

    delete pAccel;
}

void ScPosWnd::SetFormulaMode( sal_Bool bSet )
{
    if ( bSet != bFormulaMode )
    {
        bFormulaMode = bSet;

        if ( bSet )
            FillFunctions();
        else
            FillRangeNames();

        HideTip();
    }
}

void ScPosWnd::SetPos( const String& rPosStr )
{
    if ( aPosStr != rPosStr )
    {
        aPosStr = rPosStr;
        SetText(aPosStr);
    }
}

void ScPosWnd::FillRangeNames()
{
    Clear();

    SfxObjectShell* pObjSh = SfxObjectShell::Current();
    if ( pObjSh && pObjSh->ISA(ScDocShell) )
    {
        ScDocument* pDoc = ((ScDocShell*)pObjSh)->GetDocument();

        //	per Hand sortieren, weil Funktionen nicht sortiert werden:

        ScRangeName* pRangeNames = pDoc->GetRangeName();
        if (!pRangeNames->empty())
        {
            ScRange aDummy;
            std::vector<const ScRangeData*> aSortArray;
            ScRangeName::const_iterator itr = pRangeNames->begin(), itrEnd = pRangeNames->end();
            for (; itr != itrEnd; ++itr)
            {
                if (itr->IsValidReference(aDummy))
                    aSortArray.push_back(&(*itr));
            }

            if (!aSortArray.empty())
            {
#ifndef ICC
                size_t n = aSortArray.size();
                qsort( (void*)&aSortArray[0], n, sizeof(ScRangeData*),
                    &ScRangeData_QsortNameCompare );
#else
                qsort( (void*)&aSortArray[0], n, sizeof(ScRangeData*),
                    ICCQsortNameCompare );
#endif
                for (size_t i = 0; i < n; ++i)
                    InsertEntry(aSortArray[i]->GetName());
            }
        }
    }
    SetText(aPosStr);
}

void ScPosWnd::FillFunctions()
{
    Clear();

    String aFirstName;
    const ScAppOptions& rOpt = SC_MOD()->GetAppOptions();
    sal_uInt16 nMRUCount = rOpt.GetLRUFuncListCount();
    const sal_uInt16* pMRUList = rOpt.GetLRUFuncList();
    if (pMRUList)
    {
        const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
        sal_uLong nListCount = pFuncList->GetCount();
        for (sal_uInt16 i=0; i<nMRUCount; i++)
        {
            sal_uInt16 nId = pMRUList[i];
            for (sal_uLong j=0; j<nListCount; j++)
            {
                const ScFuncDesc* pDesc = pFuncList->GetFunction( j );
                if ( pDesc->nFIndex == nId && pDesc->pFuncName )
                {
                    InsertEntry( *pDesc->pFuncName );
                    if (!aFirstName.Len())
                        aFirstName = *pDesc->pFuncName;
                    break;	// nicht weitersuchen
                }
            }
        }
    }

    //!	Eintrag "Andere..." fuer Funktions-Autopilot wieder aufnehmen,
    //!	wenn der Funktions-Autopilot mit dem bisher eingegebenen Text arbeiten kann!

//	InsertEntry( ScGlobal::GetRscString(STR_FUNCTIONLIST_MORE) );

    SetText(aFirstName);
}

void ScPosWnd::Notify( SfxBroadcaster&, const SfxHint& rHint )
{
    if ( !bFormulaMode )
    {
        //	muss die Liste der Bereichsnamen updgedated werden?

        if ( rHint.ISA(SfxSimpleHint) )
        {
            sal_uLong nHintId = ((SfxSimpleHint&)rHint).GetId();
            if ( nHintId == SC_HINT_AREAS_CHANGED || nHintId == SC_HINT_NAVIGATOR_UPDATEALL)
                FillRangeNames();
        }
        else if ( rHint.ISA(SfxEventHint) )
        {
            sal_uLong nEventId = ((SfxEventHint&)rHint).GetEventId();
            if ( nEventId == SFX_EVENT_ACTIVATEDOC )
                FillRangeNames();
        }
    }
}

void ScPosWnd::HideTip()
{
    if ( nTipVisible )
    {
        Help::HideTip( nTipVisible );
        nTipVisible = 0;
    }
}

ScNameInputType lcl_GetInputType( const String& rText )
{
    ScNameInputType eRet = SC_NAME_INPUT_BAD_NAME;      // the more general error

    ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
    if ( pViewSh )
    {
        ScViewData* pViewData = pViewSh->GetViewData();
        ScDocument* pDoc = pViewData->GetDocument();
        SCTAB nTab = pViewData->GetTabNo();
        formula::FormulaGrammar::AddressConvention eConv = pDoc->GetAddressConvention();

        // test in same order as in SID_CURRENTCELL execute

        ScRange aRange;
        ScAddress aAddress;
        ScRangeUtil aRangeUtil;
        SCTAB nNameTab;
        sal_Int32 nNumeric;

        if ( aRange.Parse( rText, pDoc, eConv ) & SCA_VALID )
            eRet = SC_NAME_INPUT_NAMEDRANGE;
        else if ( aAddress.Parse( rText, pDoc, eConv ) & SCA_VALID )
            eRet = SC_NAME_INPUT_CELL;
        else if ( aRangeUtil.MakeRangeFromName( rText, pDoc, nTab, aRange, RUTL_NAMES, eConv ) )
            eRet = SC_NAME_INPUT_NAMEDRANGE;
        else if ( aRangeUtil.MakeRangeFromName( rText, pDoc, nTab, aRange, RUTL_DBASE, eConv ) )
            eRet = SC_NAME_INPUT_DATABASE;
        else if ( ByteString( rText, RTL_TEXTENCODING_ASCII_US ).IsNumericAscii() &&
                  ( nNumeric = rText.ToInt32() ) > 0 && nNumeric <= MAXROW+1 )
            eRet = SC_NAME_INPUT_ROW;
        else if ( pDoc->GetTable( rText, nNameTab ) )
            eRet = SC_NAME_INPUT_SHEET;
        else if ( ScRangeData::IsNameValid( rText, pDoc ) )     // nothing found, create new range?
        {
            if ( pViewData->GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
                eRet = SC_NAME_INPUT_DEFINE;
            else
                eRet = SC_NAME_INPUT_BAD_SELECTION;
        }
        else
            eRet = SC_NAME_INPUT_BAD_NAME;
    }

    return eRet;
}

void ScPosWnd::Modify()
{
    ComboBox::Modify();

    HideTip();

    if ( !IsTravelSelect() && !bFormulaMode )
    {
        // determine the action that would be taken for the current input

        ScNameInputType eType = lcl_GetInputType( GetText() );      // uses current view
        sal_uInt16 nStrId = 0;
        switch ( eType )
        {
            case SC_NAME_INPUT_CELL:
                nStrId = STR_NAME_INPUT_CELL;
                break;
            case SC_NAME_INPUT_RANGE:
            case SC_NAME_INPUT_NAMEDRANGE:
                nStrId = STR_NAME_INPUT_RANGE;      // named range or range reference
                break;
            case SC_NAME_INPUT_DATABASE:
                nStrId = STR_NAME_INPUT_DBRANGE;
                break;
            case SC_NAME_INPUT_ROW:
                nStrId = STR_NAME_INPUT_ROW;
                break;
            case SC_NAME_INPUT_SHEET:
                nStrId = STR_NAME_INPUT_SHEET;
                break;
            case SC_NAME_INPUT_DEFINE:
                nStrId = STR_NAME_INPUT_DEFINE;
                break;
            default:
                // other cases (error): no tip help
                break;
        }

        if ( nStrId )
        {
            // show the help tip at the text cursor position

            Window* pWin = GetSubEdit();
            if (!pWin)
                pWin = this;
            Point aPos;
            Cursor* pCur = pWin->GetCursor();
            if (pCur)
                aPos = pWin->LogicToPixel( pCur->GetPos() );
            aPos = pWin->OutputToScreenPixel( aPos );
            Rectangle aRect( aPos, aPos );

            String aText = ScGlobal::GetRscString( nStrId );
            sal_uInt16 nAlign = QUICKHELP_LEFT|QUICKHELP_BOTTOM;
            nTipVisible = Help::ShowTip(pWin, aRect, aText, nAlign);
        }
    }
}

void ScPosWnd::Select()
{
    ComboBox::Select();		//	in VCL gibt GetText() erst danach den ausgewaehlten Eintrag

    HideTip();

    if (!IsTravelSelect())
        DoEnter();
}

void ScPosWnd::DoEnter()
{
    String aText = GetText();
    if ( aText.Len() )
    {
        if ( bFormulaMode )
        {
            ScModule* pScMod = SC_MOD();
            if ( aText == ScGlobal::GetRscString(STR_FUNCTIONLIST_MORE) )
            {
                //	Funktions-Autopilot
                //!	mit dem bisher eingegebenen Text weiterarbeiten !!!

                //!	new method at ScModule to query if function autopilot is open
                SfxViewFrame* pViewFrm = SfxViewFrame::Current();
                if ( pViewFrm && !pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) )
                    pViewFrm->GetDispatcher()->Execute( SID_OPENDLG_FUNCTION,
                                              SFX_CALLMODE_SYNCHRON | SFX_CALLMODE_RECORD );
            }
            else
            {
                ScTabViewShell* pViewSh = PTR_CAST( ScTabViewShell, SfxViewShell::Current() );
                ScInputHandler* pHdl = pScMod->GetInputHdl( pViewSh );
                if (pHdl)
                    pHdl->InsertFunction( aText );
            }
        }
        else
        {
            // depending on the input, select something or create a new named range

            ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
            if ( pViewSh )
            {
                ScViewData* pViewData = pViewSh->GetViewData();
                ScDocShell* pDocShell = pViewData->GetDocShell();
                ScDocument* pDoc = pDocShell->GetDocument();

                ScNameInputType eType = lcl_GetInputType( aText );
                if ( eType == SC_NAME_INPUT_BAD_NAME || eType == SC_NAME_INPUT_BAD_SELECTION )
                {
                    sal_uInt16 nId = ( eType == SC_NAME_INPUT_BAD_NAME ) ? STR_NAME_ERROR_NAME : STR_NAME_ERROR_SELECTION;
                    pViewSh->ErrorMessage( nId );
                }
                else if ( eType == SC_NAME_INPUT_DEFINE )
                {
                    ScRangeName* pNames = pDoc->GetRangeName();
                    ScRange aSelection;
                    if ( pNames && !pNames->findByName(aText) &&
                            (pViewData->GetSimpleArea( aSelection ) == SC_MARK_SIMPLE) )
                    {
                        ScRangeName aNewRanges( *pNames );
                        ScAddress aCursor( pViewData->GetCurX(), pViewData->GetCurY(), pViewData->GetTabNo() );
                        String aContent;
                        aSelection.Format( aContent, SCR_ABS_3D, pDoc, pDoc->GetAddressConvention() );
                        ScRangeData* pNew = new ScRangeData( pDoc, aText, aContent, aCursor );
                        if ( aNewRanges.insert(pNew) )
                        {
                            ScDocFunc aFunc(*pDocShell);
                            aFunc.ModifyRangeNames( aNewRanges );
                            pViewSh->UpdateInputHandler(true);
                        }
                        else
                            delete pNew;        // shouldn't happen
                    }
                }
                else
                {
                    // for all selection types, excecute the SID_CURRENTCELL slot.
                    // Note that SID_CURRENTCELL always expects address to be
                    // in Calc A1 format.  Convert the text.
                    ScRange aRange;
                    aRange.ParseAny(aText, pDoc, pDoc->GetAddressConvention());
                    aRange.Format(aText, SCR_ABS_3D, pDoc, ::formula::FormulaGrammar::CONV_OOO);

                    SfxStringItem aPosItem( SID_CURRENTCELL, aText );
                    SfxBoolItem aUnmarkItem( FN_PARAM_1, sal_True );        // remove existing selection

                    pViewSh->GetViewData()->GetDispatcher().Execute( SID_CURRENTCELL,
                                        SFX_CALLMODE_SYNCHRON | SFX_CALLMODE_RECORD,
                                        &aPosItem, &aUnmarkItem, 0L );
                }
            }
        }
    }
    else
        SetText( aPosStr );

    ReleaseFocus_Impl();
}

long ScPosWnd::Notify( NotifyEvent& rNEvt )
{
    long nHandled = 0;

    if ( rNEvt.GetType() == EVENT_KEYINPUT )
    {
        const KeyEvent* pKEvt = rNEvt.GetKeyEvent();

        switch ( pKEvt->GetKeyCode().GetCode() )
        {
            case KEY_RETURN:
                DoEnter();
                nHandled = 1;
                break;

            case KEY_ESCAPE:
                if (nTipVisible)
                {
                    // escape when the tip help is shown: only hide the tip
                    HideTip();
                }
                else
                {
                    if (!bFormulaMode)
                        SetText( aPosStr );
                    ReleaseFocus_Impl();
                }
                nHandled = 1;
                break;
        }
    }

    if ( !nHandled )
        nHandled = ComboBox::Notify( rNEvt );

    if ( rNEvt.GetType() == EVENT_LOSEFOCUS )
        HideTip();

    return nHandled;
}

void ScPosWnd::ReleaseFocus_Impl()
{
    HideTip();

    SfxViewShell* pCurSh = SfxViewShell::Current();
    ScInputHandler* pHdl = SC_MOD()->GetInputHdl( PTR_CAST( ScTabViewShell, pCurSh ) );
    if ( pHdl && pHdl->IsTopMode() )
    {
        //	Focus wieder in die Eingabezeile?

        ScInputWindow* pInputWin = pHdl->GetInputWindow();
        if (pInputWin)
        {
            pInputWin->TextGrabFocus();
            return;
        }
    }

    //	Focus auf die aktive View

    if ( pCurSh )
    {
        Window* pShellWnd = pCurSh->GetWindow();

        if ( pShellWnd )
            pShellWnd->GrabFocus();
    }
}






/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
