/**************************************************************
 * 
 * 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_sc.hxx"



// INCLUDE ---------------------------------------------------------------
#define _ZFORLIST_DECLARE_TABLE
#include "scitems.hxx"
#include <svl/smplhint.hxx>
#include <svl/zforlist.hxx>
#include <svx/numfmtsh.hxx>
#include <svx/numinf.hxx>
#include <svx/svxids.hrc>
#include <sfx2/dispatch.hxx>
#include <sfx2/objsh.hxx>

#include "tabvwsh.hxx"
#include "sc.hrc"
#include "global.hxx"
#include "docsh.hxx"
#include "document.hxx"
#include "cell.hxx"
#include "globstr.hrc"
#include "scmod.hxx"
#include "uiitems.hxx"
#include "editsh.hxx"
#include "hints.hxx"


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

void __EXPORT ScTabViewShell::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
{
	if (rHint.ISA(SfxSimpleHint))						// ohne Parameter
	{
		sal_uLong nSlot = ((SfxSimpleHint&)rHint).GetId();
		switch ( nSlot )
		{
			case FID_DATACHANGED:
				UpdateFormulas();
				break;

			case FID_REFMODECHANGED:
				{
					sal_Bool bRefMode = SC_MOD()->IsFormulaMode();
					if (!bRefMode)
						StopRefMode();
					else
					{
						GetSelEngine()->Reset();
						GetFunctionSet()->SetAnchorFlag(sal_True);
						//	AnchorFlag, damit gleich mit Control angehaengt werden kann
					}
				}
				break;

			case FID_KILLEDITVIEW:
			case FID_KILLEDITVIEW_NOPAINT:
				StopEditShell();
				KillEditView( nSlot == FID_KILLEDITVIEW_NOPAINT );
				break;

			case SFX_HINT_DOCCHANGED:
				{
					ScDocument* pDoc = GetViewData()->GetDocument();
					if (!pDoc->HasTable( GetViewData()->GetTabNo() ))
					{
						SetTabNo(0);
					}
				}
				break;

			case SC_HINT_DRWLAYER_NEW:
				MakeDrawView();
				break;

			case SC_HINT_DOC_SAVED:
				{
					//	beim "Save as" kann ein vorher schreibgeschuetztes Dokument
					//	bearbeitbar werden, deshalb die Layer-Locks neu (#39884#)
					//	(Invalidate etc. passiert schon vom Sfx her)
					//	#42091# bei SID_EDITDOC kommt kein SFX_HINT_TITLECHANGED, darum
					//	der eigene Hint aus DoSaveCompleted
					//!	was ist mit SFX_HINT_SAVECOMPLETED ?

					UpdateLayerLocks();

					//	#54891# Design-Modus bei jedem Speichern anzupassen, waere zuviel
					//	(beim Speichern unter gleichem Namen soll er unveraendert bleiben)
					//	Darum nur bei SFX_HINT_MODECHANGED (vom ViewFrame)
				}
				break;

			case SFX_HINT_MODECHANGED:
				//	#54891#/#58510# Da man sich nicht mehr darauf verlassen kann, woher
				//	dieser Hint kommt, den Design-Modus immer dann umschalten, wenn der
				//	ReadOnly-Status sich wirklich geaendert hat:

				if ( GetViewData()->GetSfxDocShell()->IsReadOnly() != bReadOnly )
				{
					bReadOnly = GetViewData()->GetSfxDocShell()->IsReadOnly();

					SfxBoolItem aItem( SID_FM_DESIGN_MODE, !bReadOnly);
					GetViewData()->GetDispatcher().Execute( SID_FM_DESIGN_MODE, SFX_CALLMODE_ASYNCHRON,
												&aItem, 0L );

					UpdateInputContext();
				}
				break;

			case SC_HINT_SHOWRANGEFINDER:
				PaintRangeFinder();
				break;

			case SC_HINT_FORCESETTAB:
				SetTabNo( GetViewData()->GetTabNo(), sal_True );
				break;

			default:
				break;
		}
	}
	else if (rHint.ISA(ScPaintHint))					// neu zeichnen
	{
		ScPaintHint* pHint = (ScPaintHint*) &rHint;
		sal_uInt16 nParts = pHint->GetParts();
		SCTAB nTab = GetViewData()->GetTabNo();
		if (pHint->GetStartTab() <= nTab && pHint->GetEndTab() >= nTab)
		{
			if (nParts & PAINT_EXTRAS)			// zuerst, falls Tabelle weg ist !!!
				if (PaintExtras())
					nParts = PAINT_ALL;

            // if the current sheet has pending row height updates (sheet links refreshed),
            // execute them before invalidating the window
            GetViewData()->GetDocShell()->UpdatePendingRowHeights( GetViewData()->GetTabNo() );

			if (nParts & PAINT_SIZE)
				RepeatResize(); 					//! InvalidateBorder ???
			if (nParts & PAINT_GRID)
				PaintArea( pHint->GetStartCol(), pHint->GetStartRow(),
						   pHint->GetEndCol(), pHint->GetEndRow() );
			if (nParts & PAINT_MARKS)
				PaintArea( pHint->GetStartCol(), pHint->GetStartRow(),
						   pHint->GetEndCol(), pHint->GetEndRow(), SC_UPDATE_MARKS );
			if (nParts & PAINT_LEFT)
				PaintLeftArea( pHint->GetStartRow(), pHint->GetEndRow() );
			if (nParts & PAINT_TOP)
				PaintTopArea( pHint->GetStartCol(), pHint->GetEndCol() );
			if (nParts & PAINT_INVERT)
				InvertBlockMark( pHint->GetStartCol(), pHint->GetStartRow(),
								 pHint->GetEndCol(), pHint->GetEndRow() );

            // #i84689# call UpdateAllOverlays here instead of in ScTabView::PaintArea
            if (nParts & ( PAINT_LEFT | PAINT_TOP ))    // only if widths or heights changed
                UpdateAllOverlays();

			HideNoteMarker();
		}
	}
	else if (rHint.ISA(ScEditViewHint))					// Edit-View anlegen
	{
		//	ScEditViewHint kommt nur an aktiver View an

		ScEditViewHint* pHint = (ScEditViewHint*) &rHint;
		SCTAB nTab = GetViewData()->GetTabNo();
		if ( pHint->GetTab() == nTab )
		{
			SCCOL nCol = pHint->GetCol();
			SCROW nRow = pHint->GetRow();
			{
				HideNoteMarker();

				MakeEditView( pHint->GetEngine(), nCol, nRow );

				StopEditShell();					// sollte nicht gesetzt sein

				ScSplitPos eActive = GetViewData()->GetActivePart();
				if ( GetViewData()->HasEditView(eActive) )
				{
					//	MakeEditView geht schief, wenn der Cursor ausserhalb des
					//	Bildschirms steht. GetEditView gibt dann eine nicht aktive
					//	View zurueck, darum die Abfrage HasEditView.

					EditView* pView = GetViewData()->GetEditView(eActive);	// ist nicht 0

					SetEditShell(pView ,sal_True);
				}
			}
		}
	}
	else if (rHint.ISA(ScTablesHint))				// Tabelle eingefuegt / geloescht
	{
			//	aktuelle Tabelle zuerst holen (kann bei DeleteTab an ViewData geaendert werden)
		SCTAB nActiveTab = GetViewData()->GetTabNo();

		const ScTablesHint& rTabHint = (const ScTablesHint&)rHint;
		SCTAB nTab1 = rTabHint.GetTab1();
		SCTAB nTab2 = rTabHint.GetTab2();
		sal_uInt16 nId  = rTabHint.GetId();
		switch (nId)
		{
			case SC_TAB_INSERTED:
				GetViewData()->InsertTab( nTab1 );
				break;
			case SC_TAB_DELETED:
				GetViewData()->DeleteTab( nTab1 );
				break;
			case SC_TAB_MOVED:
				GetViewData()->MoveTab( nTab1, nTab2 );
				break;
			case SC_TAB_COPIED:
				GetViewData()->CopyTab( nTab1, nTab2 );
				break;
			case SC_TAB_HIDDEN:
				break;
			default:
				DBG_ERROR("unbekannter ScTablesHint");
		}

		//	hier keine Abfrage auf IsActive() mehr, weil die Aktion von Basic ausgehen
		//	kann und dann auch die aktive View umgeschaltet werden muss.

		SCTAB nNewTab = nActiveTab;
		bool bStayOnActiveTab = true;
		switch (nId)
		{
			case SC_TAB_INSERTED:
				if ( nTab1 <= nNewTab )				// vorher eingefuegt
					++nNewTab;
				break;
			case SC_TAB_DELETED:
				if ( nTab1 < nNewTab )				// vorher geloescht
					--nNewTab;
				else if ( nTab1 == nNewTab )		// aktuelle geloescht
					bStayOnActiveTab = false;
				break;
			case SC_TAB_MOVED:
				if ( nNewTab == nTab1 )				// verschobene Tabelle
					nNewTab = nTab2;
				else if ( nTab1 < nTab2 )			// nach hinten verschoben
				{
					if ( nNewTab > nTab1 && nNewTab <= nTab2 )		// nachrueckender Bereich
						--nNewTab;
				}
				else								// nach vorne verschoben
				{
					if ( nNewTab >= nTab2 && nNewTab < nTab1 )		// nachrueckender Bereich
						++nNewTab;
				}
				break;
			case SC_TAB_COPIED:
				if ( nNewTab >= nTab2 )				// vorher eingefuegt
					++nNewTab;
				break;
			case SC_TAB_HIDDEN:
				if ( nTab1 == nNewTab )				// aktuelle ausgeblendet
					bStayOnActiveTab = false;
				break;
		}

		ScDocument* pDoc = GetViewData()->GetDocument();
		if ( nNewTab >= pDoc->GetTableCount() )
			nNewTab = pDoc->GetTableCount() - 1;

        sal_Bool bForce = !bStayOnActiveTab;
		SetTabNo( nNewTab, bForce, sal_False, bStayOnActiveTab );
	}
	else if (rHint.ISA(ScIndexHint))
	{
		const ScIndexHint& rIndexHint = (const ScIndexHint&)rHint;
		sal_uInt16 nId = rIndexHint.GetId();
		sal_uInt16 nIndex = rIndexHint.GetIndex();
		switch (nId)
		{
			case SC_HINT_SHOWRANGEFINDER:
				PaintRangeFinder( nIndex );
				break;
		}
	}

	SfxViewShell::Notify( rBC, rHint );
}

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

void ScTabViewShell::MakeNumberInfoItem( ScDocument*		 pDoc,
										 ScViewData*		 pViewData,
										 SvxNumberInfoItem** ppItem )
{
	//------------------------------
	// NumberInfo-Item konstruieren:
	//------------------------------
	ScBaseCell* 		pCell = NULL;
	SvxNumberValueType	eValType		= SVX_VALUE_TYPE_UNDEFINED;
	double				nCellValue		= 0;
	String				aCellString;

	pDoc->GetCell( pViewData->GetCurX(),
				   pViewData->GetCurY(),
				   pViewData->GetTabNo(),
				   pCell );

	if ( pCell )
	{
		switch ( pCell->GetCellType() )
		{
			case CELLTYPE_VALUE:
				{
					nCellValue = ((ScValueCell*)pCell)->GetValue();
					eValType = SVX_VALUE_TYPE_NUMBER;
					aCellString.Erase();
				}
				break;

			case CELLTYPE_STRING:
				{
					((ScStringCell*)pCell)->GetString( aCellString );
					eValType = SVX_VALUE_TYPE_STRING;
				}
				break;

			case CELLTYPE_FORMULA:
				{
					if ( ((ScFormulaCell*)pCell)->IsValue() )
					{
						nCellValue = ((ScFormulaCell*)pCell)->GetValue();
						eValType = SVX_VALUE_TYPE_NUMBER;
					}
					else
					{
						nCellValue = 0;
						eValType   = SVX_VALUE_TYPE_UNDEFINED;
					}
					aCellString.Erase();
				}
				break;

			default:
				nCellValue = 0;
				eValType   = SVX_VALUE_TYPE_UNDEFINED;
				aCellString.Erase();
		}
	}
	else // Zelle noch leer (== nicht erzeugt)
	{
		nCellValue = 0;
		eValType   = SVX_VALUE_TYPE_UNDEFINED;
		aCellString.Erase();
	}

	switch ( eValType )
	{
		case SVX_VALUE_TYPE_STRING:
			*ppItem = new SvxNumberInfoItem(
								pDoc->GetFormatTable(),
								aCellString,
								SID_ATTR_NUMBERFORMAT_INFO );
			break;

		case SVX_VALUE_TYPE_NUMBER:
			*ppItem = new SvxNumberInfoItem(
								pDoc->GetFormatTable(),
								nCellValue,
								SID_ATTR_NUMBERFORMAT_INFO );
			break;

		case SVX_VALUE_TYPE_UNDEFINED:
		default:
			*ppItem = new SvxNumberInfoItem(
								pDoc->GetFormatTable(),
								(const sal_uInt16)
								SID_ATTR_NUMBERFORMAT_INFO );
	}
}

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

void ScTabViewShell::UpdateNumberFormatter(
						ScDocument* 			 pDoc,
						const SvxNumberInfoItem& rInfoItem )
{
	const sal_uInt32 nDelCount = rInfoItem.GetDelCount();

	if ( nDelCount > 0 )
	{
		const sal_uInt32* pDelArr = rInfoItem.GetDelArray();

		for ( sal_uInt16 i=0; i<nDelCount; i++ )
			rInfoItem.GetNumberFormatter()->DeleteEntry( pDelArr[i] );
	}

	// sollte besser UpdateNumberFormats() heissen ?
	pDoc->DeleteNumberFormat( rInfoItem.GetDelArray(),
							  rInfoItem.GetDelCount() );
}