/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svtools.hxx"
#include <svtools/editbrowsebox.hxx>
#include <vcl/decoview.hxx>
#include <svtools/fmtfield.hxx>
#include <svtools/xtextedt.hxx>

#include <algorithm>

// .......................................................................
namespace svt
{
// .......................................................................

	TYPEINIT0(CellController);
	TYPEINIT1(EditCellController, CellController);
	TYPEINIT1(SpinCellController, CellController);
	TYPEINIT1(CheckBoxCellController, CellController);
	TYPEINIT1(ComboBoxCellController, CellController);
	TYPEINIT1(ListBoxCellController, CellController);

	TYPEINIT1( FormattedFieldCellController, EditCellController );

	//==================================================================
	//= ComboBoxControl
	//==================================================================
	ComboBoxControl::ComboBoxControl(Window* pParent, WinBits nWinStyle)
				   :ComboBox(pParent, nWinStyle|WB_DROPDOWN|WB_NOBORDER)
	{
		EnableAutoSize(sal_False);
		EnableAutocomplete(sal_True);
		SetDropDownLineCount(5);
	}

	//------------------------------------------------------------------
	long ComboBoxControl::PreNotify( NotifyEvent& rNEvt )
	{
		switch (rNEvt.GetType())
		{
			case EVENT_KEYINPUT:
				if (!IsInDropDown())
				{
					const KeyEvent *pEvt = rNEvt.GetKeyEvent();
					const KeyCode rKey = pEvt->GetKeyCode();

					if ((rKey.GetCode() == KEY_UP || rKey.GetCode() == KEY_DOWN) &&
						(!pEvt->GetKeyCode().IsShift() && pEvt->GetKeyCode().IsMod1()))
					{
						// select next resp. previous entry
						int nPos = GetEntryPos(GetText());
						nPos = nPos + (rKey.GetCode() == KEY_DOWN ? 1 : -1);
						if (nPos < 0)
							nPos = 0;
						if (nPos >= GetEntryCount())
							nPos = GetEntryCount() - 1;
						SetText(GetEntry(sal::static_int_cast< sal_uInt16 >(nPos)));
						return 1;
					}
				}
				break;
		}
		return ComboBox::PreNotify(rNEvt);
	}

	//==================================================================
	//= ComboBoxCellController
	//==================================================================
	//------------------------------------------------------------------
	ComboBoxCellController::ComboBoxCellController(ComboBoxControl* pWin)
							 :CellController(pWin)
	{
	}

	//------------------------------------------------------------------
	sal_Bool ComboBoxCellController::MoveAllowed(const KeyEvent& rEvt) const
	{
		ComboBoxControl& rBox = GetComboBox();
		switch (rEvt.GetKeyCode().GetCode())
		{
			case KEY_END:
			case KEY_RIGHT:
			{
				Selection aSel = rBox.GetSelection();
				return !aSel && aSel.Max() == rBox.GetText().Len();
			}
			case KEY_HOME:
			case KEY_LEFT:
			{
				Selection aSel = rBox.GetSelection();
				return !aSel && aSel.Min() == 0;
			}
			case KEY_UP:
			case KEY_DOWN:
				if (rBox.IsInDropDown())
					return sal_False;
				if (!rEvt.GetKeyCode().IsShift() &&
					 rEvt.GetKeyCode().IsMod1())
					return sal_False;
				// drop down the list box
				else if (rEvt.GetKeyCode().IsMod2() && rEvt.GetKeyCode().GetCode() == KEY_DOWN)
					return sal_False;
			case KEY_PAGEUP:
			case KEY_PAGEDOWN:
			case KEY_RETURN:
				if (rBox.IsInDropDown())
					return sal_False;
			default:
				return sal_True;
		}
	}

	//------------------------------------------------------------------
	sal_Bool ComboBoxCellController::IsModified() const
	{
		return GetComboBox().GetSavedValue() != GetComboBox().GetText();
	}

	//------------------------------------------------------------------
	void ComboBoxCellController::ClearModified()
	{
		GetComboBox().SaveValue();
	}

	//------------------------------------------------------------------
	void ComboBoxCellController::SetModifyHdl(const Link& rLink)
	{
		GetComboBox().SetModifyHdl(rLink);
	}

	//==================================================================
	//= ListBoxControl
	//==================================================================
	//------------------------------------------------------------------
	ListBoxControl::ListBoxControl(Window* pParent, WinBits nWinStyle)
				  :ListBox(pParent, nWinStyle|WB_DROPDOWN|WB_NOBORDER)
	{
		EnableAutoSize(sal_False);
		EnableMultiSelection(sal_False);
		SetDropDownLineCount(20);
	}

	//------------------------------------------------------------------
	long ListBoxControl::PreNotify( NotifyEvent& rNEvt )
	{
		switch (rNEvt.GetType())
		{
			case EVENT_KEYINPUT:
				if (!IsInDropDown())
				{
					const KeyEvent *pEvt = rNEvt.GetKeyEvent();
					const KeyCode rKey = pEvt->GetKeyCode();

					if ((rKey.GetCode() == KEY_UP || rKey.GetCode() == KEY_DOWN) &&
						(!pEvt->GetKeyCode().IsShift() && pEvt->GetKeyCode().IsMod1()))
					{
						// select next resp. previous entry
						int nPos = GetSelectEntryPos();
						nPos = nPos + (rKey.GetCode() == KEY_DOWN ? 1 : -1);
						if (nPos < 0)
							nPos = 0;
						if (nPos >= GetEntryCount())
							nPos = GetEntryCount() - 1;
						SelectEntryPos(sal::static_int_cast< sal_uInt16 >(nPos));
						Select();	// for calling Modify
						return 1;
					}
					else if (GetParent()->PreNotify(rNEvt))
						return 1;
				}
				break;
		}
		return ListBox::PreNotify(rNEvt);
	}

	//==================================================================
	//= ListBoxCellController
	//==================================================================
	//------------------------------------------------------------------
	ListBoxCellController::ListBoxCellController(ListBoxControl* pWin)
							 :CellController(pWin)
	{
	}

	//------------------------------------------------------------------
	sal_Bool ListBoxCellController::MoveAllowed(const KeyEvent& rEvt) const
	{
		ListBoxControl& rBox = GetListBox();
		switch (rEvt.GetKeyCode().GetCode())
		{
			case KEY_UP:
			case KEY_DOWN:
				if (!rEvt.GetKeyCode().IsShift() &&
					 rEvt.GetKeyCode().IsMod1())
					return sal_False;
				// drop down the list box
				else
					if (rEvt.GetKeyCode().IsMod2() && rEvt.GetKeyCode().GetCode() == KEY_DOWN)
						return sal_False;
			case KEY_PAGEUP:
			case KEY_PAGEDOWN:
				if (rBox.IsTravelSelect())
					return sal_False;
			default:
				return sal_True;
		}
	}

	//------------------------------------------------------------------
	sal_Bool ListBoxCellController::IsModified() const
	{
		return GetListBox().GetSelectEntryPos() != GetListBox().GetSavedValue();
	}

	//------------------------------------------------------------------
	void ListBoxCellController::ClearModified()
	{
		GetListBox().SaveValue();
	}

	//------------------------------------------------------------------
	void ListBoxCellController::SetModifyHdl(const Link& rLink)
	{
		GetListBox().SetSelectHdl(rLink);
	}

	//==================================================================
	//= CheckBoxControl
	//==================================================================
	//------------------------------------------------------------------
	CheckBoxControl::CheckBoxControl(Window* pParent, WinBits nWinStyle)
				   :Control(pParent, nWinStyle)
	{
		const Wallpaper& rParentBackground = pParent->GetBackground();
		if ( (pParent->GetStyle() & WB_CLIPCHILDREN) || rParentBackground.IsFixed() )
			SetBackground( rParentBackground );
		else
		{
			SetPaintTransparent( sal_True );
			SetBackground();
		}

		EnableChildTransparentMode();

		pBox = new TriStateBox(this,WB_CENTER|WB_VCENTER);
		pBox->EnableChildTransparentMode();
		pBox->SetPaintTransparent( sal_True );
		pBox->SetClickHdl( LINK( this, CheckBoxControl, OnClick ) );
		pBox->Show();
	}

	//------------------------------------------------------------------
	CheckBoxControl::~CheckBoxControl()
	{
		delete pBox;
	}

	//------------------------------------------------------------------
	IMPL_LINK( CheckBoxControl, OnClick, void*, EMPTYARG )
	{
		m_aClickLink.Call(pBox);
		return m_aModifyLink.Call(pBox);
	}

	//------------------------------------------------------------------
	void CheckBoxControl::Resize()
	{
		Control::Resize();
		pBox->SetPosSizePixel(Point(0,0),GetSizePixel());
	}

	//------------------------------------------------------------------------------
	void CheckBoxControl::DataChanged( const DataChangedEvent& _rEvent )
    {
        if ( _rEvent.GetType() == DATACHANGED_SETTINGS )
            pBox->SetSettings( GetSettings() );
    }

	//------------------------------------------------------------------------------
	void CheckBoxControl::StateChanged( StateChangedType nStateChange )
	{
		Control::StateChanged(nStateChange);
		if ( nStateChange == STATE_CHANGE_ZOOM )
			pBox->SetZoom(GetZoom());
	}

	//------------------------------------------------------------------
	void CheckBoxControl::Draw( OutputDevice* pDev, const Point& rPos, const Size& rSize, sal_uLong nFlags )
	{
		pBox->Draw(pDev,rPos,rSize,nFlags);
	}

	//------------------------------------------------------------------
	void CheckBoxControl::GetFocus()
	{
		pBox->GrabFocus();
	}

	//------------------------------------------------------------------
	void CheckBoxControl::Paint(const Rectangle& rClientRect)
	{
		Control::Paint(rClientRect);
		if (HasFocus())
			ShowFocus(aFocusRect);
	}

	//------------------------------------------------------------------
	long CheckBoxControl::PreNotify(NotifyEvent& rEvt)
	{
		switch (rEvt.GetType())
		{
			case EVENT_GETFOCUS:
				ShowFocus(aFocusRect);
				break;
			case EVENT_LOSEFOCUS:
				HideFocus();
		}
		return Control::PreNotify(rEvt);
	}

	//==================================================================
	//= CheckBoxCellController
	//==================================================================
	//------------------------------------------------------------------
	sal_Bool CheckBoxCellController::WantMouseEvent() const
	{
		return sal_True;
	}

	//------------------------------------------------------------------
	CheckBox& CheckBoxCellController::GetCheckBox() const
	{
		return ((CheckBoxControl &)GetWindow()).GetBox();
	}

	//------------------------------------------------------------------
	sal_Bool CheckBoxCellController::IsModified() const
	{
		return GetCheckBox().GetSavedValue() != GetCheckBox().GetState();
	}

	//------------------------------------------------------------------
	void CheckBoxCellController::ClearModified()
	{
		GetCheckBox().SaveValue();
	}

	//------------------------------------------------------------------
	void CheckBoxCellController::SetModifyHdl(const Link& rLink)
	{
		((CheckBoxControl &)GetWindow()).SetModifyHdl(rLink);
	}

	//==================================================================
	//= MultiLineEditImplementation
	//==================================================================
	//------------------------------------------------------------------
    String MultiLineEditImplementation::GetText( LineEnd aSeparator ) const
    {
        return const_cast< MultiLineEditImplementation* >( this )->GetEditWindow().GetText( aSeparator );
    }

	//------------------------------------------------------------------
    String MultiLineEditImplementation::GetSelected( LineEnd aSeparator ) const
    {
        return const_cast< MultiLineEditImplementation* >( this )->GetEditWindow().GetSelected( aSeparator );
    }

	//==================================================================
	//= EditCellController
	//==================================================================
	//------------------------------------------------------------------
	EditCellController::EditCellController( Edit* _pEdit )
        :CellController( _pEdit )
        ,m_pEditImplementation( new EditImplementation( *_pEdit ) )
        ,m_bOwnImplementation( sal_True )
	{
	}

	//------------------------------------------------------------------
	EditCellController::EditCellController( MultiLineTextCell* _pEdit )
        :CellController( _pEdit )
        ,m_pEditImplementation( new MultiLineEditImplementation( *_pEdit ) )
        ,m_bOwnImplementation( sal_True )
	{
	}

	//------------------------------------------------------------------
	EditCellController::EditCellController( IEditImplementation* _pImplementation )
        :CellController( &_pImplementation->GetControl() )
        ,m_pEditImplementation( _pImplementation )
        ,m_bOwnImplementation( sal_False )
	{
	}

	//-----------------------------------------------------------------------------
    EditCellController::~EditCellController( )
    {
        if ( m_bOwnImplementation )
            DELETEZ( m_pEditImplementation );
    }

	//-----------------------------------------------------------------------------
	void EditCellController::SetModified()
	{
		m_pEditImplementation->SetModified();
	}

	//-----------------------------------------------------------------------------
	void EditCellController::ClearModified()
	{
		m_pEditImplementation->ClearModified();
	}

	//------------------------------------------------------------------
	sal_Bool EditCellController::MoveAllowed(const KeyEvent& rEvt) const
	{
		sal_Bool bResult;
		switch (rEvt.GetKeyCode().GetCode())
		{
			case KEY_END:
			case KEY_RIGHT:
			{
				Selection aSel = m_pEditImplementation->GetSelection();
				bResult = !aSel && aSel.Max() == m_pEditImplementation->GetText( LINEEND_LF ).Len();
			}	break;
			case KEY_HOME:
			case KEY_LEFT:
			{
				Selection aSel = m_pEditImplementation->GetSelection();
				bResult = !aSel && aSel.Min() == 0;
			}	break;
			default:
				bResult = sal_True;
		}
		return bResult;
	}

	//------------------------------------------------------------------
	sal_Bool EditCellController::IsModified() const
	{
		return m_pEditImplementation->IsModified();
	}

	//------------------------------------------------------------------
	void EditCellController::SetModifyHdl(const Link& rLink)
	{
		m_pEditImplementation->SetModifyHdl(rLink);
	}

	//==================================================================
	//= SpinCellController
	//==================================================================
	//------------------------------------------------------------------
	SpinCellController::SpinCellController(SpinField* pWin)
						 :CellController(pWin)
	{
	}

	//-----------------------------------------------------------------------------
	void SpinCellController::SetModified()
	{
		GetSpinWindow().SetModifyFlag();
	}

	//-----------------------------------------------------------------------------
	void SpinCellController::ClearModified()
	{
		GetSpinWindow().ClearModifyFlag();
	}

	//------------------------------------------------------------------
	sal_Bool SpinCellController::MoveAllowed(const KeyEvent& rEvt) const
	{
		sal_Bool bResult;
		switch (rEvt.GetKeyCode().GetCode())
		{
			case KEY_END:
			case KEY_RIGHT:
			{
				Selection aSel = GetSpinWindow().GetSelection();
				bResult = !aSel && aSel.Max() == GetSpinWindow().GetText().Len();
			}	break;
			case KEY_HOME:
			case KEY_LEFT:
			{
				Selection aSel = GetSpinWindow().GetSelection();
				bResult = !aSel && aSel.Min() == 0;
			}	break;
			default:
				bResult = sal_True;
		}
		return bResult;
	}

	//------------------------------------------------------------------
	sal_Bool SpinCellController::IsModified() const
	{
		return GetSpinWindow().IsModified();
	}

	//------------------------------------------------------------------
	void SpinCellController::SetModifyHdl(const Link& rLink)
	{
		GetSpinWindow().SetModifyHdl(rLink);
	}

	//==================================================================
	//= FormattedFieldCellController
	//==================================================================
	//------------------------------------------------------------------
	FormattedFieldCellController::FormattedFieldCellController( FormattedField* _pFormatted )
		:EditCellController( _pFormatted )
	{
	}

	//------------------------------------------------------------------
	void FormattedFieldCellController::CommitModifications()
	{
		static_cast< FormattedField& >( GetWindow() ).Commit();
	}

	//==================================================================
	//= MultiLineTextCell
	//==================================================================
	//------------------------------------------------------------------
    void MultiLineTextCell::Modify()
    {
        GetTextEngine()->SetModified( sal_True );
        MultiLineEdit::Modify();
    }

	//------------------------------------------------------------------
    sal_Bool MultiLineTextCell::dispatchKeyEvent( const KeyEvent& _rEvent )
    {
        Selection aOldSelection( GetSelection() );

        sal_Bool bWasModified = IsModified();
        ClearModifyFlag( );

        sal_Bool bHandled = GetTextView()->KeyInput( _rEvent );

        sal_Bool bIsModified = IsModified();
        if ( bWasModified && !bIsModified )
            // not sure whether this can really happen
            SetModifyFlag();

        if ( bHandled ) // the view claimed it handled the key input
        {
            // unfortunately, KeyInput also returns <TRUE/> (means "I handled this key input")
            // when nothing really changed. Let's care for this.
            Selection aNewSelection( GetSelection() );
            if  (  aNewSelection != aOldSelection   // selection changed
                || bIsModified                      // or some other modification
                )
                return sal_True;
        }
        return sal_False;
    }

	//------------------------------------------------------------------
    long MultiLineTextCell::PreNotify( NotifyEvent& rNEvt )
    {
        if ( rNEvt.GetType() == EVENT_KEYINPUT )
        {
            if ( IsWindowOrChild( rNEvt.GetWindow() ) )
            {
                // give the text view a chance to handle the keys
                // this is necessary since a lot of keys which are normally handled
                // by this view (in KeyInput) are intercepted by the EditBrowseBox,
                // which uses them for other reasons. An example is the KeyUp key,
                // which is used by both the text view and the edit browse box

                const KeyEvent* pKeyEvent = rNEvt.GetKeyEvent();
                const KeyCode&  rKeyCode  = pKeyEvent->GetKeyCode();
                sal_uInt16 nCode = rKeyCode.GetCode();

                if ( ( nCode == KEY_RETURN ) && ( rKeyCode.GetModifier() == KEY_MOD1 ) )
                {
                    KeyEvent aEvent( pKeyEvent->GetCharCode(),
                        KeyCode( KEY_RETURN ),
                        pKeyEvent->GetRepeat()
                    );
                    if ( dispatchKeyEvent( aEvent ) )
                        return 1;
                }

                if ( ( nCode != KEY_TAB ) && ( nCode != KEY_RETURN ) )   // everything but tab and enter
                {
                    if ( dispatchKeyEvent( *pKeyEvent ) )
                        return 1;
                }
            }
        }
        return MultiLineEdit::PreNotify( rNEvt );
    }

// .......................................................................
}	// namespace svt
// .......................................................................