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

#include <svx/dialogs.hrc>


#include <tools/list.hxx>
#include <sfx2/viewsh.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/dispatch.hxx>
#include <vcl/image.hxx>

#include <svx/colrctrl.hxx>

#include <svx/svdview.hxx>
#include "svx/drawitem.hxx"
#include <editeng/colritem.hxx>
#include "svx/xattr.hxx"
#include <svx/xtable.hxx>
#include <svx/dialmgr.hxx>
#include "svx/xexch.hxx"
#include <vcl/svapp.hxx>

// ------------------------
// - SvxColorValueSetData -
// ------------------------

class SvxColorValueSetData : public TransferableHelper
{
private:

	XFillExchangeData		maData;

protected:

	virtual void			AddSupportedFormats();
	virtual sal_Bool		GetData( const ::com::sun::star::datatransfer::DataFlavor& rFlavor );
	virtual sal_Bool		WriteObject( SotStorageStreamRef& rxOStm, void* pUserObject, sal_uInt32 nUserObjectId, const ::com::sun::star::datatransfer::DataFlavor& rFlavor );

public:

							SvxColorValueSetData( const XFillAttrSetItem& rSetItem ) :
								maData( rSetItem ) {}
};

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

void SvxColorValueSetData::AddSupportedFormats()
{
	AddFormat( SOT_FORMATSTR_ID_XFA );
}

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

sal_Bool SvxColorValueSetData::GetData( const ::com::sun::star::datatransfer::DataFlavor& rFlavor )
{
	sal_Bool bRet = sal_False;

	if( SotExchange::GetFormat( rFlavor ) == SOT_FORMATSTR_ID_XFA )
	{
		SetObject( &maData, 0, rFlavor );
		bRet = sal_True;
	}

	return bRet;
}

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

sal_Bool SvxColorValueSetData::WriteObject( SotStorageStreamRef& rxOStm, void*, sal_uInt32 , const ::com::sun::star::datatransfer::DataFlavor&  )
{
	*rxOStm << maData;
	return( rxOStm->GetError() == ERRCODE_NONE );
}

/*************************************************************************
|*
|* SvxColorValueSet_docking: Ctor
|*
\************************************************************************/

SvxColorValueSet_docking::SvxColorValueSet_docking( Window* _pParent, WinBits nWinStyle ) :
	SvxColorValueSet( _pParent, nWinStyle ),
	DragSourceHelper( this ),
    mbLeftButton(true)
{
	SetAccessibleName(String( SVX_RES( STR_COLORTABLE ) ) );
}

/*************************************************************************
|*
|* SvxColorValueSet_docking: Ctor
|*
\************************************************************************/

SvxColorValueSet_docking::SvxColorValueSet_docking( Window* _pParent, const ResId& rResId ) :
	SvxColorValueSet( _pParent, rResId ),
	DragSourceHelper( this ),
    mbLeftButton(true)
{
	SetAccessibleName(String( SVX_RES( STR_COLORTABLE )  ));
}

/*************************************************************************
|*
|* SvxColorValueSet_docking: MouseButtonDown
|*
\************************************************************************/

void SvxColorValueSet_docking::MouseButtonDown( const MouseEvent& rMEvt )
{
	// Fuer Mac noch anders handlen !
	if( rMEvt.IsLeft() )
	{
		mbLeftButton = true;
		SvxColorValueSet::MouseButtonDown( rMEvt );
	}
	else
	{
		mbLeftButton = false;
		MouseEvent aMEvt( rMEvt.GetPosPixel(),
						  rMEvt.GetClicks(),
						  rMEvt.GetMode(),
						  MOUSE_LEFT,
						  rMEvt.GetModifier() );
		SvxColorValueSet::MouseButtonDown( aMEvt );
	}

	aDragPosPixel = GetPointerPosPixel();
}

/*************************************************************************
|*
|* SvxColorValueSet_docking: MouseButtonUp
|*
\************************************************************************/

void SvxColorValueSet_docking::MouseButtonUp( const MouseEvent& rMEvt )
{
	// Fuer Mac noch anders handlen !
	if( rMEvt.IsLeft() )
	{
		mbLeftButton = true;
		SvxColorValueSet::MouseButtonUp( rMEvt );
	}
	else
	{
		mbLeftButton = false;
		MouseEvent aMEvt( rMEvt.GetPosPixel(),
						  rMEvt.GetClicks(),
						  rMEvt.GetMode(),
						  MOUSE_LEFT,
						  rMEvt.GetModifier() );
		SvxColorValueSet::MouseButtonUp( aMEvt );
	}
	SetNoSelection();
}

/*************************************************************************
|*
|* Command-Event
|*
\************************************************************************/

void SvxColorValueSet_docking::Command(const CommandEvent& rCEvt)
{
	// Basisklasse
	SvxColorValueSet::Command(rCEvt);
}

/*************************************************************************
|*
|* StartDrag
|*
\************************************************************************/

void SvxColorValueSet_docking::StartDrag( sal_Int8 , const Point&  )
{
	Application::PostUserEvent(STATIC_LINK(this, SvxColorValueSet_docking, ExecDragHdl));
}

/*************************************************************************
|*
|* Drag&Drop asynchron ausfuehren
|*
\************************************************************************/

void SvxColorValueSet_docking::DoDrag()
{
	SfxObjectShell* pDocSh = SfxObjectShell::Current();
	sal_uInt16			nItemId = GetItemId( aDragPosPixel );

	if( pDocSh && nItemId )
	{
		XFillAttrSetItem	aXFillSetItem( &pDocSh->GetPool() );
		SfxItemSet&			rSet = aXFillSetItem.GetItemSet();

		rSet.Put( XFillColorItem( GetItemText( nItemId ), GetItemColor( nItemId ) ) );
		rSet.Put(XFillStyleItem( ( 1 == nItemId ) ? XFILL_NONE : XFILL_SOLID ) );

		EndSelection();
		( new SvxColorValueSetData( aXFillSetItem ) )->StartDrag( this, DND_ACTION_COPY );
		ReleaseMouse();
	}
}

/*************************************************************************
|*
|*
|*
\************************************************************************/

IMPL_STATIC_LINK(SvxColorValueSet_docking, ExecDragHdl, void*, EMPTYARG)
{
	// Als Link, damit asynchron ohne ImpMouseMoveMsg auf dem Stack auch die
	// Farbleiste geloescht werden darf
	pThis->DoDrag();
	return(0);
}

/*************************************************************************
|*
|* Ctor: SvxColorDockingWindow
|*
\************************************************************************/

SvxColorDockingWindow::SvxColorDockingWindow
(
	SfxBindings* _pBindings,
	SfxChildWindow* pCW,
	Window* _pParent,
	const ResId& rResId
) :

	SfxDockingWindow( _pBindings, pCW, _pParent, rResId ),

	maColorTable(),
	aColorSet		( this, ResId( 1, *rResId.GetResMgr() ) ),
	nLeftSlot		( SID_ATTR_FILL_COLOR ),
	nRightSlot		( SID_ATTR_LINE_COLOR ),
	nCols			( 20 ),
	nLines			( 1 )

{
	FreeResource();

	aColorSet.SetStyle( aColorSet.GetStyle() | WB_ITEMBORDER );
	aColorSet.SetSelectHdl( LINK( this, SvxColorDockingWindow, SelectHdl ) );

    // Get the model from the view shell.  Using SfxObjectShell::Current()
    // is unreliable when called at the wrong times.
    SfxObjectShell*	pDocSh = NULL;
    if (_pBindings != NULL)
    {
        SfxDispatcher* pDispatcher = _pBindings->GetDispatcher();
        if (pDispatcher != NULL)
        {
            SfxViewFrame* pFrame = pDispatcher->GetFrame();
            if (pFrame != NULL)
            {
                SfxViewShell* pViewShell = pFrame->GetViewShell();
                if (pViewShell != NULL)
                    pDocSh = pViewShell->GetObjectShell();
            }
        }
    }

	if ( pDocSh )
	{
		const SfxPoolItem*	pItem = pDocSh->GetItem( SID_COLOR_TABLE );
		if( pItem )
		{
			maColorTable = static_cast< const SvxColorTableItem* >(pItem)->GetColorTable();
            FillValueSet();
		}
	}

	aItemSize = aColorSet.CalcItemSizePixel(Size(aColorSet.getEntryEdgeLength(), aColorSet.getEntryEdgeLength()));
	aItemSize.Width() = aItemSize.Width() + aColorSet.getEntryEdgeLength();
	aItemSize.Width() /= 2;
	aItemSize.Height() = aItemSize.Height() + aColorSet.getEntryEdgeLength();
	aItemSize.Height() /= 2;

	SetSize();
	aColorSet.Show();
	StartListening( *_pBindings, sal_True );
}


/*************************************************************************
|*
|* Dtor: SvxColorDockingWindow
|*
\************************************************************************/

SvxColorDockingWindow::~SvxColorDockingWindow()
{
	EndListening( GetBindings() );
}

/*************************************************************************
|*
|* Notify
|*
\************************************************************************/

void SvxColorDockingWindow::Notify( SfxBroadcaster& , const SfxHint& rHint )
{
	const SfxPoolItemHint *pPoolItemHint = PTR_CAST(SfxPoolItemHint, &rHint);
	if ( pPoolItemHint
		 && ( pPoolItemHint->GetObject()->ISA( SvxColorTableItem ) ) )
	{
		// Die Liste der Farben hat sich geaendert
		maColorTable = static_cast< SvxColorTableItem* >(pPoolItemHint->GetObject())->GetColorTable();
		FillValueSet();
	}
}

/*************************************************************************
|*
|* FillValueSet
|*
\************************************************************************/

void SvxColorDockingWindow::FillValueSet()
{
	if( maColorTable.get() )
	{
		nCount = maColorTable->Count();
		aColorSet.Clear();

		// create the first entry for 'invisible/none'
        const Size aColorSize(aColorSet.getEntryEdgeLength(), aColorSet.getEntryEdgeLength());
		long nPtX = aColorSize.Width() - 1;
		long nPtY = aColorSize.Height() - 1;
		VirtualDevice aVD;

        aVD.SetOutputSizePixel( aColorSize );
		aVD.SetLineColor( Color( COL_BLACK ) );
		aVD.SetBackground( Wallpaper( Color( COL_WHITE ) ) );
		aVD.DrawLine( Point(), Point( nPtX, nPtY ) );
		aVD.DrawLine( Point( 0, nPtY ), Point( nPtX, 0 ) );

		Bitmap aBmp( aVD.GetBitmap( Point(), aColorSize ) );

		aColorSet.InsertItem( (sal_uInt16)1, Image(aBmp), SVX_RESSTR( RID_SVXSTR_INVISIBLE ) );
        aColorSet.addEntriesForXColorList(maColorTable, 2);
	}
}

/*************************************************************************
|*
|* SetSize
|*
\************************************************************************/

void SvxColorDockingWindow::SetSize()
{
	// Groesse fuer ValueSet berechnen
	Size aSize = GetOutputSizePixel();
	aSize.Width()  -= 4;
	aSize.Height() -= 4;

	// Zeilen und Spalten berechnen
	nCols = (sal_uInt16) ( aSize.Width() / aItemSize.Width() );
	nLines = (sal_uInt16) ( (float) aSize.Height() / (float) aItemSize.Height() /*+ 0.35*/ );
	if( nLines == 0 )
		nLines++;

	// Scrollbar setzen/entfernen
	WinBits nBits = aColorSet.GetStyle();
	if ( nLines * nCols >= nCount )
		nBits &= ~WB_VSCROLL;
	else
		nBits |= WB_VSCROLL;
	aColorSet.SetStyle( nBits );

	// ScrollBar ?
	long nScrollWidth = aColorSet.GetScrollWidth();
	if( nScrollWidth > 0 )
	{
		// Spalten mit ScrollBar berechnen
		nCols = (sal_uInt16) ( ( aSize.Width() - nScrollWidth ) / aItemSize.Width() );
	}
	aColorSet.SetColCount( nCols );

	if( IsFloatingMode() )
		aColorSet.SetLineCount( nLines );
	else
	{
		aColorSet.SetLineCount( 0 ); // sonst wird LineHeight ignoriert
		aColorSet.SetItemHeight( aItemSize.Height() );
	}

	aColorSet.SetPosSizePixel( Point( 2, 2 ), aSize );
}

/*************************************************************************
|*
|* SvxColorDockingWindow: Close
|*
\************************************************************************/

sal_Bool SvxColorDockingWindow::Close()
{
	SfxBoolItem aItem( SID_COLOR_CONTROL, sal_False );
	GetBindings().GetDispatcher()->Execute(
		SID_COLOR_CONTROL, SFX_CALLMODE_ASYNCHRON | SFX_CALLMODE_RECORD, &aItem, 0L );
	SfxDockingWindow::Close();
	return( sal_True );
}

/*************************************************************************
|*
|* SelectHdl
|*
\************************************************************************/

IMPL_LINK( SvxColorDockingWindow, SelectHdl, void *, EMPTYARG )
{
	SfxDispatcher* pDispatcher = GetBindings().GetDispatcher();
	sal_uInt16 nPos = aColorSet.GetSelectItemId();
	Color  aColor( aColorSet.GetItemColor( nPos ) );
	String aStr( aColorSet.GetItemText( nPos ) );

    if (aColorSet.IsLeftButton())
    {
		if ( nLeftSlot == SID_ATTR_FILL_COLOR )
		{
			if ( nPos == 1 )		// unsichtbar
			{
				XFillStyleItem aXFillStyleItem( XFILL_NONE );
				pDispatcher->Execute( nLeftSlot, SFX_CALLMODE_RECORD, &aXFillStyleItem, 0L );
			}
			else
			{
				sal_Bool bDone = sal_False;

				// Wenn wir eine DrawView haben und uns im TextEdit-Modus befinden,
				// wird nicht die Flaechen-, sondern die Textfarbe zugewiesen
				SfxViewShell* pViewSh = SfxViewShell::Current();
				if ( pViewSh )
				{
					SdrView* pView = pViewSh->GetDrawView();
					if ( pView && pView->IsTextEdit() )
					{
						SvxColorItem aTextColorItem( aColor, SID_ATTR_CHAR_COLOR );
						pDispatcher->Execute(
							SID_ATTR_CHAR_COLOR, SFX_CALLMODE_RECORD, &aTextColorItem, 0L );
						bDone = sal_True;
					}
				}
				if ( !bDone )
				{
					XFillStyleItem aXFillStyleItem( XFILL_SOLID );
					XFillColorItem aXFillColorItem( aStr, aColor );
					pDispatcher->Execute(
						nLeftSlot, SFX_CALLMODE_RECORD, &aXFillColorItem, &aXFillStyleItem, 0L );
				}
			}
		}
		else if ( nPos != 1 )		// unsichtbar
		{
			SvxColorItem aLeftColorItem( aColor, nLeftSlot );
			pDispatcher->Execute( nLeftSlot, SFX_CALLMODE_RECORD, &aLeftColorItem, 0L );
		}
    }
    else
	{
		if ( nRightSlot == SID_ATTR_LINE_COLOR )
		{
			if( nPos == 1 )		// unsichtbar
			{
				XLineStyleItem aXLineStyleItem( XLINE_NONE );
				pDispatcher->Execute( nRightSlot, SFX_CALLMODE_RECORD, &aXLineStyleItem, 0L );
			}
			else
			{
				// Sollte der LineStyle unsichtbar sein, so wird er auf SOLID gesetzt
				SfxViewShell* pViewSh = SfxViewShell::Current();
				if ( pViewSh )
				{
					SdrView* pView = pViewSh->GetDrawView();
					if ( pView )
					{
						SfxItemSet aAttrSet( pView->GetModel()->GetItemPool() );
						pView->GetAttributes( aAttrSet );
						if ( aAttrSet.GetItemState( XATTR_LINESTYLE ) != SFX_ITEM_DONTCARE )
						{
							XLineStyle eXLS = (XLineStyle)
								( (const XLineStyleItem&)aAttrSet.Get( XATTR_LINESTYLE ) ).GetValue();
							if ( eXLS == XLINE_NONE )
							{
								XLineStyleItem aXLineStyleItem( XLINE_SOLID );
								pDispatcher->Execute( nRightSlot, SFX_CALLMODE_RECORD, &aXLineStyleItem, 0L );
							}
						}
					}
				}

				XLineColorItem aXLineColorItem( aStr, aColor );
				pDispatcher->Execute( nRightSlot, SFX_CALLMODE_RECORD, &aXLineColorItem, 0L );
			}
		}
		else if ( nPos != 1 )		// unsichtbar
		{
			SvxColorItem aRightColorItem( aColor, nRightSlot );
			pDispatcher->Execute( nRightSlot, SFX_CALLMODE_RECORD, &aRightColorItem, 0L );
		}
	}

	return 0;
}

/*************************************************************************
|*
|* Resizing
|*
\************************************************************************/


void SvxColorDockingWindow::Resizing( Size& rNewSize )
{
	rNewSize.Width()  -= 4;
	rNewSize.Height() -= 4;

	// Spalten und Reihen ermitteln
	nCols = (sal_uInt16) ( (float) rNewSize.Width() / (float) aItemSize.Width() + 0.5 );
	nLines = (sal_uInt16) ( (float) rNewSize.Height() / (float) aItemSize.Height() + 0.5 );
	if( nLines == 0 )
		nLines = 1;

	// Scrollbar setzen/entfernen
	WinBits nBits = aColorSet.GetStyle();
	if ( nLines * nCols >= nCount )
		nBits &= ~WB_VSCROLL;
	else
		nBits |= WB_VSCROLL;
	aColorSet.SetStyle( nBits );

	// ScrollBar ?
	long nScrollWidth = aColorSet.GetScrollWidth();
	if( nScrollWidth > 0 )
	{
		// Spalten mit ScrollBar berechnen
		nCols = (sal_uInt16) ( ( ( (float) rNewSize.Width() - (float) nScrollWidth ) )
							/ (float) aItemSize.Width() + 0.5 );
	}
	if( nCols <= 1 )
		nCols = 2;

	// Max. Reihen anhand der gegebenen Spalten berechnen
	long nMaxLines = nCount / nCols;
	if( nCount %  nCols )
		nMaxLines++;

	nLines = sal::static_int_cast< sal_uInt16 >(
        std::min< long >( nLines, nMaxLines ) );

	// Groesse des Windows setzen
	rNewSize.Width()  = nCols * aItemSize.Width() + nScrollWidth + 4;
	rNewSize.Height() = nLines * aItemSize.Height() + 4;
}

/*************************************************************************
|*
|* Resize
|*
\************************************************************************/

void SvxColorDockingWindow::Resize()
{
	if ( !IsFloatingMode() || !GetFloatingWindow()->IsRollUp() )
		SetSize();
	SfxDockingWindow::Resize();
}



void SvxColorDockingWindow::GetFocus (void)
{
	SfxDockingWindow::GetFocus();
    // Grab the focus to the color value set so that it can be controlled
    // with the keyboard.
	aColorSet.GrabFocus();
}

long SvxColorDockingWindow::Notify( NotifyEvent& rNEvt )
{
	long nRet = 0;
	if( ( rNEvt.GetType() == EVENT_KEYINPUT ) )
	{
		KeyEvent aKeyEvt = *rNEvt.GetKeyEvent();
		sal_uInt16	 nKeyCode = aKeyEvt.GetKeyCode().GetCode();
		switch( nKeyCode )
		{
			case KEY_ESCAPE:
				GrabFocusToDocument();
				nRet = 1;
				break;
		}
	}

	return nRet ? nRet : SfxDockingWindow::Notify( rNEvt );
}