/**************************************************************
 * 
 * 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 <limits.h>
#ifndef _METRIC_HXX
#include <vcl/metric.hxx>
#endif
#include <vcl/svapp.hxx>
#ifdef DBG_UTIL
#include <vcl/sound.hxx>
#endif

#include <svtools/svlbox.hxx>
#include <svtools/svicnvw.hxx>
#include <svimpicn.hxx>
#ifndef _SVLBITM_HXX
#include <svtools/svlbitm.hxx>
#endif
#include <svl/svarray.hxx>



#define VIEWMODE_ICON 	0x0001	// Text unter Bitmap
#define VIEWMODE_NAME 	0x0002  // Text rechts neben Bitmap
#define VIEWMODE_TEXT 	0x0004  // Text ohne Bitmap

#define DD_SCROLL_PIXEL 10

// alle Angaben in Pixel

#define ICONVIEW_OFFS_BMP_STRING	3

// fuer das Bounding-Rectangle
#define LROFFS_BOUND				2
#define TBOFFS_BOUND				2

// fuer das Focus-Rectangle um Icons
#define LROFFS_ICON					2
#define TBOFFS_ICON					2

#define NAMEVIEW_OFFS_BMP_STRING	3

// Abstaende von Fensterraendern
#define LROFFS_WINBORDER			4
#define TBOFFS_WINBORDER			4

// Breitenoffset Highlight-Rect bei Text
#define LROFFS_TEXT					2


#define ICNVIEWDATA(xPtr) (SvIcnVwDataEntry*)(pView->GetViewDataEntry(xPtr))
#define ICNVIEWDATA2(xPtr) (SvIcnVwDataEntry*)(pView->pView->GetViewDataEntry(xPtr))

//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// -------------------------------------------------------------------------
// Hilfsfunktionen von Thomas Hosemann zur mehrzeiligen Ausgabe von
// Strings. Die Funktionen werden spaeter in StarView integriert.
// -------------------------------------------------------------------------
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------

// keine doppelten Defines
#ifdef TEXT_DRAW_CLIP
#undef TEXT_DRAW_CLIP
#endif
#ifdef TEXT_DRAW_MULTILINE
#undef TEXT_DRAW_MULTILINE
#endif
#ifdef TEXT_DRAW_WORDBREAK
#undef TEXT_DRAW_WORDBREAK
#endif

// #define TEXT_DRAW_DISABLE           ((sal_uInt16)0x0001)
// #define TEXT_DRAW_3DLOOK            ((sal_uInt16)0x0002)
// #define TEXT_DRAW_MNEMONIC          ((sal_uInt16)0x0004)
#define TEXT_DRAW_LEFT              ((sal_uInt16)0x0010)
#define TEXT_DRAW_CENTER            ((sal_uInt16)0x0020)
#define TEXT_DRAW_RIGHT             ((sal_uInt16)0x0040)
#define TEXT_DRAW_TOP               ((sal_uInt16)0x0080)
#define TEXT_DRAW_VCENTER           ((sal_uInt16)0x0100)
#define TEXT_DRAW_BOTTOM            ((sal_uInt16)0x0200)
#define TEXT_DRAW_ENDELLIPSIS       ((sal_uInt16)0x0400)
#define TEXT_DRAW_PATHELLIPSIS      ((sal_uInt16)0x0800)
#define TEXT_DRAW_CLIP              ((sal_uInt16)0x1000)
#define TEXT_DRAW_MULTILINE         ((sal_uInt16)0x2000)
#define TEXT_DRAW_WORDBREAK         ((sal_uInt16)0x4000)

XubString GetEllipsisString( OutputDevice* pDev,
							const XubString& rStr, long nMaxWidth,
							sal_uInt16 nStyle = TEXT_DRAW_ENDELLIPSIS )
{
	XubString aStr = rStr;

	if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
	{
		sal_uInt16 nIndex = pDev->GetTextBreak( rStr, nMaxWidth );
		if ( nIndex != STRING_LEN )
		{
			aStr.Erase( nIndex );
			if ( nIndex > 1 )
			{
				aStr.AppendAscii("...");
				while ( aStr.Len() &&
						(pDev->GetTextWidth( aStr ) > nMaxWidth) )
				{
					if ( (nIndex > 1) || (nIndex == aStr.Len()) )
						nIndex--;
					aStr.Erase( nIndex, 1 );
				}
			}

			if ( !aStr.Len() && (nStyle & TEXT_DRAW_CLIP) )
				aStr += rStr.GetChar( 0 );
		}
	}

	return aStr;
}

class TextLineInfo
{
private:
	long        mnWidth;
	sal_uInt16      mnIndex;
	sal_uInt16      mnLen;

public:
				TextLineInfo( long nWidth, sal_uInt16 nIndex, sal_uInt16 nLen )
				{
					mnWidth = nWidth;
					mnIndex = nIndex;
					mnLen   = nLen;
				}

	long        GetWidth() const { return mnWidth; }
	sal_uInt16      GetIndex() const { return mnIndex; }
	sal_uInt16      GetLen() const { return mnLen; }
};

#define MULTITEXTLINEINFO_RESIZE    16
typedef TextLineInfo* PTextLineInfo;

class MultiTextLineInfo
{
private:
	PTextLineInfo*      mpLines;
	sal_uInt16              mnLines;
	sal_uInt16              mnSize;

public:
						MultiTextLineInfo();
						~MultiTextLineInfo();

	void                AddLine( TextLineInfo* pLine );
	void                Clear();

	TextLineInfo*       GetLine( sal_uInt16 nLine ) const
							{ return mpLines[nLine]; }
	sal_uInt16              Count() const { return mnLines; }

private:
						MultiTextLineInfo( const MultiTextLineInfo& );
	MultiTextLineInfo&  operator=( const MultiTextLineInfo& );
};

MultiTextLineInfo::MultiTextLineInfo()
{
	mpLines         = new PTextLineInfo[MULTITEXTLINEINFO_RESIZE];
	mnLines         = 0;
	mnSize          = MULTITEXTLINEINFO_RESIZE;
}

MultiTextLineInfo::~MultiTextLineInfo()
{
	for ( sal_uInt16 i = 0; i < mnLines; i++ )
		delete mpLines[i];
	delete [] mpLines;
}

void MultiTextLineInfo::AddLine( TextLineInfo* pLine )
{
	if ( mnSize == mnLines )
	{
		mnSize += MULTITEXTLINEINFO_RESIZE;
		PTextLineInfo* pNewLines = new PTextLineInfo[mnSize];
		memcpy( pNewLines, mpLines, mnLines*sizeof(PTextLineInfo) );
		mpLines = pNewLines;
	}

	mpLines[mnLines] = pLine;
	mnLines++;
}

void MultiTextLineInfo::Clear()
{
	for ( sal_uInt16 i = 0; i < mnLines; i++ )
		delete mpLines[i];
	mnLines = 0;
}

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

long GetTextLines( OutputDevice* pDev, MultiTextLineInfo& rLineInfo,
				   long nWidth, const XubString& rStr,
				   sal_uInt16 nStyle = TEXT_DRAW_WORDBREAK )
{
	rLineInfo.Clear();
	if ( !rStr.Len() )
		return 0;
	if ( nWidth <= 0 )
		nWidth = 1;

	sal_uInt16          nStartPos       = 0;                // Start-Position der Zeile
	sal_uInt16          nLastLineLen    = 0;                // Zeilenlaenge bis zum vorherigen Wort
	sal_uInt16          nLastWordPos    = 0;                // Position des letzten Wortanfangs
	sal_uInt16          i               = 0;
	sal_uInt16          nPos;                               // StartPositon der Zeile (nur Temp)
	sal_uInt16          nLen;                               // Laenge der Zeile (nur Temp)
	sal_uInt16          nStrLen         = rStr.Len();
	long            nMaxLineWidth   = 0;                // Maximale Zeilenlaenge
	long            nLineWidth;                         // Aktuelle Zeilenlaenge
	long            nLastLineWidth  = 0;                // Zeilenlaenge der letzten Zeile
	xub_Unicode          c;
	xub_Unicode          c2;
	const xub_Unicode*   pStr       = rStr.GetBuffer();
	sal_Bool            bHardBreak      = sal_False;

	do
	{
		c = pStr[i];

		// Auf Zeilenende ermitteln
		if ( (c == _CR) || (c == _LF) )
			bHardBreak = sal_True;
		else
			bHardBreak = sal_False;

		// Testen, ob ein Wortende erreicht ist
		if ( bHardBreak || (i == nStrLen) ||
			 (((c == ' ') || (c == '-')) && (nStyle & TEXT_DRAW_WORDBREAK)) )
		{
			nLen = i-nStartPos;
			if ( c == '-' )
				nLen++;
			nLineWidth = pDev->GetTextWidth( rStr, nStartPos, nLen );

			// Findet ein Zeilenumbruch statt
			if ( bHardBreak || (i == nStrLen) ||
				((nLineWidth >= nWidth) && (nStyle & TEXT_DRAW_WORDBREAK)) )
			{
				nPos = nStartPos;

				if ( (nLineWidth >= nWidth) && (nStyle & TEXT_DRAW_WORDBREAK) )
				{
					nLineWidth      = nLastLineWidth;
					nLen            = nLastLineLen;
					nStartPos       = nLastWordPos;
					nLastLineLen    = i-nStartPos;
					nLastWordPos    = nStartPos+nLastLineLen+1;
					if ( c == '-' )
						nLastLineLen++;
					else if ( bHardBreak && (i > nStartPos) )
						i--;
				}
				else
				{
					nStartPos = i;
					// Zeilenende-Zeichen und '-' beruecksichtigen
					if ( bHardBreak )
					{
						nStartPos++;
						c2 = pStr[i+1];
						if ( (c != c2) && ((c2 == _CR) || (c2 == _LF)) )
						{
							nStartPos++;
							i++;
						}
					}
					else if ( c != '-' )
						nStartPos++;
					nLastWordPos    = nStartPos;
					nLastLineLen    = 0;
				}

				if ( nLineWidth > nMaxLineWidth )
					nMaxLineWidth = nLineWidth;

				if ( nLen || bHardBreak  )
					rLineInfo.AddLine( new TextLineInfo( nLineWidth, nPos, nLen ) );

				// Testen, ob aktuelles Wort noch auf die Zeile passt,
				// denn ansonsten mueessen wir es auftrennen
				if ( nLastLineLen )
				{
					nLineWidth = pDev->GetTextWidth( rStr, nStartPos, nLastLineLen );
					if ( nLineWidth > nWidth )
					{
						// Wenn ein Wortumbruch in einem Wort stattfindet,
						// ist die maximale Zeilenlaenge die Laenge
						// des laengsten Wortes
						if ( nLineWidth > nMaxLineWidth )
							nMaxLineWidth = nLineWidth;

						// Solange Wort auftrennen, bis es auf eine Zeile passt
						do
						{
							nPos = pDev->GetTextBreak( rStr, nWidth, nStartPos, nLastLineLen );
							nLen = nPos-nStartPos;
							if ( !nLen )
							{
								nPos++;
								nLen++;
							}
							nLineWidth = pDev->GetTextWidth( rStr, nStartPos, nLen );
							rLineInfo.AddLine( new TextLineInfo( nLineWidth, nStartPos, nLen ) );
							nStartPos       = nPos;
							nLastLineLen = nLastLineLen - nLen;
							nLineWidth = pDev->GetTextWidth( rStr, nStartPos, nLastLineLen );
						}
						while ( nLineWidth > nWidth );
					}
					nLastLineWidth = nLineWidth;

					// Bei Stringende muessen wir die letzte Zeile auch noch
					// dranhaengen
					if ( (i == nStrLen) && nLastLineLen )
						rLineInfo.AddLine( new TextLineInfo( nLastLineWidth, nStartPos, nLastLineLen ) );
				}
				else
					nLastLineWidth = 0;
			}
			else
			{
				nLastLineWidth  = nLineWidth;
				nLastLineLen    = nLen;
				nLastWordPos    = nStartPos+nLastLineLen;
				if ( c != '-' )
					nLastWordPos++;
			}
		}

		i++;
	}
	while ( i <= nStrLen );

	return nMaxLineWidth;
}

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

sal_uInt16 GetTextLines( OutputDevice* pDev, const Rectangle& rRect,
					 const XubString& rStr,
					 sal_uInt16 nStyle = TEXT_DRAW_WORDBREAK,
					 long* pMaxWidth = NULL )
{
	MultiTextLineInfo aMultiLineInfo;
	long nMaxWidth = GetTextLines( pDev, aMultiLineInfo,
								   rRect.GetWidth(), rStr, nStyle );
	if ( pMaxWidth )
		*pMaxWidth = nMaxWidth;
	return aMultiLineInfo.Count();
}

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

Rectangle GetTextRect( OutputDevice* pDev, const Rectangle& rRect,
					   const XubString& rStr,
					   sal_uInt16 nStyle = TEXT_DRAW_WORDBREAK )
{
	Rectangle           aRect = rRect;
	sal_uInt16              nLines;
	long                nWidth = rRect.GetWidth();
	long                nMaxWidth;
	long                nTextHeight;

	if ( nStyle & TEXT_DRAW_MULTILINE )
	{
		MultiTextLineInfo   aMultiLineInfo;
		TextLineInfo*       pLineInfo;
		sal_uInt16              nFormatLines;

		nMaxWidth = 0;
		GetTextLines( pDev, aMultiLineInfo, nWidth, rStr, nStyle );
		nFormatLines = aMultiLineInfo.Count();
		nTextHeight = pDev->GetTextHeight();
		nLines = (sal_uInt16)(aRect.GetHeight()/nTextHeight);
		if ( nFormatLines <= nLines )
			nLines = nFormatLines;
		else
		{
			if ( !(nStyle & TEXT_DRAW_ENDELLIPSIS) )
				nLines = nFormatLines;
			else
				nMaxWidth = nWidth;
		}
		for ( sal_uInt16 i = 0; i < nLines; i++ )
		{
			pLineInfo = aMultiLineInfo.GetLine( i );
			if ( pLineInfo->GetWidth() > nMaxWidth )
				nMaxWidth = pLineInfo->GetWidth();
		}
	}
	else
	{
		nLines          = 1;
		nMaxWidth       = pDev->GetTextWidth( rStr );
		nTextHeight     = pDev->GetTextHeight();
		if ( (nMaxWidth > nWidth) && (nStyle & TEXT_DRAW_ENDELLIPSIS) )
			nMaxWidth = nWidth;
	}

	if ( nStyle & TEXT_DRAW_RIGHT )
		aRect.Left() = aRect.Right()-nMaxWidth+1;
	else if ( nStyle & TEXT_DRAW_CENTER )
	{
		aRect.Left() += (nWidth-nMaxWidth)/2;
		aRect.Right() = aRect.Left()+nMaxWidth-1;
	}
	else
		aRect.Right() = aRect.Left()+nMaxWidth-1;

	if ( nStyle & TEXT_DRAW_BOTTOM )
		aRect.Top() = aRect.Bottom()-(nTextHeight*nLines)+1;
	else if ( nStyle & TEXT_DRAW_VCENTER )
	{
		aRect.Top()   += (aRect.GetHeight()-(nTextHeight*nLines))/2;
		aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1;
	}
	else
		aRect.Bottom() = aRect.Top()+(nTextHeight*nLines)-1;

	return aRect;
}

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

void DrawText( OutputDevice* pDev, const Rectangle& rRect,
			   const XubString& rStr, sal_uInt16 nStyle = 0 )
{
	if ( !rStr.Len() || rRect.IsEmpty() )
		return;

	Point       aPos    = rRect.TopLeft();
	long        nWidth  = rRect.GetWidth();
	long        nHeight = rRect.GetHeight();
	FontAlign   eAlign  = pDev->GetFont().GetAlign();

	if ( ((nWidth <= 0) || (nHeight <= 0)) && (nStyle & TEXT_DRAW_CLIP) )
		return;

	// Mehrzeiligen Text behandeln wir anders
	if ( nStyle & TEXT_DRAW_MULTILINE )
	{
		String              aLastLine;
		Region              aOldRegion;
		MultiTextLineInfo   aMultiLineInfo;
		TextLineInfo*       pLineInfo;
		long                nTextHeight     = pDev->GetTextHeight();
		long                nMaxTextWidth;
		sal_uInt16              i;
		sal_uInt16              nLines          = (sal_uInt16)(nHeight/nTextHeight);
		sal_uInt16              nFormatLines;
		sal_Bool                bIsClipRegion = sal_False;
		nMaxTextWidth = GetTextLines( pDev, aMultiLineInfo, nWidth, rStr, nStyle );

		nFormatLines = aMultiLineInfo.Count();
		if ( nFormatLines > nLines )
		{
			if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
			{
				// Letzte Zeile zusammenbauen und kuerzen
				nFormatLines = nLines-1;
				pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
				aLastLine = rStr.Copy( pLineInfo->GetIndex() );
				aLastLine.ConvertLineEnd( LINEEND_LF );
				aLastLine.SearchAndReplace( _LF, ' ' );
				aLastLine = GetEllipsisString( pDev, aLastLine, nWidth, nStyle );
				nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM);
				nStyle |= TEXT_DRAW_TOP;
			}
		}
		else
		{
			if ( nMaxTextWidth <= nWidth )
				nStyle &= ~TEXT_DRAW_CLIP;
		}

		// Clipping setzen
		if ( nStyle & TEXT_DRAW_CLIP )
		{
			bIsClipRegion = pDev->IsClipRegion();
			if ( bIsClipRegion )
			{
				aOldRegion = pDev->GetClipRegion();
				pDev->IntersectClipRegion( rRect );
			}
			else
			{
				Region aRegion( rRect );
				pDev->SetClipRegion( aRegion );
			}
		}

		// Vertikales Alignment
		if ( nStyle & TEXT_DRAW_BOTTOM )
			aPos.Y() += nHeight-(nFormatLines*nTextHeight);
		else if ( nStyle & TEXT_DRAW_VCENTER )
			aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;

		// Font Alignment
		if ( eAlign == ALIGN_BOTTOM )
			aPos.Y() += nTextHeight;
		else if ( eAlign == ALIGN_BASELINE )
			aPos.Y() += pDev->GetFontMetric().GetAscent();

		// Alle Zeilen ausgeben, bis auf die letzte
		for ( i = 0; i < nFormatLines; i++ )
		{
			pLineInfo = aMultiLineInfo.GetLine( i );
			if ( nStyle & TEXT_DRAW_RIGHT )
				aPos.X() += nWidth-pLineInfo->GetWidth();
			else if ( nStyle & TEXT_DRAW_CENTER )
				aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
			pDev->DrawText( aPos, rStr, pLineInfo->GetIndex(), pLineInfo->GetLen() );
			aPos.Y() += nTextHeight;
			aPos.X() = rRect.Left();
		}

		// Gibt es noch eine letzte Zeile, dann diese linksbuendig ausgeben,
		// da die Zeile gekuerzt wurde
		if ( aLastLine.Len() )
			pDev->DrawText( aPos, aLastLine );

		// Clipping zuruecksetzen
		if ( nStyle & TEXT_DRAW_CLIP )
		{
			if ( bIsClipRegion )
				pDev->SetClipRegion( aOldRegion );
			else
				pDev->SetClipRegion();
		}
	}
	else
	{
		XubString    aStr = rStr;
		Size        aTextSize(pDev->GetTextWidth( aStr ), pDev->GetTextHeight());

		// Evt. Text kuerzen
		if ( aTextSize.Width() > nWidth )
		{
			if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
			{
				aStr = GetEllipsisString( pDev, rStr, nWidth, nStyle );
				nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT);
				nStyle |= TEXT_DRAW_LEFT;
				aTextSize.Width() = pDev->GetTextWidth(aStr);
			}
		}
		else
		{
			if ( aTextSize.Height() <= nHeight )
				nStyle &= ~TEXT_DRAW_CLIP;
		}

		// Vertikales Alignment
		if ( nStyle & TEXT_DRAW_RIGHT )
			aPos.X() += nWidth-aTextSize.Width();
		else if ( nStyle & TEXT_DRAW_CENTER )
			aPos.X() += (nWidth-aTextSize.Width())/2;

		// Font Alignment
		if ( eAlign == ALIGN_BOTTOM )
			aPos.Y() += aTextSize.Height();
		else if ( eAlign == ALIGN_BASELINE )
			aPos.Y() += pDev->GetFontMetric().GetAscent();

		if ( nStyle & TEXT_DRAW_BOTTOM )
			aPos.Y() += nHeight-aTextSize.Height();
		else if ( nStyle & TEXT_DRAW_VCENTER )
			aPos.Y() += (nHeight-aTextSize.Height())/2;

		if ( nStyle & TEXT_DRAW_CLIP )
		{
			sal_Bool bIsClipRegion = pDev->IsClipRegion();
			if ( bIsClipRegion )
			{
				Region aOldRegion = pDev->GetClipRegion();
				pDev->IntersectClipRegion( rRect );
				pDev->DrawText( aPos, aStr );
				pDev->SetClipRegion( aOldRegion );
			}
			else
			{
				Region aRegion( rRect );
				pDev->SetClipRegion( aRegion );
				pDev->DrawText( aPos, aStr );
				pDev->SetClipRegion();
			}
		}
		else
			pDev->DrawText( aPos, aStr );
	}
}

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


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


#define DRAWTEXT_FLAGS (TEXT_DRAW_CENTER|TEXT_DRAW_TOP|TEXT_DRAW_ENDELLIPSIS|\
						TEXT_DRAW_CLIP|TEXT_DRAW_MULTILINE|TEXT_DRAW_WORDBREAK)


class ImpIcnCursor
{
	SvImpIconView*	pView;
	SvPtrarr* 		pColumns;
	SvPtrarr* 		pRows;
	sal_Bool*			pGridMap;
	long			nGridDX, nGridDY;
	long			nGridCols, nGridRows;
	long			nCols;
	long			nRows;
	short 			nDeltaWidth;
	short 			nDeltaHeight;
	SvLBoxEntry* 	pCurEntry;
	void 			SetDeltas();
	void 			ImplCreate();
	void 			Create() {	if( !pColumns )	ImplCreate(); }

	sal_uInt16 			GetSortListPos( SvPtrarr* pList, long nValue, int bVertical);
	SvLBoxEntry* 	SearchCol(sal_uInt16 nCol,sal_uInt16 nTop,sal_uInt16 nBottom,sal_uInt16 nPref,
						sal_Bool bDown, sal_Bool bSimple );
	SvLBoxEntry* 	SearchRow(sal_uInt16 nRow,sal_uInt16 nRight,sal_uInt16 nLeft,sal_uInt16 nPref,
						sal_Bool bRight, sal_Bool bSimple );

	void			ExpandGrid();
	void			CreateGridMap();
	// Rueckgabe sal_False: Eintrag liegt nicht in der GridMap. rGridx,y werden
	// dann an nGridCols, nGridRows geclippt
	sal_Bool	 		GetGrid( const Point& rDocPos, sal_uInt16& rGridX, sal_uInt16& rGridY ) const;
	void			SetGridUsed( sal_uInt16 nDX, sal_uInt16 nDY, sal_Bool bUsed )
					{
						pGridMap[ (nDY * nGridCols) + nDX ] = bUsed;
					}
	sal_Bool			IsGridUsed( sal_uInt16 nDX, sal_uInt16 nDY )
					{
						return pGridMap[ (nDY * nGridCols) + nDX ];
					}
public:
					ImpIcnCursor( SvImpIconView* pOwner );
					~ImpIcnCursor();
	void 			Clear( sal_Bool bGridToo = sal_True );

	// fuer Cursortravelling usw.
	SvLBoxEntry* 	GoLeftRight( SvLBoxEntry*, sal_Bool bRight );
	SvLBoxEntry* 	GoUpDown( SvLBoxEntry*, sal_Bool bDown );

	// Rueckgaebe: sal_False == Das leere Rect steht hinter dem letzten
	// Eintrag; d.h. beim naechsten Einfuegen ergibt sich das naechste
	// leere Rechteck durch Addition. Hinweis: Das Rechteck kann dann
	// ausserhalb des View-Space liegen
	sal_Bool			FindEmptyGridRect( Rectangle& rRect );

	// Erzeugt fuer jede Zeile (Hoehe=nGridDY) eine nach BoundRect.Left()
	// sortierte Liste der Eintraege, die in ihr stehen. Eine Liste kann
	// leer sein. Die Listen gehen in das Eigentum des Rufenden ueber und
	// muessen mit DestroyGridAdjustData geloescht werden
	void			CreateGridAjustData( SvPtrarr& pLists, SvLBoxEntry* pRow=0);
	static void 	DestroyGridAdjustData( SvPtrarr& rLists );
	void			SetGridUsed( const Rectangle&, sal_Bool bUsed = sal_True );
};




SvImpIconView::SvImpIconView( SvIconView* pCurView, SvLBoxTreeList* pTree,
	WinBits i_nWinStyle ) :
	aVerSBar( pCurView, WB_DRAG | WB_VSCROLL ),
	aHorSBar( pCurView, WB_DRAG | WB_HSCROLL )
{
	pView = pCurView;
	pModel = pTree;
	pCurParent = 0;
	pZOrderList = new SvPtrarr;
	SetStyle( i_nWinStyle );
	nHorDist = 0;
	nVerDist = 0;
	nFlags = 0;
	nCurUserEvent = 0;
	nMaxVirtWidth = 200;
	pDDRefEntry = 0;
	pDDDev = 0;
	pDDBufDev = 0;
	pDDTempDev = 0;
	eTextMode = ShowTextShort;
	pImpCursor = new ImpIcnCursor( this );

	aVerSBar.SetScrollHdl( LINK( this, SvImpIconView, ScrollUpDownHdl ) );
	aHorSBar.SetScrollHdl( LINK( this, SvImpIconView, ScrollLeftRightHdl ) );
	nHorSBarHeight = aHorSBar.GetSizePixel().Height();
	nVerSBarWidth = aVerSBar.GetSizePixel().Width();

	aMouseMoveTimer.SetTimeout( 20 );
	aMouseMoveTimer.SetTimeoutHdl(LINK(this,SvImpIconView,MouseMoveTimeoutHdl));

	aEditTimer.SetTimeout( 800 );
	aEditTimer.SetTimeoutHdl(LINK(this,SvImpIconView,EditTimeoutHdl));

	Clear( sal_True );
}

SvImpIconView::~SvImpIconView()
{
	StopEditTimer();
	CancelUserEvent();
	delete pZOrderList;
	delete pImpCursor;
	delete pDDDev;
	delete pDDBufDev;
	delete pDDTempDev;
	ClearSelectedRectList();
}

void SvImpIconView::Clear( sal_Bool bInCtor )
{
	StopEditTimer();
	CancelUserEvent();
	nMaxBmpWidth = 0;
	nMaxBmpHeight = 0;
	nMaxTextWidth = 0;
	bMustRecalcBoundingRects = sal_False;
	nMaxBoundHeight = 0;

	//XXX
	nFlags |= F_GRID_INSERT;
	nFlags &= ~F_PAINTED;
	SetNextEntryPos( Point( LROFFS_WINBORDER, TBOFFS_WINBORDER ) );
	pCursor = 0;
	if( !bInCtor )
	{
		pImpCursor->Clear();
		aVirtOutputSize.Width() = 0;
		aVirtOutputSize.Height() = 0;
		pZOrderList->Remove(0,pZOrderList->Count());
		MapMode aMapMode( pView->GetMapMode());
		aMapMode.SetOrigin( Point() );
		pView->SetMapMode( aMapMode );
		if( pView->IsUpdateMode() )
			pView->Invalidate();
	}
	AdjustScrollBars();
}

void SvImpIconView::SetStyle( const WinBits i_nWinStyle )
{
	nViewMode = VIEWMODE_TEXT;
	if( i_nWinStyle & WB_NAME )
		nViewMode = VIEWMODE_NAME;
	if( i_nWinStyle & WB_ICON )
		nViewMode = VIEWMODE_ICON;
}


IMPL_LINK( SvImpIconView, ScrollUpDownHdl, ScrollBar *, pScrollBar )
{
	pView->EndEditing( sal_True );
	// Pfeil hoch: delta=-1; Pfeil runter: delta=+1
	Scroll( 0, pScrollBar->GetDelta(), sal_True );
	return 0;
}

IMPL_LINK( SvImpIconView, ScrollLeftRightHdl, ScrollBar *, pScrollBar )
{
	pView->EndEditing( sal_True );
	// Pfeil links: delta=-1; Pfeil rechts: delta=+1
	Scroll( pScrollBar->GetDelta(), 0, sal_True );
	return 0;
}

void SvImpIconView::ChangedFont()
{
	StopEditTimer();
	ImpArrange();
}


void SvImpIconView::CheckAllSizes()
{
	nMaxTextWidth = 0;
	nMaxBmpWidth = 0;
	nMaxBmpHeight = 0;
	SvLBoxEntry* pEntry = pModel->First();
	while( pEntry )
	{
		CheckSizes( pEntry );
		pEntry = pModel->Next( pEntry );
	}
}

void SvImpIconView::CheckSizes( SvLBoxEntry* pEntry,
	const SvIcnVwDataEntry* pViewData )
{
	Size aSize;

	if( !pViewData )
		pViewData = ICNVIEWDATA(pEntry);

	SvLBoxString* pStringItem = (SvLBoxString*)(pEntry->GetFirstItem(SV_ITEM_ID_LBOXSTRING));
	if( pStringItem )
	{
		aSize = GetItemSize( pView, pEntry, pStringItem, pViewData );
		if( aSize.Width() > nMaxTextWidth )
		{
			nMaxTextWidth = aSize.Width();
			if( !(nFlags & F_GRIDMODE ) )
				bMustRecalcBoundingRects = sal_True;
		}
	}
	SvLBoxContextBmp* pBmpItem = (SvLBoxContextBmp*)(pEntry->GetFirstItem(SV_ITEM_ID_LBOXCONTEXTBMP));
	if( pBmpItem )
	{
		aSize = GetItemSize( pView, pEntry, pBmpItem, pViewData );
		if( aSize.Width() > nMaxBmpWidth )
		{
			nMaxBmpWidth = aSize.Width();
			nMaxBmpWidth += (2*LROFFS_ICON);
			if( !(nFlags & F_GRIDMODE ) )
				bMustRecalcBoundingRects = sal_True;
		}
		if( aSize.Height() > nMaxBmpHeight )
		{
			nMaxBmpHeight = aSize.Height();
			nMaxBmpHeight += (2*TBOFFS_ICON);;
			if( !(nFlags & F_GRIDMODE ) )
				bMustRecalcBoundingRects = sal_True;
		}
	}
}

void SvImpIconView::EntryInserted( SvLBoxEntry* pEntry )
{
	if( pModel->GetParent(pEntry) == pCurParent )
	{
		StopEditTimer();
		DBG_ASSERT(pZOrderList->GetPos(pEntry)==0xffff,"EntryInserted:ZOrder?");
		pZOrderList->Insert( pEntry, pZOrderList->Count() );
		if( nFlags & F_GRIDMODE	)
			pImpCursor->Clear( sal_False );
		else
			pImpCursor->Clear( sal_True );
		SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pEntry);
		CheckSizes( pEntry, pViewData );
		if( pView->IsUpdateMode() )
		{
			FindBoundingRect( pEntry, pViewData );
			PaintEntry( pEntry, pViewData );
		}
		else
			InvalidateBoundingRect( pViewData->aRect );
	}
}

void SvImpIconView::RemovingEntry( SvLBoxEntry* pEntry )
{
	if( pModel->GetParent(pEntry) == pCurParent)
	{
		StopEditTimer();
		DBG_ASSERT(pZOrderList->GetPos(pEntry)!=0xffff,"RemovingEntry:ZOrder?");
		SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pEntry);
		if( IsBoundingRectValid( pViewData->aRect ) )
		{
			// bei gueltigem Bounding-Rect muss in EntryRemoved eine
			// Sonderbehandlung erfolgen
			nFlags |= F_ENTRY_REMOVED;
			pView->Invalidate( pViewData->aRect );
		}
		if( pEntry == pCursor )
		{
			SvLBoxEntry* pNewCursor = GetNewCursor();
			ShowCursor( sal_False );
			pCursor = 0; // damit er nicht deselektiert wird
			SetCursor( pNewCursor );
		}
		sal_uInt16 nPos = pZOrderList->GetPos( (void*)pEntry );
		pZOrderList->Remove( nPos, 1 );
		pImpCursor->Clear();
	}
}

void SvImpIconView::EntryRemoved()
{
	if( (nFlags & (F_ENTRY_REMOVED | F_PAINTED)) == (F_ENTRY_REMOVED | F_PAINTED))
	{
		// Ein Eintrag mit gueltigem BoundRect wurde geloescht und wir
		// haben schon mal gepaintet. In diesem Fall muessen wir die
		// Position des naechsten Eintrags, der eingefuegt wird oder noch
		// kein gueltiges BoundRect hat, "suchen" d.h. ein "Loch" in
		// der View auffuellen.
		nFlags &= ~( F_ENTRY_REMOVED | F_GRID_INSERT );
	}
}


void SvImpIconView::MovingEntry( SvLBoxEntry* pEntry )
{
	DBG_ASSERT(pEntry,"MovingEntry: 0!");
	pNextCursor = 0;
	StopEditTimer();
	if( pModel->GetParent(pEntry) == pCurParent )
	{
		DBG_ASSERT(pZOrderList->GetPos(pEntry)!=0xffff,"MovingEntry:ZOrder?");
		nFlags |= F_MOVING_SIBLING;
		SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pEntry);
		if( IsBoundingRectValid( pViewData->aRect ) )
			pView->Invalidate( pViewData->aRect );
		// falls Eintrag seinen Parent wechselt vorsichtshalber
		// die neue Cursorposition berechnen
		if( pEntry == pCursor )
			pNextCursor = GetNewCursor();
		pImpCursor->Clear();
	}
}


void SvImpIconView::EntryMoved( SvLBoxEntry* pEntry )
{
	ShowCursor( sal_False );
	SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pEntry);
	if( pModel->GetParent(pEntry)==pCurParent )
	{
		if( nFlags & F_MOVING_SIBLING )
		{
			// die Neu-Positionierung eines Eintrags bei D&D innerhalb
			// einer IconView findet bereits in NotifyMoving statt
			// (MovingEntry/EntryMoved wird dann nicht mehr gerufen)
			ToTop( pEntry );
		}
		else
		{
			pImpCursor->Clear();
			pZOrderList->Insert( pEntry, pZOrderList->Count() );
			DBG_ASSERT(pZOrderList->Count()==pModel->GetChildCount(pCurParent),"EntryMoved:Bad zorder count");
			FindBoundingRect( pEntry, pViewData );
		}
		PaintEntry( pEntry, pViewData );
	}
	else
	{
		if( pEntry == pCursor )
		{
			DBG_ASSERT(pNextCursor,"EntryMoved: Next cursor bad");
			SetCursor( pNextCursor );
		}
		pImpCursor->Clear();
		sal_uInt16 nPos = pZOrderList->GetPos( (void*)pEntry );
		pZOrderList->Remove( nPos, 1 );
		pView->Select( pEntry, sal_False );
		// wenn er nochmal in dieser View auftaucht, muss sein
		// Bounding-Rect neu berechnet werden
		InvalidateBoundingRect( pViewData->aRect );
	}
	nFlags &= (~F_MOVING_SIBLING);
}

void SvImpIconView::TreeInserted( SvLBoxEntry* pEntry )
{
	EntryMoved( pEntry ); // vorlaeufig
}

void SvImpIconView::EntryExpanded( SvLBoxEntry* )
{
}

void SvImpIconView::EntryCollapsed( SvLBoxEntry*)
{
}

void SvImpIconView::CollapsingEntry( SvLBoxEntry* )
{
}

void SvImpIconView::EntrySelected( SvLBoxEntry* pEntry, sal_Bool bSelect )
{
	if( pModel->GetParent(pEntry) != pCurParent  )
		return;

	// bei SingleSelection dafuer sorgen, dass der Cursor immer
	// auf dem (einzigen) selektierten Eintrag steht
	if( bSelect && pCursor &&
		pView->GetSelectionMode() == SINGLE_SELECTION &&
		pEntry != pCursor )
	{
		SetCursor( pEntry );
		DBG_ASSERT(pView->GetSelectionCount()==1,"selection count?");
	}
	// bei Gummibandselektion ist uns das zu teuer
	if( !(nFlags & F_RUBBERING ))
		ToTop( pEntry );
	if( pView->IsUpdateMode() )
	{
		if( pEntry == pCursor )
			ShowCursor( sal_False );
		if( nFlags & F_RUBBERING )
			PaintEntry( pEntry );
		else
			pView->Invalidate( GetBoundingRect( pEntry ) );
		if( pEntry == pCursor )
			ShowCursor( sal_True );
	}
}

void SvImpIconView::SetNextEntryPos(const Point& rPos)
{
	aPrevBoundRect.SetPos( rPos );
	aPrevBoundRect.Right() = LONG_MAX;	// dont know
}

Point SvImpIconView::FindNextEntryPos( const Size& rBoundSize )
{
	if( nFlags & F_GRIDMODE )
	{
		if( nFlags & F_GRID_INSERT )
		{
			if( aPrevBoundRect.Right() != LONG_MAX )
			{
				// passt der naechste Entry noch in die Zeile ?
				long nNextWidth = aPrevBoundRect.Right() + nGridDX + LROFFS_WINBORDER;
				if( nNextWidth > aVirtOutputSize.Width() )
				{
					// darf aVirtOutputSize verbreitert werden ?
					if( nNextWidth < nMaxVirtWidth )
					{
						// verbreitern & in Zeile aufnehmen
						aPrevBoundRect.Left() += nGridDX;
					}
					else
					{
						// erhoehen & neue Zeile beginnen
						aPrevBoundRect.Top() += nGridDY;
						aPrevBoundRect.Left() = LROFFS_WINBORDER;
					}
				}
				else
				{
					// in die Zeile aufnehmen
					aPrevBoundRect.Left() += nGridDX;
				}
			}
			aPrevBoundRect.SetSize( Size( nGridDX, nGridDY ) );
		}
		else
		{
			if( !pImpCursor->FindEmptyGridRect( aPrevBoundRect ) )
			{
				// mitten in den Entries gibts keine Loecher mehr,
				// wir koennen also wieder ins "Fast Insert" springen
				nFlags |= F_GRID_INSERT;
			}
		}
	}
	else
	{
		if( aPrevBoundRect.Right() != LONG_MAX )
		{
			// passt der naechste Entry noch in die Zeile ?
			long nNextWidth=aPrevBoundRect.Right()+rBoundSize.Width()+LROFFS_BOUND+nHorDist;
			if( nNextWidth > aVirtOutputSize.Width() )
			{
				// darf aVirtOutputSize verbreitert werden ?
				if( nNextWidth < nMaxVirtWidth )
				{
					// verbreitern & in Zeile aufnehmen
					aPrevBoundRect.SetPos( aPrevBoundRect.TopRight() );
					aPrevBoundRect.Left() += nHorDist;
				}
				else
				{
					// erhoehen & neue Zeile beginnen
					aPrevBoundRect.Top() += nMaxBoundHeight + nVerDist + TBOFFS_BOUND;
					aPrevBoundRect.Left() = LROFFS_WINBORDER;
				}
			}
			else
			{
				// in die Zeile aufnehmen
				aPrevBoundRect.SetPos( aPrevBoundRect.TopRight() );
				aPrevBoundRect.Left() += nHorDist;
			}
		}
		aPrevBoundRect.SetSize( rBoundSize );
	}
	return aPrevBoundRect.TopLeft();
}

void SvImpIconView::ResetVirtSize()
{
	StopEditTimer();
	aVirtOutputSize.Width() = 0;
	aVirtOutputSize.Height() = 0;
	sal_Bool bLockedEntryFound = sal_False;
	nFlags &= (~F_GRID_INSERT);
	SvLBoxEntry* pCur = pModel->FirstChild( pCurParent );
	while( pCur )
	{
		SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pCur);
		if( pViewData->IsEntryPosLocked() )
		{
			// VirtSize u.a. anpassen
			if( !IsBoundingRectValid( pViewData->aRect ) )
				FindBoundingRect( pCur, pViewData );
			else
				AdjustVirtSize( pViewData->aRect );
			bLockedEntryFound = sal_True;
		}
		else
			InvalidateBoundingRect( pViewData->aRect );

		pCur = pModel->NextSibling( pCur );
	}
	if( !bLockedEntryFound )
	{
		//XXX
		nFlags |= F_GRID_INSERT;
	}

	SetNextEntryPos( Point( LROFFS_WINBORDER, TBOFFS_WINBORDER ) );
	pImpCursor->Clear();
}


void SvImpIconView::AdjustVirtSize( const Rectangle& rRect )
{
	long nHeightOffs = 0;
	long nWidthOffs = 0;

	if( aVirtOutputSize.Width() < (rRect.Right()+LROFFS_WINBORDER) )
		nWidthOffs = (rRect.Right()+LROFFS_WINBORDER) - aVirtOutputSize.Width();

	if( aVirtOutputSize.Height() < (rRect.Bottom()+TBOFFS_WINBORDER) )
		nHeightOffs = (rRect.Bottom()+TBOFFS_WINBORDER) - aVirtOutputSize.Height();

	if( nWidthOffs || nHeightOffs )
	{
		Range aRange;
		aVirtOutputSize.Width() += nWidthOffs;
		aRange.Max() = aVirtOutputSize.Width();
		aHorSBar.SetRange( aRange );

		aVirtOutputSize.Height() += nHeightOffs;
		aRange.Max() = aVirtOutputSize.Height();
		aVerSBar.SetRange( aRange );

		pImpCursor->Clear();
		AdjustScrollBars();
	}
}

void SvImpIconView::Arrange()
{
	nMaxVirtWidth = aOutputSize.Width();
	ImpArrange();
}

void SvImpIconView::ImpArrange()
{
	StopEditTimer();
	ShowCursor( sal_False );
	ResetVirtSize();
	bMustRecalcBoundingRects = sal_False;
	MapMode aMapMode( pView->GetMapMode());
	aMapMode.SetOrigin( Point() );
	pView->SetMapMode( aMapMode );
	CheckAllSizes();
	RecalcAllBoundingRectsSmart();
	pView->Invalidate();
	ShowCursor( sal_True );
}

void SvImpIconView::Paint( const Rectangle& rRect )
{
	if( !pView->IsUpdateMode() )
		return;

#if defined(DBG_UTIL) && defined(OV_DRAWGRID)
	if( nFlags & F_GRIDMODE )
	{
		Color aOldColor = pView->GetLineColor();
		Color aNewColor( COL_BLACK );
		pView->SetLineColor( aNewColor );
		Point aOffs( pView->GetMapMode().GetOrigin());
		Size aXSize( pView->GetOutputSizePixel() );
		for( long nDX = nGridDX; nDX <= aXSize.Width(); nDX += nGridDX )
		{
			Point aStart( nDX+LROFFS_BOUND, 0 );
			Point aEnd( nDX+LROFFS_BOUND, aXSize.Height());
			aStart -= aOffs;
			aEnd -= aOffs;
			pView->DrawLine( aStart, aEnd );
		}
		for( long nDY = nGridDY; nDY <= aXSize.Height(); nDY += nGridDY )
		{
			Point aStart( 0, nDY+TBOFFS_BOUND );
			Point aEnd( aXSize.Width(), nDY+TBOFFS_BOUND );
			aStart -= aOffs;
			aEnd -= aOffs;
			pView->DrawLine( aStart, aEnd );
		}
		pView->SetLineColor( aOldColor );
	}
#endif
	nFlags |= F_PAINTED;

	if( !(pModel->HasChilds( pCurParent ) ))
		return;
	if( !pCursor )
		pCursor = pModel->FirstChild( pCurParent );

	sal_uInt16 nCount = pZOrderList->Count();
	if( !nCount )
		return;

	SvPtrarr* pNewZOrderList = new SvPtrarr;
	SvPtrarr* pPaintedEntries = new SvPtrarr;

	sal_uInt16 nPos = 0;
	while( nCount )
	{
		SvLBoxEntry* pEntry = (SvLBoxEntry*)(pZOrderList->GetObject(nPos ));
		SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pEntry);
		const Rectangle& rBoundRect = GetBoundingRect( pEntry, pViewData );
		if( rRect.IsOver( rBoundRect ) )
		{
			PaintEntry( pEntry, rBoundRect.TopLeft(), pViewData );
			// Eintraege, die neu gezeichnet werden, auf Top setzen
			pPaintedEntries->Insert( pEntry, pPaintedEntries->Count() );
		}
		else
			pNewZOrderList->Insert( pEntry, pNewZOrderList->Count() );

		nCount--;
		nPos++;
	}
	delete pZOrderList;
	pZOrderList = pNewZOrderList;
	nCount = pPaintedEntries->Count();
	if( nCount )
	{
		for( sal_uInt16 nCur = 0; nCur < nCount; nCur++ )
			pZOrderList->Insert( pPaintedEntries->GetObject( nCur ),pZOrderList->Count());
	}
	delete pPaintedEntries;

	Rectangle aRect;
	if( GetResizeRect( aRect ))
		PaintResizeRect( aRect );
}

sal_Bool SvImpIconView::GetResizeRect( Rectangle& rRect )
{
	if( aHorSBar.IsVisible() && aVerSBar.IsVisible() )
	{
		const MapMode& rMapMode = pView->GetMapMode();
		Point aOrigin( rMapMode.GetOrigin());
		aOrigin *= -1;
		aOrigin.X() += aOutputSize.Width();
		aOrigin.Y() += aOutputSize.Height();
		rRect.SetPos( aOrigin );
		rRect.SetSize( Size( nVerSBarWidth, nHorSBarHeight));
		return sal_True;
	}
	return sal_False;
}

void SvImpIconView::PaintResizeRect( const Rectangle& rRect )
{
	const StyleSettings& rStyleSettings = pView->GetSettings().GetStyleSettings();
	Color aNewColor = rStyleSettings.GetFaceColor();
	Color aOldColor = pView->GetFillColor();
	pView->SetFillColor( aNewColor );
	pView->DrawRect( rRect );
	pView->SetFillColor( aOldColor );
}

void SvImpIconView::RepaintSelectionItems()
{
	DBG_ERROR("RepaintSelectionItems");
	pView->Invalidate(); // vorlaeufig
}

SvLBoxItem* SvImpIconView::GetItem( SvLBoxEntry* pEntry,
					const Point& rAbsPos )
{
	Rectangle aRect;
	SvLBoxString* pStringItem = (SvLBoxString*)(pEntry->GetFirstItem(SV_ITEM_ID_LBOXSTRING));
	if( pStringItem )
	{
		aRect = CalcTextRect( pEntry, pStringItem );
		if( aRect.IsInside( rAbsPos ) )
			return pStringItem;
	}
	SvLBoxContextBmp* pBmpItem = (SvLBoxContextBmp*)(pEntry->GetFirstItem(SV_ITEM_ID_LBOXCONTEXTBMP));
	if( pBmpItem )
	{
		aRect = CalcBmpRect( pEntry );
		if( aRect.IsInside( rAbsPos ) )
			return pBmpItem;
	}
	return 0;
}

void SvImpIconView::CalcDocPos( Point& aMaeuschenPos )
{
	aMaeuschenPos -= pView->GetMapMode().GetOrigin();
}

void SvImpIconView::MouseButtonDown( const MouseEvent& rMEvt)
{
	StopEditTimer();
	pView->GrabFocus();
	Point aDocPos( rMEvt.GetPosPixel() );
	if(aDocPos.X()>=aOutputSize.Width() || aDocPos.Y()>=aOutputSize.Height())
		return;
	CalcDocPos( aDocPos );
	SvLBoxEntry* pEntry = GetEntry( aDocPos );
	if( !pEntry )
	{
		if( pView->GetSelectionMode() != SINGLE_SELECTION )
		{
			if( !rMEvt.IsMod1() )  // Ctrl
			{
				pView->SelectAll( sal_False );
				ClearSelectedRectList();
			}
			else
				nFlags |= F_ADD_MODE;
			nFlags |= F_RUBBERING;
			aCurSelectionRect.SetPos( aDocPos );
			pView->CaptureMouse();
		}
		return;
	}

	sal_Bool bSelected = pView->IsSelected( pEntry );
	sal_Bool bEditingEnabled = pView->IsInplaceEditingEnabled();

	if( rMEvt.GetClicks() == 2 )
	{
		DeselectAllBut( pEntry );
		pView->pHdlEntry = pEntry;
		pView->DoubleClickHdl();
	}
	else
	{
		// Inplace-Editing ?
		if( rMEvt.IsMod2() )  // Alt?
		{
			if( bEditingEnabled )
			{
				SvLBoxItem* pItem = GetItem(pEntry,aDocPos);
				if( pItem )
					pView->EditingRequest( pEntry, pItem, aDocPos);
			}
		}
		else if( pView->GetSelectionMode() == SINGLE_SELECTION )
		{
			DeselectAllBut( pEntry );
			SetCursor( pEntry );
			pView->Select( pEntry, sal_True );
			if( bEditingEnabled && bSelected && !rMEvt.GetModifier() &&
				rMEvt.IsLeft() && IsTextHit( pEntry, aDocPos ) )
			{
				nFlags |= F_START_EDITTIMER_IN_MOUSEUP;
			}
		}
		else
		{
			if( !rMEvt.GetModifier() )
			{
				if( !bSelected )
				{
					DeselectAllBut( pEntry );
					SetCursor( pEntry );
					pView->Select( pEntry, sal_True );
				}
				else
				{
					// erst im Up deselektieren, falls Move per D&D!
					nFlags |= F_DOWN_DESELECT;
					if( bEditingEnabled && IsTextHit( pEntry, aDocPos ) &&
						rMEvt.IsLeft())
					{
						nFlags |= F_START_EDITTIMER_IN_MOUSEUP;
					}
				}
			}
			else if( rMEvt.IsMod1() )
				nFlags |= F_DOWN_CTRL;
		}
	}
}

void SvImpIconView::MouseButtonUp( const MouseEvent& rMEvt )
{
	aMouseMoveTimer.Stop();
	pView->ReleaseMouse();
	// HACK, da Einar noch nicht PrepareCommandEvent aufruft
	if( rMEvt.IsRight() && (nFlags & (F_DOWN_CTRL | F_DOWN_DESELECT) ))
		nFlags &= ~(F_DOWN_CTRL | F_DOWN_DESELECT);

	if( nFlags & F_RUBBERING )
	{
		aMouseMoveTimer.Stop();
		AddSelectedRect( aCurSelectionRect );
		HideSelectionRect();
		nFlags &= ~(F_RUBBERING | F_ADD_MODE);
	}

	SvLBoxEntry* pEntry = pView->GetEntry( rMEvt.GetPosPixel(), sal_True );
	if( pEntry )
	{
		if( nFlags & F_DOWN_CTRL )
		{
			// Ctrl & MultiSelection
			ToggleSelection( pEntry );
			SetCursor( pEntry );
		}
		else if( nFlags & F_DOWN_DESELECT )
		{
			DeselectAllBut( pEntry );
			SetCursor( pEntry );
			pView->Select( pEntry, sal_True );
		}
	}

	nFlags &= ~(F_DOWN_CTRL | F_DOWN_DESELECT);
	if( nFlags & F_START_EDITTIMER_IN_MOUSEUP )
	{
		StartEditTimer();
		nFlags &= ~F_START_EDITTIMER_IN_MOUSEUP;
	}
}

void SvImpIconView::MouseMove( const MouseEvent& rMEvt )
{
	if( nFlags & F_RUBBERING )
	{
		const Point& rPosPixel = rMEvt.GetPosPixel();
		if( !aMouseMoveTimer.IsActive() )
		{
			aMouseMoveEvent = rMEvt;
			aMouseMoveTimer.Start();
			// ausserhalb des Fensters liegende Move-Events muessen
			// vom Timer kommen, damit die Scrollgeschwindigkeit
			// unabhaengig von Mausbewegungen ist.
			if( rPosPixel.X() < 0 || rPosPixel.Y() < 0 )
				return;
			const Size& rSize = pView->GetOutputSizePixel();
			if( rPosPixel.X() > rSize.Width() || rPosPixel.Y() > rSize.Height())
				return;
		}

		if( &rMEvt != &aMouseMoveEvent )
			aMouseMoveEvent = rMEvt;

		long nScrollDX, nScrollDY;

		CalcScrollOffsets(rMEvt.GetPosPixel(),nScrollDX,nScrollDY,sal_False );
		sal_Bool bSelRectHidden = sal_False;
		if( nScrollDX || nScrollDY )
		{
			HideSelectionRect();
			bSelRectHidden = sal_True;
			pView->Scroll( nScrollDX, nScrollDY );
		}
		Point aDocPos( rMEvt.GetPosPixel() );
		aDocPos = pView->PixelToLogic( aDocPos );
		Rectangle aRect( aCurSelectionRect.TopLeft(), aDocPos );
		if( aRect != aCurSelectionRect )
		{
			HideSelectionRect();
			bSelRectHidden = sal_True;
			sal_Bool bAdd = (nFlags & F_ADD_MODE) ? sal_True : sal_False;
			SelectRect( aRect, bAdd, &aSelectedRectList );
		}
		if( bSelRectHidden )
			DrawSelectionRect( aRect );
	}
}

sal_Bool SvImpIconView::KeyInput( const KeyEvent& rKEvt )
{
	StopEditTimer();
	sal_Bool bKeyUsed = sal_True;
	sal_Bool bMod1 = rKEvt.GetKeyCode().IsMod1();
	sal_Bool bInAddMode = (sal_Bool)((nFlags & F_ADD_MODE) != 0);
	int bDeselAll = (pView->GetSelectionMode() != SINGLE_SELECTION) &&
					!bInAddMode;
	SvLBoxEntry* pNewCursor;
	sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
	switch( nCode )
	{
		case KEY_UP:
			if( pCursor )
			{
				MakeVisible( pCursor );
				pNewCursor = pImpCursor->GoUpDown(pCursor,sal_False);
				if( pNewCursor )
				{
					if( bDeselAll )
						pView->SelectAll( sal_False );
					ShowCursor( sal_False );
					MakeVisible( pNewCursor );
					SetCursor( pNewCursor );
					if( !bInAddMode )
						pView->Select( pCursor, sal_True );
				}
				else
				{
					Rectangle aRect( GetBoundingRect( pCursor ) );
					if( aRect.Top())
					{
						aRect.Bottom() -= aRect.Top();
						aRect.Top() = 0;
						MakeVisible( aRect );
					}
				}
			}
			break;

		case KEY_DOWN:
			if( pCursor )
			{
				pNewCursor=pImpCursor->GoUpDown( pCursor,sal_True );
				if( pNewCursor )
				{
					MakeVisible( pCursor );
					if( bDeselAll )
						pView->SelectAll( sal_False );
					ShowCursor( sal_False );
					MakeVisible( pNewCursor );
					SetCursor( pNewCursor );
					if( !bInAddMode )
						pView->Select( pCursor, sal_True );
				}
			}
			break;

		case KEY_RIGHT:
			if( pCursor )
			{
				pNewCursor=pImpCursor->GoLeftRight(pCursor,sal_True );
				if( pNewCursor )
				{
					MakeVisible( pCursor );
					if( bDeselAll )
						pView->SelectAll( sal_False );
					ShowCursor( sal_False );
					MakeVisible( pNewCursor );
					SetCursor( pNewCursor );
					if( !bInAddMode )
						pView->Select( pCursor, sal_True );
				}
			}
			break;

		case KEY_LEFT:
			if( pCursor )
			{
				MakeVisible( pCursor );
				pNewCursor = pImpCursor->GoLeftRight(pCursor,sal_False );
				if( pNewCursor )
				{
					if( bDeselAll )
						pView->SelectAll( sal_False );
					ShowCursor( sal_False );
					MakeVisible( pNewCursor );
					SetCursor( pNewCursor );
					if( !bInAddMode )
						pView->Select( pCursor, sal_True );
				}
				else
				{
					Rectangle aRect( GetBoundingRect(pCursor));
					if( aRect.Left() )
					{
						aRect.Right() -= aRect.Left();
						aRect.Left() = 0;
						MakeVisible( aRect );
					}
				}
			}
			break;

		case KEY_ESCAPE:
			if( nFlags & F_RUBBERING )
			{
				HideSelectionRect();
				pView->SelectAll( sal_False );
				nFlags &= ~F_RUBBERING;
			}
			break;

		case KEY_F8:
			if( rKEvt.GetKeyCode().IsShift() )
			{
				if( nFlags & F_ADD_MODE )
					nFlags &= (~F_ADD_MODE);
				else
					nFlags |= F_ADD_MODE;
			}
			break;

#ifdef OS2
		case KEY_F9:
			if( rKEvt.GetKeyCode().IsShift() )
			{
				if( pCursor && pView->IsInplaceEditingEnabled() )
					pView->EditEntry( pCursor );
			}
			break;
#endif

		case KEY_SPACE:
			if( pCursor )
			{
				ToggleSelection( pCursor );
			}
			break;


		case KEY_PAGEDOWN:
			break;
		case KEY_PAGEUP:
			break;

		case KEY_ADD:
		case KEY_DIVIDE :
			if( bMod1 )
				pView->SelectAll( sal_True );
			break;

		case KEY_SUBTRACT:
		case KEY_COMMA :
			if( bMod1 )
				pView->SelectAll( sal_False );
			break;

		case KEY_RETURN:
			if( bMod1 )
			{
				if( pCursor && pView->IsInplaceEditingEnabled() )
					pView->EditEntry( pCursor );
			}
			break;

		default:
			bKeyUsed = sal_False;

	}
	return bKeyUsed;
}


void SvImpIconView::PositionScrollBars( long nRealWidth, long nRealHeight )
{
	// hor scrollbar
	Point aPos( 0, nRealHeight );
	aPos.Y() -= nHorSBarHeight;

#ifdef OS2
	aPos.Y()++;
#endif
	if( aHorSBar.GetPosPixel() != aPos )
		aHorSBar.SetPosPixel( aPos );

	// ver scrollbar
	aPos.X() = nRealWidth; aPos.Y() = 0;
	aPos.X() -= nVerSBarWidth;

#if defined(WNT)
	aPos.X()++;
	aPos.Y()--;
#endif

#ifdef OS2
	aPos.Y()--;
	aPos.X()++;
#endif

	if( aVerSBar.GetPosPixel() != aPos )
		aVerSBar.SetPosPixel( aPos );
}



void SvImpIconView::AdjustScrollBars()
{
	long nVirtHeight = aVirtOutputSize.Height();
	long nVirtWidth = aVirtOutputSize.Width();

	Size aOSize( pView->Control::GetOutputSizePixel() );
	long nRealHeight = aOSize.Height();
	long nRealWidth = aOSize.Width();

	PositionScrollBars( nRealWidth, nRealHeight );

	const MapMode& rMapMode = pView->GetMapMode();
	Point aOrigin( rMapMode.GetOrigin() );

	long nVisibleWidth;
	if( nRealWidth > nVirtWidth )
		nVisibleWidth = nVirtWidth + aOrigin.X();
	else
		nVisibleWidth = nRealWidth;

	long nVisibleHeight;
	if( nRealHeight > nVirtHeight )
		nVisibleHeight = nVirtHeight + aOrigin.Y();
	else
		nVisibleHeight = nRealHeight;

	bool bVerSBar = (pView->GetStyle() & WB_VSCROLL) ? true : false;
	bool bHorSBar = (pView->GetStyle() & WB_HSCROLL) ? true : false;

	sal_uInt16 nResult = 0;
	if( nVirtHeight )
	{
		// activate ver scrollbar ?
		if( bVerSBar || ( nVirtHeight > nVisibleHeight) )
		{
			nResult = 0x0001;
			nRealWidth -= nVerSBarWidth;

			if( nRealWidth > nVirtWidth )
				nVisibleWidth = nVirtWidth + aOrigin.X();
			else
				nVisibleWidth = nRealWidth;

			nFlags |= F_HOR_SBARSIZE_WITH_VBAR;
		}
		// activate hor scrollbar ?
		if( bHorSBar || (nVirtWidth > nVisibleWidth) )
		{
			nResult |= 0x0002;
			nRealHeight -= nHorSBarHeight;

			if( nRealHeight > nVirtHeight )
				nVisibleHeight = nVirtHeight + aOrigin.Y();
			else
				nVisibleHeight = nRealHeight;

			// brauchen wir jetzt doch eine senkrechte Scrollbar ?
			if( !(nResult & 0x0001) &&  // nur wenn nicht schon da
				( (nVirtHeight > nVisibleHeight) || bVerSBar) )
			{
				nResult = 3; // both are active
				nRealWidth -= nVerSBarWidth;

				if( nRealWidth > nVirtWidth )
					nVisibleWidth = nVirtWidth + aOrigin.X();
				else
					nVisibleWidth = nRealWidth;

				nFlags |= F_VER_SBARSIZE_WITH_HBAR;
			}
		}
	}

	// size ver scrollbar
	long nThumb = aVerSBar.GetThumbPos();
	Size aSize( nVerSBarWidth, nRealHeight );
#if defined(WNT)
	aSize.Height() += 2;
#endif
#ifdef OS2
	aSize.Height() += 3;
#endif
	if( aSize != aVerSBar.GetSizePixel() )
		aVerSBar.SetSizePixel( aSize );
	aVerSBar.SetVisibleSize( nVisibleHeight );
	aVerSBar.SetPageSize( (nVisibleHeight*75)/100 );
	if( nResult & 0x0001 )
	{
		aVerSBar.SetThumbPos( nThumb );
		aVerSBar.Show();
	}
	else
	{
		aVerSBar.SetThumbPos( 0 );
		aVerSBar.Hide();
	}

	// size hor scrollbar
	nThumb = aHorSBar.GetThumbPos();
	aSize.Width() = nRealWidth;
	aSize.Height() = nHorSBarHeight;
#if defined(WNT)
	aSize.Width()++;
#endif
#ifdef OS2
	aSize.Width() += 3;
	if( nResult & 0x0001 ) // vertikale Scrollbar ?
		aSize.Width()--;
#endif
#if defined(WNT)
	if( nResult & 0x0001 ) // vertikale Scrollbar ?
	{
		aSize.Width()++;
		nRealWidth++;
	}
#endif
	if( aSize != aHorSBar.GetSizePixel() )
		aHorSBar.SetSizePixel( aSize );
	aHorSBar.SetVisibleSize( nVisibleWidth ); //nRealWidth );
	aHorSBar.SetPageSize( (nVisibleWidth*75)/100 );
	if( nResult & 0x0002 )
	{
		aHorSBar.SetThumbPos( nThumb );
		aHorSBar.Show();
	}
	else
	{
		aHorSBar.SetThumbPos( 0 );
		aHorSBar.Hide();
	}

#ifdef OS2
	nRealWidth++;
#endif
	aOutputSize.Width() = nRealWidth;
#if defined(WNT)
	if( nResult & 0x0002 ) // hor scrollbar ?
		nRealHeight++; // weil unterer Rand geclippt wird
#endif
#ifdef OS2
	if( nResult & 0x0002 ) // hor scrollbar ?
		nRealHeight++;
#endif
	aOutputSize.Height() = nRealHeight;
}

void __EXPORT SvImpIconView::Resize()
{
	StopEditTimer();
	Rectangle aRect;
	if( GetResizeRect(aRect) )
		pView->Invalidate( aRect );
	aOutputSize = pView->GetOutputSizePixel();
	pImpCursor->Clear();

#if 1
	const Size& rSize = pView->Control::GetOutputSizePixel();
	PositionScrollBars( rSize.Width(), rSize.Height() );
	// Die ScrollBars werden asynchron ein/ausgeblendet, damit abgeleitete
	// Klassen im Resize ein Arrange durchfuehren koennen, ohne dass
	// die ScrollBars aufblitzen (SfxExplorerIconView!)
	nCurUserEvent = Application::PostUserEvent(LINK(this,SvImpIconView,UserEventHdl),0);
#else
	AdjustScrollBars();
	if( GetResizeRect(aRect) )
		PaintResizeRect( aRect );
#endif
}

sal_Bool SvImpIconView::CheckHorScrollBar()
{
	if( !pZOrderList || !aHorSBar.IsVisible() )
		return sal_False;
	const MapMode& rMapMode = pView->GetMapMode();
	Point aOrigin( rMapMode.GetOrigin() );
	if(!(pView->GetStyle() & WB_HSCROLL) && !aOrigin.X() )
	{
		long nWidth = aOutputSize.Width();
		sal_uInt16 nCount = pZOrderList->Count();
		long nMostRight = 0;
		for( sal_uInt16 nCur = 0; nCur < nCount; nCur++ )
		{
			SvLBoxEntry* pEntry = (SvLBoxEntry*)pZOrderList->operator[](nCur);
			long nRight = GetBoundingRect(pEntry).Right();
			if( nRight > nWidth )
				return sal_False;
			if( nRight > nMostRight )
				nMostRight = nRight;
		}
		aHorSBar.Hide();
		aOutputSize.Height() += nHorSBarHeight;
		aVirtOutputSize.Width() = nMostRight;
		aHorSBar.SetThumbPos( 0 );
		Range aRange;
		aRange.Max() = nMostRight - 1;
		aHorSBar.SetRange( aRange  );
		if( aVerSBar.IsVisible() )
		{
			Size aSize( aVerSBar.GetSizePixel());
			aSize.Height() += nHorSBarHeight;
			aVerSBar.SetSizePixel( aSize );
		}
		return sal_True;
	}
	return sal_False;
}

sal_Bool SvImpIconView::CheckVerScrollBar()
{
	if( !pZOrderList || !aVerSBar.IsVisible() )
		return sal_False;
	const MapMode& rMapMode = pView->GetMapMode();
	Point aOrigin( rMapMode.GetOrigin() );
	if(!(pView->GetStyle() & WB_VSCROLL) && !aOrigin.Y() )
	{
		long nDeepest = 0;
		long nHeight = aOutputSize.Height();
		sal_uInt16 nCount = pZOrderList->Count();
		for( sal_uInt16 nCur = 0; nCur < nCount; nCur++ )
		{
			SvLBoxEntry* pEntry = (SvLBoxEntry*)pZOrderList->operator[](nCur);
			long nBottom = GetBoundingRect(pEntry).Bottom();
			if( nBottom > nHeight )
				return sal_False;
			if( nBottom > nDeepest )
				nDeepest = nBottom;
		}
		aVerSBar.Hide();
		aOutputSize.Width() += nVerSBarWidth;
		aVirtOutputSize.Height() = nDeepest;
		aVerSBar.SetThumbPos( 0 );
		Range aRange;
		aRange.Max() = nDeepest - 1;
		aVerSBar.SetRange( aRange  );
		if( aHorSBar.IsVisible() )
		{
			Size aSize( aHorSBar.GetSizePixel());
			aSize.Width() += nVerSBarWidth;
			aHorSBar.SetSizePixel( aSize );
		}
		return sal_True;
	}
	return sal_False;
}


// blendet Scrollbars aus, wenn sie nicht mehr benoetigt werden
void SvImpIconView::CheckScrollBars()
{
	CheckVerScrollBar();
	if( CheckHorScrollBar() )
		CheckVerScrollBar();
}


void __EXPORT SvImpIconView::GetFocus()
{
	if( pCursor )
	{
		pView->SetEntryFocus( pCursor, sal_True );
		ShowCursor( sal_True );
	}
}

void __EXPORT SvImpIconView::LoseFocus()
{
	StopEditTimer();
	if( pCursor )
		pView->SetEntryFocus( pCursor,sal_False );
	ShowCursor( sal_False );
}

void SvImpIconView::UpdateAll()
{
	AdjustScrollBars();
	pImpCursor->Clear();
	pView->Invalidate();
}

void SvImpIconView::PaintEntry( SvLBoxEntry* pEntry, SvIcnVwDataEntry* pViewData )
{
	Point aPos( GetEntryPosition( pEntry ) );
	PaintEntry( pEntry, aPos, pViewData );
}

void SvImpIconView::PaintEmphasis( const Rectangle& rRect, sal_Bool bSelected,
								   sal_Bool bCursored, OutputDevice* pOut )
{
	// HACK fuer D&D
	if( nFlags & F_NO_EMPHASIS )
		return;

	if( !pOut )
		pOut = pView;

	// Selektion painten
	Color aOldFillColor =  pOut->GetFillColor();
	Color aOldLineColor =  pOut->GetLineColor();
	Color aNewColor;
	const StyleSettings& rStyleSettings = pOut->GetSettings().GetStyleSettings();
	if( bSelected )
	{
		aNewColor = rStyleSettings.GetHighlightColor();
	}
	else
	{
#ifndef OS2
		aNewColor =rStyleSettings.GetFieldColor();
#else
		aNewColor = pOut->GetBackground().GetColor();
#endif
	}

	if( bCursored )
	{
		pOut->SetLineColor( Color( COL_BLACK ) );
	}
	pOut->SetFillColor( aNewColor );
	pOut->DrawRect( rRect );
	pOut->SetFillColor( aOldFillColor );
	pOut->SetLineColor( aOldLineColor );
}

void SvImpIconView::PaintItem( const Rectangle& rRect,
	SvLBoxItem* pItem, SvLBoxEntry* pEntry, sal_uInt16 nPaintFlags,
	OutputDevice* pOut )
{
	if( nViewMode == VIEWMODE_ICON && pItem->IsA() == SV_ITEM_ID_LBOXSTRING )
	{
		const String& rStr = ((SvLBoxString*)pItem)->GetText();
		DrawText( pOut, rRect, rStr, DRAWTEXT_FLAGS );
	}
	else
	{
		Point aPos( rRect.TopLeft() );
		const Size& rSize = GetItemSize( pView, pEntry, pItem );
		if( nPaintFlags & PAINTFLAG_HOR_CENTERED )
			aPos.X() += (rRect.GetWidth() - rSize.Width() ) / 2;
		if( nPaintFlags & PAINTFLAG_VER_CENTERED )
			aPos.Y() += (rRect.GetHeight() - rSize.Height() ) / 2;
		pItem->Paint( aPos, *(SvLBox*)pOut, 0, pEntry );
	}
}

void SvImpIconView::PaintEntry( SvLBoxEntry* pEntry, const Point& rPos,
	SvIcnVwDataEntry* pViewData, OutputDevice* pOut )
{
	if( !pView->IsUpdateMode() )
		return;

	if( !pOut )
		pOut = pView;

	SvLBoxContextBmp* pBmpItem;

	pView->PreparePaint( pEntry );

	if( !pViewData )
		pViewData = ICNVIEWDATA(pEntry);

	SvLBoxString* pStringItem = (SvLBoxString*)(pEntry->GetFirstItem(SV_ITEM_ID_LBOXSTRING));

	sal_Bool bSelected	= pViewData->IsSelected();
	sal_Bool bCursored	= pViewData->IsCursored();

	Font aTempFont( pOut->GetFont() );
	// waehrend D&D nicht die Fontfarbe wechseln, da sonst auch die
	// Emphasis gezeichnet werden muss! (weisser Adler auf weissem Grund)
	if( bSelected && !(nFlags & F_NO_EMPHASIS) )
	{
		const StyleSettings& rStyleSettings = pOut->GetSettings().GetStyleSettings();
		Font aNewFont( aTempFont );
		aNewFont.SetColor( rStyleSettings.GetHighlightTextColor() );
		pOut->SetFont( aNewFont );
	}
	Rectangle aTextRect( CalcTextRect(pEntry,pStringItem,&rPos,sal_False,pViewData));
	Rectangle aBmpRect( CalcBmpRect(pEntry, &rPos, pViewData ) );

	switch( nViewMode )
	{
		case VIEWMODE_ICON:
			pBmpItem = (SvLBoxContextBmp*)(pEntry->GetFirstItem(SV_ITEM_ID_LBOXCONTEXTBMP));
			PaintEmphasis( aBmpRect, bSelected, bCursored, pOut );
			PaintItem( aBmpRect, pBmpItem, pEntry,
				PAINTFLAG_HOR_CENTERED | PAINTFLAG_VER_CENTERED, pOut );
			PaintEmphasis( aTextRect, bSelected, sal_False, pOut );
			PaintItem( aTextRect, pStringItem, pEntry, PAINTFLAG_HOR_CENTERED, pOut );
			break;

		case VIEWMODE_NAME:
			pBmpItem = (SvLBoxContextBmp*)(pEntry->GetFirstItem(SV_ITEM_ID_LBOXCONTEXTBMP));
			PaintEmphasis( aBmpRect, bSelected, bCursored, pOut );
			PaintItem( aBmpRect, pBmpItem, pEntry, PAINTFLAG_VER_CENTERED, pOut );
			PaintEmphasis( aTextRect, bSelected, sal_False, pOut );
			PaintItem( aTextRect, pStringItem, pEntry,PAINTFLAG_VER_CENTERED, pOut );
			break;

		case VIEWMODE_TEXT:
			PaintEmphasis( aTextRect, bSelected, bCursored, pOut );
			PaintItem( aTextRect, pStringItem, pEntry, PAINTFLAG_VER_CENTERED, pOut );
			break;
	}
	pOut->SetFont( aTempFont );
}

void SvImpIconView::SetEntryPosition( SvLBoxEntry* pEntry, const Point& rPos,
	sal_Bool bAdjustAtGrid, sal_Bool bCheckScrollBars )
{
	if( pModel->GetParent(pEntry) == pCurParent )
	{
		ShowCursor( sal_False );
		SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pEntry);
		Rectangle aBoundRect( GetBoundingRect( pEntry, pViewData ));
		pView->Invalidate( aBoundRect );
		ToTop( pEntry );
		if( rPos != aBoundRect.TopLeft() )
		{
			Point aGridOffs = pViewData->aGridRect.TopLeft() -
							  pViewData->aRect.TopLeft();
			pImpCursor->Clear();
			nFlags &= ~F_GRID_INSERT;
			aBoundRect.SetPos( rPos );
			pViewData->aRect = aBoundRect;
			pViewData->aGridRect.SetPos( rPos + aGridOffs );
			AdjustVirtSize( aBoundRect );
		}
		//HACK(Billigloesung, die noch verbessert werden muss)
		if( bAdjustAtGrid )
		{
			AdjustAtGrid( pEntry );
			ToTop( pEntry );
		}
		if( bCheckScrollBars && pView->IsUpdateMode() )
			CheckScrollBars();

		PaintEntry( pEntry, pViewData );
		ShowCursor( sal_True );
	}
}

void SvImpIconView::ViewDataInitialized( SvLBoxEntry*)
{
}

void SvImpIconView::ModelHasEntryInvalidated( SvListEntry* pEntry )
{
	if( pEntry == pCursor )
		ShowCursor( sal_False );
	SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pEntry);
	pView->Invalidate( pViewData->aRect );

	if( nFlags & F_GRIDMODE )
		Center( (SvLBoxEntry*)pEntry, pViewData );
	else
		pViewData->aRect.SetSize( CalcBoundingSize(
			(SvLBoxEntry*)pEntry, pViewData ) );

	ViewDataInitialized( (SvLBoxEntry*)pEntry );
	pView->Invalidate( pViewData->aRect );
	if( pEntry == pCursor )
		ShowCursor( sal_True );
}


void SvImpIconView::InvalidateEntry( SvLBoxEntry* pEntry )
{
	const Rectangle& rRect = GetBoundingRect( pEntry );
	pView->Invalidate( rRect );
}

void SvImpIconView::SetNoSelection()
{
}

void SvImpIconView::SetDragDropMode( DragDropMode )
{
}

void SvImpIconView::SetSelectionMode( SelectionMode )
{
}

sal_Bool SvImpIconView::IsEntryInView( SvLBoxEntry* )
{
	return sal_False;
}

SvLBoxEntry* SvImpIconView::GetDropTarget( const Point& rPos )
{
	Point aDocPos( rPos );
	CalcDocPos( aDocPos );
	SvLBoxEntry* pTarget = GetEntry( aDocPos );
	if( !pTarget || !pTarget->HasChilds() )
		pTarget = pCurParent;
	return pTarget;
}

SvLBoxEntry* SvImpIconView::GetEntry( const Point& rDocPos )
{
	CheckBoundingRects();
	SvLBoxEntry* pTarget = 0;
	// Z-Order-Liste vom Ende her absuchen
	sal_uInt16 nCount = pZOrderList->Count();
	while( nCount )
	{
		nCount--;
		SvLBoxEntry* pEntry = (SvLBoxEntry*)(pZOrderList->GetObject(nCount));
		SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pEntry);
		if( pViewData->aRect.IsInside( rDocPos ) )
		{
			pTarget = pEntry;
			break;
		}
	}
	return pTarget;
}

SvLBoxEntry* SvImpIconView::GetNextEntry( const Point& rDocPos, SvLBoxEntry* pCurEntry )
{
	CheckBoundingRects();
	SvLBoxEntry* pTarget = 0;
	sal_uInt16 nStartPos = pZOrderList->GetPos( (void*)pCurEntry );
	if( nStartPos != USHRT_MAX )
	{
		sal_uInt16 nCount = pZOrderList->Count();
		for( sal_uInt16 nCur = nStartPos+1; nCur < nCount; nCur++ )
		{
			SvLBoxEntry* pEntry = (SvLBoxEntry*)(pZOrderList->GetObject(nCur));
			SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pEntry);
			if( pViewData->aRect.IsInside( rDocPos ) )
			{
				pTarget = pEntry;
				break;
			}
		}
	}
	return pTarget;
}

SvLBoxEntry* SvImpIconView::GetPrevEntry( const Point& rDocPos, SvLBoxEntry* pCurEntry )
{
	CheckBoundingRects();
	SvLBoxEntry* pTarget = 0;
	sal_uInt16 nStartPos = pZOrderList->GetPos( (void*)pCurEntry );
	if( nStartPos != USHRT_MAX && nStartPos != 0 )
	{
		nStartPos--;
		do
		{
			SvLBoxEntry* pEntry = (SvLBoxEntry*)(pZOrderList->GetObject(nStartPos));
			SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pEntry);
			if( pViewData->aRect.IsInside( rDocPos ) )
			{
				pTarget = pEntry;
				break;
			}
		} while( nStartPos > 0 );
	}
	return pTarget;
}


Point SvImpIconView::GetEntryPosition( SvLBoxEntry* pEntry )
{
	SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pEntry);
	DBG_ASSERT(pViewData,"Entry not in model");
	return pViewData->aRect.TopLeft();
}

const Rectangle& SvImpIconView::GetBoundingRect( SvLBoxEntry* pEntry, SvIcnVwDataEntry* pViewData )
{
	if( !pViewData )
		pViewData = ICNVIEWDATA(pEntry);
	DBG_ASSERT(pViewData,"Entry not in model");
	if( !IsBoundingRectValid( pViewData->aRect ))
		FindBoundingRect( pEntry, pViewData );
	return pViewData->aRect;
}

void SvImpIconView::SetSpaceBetweenEntries( long nHor, long nVer )
{
	nHorDist = nHor;
	nVerDist = nVer;
}

Rectangle SvImpIconView::CalcBmpRect( SvLBoxEntry* pEntry, const Point* pPos,
	SvIcnVwDataEntry* pViewData  )
{
	if( !pViewData )
		pViewData = ICNVIEWDATA(pEntry);

	Rectangle aBound = GetBoundingRect( pEntry, pViewData );
	if( pPos )
		aBound.SetPos( *pPos );
	Point aPos( aBound.TopLeft() );

	switch( nViewMode )
	{
		case VIEWMODE_ICON:
		{
			aPos.X() += ( aBound.GetWidth() - nMaxBmpWidth ) / 2;
			Size aSize( nMaxBmpWidth, nMaxBmpHeight );
			// das Bitmap-Rechteck soll nicht das TextRect beruehren
			aSize.Height() -= 3;
			return Rectangle( aPos, aSize );
		}

		case VIEWMODE_NAME:
			return Rectangle( aPos,
				Size( nMaxBmpWidth, aBound.GetHeight() ));

		case VIEWMODE_TEXT:
			return Rectangle( aPos, aBound.GetSize() );

		default:
		{
			Rectangle aRect;
			return aRect;
		}
	}
}

Rectangle SvImpIconView::CalcTextRect( SvLBoxEntry* pEntry,
	SvLBoxString* pItem, const Point* pPos, sal_Bool bForInplaceEdit,
	SvIcnVwDataEntry* pViewData )
{
	long nBmpHeight, nBmpWidth;

	if( !pItem )
		pItem = (SvLBoxString*)(pEntry->GetFirstItem(SV_ITEM_ID_LBOXSTRING));

	if( !pViewData )
		pViewData = ICNVIEWDATA(pEntry);

	Size aTextSize( GetItemSize( pView, pEntry, pItem, pViewData ));
	aTextSize.Width() += 2*LROFFS_TEXT;

	Size aContextBmpSize(pEntry->GetFirstItem(SV_ITEM_ID_LBOXCONTEXTBMP)->GetSize(pView,pEntry));
	Rectangle aBound = GetBoundingRect( pEntry, pViewData );
	if( pPos )
		aBound.SetPos( *pPos );
	Point aPos( aBound.TopLeft() );

	switch( nViewMode )
	{
		case VIEWMODE_ICON:
			nBmpHeight = aContextBmpSize.Height();
			if( nBmpHeight < nMaxBmpHeight )
				nBmpHeight = nMaxBmpHeight;
			aPos.Y() += nBmpHeight;

			// beim Inplace-Editieren, spendieren wir ein bisschen mehr Platz
			if( bForInplaceEdit )
			{
				// 20% rauf
				long nMinWidth = (( (aContextBmpSize.Width()*10) / 100 ) * 2 ) +
								 aContextBmpSize.Width();
				if( nMinWidth > aBound.GetWidth() )
					nMinWidth = aBound.GetWidth();

				if( aTextSize.Width() < nMinWidth )
					aTextSize.Width() = nMinWidth;

				// beim Inplace-Ed. darfs auch untere Eintraege ueberlappen
				Rectangle aMaxGridTextRect = CalcMaxTextRect(pEntry, pViewData);
				Size aOptSize = aMaxGridTextRect.GetSize();
				if( aOptSize.Height() > aTextSize.Height() )
					aTextSize.Height() = aOptSize.Height();
			}


			aPos.X() += ( aBound.GetWidth() - aTextSize.Width() ) / 2;
			break;

		case VIEWMODE_NAME:
			nBmpWidth = aContextBmpSize.Width();
			if( nBmpWidth < nMaxBmpWidth )
				nBmpWidth = nMaxBmpWidth;
			aPos.X() += nBmpWidth;
			// vertikal ausrichten
			aPos.Y() += ( nBmpWidth - aTextSize.Height() ) / 2;
			break;
	}

	Rectangle aRect( aPos, aTextSize );
// KNALLT BEIM D&D, WENN GECLIPPT WIRD (In DrawText von Thomas)
//	ClipAtVirtOutRect( aRect );
	return aRect;
}


long SvImpIconView::CalcBoundingWidth( SvLBoxEntry* pEntry,
	const SvIcnVwDataEntry* pViewData ) const
{
	DBG_ASSERT(pEntry->GetFirstItem(SV_ITEM_ID_LBOXCONTEXTBMP),"No Bitmaps");
	DBG_ASSERT(pEntry->GetFirstItem(SV_ITEM_ID_LBOXSTRING),"No Text");
	long nStringWidth = GetItemSize( pView, pEntry, pEntry->GetFirstItem(SV_ITEM_ID_LBOXSTRING),pViewData).Width();
	nStringWidth += 2*LROFFS_TEXT;
	long nBmpWidth = pEntry->GetFirstItem(SV_ITEM_ID_LBOXCONTEXTBMP)->GetSize(pView,pEntry).Width();
	long nWidth = 0;

	switch( nViewMode )
	{
		case VIEWMODE_ICON:
			nWidth = Max( nStringWidth, nBmpWidth );
			nWidth = Max( nWidth, nMaxBmpWidth );
			break;

		case VIEWMODE_NAME:
			nWidth = Max( nBmpWidth, nMaxBmpWidth );
			nWidth += NAMEVIEW_OFFS_BMP_STRING;  // Abstand Bitmap String
			nWidth += nStringWidth;
			break;

		case VIEWMODE_TEXT:
			nWidth = nStringWidth;
			break;
	}
	return nWidth;
}

long SvImpIconView::CalcBoundingHeight( SvLBoxEntry* pEntry,
	const SvIcnVwDataEntry* pViewData ) const
{
	DBG_ASSERT(pEntry->GetFirstItem(SV_ITEM_ID_LBOXCONTEXTBMP),"No Bitmaps");
	DBG_ASSERT(pEntry->GetFirstItem(SV_ITEM_ID_LBOXSTRING),"No Text");
	long nStringHeight = GetItemSize(pView,pEntry,pEntry->GetFirstItem(SV_ITEM_ID_LBOXSTRING),pViewData).Height();
	long nBmpHeight = pEntry->GetFirstItem(SV_ITEM_ID_LBOXCONTEXTBMP)->GetSize(pView,pEntry).Height();
	long nHeight = 0;

	switch( nViewMode )
	{
		case VIEWMODE_ICON:
			nHeight = Max( nBmpHeight, nMaxBmpHeight );
			nHeight += ICONVIEW_OFFS_BMP_STRING; // Abstand Bitmap String
			nHeight += nStringHeight;
			break;

		case VIEWMODE_NAME:
			nHeight = Max( nBmpHeight, nMaxBmpHeight );
			nHeight = Max( nHeight, nStringHeight );
			break;

		case VIEWMODE_TEXT:
			nHeight = nStringHeight;
			break;
	}
	if( nHeight > nMaxBoundHeight )
	{
		((SvImpIconView*)this)->nMaxBoundHeight = nHeight;
		((SvImpIconView*)this)->aHorSBar.SetLineSize( nHeight / 2 );
		((SvImpIconView*)this)->aVerSBar.SetLineSize( nHeight / 2 );
	}
	return nHeight;
}

Size SvImpIconView::CalcBoundingSize( SvLBoxEntry* pEntry,
	SvIcnVwDataEntry* pViewData ) const
{
	if( !pViewData )
		pViewData = ICNVIEWDATA(pEntry);
	return Size( CalcBoundingWidth(pEntry,pViewData),
				 CalcBoundingHeight(pEntry,pViewData) );
}

void SvImpIconView::RecalcAllBoundingRects()
{
	nMaxBoundHeight	= 0;
	pZOrderList->Remove(0, pZOrderList->Count() );
	SvLBoxEntry* pEntry = pModel->FirstChild( pCurParent );
	while( pEntry )
	{
		FindBoundingRect( pEntry );
		pZOrderList->Insert( pEntry, pZOrderList->Count() );
		pEntry = pModel->NextSibling( pEntry );
	}
	bMustRecalcBoundingRects = sal_False;
	AdjustScrollBars();
}

void SvImpIconView::RecalcAllBoundingRectsSmart()
{
	nMaxBoundHeight	= 0;
	pZOrderList->Remove(0, pZOrderList->Count() );
	SvLBoxEntry* pEntry = pModel->FirstChild( pCurParent );
	while( pEntry )
	{
		SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pEntry);
		if( IsBoundingRectValid( pViewData->aRect ))
		{
			Size aBoundSize( pViewData->aRect.GetSize() );
			if( aBoundSize.Height() > nMaxBoundHeight )
				nMaxBoundHeight = aBoundSize.Height();
			pZOrderList->Insert( pEntry, pZOrderList->Count() );
		}
		else
		{
			FindBoundingRect( pEntry, pViewData );
		}
		pZOrderList->Insert( pEntry, pZOrderList->Count() );
		pEntry = pModel->NextSibling( pEntry );
	}
	AdjustScrollBars();
}

void SvImpIconView::UpdateBoundingRects()
{
	SvLBoxEntry* pEntry = pModel->FirstChild( pCurParent );
	while( pEntry )
	{
		GetBoundingRect( pEntry );
		pEntry = pModel->NextSibling( pEntry );
	}
}

void SvImpIconView::FindBoundingRect( SvLBoxEntry* pEntry,
	SvIcnVwDataEntry* pViewData )
{
	if( !pViewData )
		pViewData = ICNVIEWDATA(pEntry);

	Size aSize( CalcBoundingSize( pEntry, pViewData ) );
	Point aPos;

	DBG_ASSERT(!pViewData->IsEntryPosLocked(),"Locked entry pos in FindBoundingRect");
	// damits in der IconView nicht drunter & drueber geht
	if( pViewData->IsEntryPosLocked() && IsBoundingRectValid(pViewData->aRect) )
	{
		AdjustVirtSize( pViewData->aRect );
		return;
	}

	aPos = FindNextEntryPos( aSize );

	if( nFlags & F_GRIDMODE )
	{
		Rectangle aGridRect( aPos, Size(nGridDX, nGridDY) );
		pViewData->aGridRect = aGridRect;
		Center( pEntry, pViewData );
		AdjustVirtSize( pViewData->aRect );
		pImpCursor->SetGridUsed( pViewData->aRect );
	}
	else
	{
		pViewData->aRect = Rectangle( aPos, aSize );
		AdjustVirtSize( pViewData->aRect );
	}
}


void SvImpIconView::SetCursor( SvLBoxEntry* pEntry )
{
	if( pEntry == pCursor )
		return;

	ShowCursor( sal_False );
	if( pCursor )
	{
		pView->SetEntryFocus( pCursor, sal_False );
		if( pView->GetSelectionMode() == SINGLE_SELECTION )
			pView->Select( pCursor, sal_False );
	}
	pCursor = pEntry;
	ToTop( pCursor );
	if( pCursor )
	{
		pView->SetEntryFocus(pCursor, sal_True );
		if( pView->GetSelectionMode() == SINGLE_SELECTION )
			pView->Select( pCursor, sal_True );
		ShowCursor( sal_True );
	}
}


void SvImpIconView::ShowCursor( sal_Bool bShow )
{
	if( !pCursor || !bShow || !pView->HasFocus() )
	{
		pView->HideFocus();
		return;
	}
	Rectangle aRect ( CalcFocusRect( pCursor ) );
	pView->ShowFocus( aRect );
}


void SvImpIconView::HideDDIcon()
{
	pView->Update();
	ImpHideDDIcon();
	pDDBufDev = pDDDev;
	pDDDev = 0;
}

void SvImpIconView::ImpHideDDIcon()
{
	if( pDDDev )
	{
		Size aSize( pDDDev->GetOutputSizePixel() );
		// pView restaurieren
		pView->DrawOutDev( aDDLastRectPos, aSize, Point(), aSize, *pDDDev );
	}
}


void SvImpIconView::ShowDDIcon( SvLBoxEntry* pRefEntry, const Point& rPosPix )
{
	pView->Update();
	if( pRefEntry != pDDRefEntry )
	{
		DELETEZ(pDDDev);
		DELETEZ(pDDBufDev);
	}
	sal_Bool bSelected = pView->SvListView::Select( pRefEntry, sal_False );
	if( !pDDDev )
	{
		if( pDDBufDev )
		{
			// nicht bei jedem Move ein Device anlegen, da dies besonders
			// auf Remote-Clients zu langsam ist
			pDDDev = pDDBufDev;
			pDDBufDev = 0;
		}
		else
		{
			pDDDev = new VirtualDevice( *pView );
			pDDDev->SetFont( pView->GetFont() );
		}
	}
	else
	{
		ImpHideDDIcon();
	}
	const Rectangle& rRect = GetBoundingRect( pRefEntry );
	pDDDev->SetOutputSizePixel( rRect.GetSize() );

	Point aPos( rPosPix );
	CalcDocPos( aPos );

	Size aSize( pDDDev->GetOutputSizePixel() );
	pDDRefEntry = pRefEntry;
	aDDLastEntryPos = aPos;
	aDDLastRectPos = aPos;

	// Hintergrund sichern
	pDDDev->DrawOutDev( Point(), aSize, aPos, aSize, *pView );
	// Icon in pView malen
	nFlags |= F_NO_EMPHASIS;
	PaintEntry( pRefEntry, aPos );
	nFlags &= ~F_NO_EMPHASIS;
	if( bSelected )
		pView->SvListView::Select( pRefEntry, sal_True );
}

void SvImpIconView::HideShowDDIcon( SvLBoxEntry* pRefEntry, const Point& rPosPix )
{
/*  In Notfaellen folgenden flackernden Code aktivieren:

		HideDDIcon();
		ShowDDIcon( pRefEntry, rPosPix );
		return;
*/
	if( !pDDDev )
	{
		ShowDDIcon( pRefEntry, rPosPix );
		return;
	}

	if( pRefEntry != pDDRefEntry )
	{
		HideDDIcon();
		ShowDDIcon( pRefEntry, rPosPix );
		return;
	}

	Point aEmptyPoint;

	Point aCurEntryPos( rPosPix );
	CalcDocPos( aCurEntryPos );

	const Rectangle& rRect = GetBoundingRect( pRefEntry );
	Size aEntrySize( rRect.GetSize() );
	Rectangle aPrevEntryRect( aDDLastEntryPos, aEntrySize );
	Rectangle aCurEntryRect( aCurEntryPos, aEntrySize );

	if( !aPrevEntryRect.IsOver( aCurEntryRect ) )
	{
		HideDDIcon();
		ShowDDIcon( pRefEntry, rPosPix );
		return;
	}

	// Ueberlappung des neuen und alten D&D-Pointers!

	Rectangle aFullRect( aPrevEntryRect.Union( aCurEntryRect ) );
	if( !pDDTempDev )
	{
		pDDTempDev = new VirtualDevice( *pView );
		pDDTempDev->SetFont( pView->GetFont() );
	}

	Size aFullSize( aFullRect.GetSize() );
	Point aFullPos( aFullRect.TopLeft() );

	pDDTempDev->SetOutputSizePixel( aFullSize );

	// Hintergrund (mit dem alten D&D-Pointer!) sichern
	pDDTempDev->DrawOutDev( aEmptyPoint, aFullSize, aFullPos, aFullSize, *pView );
	// den alten Buffer in den neuen Buffer pasten
	aDDLastRectPos = aDDLastRectPos - aFullPos;

	pDDTempDev->DrawOutDev(
		aDDLastRectPos,
		pDDDev->GetOutputSizePixel(),
		aEmptyPoint,
		pDDDev->GetOutputSizePixel(),
		*pDDDev );

	// Swap
	VirtualDevice* pTemp = pDDDev;
	pDDDev = pDDTempDev;
	pDDTempDev = pTemp;

	// in den restaurierten Hintergrund den neuen D&D-Pointer zeichnen
	pDDTempDev->SetOutputSizePixel( pDDDev->GetOutputSizePixel() );
	pDDTempDev->DrawOutDev(
		aEmptyPoint, aFullSize, aEmptyPoint, aFullSize, *pDDDev );
	Point aRelPos = aCurEntryPos - aFullPos;
	nFlags |= F_NO_EMPHASIS;
	PaintEntry( pRefEntry, aRelPos, 0, pDDTempDev );
	nFlags &= ~F_NO_EMPHASIS;

	aDDLastRectPos = aFullPos;
	aDDLastEntryPos = aCurEntryPos;

	pView->DrawOutDev(
		aDDLastRectPos,
		pDDDev->GetOutputSizePixel(),
		aEmptyPoint,
		pDDDev->GetOutputSizePixel(),
		*pDDTempDev );

	sal_Bool bSelected = pView->SvListView::Select( pRefEntry, sal_False );
	if( bSelected )
		pView->SvListView::Select( pRefEntry, sal_True );
}

void SvImpIconView::ShowTargetEmphasis( SvLBoxEntry* pEntry, sal_Bool )
{
	CheckBoundingRects();
	Rectangle aRect;
	if( pEntry != pCurParent &&
		(pEntry->HasChilds() || pEntry->HasChildsOnDemand()) )
		aRect = CalcBmpRect( pEntry );
	else
	{
		aRect.SetSize( aOutputSize );
		const MapMode& rMapMode = pView->GetMapMode();
		Point aOrigin( rMapMode.GetOrigin());
		aOrigin *= -1; // in Doc-Koord wandeln
		aRect.SetPos( aOrigin );
		aRect.Left()++; aRect.Top()++;
		aRect.Right()--; aRect.Bottom()--;
	}
	ImpDrawXORRect( aRect );
}

sal_Bool SvImpIconView::NotifyMoving( SvLBoxEntry* pTarget, SvLBoxEntry* pEntry,
	SvLBoxEntry*& rpNewPar, sal_uLong& rNewChildPos )
{
	if( pTarget == pCurParent && pModel->GetParent(pEntry) == pCurParent )
	{
		// D&D innerhalb einer Childlist
		StopEditTimer();
		SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pEntry);
		Size aSize( pViewData->aRect.GetSize() );
		Point aNewPos = FindNextEntryPos( aSize );
		AdjustVirtSize( Rectangle( aNewPos, aSize ) );
		SetEntryPosition( pEntry, aNewPos, sal_False, sal_True );
		return sal_False;
	}
	return pView->SvLBox::NotifyMoving(pTarget,pEntry,rpNewPar,rNewChildPos);
}

sal_Bool SvImpIconView::NotifyCopying( SvLBoxEntry* pTarget, SvLBoxEntry* pEntry,
	SvLBoxEntry*& rpNewParent, sal_uLong& rNewChildPos )
{
	return pView->SvLBox::NotifyCopying(pTarget,pEntry,rpNewParent,rNewChildPos);
}

void SvImpIconView::WriteDragServerInfo( const Point& rPos, SvLBoxDDInfo* pInfo)
{
	SvLBoxEntry* pCurEntry = GetCurEntry();
	Point aEntryPos;
	if( pCurEntry )
	{
		aEntryPos = rPos;
		aEntryPos -= GetEntryPosition( pCurEntry );
	}
	pInfo->nMouseRelX = aEntryPos.X();
	pInfo->nMouseRelY = aEntryPos.Y();
}

void SvImpIconView::ReadDragServerInfo( const Point& rPos, SvLBoxDDInfo* pInfo )
{
	Point aDropPos( rPos );
	aDropPos.X() -= pInfo->nMouseRelX;
	aDropPos.Y() -= pInfo->nMouseRelY;
	SetNextEntryPos( aDropPos );
}

void SvImpIconView::InvalidateBoundingRect( SvLBoxEntry* pEntry )
{
	SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pEntry);
	InvalidateBoundingRect( pViewData->aRect );
}

void SvImpIconView::PrepareCommandEvent( const Point& rPt )
{
	aMouseMoveTimer.Stop();
	StopEditTimer();
	nFlags |= F_CMD_ARRIVED;
	SvLBoxEntry* pEntry = pView->GetEntry( rPt, sal_True );
	if( (nFlags & F_DOWN_CTRL) && pEntry && !pView->IsSelected(pEntry) )
		pView->Select( pEntry, sal_True );
	nFlags &= ~(F_DOWN_CTRL | F_DOWN_DESELECT);
}

void SvImpIconView::SttDrag( const Point& rPos )
{
	PrepareCommandEvent( rPos );

	nFlags |= F_DRAG_SOURCE;
	ShowCursor( sal_False );
}

void SvImpIconView::EndDrag()
{
	ShowCursor( sal_True );
	nFlags &= (~F_DRAG_SOURCE);
}

void SvImpIconView::ToTop( SvLBoxEntry* pEntry )
{
	DBG_ASSERT(pZOrderList->GetPos(pEntry)!=0xffff,"ToTop:ZOrder?");
	if( pZOrderList->GetObject( pZOrderList->Count() -1 ) != pEntry )
	{
		sal_uInt16 nPos = pZOrderList->GetPos( (void*)pEntry );
		pZOrderList->Remove( nPos, 1 );
		pZOrderList->Insert( pEntry, pZOrderList->Count() );
	}
}

void SvImpIconView::SetCurParent( SvLBoxEntry* pNewParent )
{
	Clear();
	pCurParent = pNewParent;
	ImpArrange();
}

void SvImpIconView::ClipAtVirtOutRect( Rectangle& rRect ) const
{
	if( rRect.Bottom() >= aVirtOutputSize.Height() )
		rRect.Bottom() = aVirtOutputSize.Height() - 1;
	if( rRect.Right() >= aVirtOutputSize.Width() )
		rRect.Right() = aVirtOutputSize.Width() - 1;
	if( rRect.Top() < 0 )
		rRect.Top() = 0;
	if( rRect.Left() < 0 )
		rRect.Left() = 0;
}

// rRect: Bereich des Dokumentes (in Dokumentkoordinaten), der
// sichtbar gemacht werden soll.
// bScrBar == sal_True: Das Rect wurde aufgrund eines ScrollBar-Events berechnet

void SvImpIconView::MakeVisible( const Rectangle& rRect, sal_Bool bScrBar )
{
	Rectangle aRect( rRect );
	ClipAtVirtOutRect( aRect );
	MapMode aMapMode( pView->GetMapMode() );
	Point aOrigin( aMapMode.GetOrigin() );
	// in Dokumentkoordinate umwandeln
	aOrigin *= -1;

	Rectangle aOutputArea( aOrigin, aOutputSize );
	if( aOutputArea.IsInside( aRect ) )
		return;	// ist schon sichtbar

	long nDy;
	if( aRect.Top() < aOutputArea.Top() )
	{
		// nach oben scrollen (nDy < 0)
		nDy = aRect.Top() - aOutputArea.Top();
	}
	else if( aRect.Bottom() > aOutputArea.Bottom() )
	{
		// nach unten scrollen (nDy > 0)
		nDy = aRect.Bottom() - aOutputArea.Bottom();
	}
	else
		nDy = 0;

	long nDx;
	if( aRect.Left() < aOutputArea.Left() )
	{
		// nach links scrollen (nDx < 0)
		nDx = aRect.Left() - aOutputArea.Left();
	}
	else if( aRect.Right() > aOutputArea.Right() )
	{
		// nach rechts scrollen (nDx > 0)
		nDx = aRect.Right() - aOutputArea.Right();
	}
	else
		nDx = 0;

	aOrigin.X() += nDx;
	aOrigin.Y() += nDy;
	aOutputArea.SetPos( aOrigin );

	pView->Update();

	// Origin fuer SV invertieren (damit wir in
	// Dokumentkoordinaten scrollen/painten koennen)
	aOrigin *= -1;
	aMapMode.SetOrigin( aOrigin );
	pView->SetMapMode( aMapMode );

	// in umgekehrte Richtung scrollen!
	pView->Control::Scroll( -nDx, -nDy, aOutputArea, sal_True );
	if( aHorSBar.IsVisible() || aVerSBar.IsVisible() )
	{
		if( !bScrBar )
		{
			aOrigin *= -1;
			// Thumbs korrigieren
			if(aHorSBar.IsVisible() && aHorSBar.GetThumbPos() != aOrigin.X())
				aHorSBar.SetThumbPos( aOrigin.X() );
			if(aVerSBar.IsVisible() && aVerSBar.GetThumbPos() != aOrigin.Y())
				aVerSBar.SetThumbPos( aOrigin.Y() );
		}
	}
	// pruefen, ob ScrollBars noch benoetigt werden
	CheckScrollBars();
	pView->Update();
}


SvLBoxEntry* SvImpIconView::GetNewCursor()
{
	SvLBoxEntry* pNewCursor;
	if( pCursor )
	{
		pNewCursor = pImpCursor->GoLeftRight( pCursor, sal_False );
		if( !pNewCursor )
		{
			pNewCursor = pImpCursor->GoLeftRight( pCursor, sal_True );
			if( !pNewCursor )
			{
				pNewCursor = pImpCursor->GoUpDown( pCursor, sal_False );
				if( !pNewCursor )
					pNewCursor = pImpCursor->GoUpDown( pCursor, sal_True );
			}
		}
	}
	else
		pNewCursor = pModel->FirstChild( pCurParent );
	DBG_ASSERT(!pNewCursor|| (pCursor&&pCursor!=pNewCursor),"GetNewCursor failed");
	return pNewCursor;
}


sal_uInt16 SvImpIconView:: GetSelectionCount() const
{
	sal_uInt16 nSelected = 0;
	SvLBoxEntry* pEntry = pModel->FirstChild( pCurParent);
	while( pEntry )
	{
		if( pView->IsSelected( pEntry ) )
			nSelected++;
		pEntry = pModel->NextSibling( pEntry );
	}
	return nSelected;
}


void SvImpIconView::ToggleSelection( SvLBoxEntry* pEntry )
{
	sal_Bool bSel;
	if( pView->IsSelected( pEntry ) )
		bSel = sal_False;
	else
		bSel = sal_True;
	pView->Select( pEntry, bSel );
}

void SvImpIconView::DeselectAllBut( SvLBoxEntry* pThisEntryNot )
{
	ClearSelectedRectList();
	SvLBoxEntry* pEntry = pModel->FirstChild( pCurParent );
	while( pEntry )
	{
		if( pEntry != pThisEntryNot && pView->IsSelected( pEntry ))
			pView->Select( pEntry, sal_False );
		pEntry = pModel->NextSibling( pEntry );
	}
}

#define ICN_ROWS	50
#define ICN_COLS	30

ImpIcnCursor::ImpIcnCursor( SvImpIconView* pOwner )
{
	pView 		= pOwner;
	pColumns	= 0;
	pRows 		= 0;
	pCurEntry 	= 0;
	nDeltaWidth = 0;
	nDeltaHeight= 0;
	nCols		= 0;
	nRows		= 0;
	nGridCols	= 0;
	nGridRows	= 0;
	pGridMap	= 0;
}

ImpIcnCursor::~ImpIcnCursor()
{
	delete[] pColumns;
	delete[] pRows;
	delete pGridMap;
}

sal_uInt16 ImpIcnCursor::GetSortListPos( SvPtrarr* pList, long nValue,
	int bVertical )
{
	sal_uInt16 nCount = (sal_uInt16)pList->Count();
	if( !nCount )
		return 0;

	sal_uInt16 nCurPos = 0;
	long nPrevValue = LONG_MIN;
	while( nCount )
	{
		const Rectangle& rRect=
			pView->GetBoundingRect((SvLBoxEntry*)(pList->GetObject(nCurPos)));
		long nCurValue;
		if( bVertical )
			nCurValue = rRect.Top();
		else
			nCurValue = rRect.Left();
		if( nValue >= nPrevValue && nValue <= nCurValue )
			return (sal_uInt16)nCurPos;
		nPrevValue = nCurValue;
		nCount--;
		nCurPos++;
	}
	return pList->Count();
}

void ImpIcnCursor::ImplCreate()
{
	pView->CheckBoundingRects();
	DBG_ASSERT(pColumns==0&&pRows==0,"ImplCreate: Not cleared");

	SetDeltas();

	pColumns = new SvPtrarr[ nCols ];
	pRows = new SvPtrarr[ nRows ];

	DELETEZ(pGridMap);

	SvLBoxTreeList* pModel = pView->pModel;
	SvLBoxEntry* pEntry = pModel->FirstChild( pView->pCurParent );
	while( pEntry )
	{
		SvIcnVwDataEntry* pViewData = ICNVIEWDATA2(pEntry);
		// const Rectangle& rRect = pView->GetBoundingRect( pEntry );
		Rectangle rRect( pView->CalcBmpRect( pEntry,0,pViewData ) );
		short nY = (short)( ((rRect.Top()+rRect.Bottom())/2) / nDeltaHeight );
		short nX = (short)( ((rRect.Left()+rRect.Right())/2) / nDeltaWidth );

		// Rundungsfehler abfangen
		if( nY >= nRows )
			nY = sal::static_int_cast< short >(nRows - 1);
		if( nX >= nCols )
			nX = sal::static_int_cast< short >(nCols - 1);

		sal_uInt16 nIns = GetSortListPos( &pColumns[nX], rRect.Top(), sal_True );
		pColumns[ nX ].Insert( pEntry, nIns );

		nIns = GetSortListPos( &pRows[nY], rRect.Left(), sal_False );
		pRows[ nY ].Insert( pEntry, nIns );

		pViewData->nX = nX;
		pViewData->nY = nY;

		pEntry = pModel->NextSibling( pEntry );
	}
}

void ImpIcnCursor::CreateGridMap()
{
	if( pGridMap )
		return;

	const Size& rSize = pView->aVirtOutputSize;
	long nWidth = rSize.Width();
	if( nWidth < pView->nMaxVirtWidth )
		nWidth = pView->nMaxVirtWidth;
	nWidth -= 2*LROFFS_WINBORDER;
	if( nWidth <= 0 )
		nWidth = 1;

	nGridDX = pView->nGridDX;
	nGridDY = pView->nGridDY;

	// Hinweis: Wegen der Abrundung bei Berechnung von nGridCols
	// ist es moeglich, dass Eintrage nicht im Grid liegen. Diese
	// wurden typischerweise manuell verschoben und gelockt
	nGridCols = nWidth / nGridDX;
	if( !nGridCols ) nGridCols = 1;

	nGridRows = rSize.Height() / nGridDY;
	// nRows nicht abrunden, da zur Vermeidung von Ueberlappungen
	// das gesamte BoundingRect des Eintrags zur Markierung im Grid
	// herangezogen wird.
	if( (nGridRows * nGridDY) < rSize.Height() )
		nGridRows++;
	else if( !nGridRows )
		nGridRows = 1;

	//XXX
	//nGridRows += 50; // in fuenfziger-Schritten

	pGridMap = new sal_Bool[ nGridRows*nGridCols];
	memset( (void*)pGridMap, 0, nGridRows*nGridCols );

	SvLBoxTreeList* pModel = pView->pModel;
	SvLBoxEntry* pEntry = pModel->FirstChild( pView->pCurParent );
	while( pEntry )
	{
		SvIcnVwDataEntry* pViewData = ICNVIEWDATA2(pEntry);
		const Rectangle& rRect = pViewData->aRect;
		// nur, wenn der Entry schon plaziert ist
		if( pView->IsBoundingRectValid( rRect ))
		{
			// Alle vom Eintrag beruehrten Grids kennzeichnen
			SetGridUsed( pView->GetBoundingRect( pEntry, pViewData ) );
		}
		pEntry = pModel->NextSibling( pEntry );
	}
}

sal_Bool ImpIcnCursor::GetGrid( const Point& rDocPos, sal_uInt16& rGridX, sal_uInt16& rGridY ) const
{
	Point aPos( rDocPos );
	aPos.X() -= LROFFS_WINBORDER;
	aPos.Y() -= TBOFFS_WINBORDER;
	rGridX = (sal_uInt16)(aPos.X() / nGridDX);
	rGridY = (sal_uInt16)(aPos.Y() / nGridDY);
	sal_Bool bInGrid = sal_True;
	if( rGridX >= nGridCols )
	{
		rGridX = sal::static_int_cast< sal_uInt16 >(nGridCols - 1);
		bInGrid = sal_False;
	}
	if( rGridY >= nGridRows )
	{
		rGridY = sal::static_int_cast< sal_uInt16 >(nGridRows - 1);
		if( !bInGrid )
			return sal_False; // beide Koordinaten nicht im Grid
	}
	return sal_True;
}

void ImpIcnCursor::SetGridUsed( const Rectangle& rRect, sal_Bool bUsed  )
{
	CreateGridMap();
	sal_uInt16 nTLX, nTLY, nBRX, nBRY;

	sal_Bool bTLInGrid = GetGrid( rRect.TopLeft(), nTLX, nTLY );
	sal_Bool bBRInGrid = GetGrid( rRect.BottomRight(), nBRX, nBRY );

	if( !bTLInGrid && !bBRInGrid )
		return;

	for( sal_uInt16 nCurY = nTLY; nCurY <= nBRY; nCurY++ )
	{
		for( sal_uInt16 nCurX = nTLX; nCurX <= nBRX; nCurX++ )
		{
			SetGridUsed( nCurX, nCurY, bUsed );
		}
	}
}

void ImpIcnCursor::Clear( sal_Bool bGridToo )
{
	if( pColumns )
	{
		delete[] pColumns;
		delete[] pRows;
		pColumns = 0;
		pRows = 0;
		pCurEntry = 0;
		nDeltaWidth = 0;
		nDeltaHeight = 0;
	}
	if( bGridToo && pGridMap )
	{
		DELETEZ(pGridMap);
		nGridRows = 0;
		nGridCols = 0;
	}
}

SvLBoxEntry* ImpIcnCursor::SearchCol(sal_uInt16 nCol,sal_uInt16 nTop,sal_uInt16 nBottom,
	sal_uInt16, sal_Bool bDown, sal_Bool bSimple  )
{
	DBG_ASSERT(pCurEntry,"SearchCol: No reference entry");
	SvPtrarr* pList = &(pColumns[ nCol ]);
	sal_uInt16 nCount = pList->Count();
	if( !nCount )
		return 0;

	const Rectangle& rRefRect = pView->GetBoundingRect(pCurEntry);

	if( bSimple )
	{
		sal_uInt16 nListPos = pList->GetPos( pCurEntry );
		DBG_ASSERT(nListPos!=0xffff,"Entry not in Col-List");
		if( bDown )
		{
			while( nListPos < nCount-1 )
			{
				nListPos++;
				SvLBoxEntry* pEntry = (SvLBoxEntry*)pList->GetObject( nListPos );
				const Rectangle& rRect = pView->GetBoundingRect( pEntry );
				if( rRect.Top() > rRefRect.Top() )
					return pEntry;
			}
			return 0;
		}
		else
		{
			while( nListPos )
			{
				nListPos--;
				if( nListPos < nCount )
				{
					SvLBoxEntry* pEntry = (SvLBoxEntry*)pList->GetObject( nListPos );
					const Rectangle& rRect = pView->GetBoundingRect( pEntry );
					if( rRect.Top() < rRefRect.Top() )
						return pEntry;
				}
			}
			return 0;
		}
	}

	if( nTop > nBottom )
	{
		sal_uInt16 nTemp = nTop;
		nTop = nBottom;
		nBottom = nTemp;
	}
	long nMinDistance = LONG_MAX;
	SvLBoxEntry* pResult = 0;
	for( sal_uInt16 nCur = 0; nCur < nCount; nCur++ )
	{
		SvLBoxEntry* pEntry = (SvLBoxEntry*)(pList->GetObject( nCur ));
		if( pEntry != pCurEntry )
		{
			SvIcnVwDataEntry* pViewData = ICNVIEWDATA2(pEntry);
			sal_uInt16 nY = pViewData->nY;
			if( nY >= nTop && nY <= nBottom )
			{
				const Rectangle& rRect = pView->GetBoundingRect( pEntry );
				long nDistance = rRect.Top() - rRefRect.Top();
				if( nDistance < 0 )
					nDistance *= -1;
				if( nDistance && nDistance < nMinDistance )
				{
					nMinDistance = nDistance;
					pResult = pEntry;
				}
			}
		}
	}
	return pResult;
}

SvLBoxEntry* ImpIcnCursor::SearchRow(sal_uInt16 nRow,sal_uInt16 nLeft,sal_uInt16 nRight,
	sal_uInt16, sal_Bool bRight, sal_Bool bSimple )
{
	DBG_ASSERT(pCurEntry,"SearchRow: No reference entry");
	SvPtrarr* pList = &(pRows[ nRow ]);
	sal_uInt16 nCount = pList->Count();
	if( !nCount )
		return 0;

	const Rectangle& rRefRect = pView->GetBoundingRect(pCurEntry);

	if( bSimple )
	{
		sal_uInt16 nListPos = pList->GetPos( pCurEntry );
		DBG_ASSERT(nListPos!=0xffff,"Entry not in Row-List");
		if( bRight )
		{
			while( nListPos < nCount-1 )
			{
				nListPos++;
				SvLBoxEntry* pEntry = (SvLBoxEntry*)pList->GetObject( nListPos );
				const Rectangle& rRect = pView->GetBoundingRect( pEntry );
				if( rRect.Left() > rRefRect.Left() )
					return pEntry;
			}
			return 0;
		}
		else
		{
			while( nListPos )
			{
				nListPos--;
				if( nListPos < nCount )
				{
					SvLBoxEntry* pEntry = (SvLBoxEntry*)pList->GetObject( nListPos );
					const Rectangle& rRect = pView->GetBoundingRect( pEntry );
					if( rRect.Left() < rRefRect.Left() )
						return pEntry;
				}
			}
			return 0;
		}

	}
	if( nRight < nLeft )
	{
		sal_uInt16 nTemp = nRight;
		nRight = nLeft;
		nLeft = nTemp;
	}
	long nMinDistance = LONG_MAX;
	SvLBoxEntry* pResult = 0;
	for( sal_uInt16 nCur = 0; nCur < nCount; nCur++ )
	{
		SvLBoxEntry* pEntry = (SvLBoxEntry*)(pList->GetObject( nCur ));
		if( pEntry != pCurEntry )
		{
			SvIcnVwDataEntry* pViewData = ICNVIEWDATA2(pEntry);
			sal_uInt16 nX = pViewData->nX;
			if( nX >= nLeft && nX <= nRight )
			{
				const Rectangle& rRect = pView->GetBoundingRect( pEntry );
				long nDistance = rRect.Left() - rRefRect.Left();
				if( nDistance < 0 )
					nDistance *= -1;
				if( nDistance && nDistance < nMinDistance )
				{
					nMinDistance = nDistance;
					pResult = pEntry;
				}
			}
		}
	}
	return pResult;
}



/*
	Sucht ab dem uebergebenen Eintrag den naechsten rechts- bzw.
	linksstehenden. Suchverfahren am Beispiel bRight = sal_True:

				  c
				b c
			  a b c
			S 1 1 1      ====> Suchrichtung
			  a b c
				b c
				  c

	S : Startposition
	1 : erstes Suchrechteck
	a,b,c : 2., 3., 4. Suchrechteck
*/

SvLBoxEntry* ImpIcnCursor::GoLeftRight( SvLBoxEntry* pIcnEntry, sal_Bool bRight )
{
	SvLBoxEntry* pResult;
	pCurEntry = pIcnEntry;
	Create();
	SvIcnVwDataEntry* pViewData = ICNVIEWDATA2(pIcnEntry);
	sal_uInt16 nY = pViewData->nY;
	sal_uInt16 nX = pViewData->nX;
	DBG_ASSERT(nY< nRows,"GoLeftRight:Bad column");
	DBG_ASSERT(nX< nCols,"GoLeftRight:Bad row");
	// Nachbar auf gleicher Zeile ?
	if( bRight )
		pResult = SearchRow(
            nY, nX, sal::static_int_cast< sal_uInt16 >(nCols-1), nX, sal_True, sal_True );
	else
		pResult = SearchRow( nY, nX ,0, nX, sal_False, sal_True );
	if( pResult )
		return pResult;

	long nCurCol = nX;

	long nColOffs, nLastCol;
	if( bRight )
	{
		nColOffs = 1;
		nLastCol = nCols;
	}
	else
	{
		nColOffs = -1;
		nLastCol = -1;   // 0-1
	}

	sal_uInt16 nRowMin = nY;
	sal_uInt16 nRowMax = nY;
	do
	{
		SvLBoxEntry* pEntry = SearchCol((sal_uInt16)nCurCol,nRowMin,nRowMax,nY,sal_True, sal_False);
		if( pEntry )
			return pEntry;
		if( nRowMin )
			nRowMin--;
		if( nRowMax < (nRows-1))
			nRowMax++;
		nCurCol += nColOffs;
	} while( nCurCol != nLastCol );
	return 0;
}

SvLBoxEntry* ImpIcnCursor::GoUpDown( SvLBoxEntry* pIcnEntry, sal_Bool bDown)
{
	SvLBoxEntry* pResult;
	pCurEntry = pIcnEntry;
	Create();
	SvIcnVwDataEntry* pViewData = ICNVIEWDATA2(pIcnEntry);
	sal_uInt16 nY = pViewData->nY;
	sal_uInt16 nX = pViewData->nX;
	DBG_ASSERT(nY<nRows,"GoUpDown:Bad column");
	DBG_ASSERT(nX<nCols,"GoUpDown:Bad row");

	// Nachbar in gleicher Spalte ?
	if( bDown )
		pResult = SearchCol(
            nX, nY, sal::static_int_cast< sal_uInt16 >(nRows-1), nY, sal_True, sal_True );
	else
		pResult = SearchCol( nX, nY ,0, nY, sal_False, sal_True );
	if( pResult )
		return pResult;

	long nCurRow = nY;

	long nRowOffs, nLastRow;
	if( bDown )
	{
		nRowOffs = 1;
		nLastRow = nRows;
	}
	else
	{
		nRowOffs = -1;
		nLastRow = -1;   // 0-1
	}

	sal_uInt16 nColMin = nX;
	sal_uInt16 nColMax = nX;
	do
	{
		SvLBoxEntry* pEntry = SearchRow((sal_uInt16)nCurRow,nColMin,nColMax,nX,sal_True, sal_False);
		if( pEntry )
			return pEntry;
		if( nColMin )
			nColMin--;
		if( nColMax < (nCols-1))
			nColMax++;
		nCurRow += nRowOffs;
	} while( nCurRow != nLastRow );
	return 0;
}

void ImpIcnCursor::SetDeltas()
{
	const Size& rSize = pView->aVirtOutputSize;
	if( pView->nFlags & F_GRIDMODE )
	{
		nGridDX = pView->nGridDX;
		nGridDY = pView->nGridDY;
	}
	else
	{
		nGridDX = 20;
		nGridDY = 20;
	}
	nCols = rSize.Width() / nGridDX;
	if( !nCols )
		nCols = 1;
	nRows = rSize.Height() / nGridDY;
	if( (nRows * nGridDY) < rSize.Height() )
		nRows++;
	if( !nRows )
		nRows = 1;

	nDeltaWidth = (short)(rSize.Width() / nCols);
	nDeltaHeight = (short)(rSize.Height() / nRows);
	if( !nDeltaHeight )
	{
		nDeltaHeight = 1;
		DBG_WARNING("SetDeltas:Bad height");
	}
	if( !nDeltaWidth )
	{
		nDeltaWidth = 1;
		DBG_WARNING("SetDeltas:Bad width");
	}
}


void ImpIcnCursor::ExpandGrid()
{
	if( pGridMap )
	{
		long nNewGridRows = nGridRows + 20;
		unsigned char* pTempMap = new unsigned char[ nNewGridRows * nGridCols ];
		memcpy( pTempMap, pGridMap, nGridRows * nGridCols );
		delete pGridMap;
		pGridMap = pTempMap;
		nGridRows = nNewGridRows;
	}
}

sal_Bool ImpIcnCursor::FindEmptyGridRect( Rectangle& rRect )
{
	CreateGridMap();
	sal_uInt16 nCount = (sal_uInt16)(nGridCols * nGridRows);
	if( !nCount )
		return sal_False;
	for( sal_uInt16 nCur = 0; nCur < nCount; nCur++ )
	{
		if( !pGridMap[ nCur ] )
		{
			sal_uInt16 nCol = (sal_uInt16)(nCur % nGridCols);
			sal_uInt16 nRow = (sal_uInt16)(nCur / nGridCols);
			rRect.Top() = nRow * nGridDY + TBOFFS_WINBORDER;
			rRect.Bottom() = rRect.Top() + nGridDY;
			rRect.Left() = nCol * nGridDX+ LROFFS_WINBORDER;
			rRect.Right() = rRect.Left() + nGridDX;
			SetGridUsed( nCol, nRow, sal_True );

			//XXX
			//if( nRow + 5 > nGridRows )
			//	ExpandGrid();
			DBG_ASSERT(pGridMap[nCur],"SetGridUsed failed");
			return sal_True;
		}
	}
	// Gridmap ist voll: Um eine Zeile erweitern
	rRect.Top() = nGridRows * nGridDY + TBOFFS_WINBORDER;
	rRect.Bottom() = rRect.Top() + nGridDY;
	rRect.Left() = LROFFS_WINBORDER;
	rRect.Right() = rRect.Left() + nGridDX;
	return sal_False;
	//XXX
	//ExpandGrid();
	//return sal_True;
}

void ImpIcnCursor::CreateGridAjustData( SvPtrarr& rLists, SvLBoxEntry* pRefEntry)
{
	if( !pRefEntry )
	{
		sal_uInt16 nAdjustRows = (sal_uInt16)(pView->aVirtOutputSize.Height() / pView->nGridDY);
		nAdjustRows++; // wg. Abrundung!

		if( !nAdjustRows )
			return;
		for( sal_uInt16 nCurList = 0; nCurList < nAdjustRows; nCurList++ )
		{
			SvPtrarr* pRow = new SvPtrarr;
			rLists.Insert( (void*)pRow, nCurList );
		}
		SvLBoxEntry* pEntry = pView->pModel->FirstChild( pView->pCurParent );
		while( pEntry )
		{
			const Rectangle& rRect = pView->GetBoundingRect( pEntry );
			short nY = (short)( ((rRect.Top()+rRect.Bottom())/2) / pView->nGridDY );
			sal_uInt16 nIns = GetSortListPos((SvPtrarr*)rLists[nY],rRect.Left(),sal_False);
			((SvPtrarr*)rLists[ nY ])->Insert( pEntry, nIns );
			pEntry = pView->pModel->NextSibling( pEntry );
		}
	}
	else
	{
		// Aufbau eines hor. "Schlauchs" auf der RefEntry-Zeile

		// UEBERLEGEN: BoundingRect nehmen wg. Ueberlappungen???

		Rectangle rRefRect( pView->CalcBmpRect( pRefEntry ) );
		//const Rectangle& rRefRect = pView->GetBoundingRect( pRefEntry );
		short nRefRow = (short)( ((rRefRect.Top()+rRefRect.Bottom())/2) / pView->nGridDY );
		SvPtrarr* pRow = new SvPtrarr;
		rLists.Insert( (void*)pRow, 0 );
		SvLBoxEntry* pEntry = pView->pModel->FirstChild( pView->pCurParent );
		while( pEntry )
		{
			Rectangle rRect( pView->CalcBmpRect(pEntry) );
			//const Rectangle& rRect = pView->GetBoundingRect( pEntry );
			short nY = (short)( ((rRect.Top()+rRect.Bottom())/2) / pView->nGridDY );
			if( nY == nRefRow )
			{
				sal_uInt16 nIns = GetSortListPos( pRow, rRect.Left(), sal_False );
				pRow->Insert( pEntry, nIns );
			}
			pEntry = pView->pModel->NextSibling( pEntry );
		}
	}
}

//static
void ImpIcnCursor::DestroyGridAdjustData( SvPtrarr& rLists )
{
	sal_uInt16 nCount = rLists.Count();
	for( sal_uInt16 nCur = 0; nCur < nCount; nCur++ )
	{
		SvPtrarr* pArr = (SvPtrarr*)rLists[ nCur ];
		delete pArr;
	}
	rLists.Remove( 0, rLists.Count() );
}

void SvImpIconView::SetGrid( long nDX, long nDY )
{
	nGridDX = nDX;
	nGridDY = nDY;
	nFlags |= F_GRIDMODE;
}

Rectangle SvImpIconView::CalcMaxTextRect( const SvLBoxEntry* pEntry,
	const SvIcnVwDataEntry* pViewData ) const
{
	Rectangle aRect = pViewData->aGridRect;
	long nBmpHeight = ((SvLBoxEntry*)pEntry)->GetFirstItem(SV_ITEM_ID_LBOXCONTEXTBMP)->GetSize(pView,(SvLBoxEntry*)pEntry).Height();
	aRect.Top() += nBmpHeight;
	aRect.Top() += ICONVIEW_OFFS_BMP_STRING;
	if( aRect.Top() > aRect.Bottom())
		aRect.Top() = aRect.Bottom();
	aRect.Left() += LROFFS_BOUND;
	aRect.Left()++;
	aRect.Right() -= LROFFS_BOUND;
	aRect.Right()--;
	if( aRect.Left() > aRect.Right())
		aRect.Left() = aRect.Right();
	if( GetTextMode( pEntry, pViewData ) == ShowTextFull )
		aRect.Bottom() = LONG_MAX;
	return aRect;
}

void SvImpIconView::Center( SvLBoxEntry* pEntry,
	SvIcnVwDataEntry* pViewData ) const
{
	SvLBoxString* pStringItem = (SvLBoxString*)(pEntry->GetFirstItem(SV_ITEM_ID_LBOXSTRING));
	const String& rEntryText = pStringItem->GetText();

	Rectangle aTextRect = CalcMaxTextRect(pEntry,pViewData);
	aTextRect = GetTextRect( pView, aTextRect, rEntryText, DRAWTEXT_FLAGS );
	pViewData->aTextSize = aTextRect.GetSize();

	pViewData->aRect = pViewData->aGridRect;
	Size aSize( CalcBoundingSize( pEntry, pViewData ) );
	long nBorder = pViewData->aGridRect.GetWidth() - aSize.Width();
	pViewData->aRect.Left() += nBorder / 2;
	pViewData->aRect.Right() -= nBorder / 2;
	pViewData->aRect.Bottom() = pViewData->aRect.Top() + aSize.Height();
}


// Die Deltas entsprechen Offsets, um die die View auf dem Doc verschoben wird
// links, hoch: Offsets < 0
// rechts, runter: Offsets > 0
void SvImpIconView::Scroll( long nDeltaX, long nDeltaY, sal_Bool bScrollBar )
{
	const MapMode& rMapMode = pView->GetMapMode();
	Point aOrigin( rMapMode.GetOrigin() );
	// in Dokumentkoordinate umwandeln
	aOrigin *= -1;
	aOrigin.Y() += nDeltaY;
	aOrigin.X() += nDeltaX;
	Rectangle aRect( aOrigin, aOutputSize );
	MakeVisible( aRect, bScrollBar );
}


const Size& SvImpIconView::GetItemSize( SvIconView* pIconView,
	SvLBoxEntry* pEntry, SvLBoxItem* pItem, const SvIcnVwDataEntry* pViewData) const
{
	if( (nFlags & F_GRIDMODE) && pItem->IsA() == SV_ITEM_ID_LBOXSTRING )
	{
		if( !pViewData )
			pViewData = ICNVIEWDATA(pEntry);
		return pViewData->aTextSize;
	}
	else
		return pItem->GetSize( pIconView, pEntry );
}

Rectangle SvImpIconView::CalcFocusRect( SvLBoxEntry* pEntry )
{
#if !defined(OS2)
	SvLBoxString* pStringItem = (SvLBoxString*)(pEntry->GetFirstItem(SV_ITEM_ID_LBOXSTRING));
	DBG_ASSERT(pStringItem,"Text not set");
	return CalcTextRect( pEntry, pStringItem );
#else
	return CalcBmpRect( pEntry );
#endif
}


void SvImpIconView::SelectRect( const Rectangle& rRect, sal_Bool bAdd,
	SvPtrarr* pOtherRects, short nBorderOffs )
{
	if( !pZOrderList || !pZOrderList->Count() )
		return;

	CheckBoundingRects();
	pView->Update();
	sal_uInt16 nCount = pZOrderList->Count();

	Rectangle aRect( rRect );
	aRect.Justify();
	if( nBorderOffs )
	{
		aRect.Left() -= nBorderOffs;
		aRect.Right() += nBorderOffs;
		aRect.Top() -= nBorderOffs;
		aRect.Bottom() += nBorderOffs;
	}
	sal_Bool bCalcOverlap = (bAdd && pOtherRects && pOtherRects->Count()) ? sal_True : sal_False;

	for( sal_uInt16 nPos = 0; nPos < nCount; nPos++ )
	{
		SvLBoxEntry* pEntry = (SvLBoxEntry*)(pZOrderList->GetObject(nPos ));

		SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pEntry);
		DBG_ASSERT(pViewData,"Entry not in model");
		if( !IsBoundingRectValid( pViewData->aRect ))
			FindBoundingRect( pEntry, pViewData );
		const Rectangle& rBoundRect = pViewData->aRect;
		sal_Bool bSelected = pViewData->IsSelected();

		sal_Bool bOverlaps;
		if( bCalcOverlap )
			bOverlaps = IsOver( pOtherRects, rBoundRect );
		else
			bOverlaps = sal_False;
		sal_Bool bOver = aRect.IsOver( rBoundRect );

		if( bOver && !bOverlaps )
		{
			// Ist im neuen Selektionsrechteck und in keinem alten
			// => selektieren
			if( !bSelected )
				pView->Select( pEntry, sal_True );
		}
		else if( !bAdd )
		{
			// ist ausserhalb des Selektionsrechtecks
			// => Selektion entfernen
			if( bSelected )
				pView->Select( pEntry, sal_False );
		}
		else if( bAdd && bOverlaps )
		{
			// Der Eintrag befindet sich in einem alten (=>Aufspannen
			// mehrerer Rechtecke mit Ctrl!) Selektionsrechteck

			// Hier ist noch ein Bug! Der Selektionsstatus eines Eintrags
			// in einem vorherigen Rechteck, muss restauriert werden, wenn
			// er vom aktuellen Selektionsrechteck beruehrt wurde, jetzt aber
			// nicht mehr in ihm liegt. Ich gehe hier der Einfachheit halber
			// pauschal davon aus, dass die Eintraege in den alten Rechtecken
			// alle selektiert sind. Ebenso ist es falsch, die Schnittmenge
			// nur zu deselektieren.
			// Loesungsmoeglichkeit: Snapshot der Selektion vor dem Auf-
			// spannen des Rechtecks merken
			if( rBoundRect.IsOver( rRect))
			{
				// Schnittmenge zwischen alten Rects & aktuellem Rect desel.
				if( bSelected )
					pView->Select( pEntry, sal_False );
			}
			else
			{
				// Eintrag eines alten Rects selektieren
				if( !bSelected )
					pView->Select( pEntry, sal_True );
			}
		}
		else if( !bOver && bSelected )
		{
			// Der Eintrag liegt voellig ausserhalb und wird deshalb desel.
			pView->Select( pEntry, sal_False );
		}
	}
	pView->Update();
}

sal_Bool SvImpIconView::IsOver( SvPtrarr* pRectList, const Rectangle& rBoundRect ) const
{
	sal_uInt16 nCount = pRectList->Count();
	for( sal_uInt16 nCur = 0; nCur < nCount; nCur++ )
	{
		Rectangle* pRect = (Rectangle*)pRectList->GetObject( nCur );
		if( rBoundRect.IsOver( *pRect ))
			return sal_True;
	}
	return sal_False;
}

void SvImpIconView::AddSelectedRect( const Rectangle& rRect, short nBorderOffs )
{
	Rectangle* pRect = new Rectangle( rRect );
	pRect->Justify();
	if( nBorderOffs )
	{
		pRect->Left() -= nBorderOffs;
		pRect->Right() += nBorderOffs;
		pRect->Top() -= nBorderOffs;
		pRect->Bottom() += nBorderOffs;
	}
	aSelectedRectList.Insert( (void*)pRect, aSelectedRectList.Count() );
}

void SvImpIconView::ClearSelectedRectList()
{
	sal_uInt16 nCount = aSelectedRectList.Count();
	for( sal_uInt16 nCur = 0; nCur < nCount; nCur++ )
	{
		Rectangle* pRect = (Rectangle*)aSelectedRectList.GetObject( nCur );
		delete pRect;
	}
	aSelectedRectList.Remove( 0, aSelectedRectList.Count() );
}


void SvImpIconView::DrawSelectionRect( const Rectangle& rRect )
{
	pView->HideTracking();
	nFlags |= F_SELRECT_VISIBLE;
	pView->ShowTracking( rRect, SHOWTRACK_SMALL | SHOWTRACK_WINDOW );
	aCurSelectionRect = rRect;
}

void SvImpIconView::HideSelectionRect()
{
	if( nFlags & F_SELRECT_VISIBLE )
	{
		pView->HideTracking();
		nFlags &= ~F_SELRECT_VISIBLE;
	}
}

void SvImpIconView::ImpDrawXORRect( const Rectangle& rRect )
{
	RasterOp eOldOp = pView->GetRasterOp();
	pView->SetRasterOp( ROP_XOR );
	Color aOldColor = pView->GetFillColor();
	pView->SetFillColor();
	pView->DrawRect( rRect );
	pView->SetFillColor( aOldColor );
	pView->SetRasterOp( eOldOp );
}

void SvImpIconView::CalcScrollOffsets( const Point& rPosPixel,
	long& rX, long& rY, sal_Bool bInDragDrop, sal_uInt16 nBorderWidth)
{
	// Scrolling der View, falls sich der Mauszeiger im Grenzbereich des
	// Fensters befindet
	long nPixelToScrollX = 0;
	long nPixelToScrollY = 0;
	Size aWndSize = aOutputSize;

	nBorderWidth = (sal_uInt16)(Min( (long)(aWndSize.Height()-1), (long)nBorderWidth ));
	nBorderWidth = (sal_uInt16)(Min( (long)(aWndSize.Width()-1), (long)nBorderWidth ));

	if ( rPosPixel.X() < nBorderWidth )
	{
		if( bInDragDrop )
			nPixelToScrollX = -DD_SCROLL_PIXEL;
		else
			nPixelToScrollX = rPosPixel.X()- nBorderWidth;
	}
	else if ( rPosPixel.X() > aWndSize.Width() - nBorderWidth )
	{
		if( bInDragDrop )
			nPixelToScrollX = DD_SCROLL_PIXEL;
		else
			nPixelToScrollX = rPosPixel.X() - (aWndSize.Width() - nBorderWidth);
	}
	if ( rPosPixel.Y() < nBorderWidth )
	{
		if( bInDragDrop )
			nPixelToScrollY = -DD_SCROLL_PIXEL;
		else
			nPixelToScrollY = rPosPixel.Y() - nBorderWidth;
	}
	else if ( rPosPixel.Y() > aWndSize.Height() - nBorderWidth )
	{
		if( bInDragDrop )
			nPixelToScrollY = DD_SCROLL_PIXEL;
		else
			nPixelToScrollY = rPosPixel.Y() - (aWndSize.Height() - nBorderWidth);
	}

	rX = nPixelToScrollX;
	rY = nPixelToScrollY;
}

IMPL_LINK(SvImpIconView, MouseMoveTimeoutHdl, Timer*, pTimer )
{
	pTimer->Start();
	MouseMove( aMouseMoveEvent );
	return 0;
}

void SvImpIconView::EndTracking()
{
	pView->ReleaseMouse();
	if( nFlags & F_RUBBERING )
	{
		aMouseMoveTimer.Stop();
		nFlags &= ~(F_RUBBERING | F_ADD_MODE);
	}
}

sal_Bool SvImpIconView::IsTextHit( SvLBoxEntry* pEntry, const Point& rDocPos )
{
	SvLBoxString* pItem = (SvLBoxString*)(pEntry->GetFirstItem(SV_ITEM_ID_LBOXSTRING));
	if( pItem )
	{
		Rectangle aRect( CalcTextRect( pEntry, pItem ));
		if( aRect.IsInside( rDocPos ) )
			return sal_True;
	}
	return sal_False;
}

IMPL_LINK(SvImpIconView, EditTimeoutHdl, Timer*, EMPTYARG )
{
	SvLBoxEntry* pEntry = GetCurEntry();
	if( pView->IsInplaceEditingEnabled() && pEntry &&
		pView->IsSelected( pEntry ))
	{
		pView->EditEntry( pEntry );
	}
	return 0;
}


//
// Funktionen zum Ausrichten der Eintraege am Grid
//

// pStart == 0: Alle Eintraege werden ausgerichtet
// sonst: Alle Eintraege der Zeile ab einschliesslich pStart werden ausgerichtet
void SvImpIconView::AdjustAtGrid( SvLBoxEntry* pStart )
{
	SvPtrarr aLists;
	pImpCursor->CreateGridAjustData( aLists, pStart );
	sal_uInt16 nCount = aLists.Count();
	for( sal_uInt16 nCur = 0; nCur < nCount; nCur++ )
	{
		AdjustAtGrid( *(SvPtrarr*)aLists[ nCur ], pStart );
	}
	ImpIcnCursor::DestroyGridAdjustData( aLists );
	CheckScrollBars();
}

// Richtet eine Zeile aus, erweitert ggf. die Breite; Bricht die Zeile nicht um
void SvImpIconView::AdjustAtGrid( const SvPtrarr& rRow, SvLBoxEntry* pStart )
{
	if( !rRow.Count() )
		return;

	sal_Bool bGo;
	if( !pStart )
		bGo = sal_True;
	else
		bGo = sal_False;

	long nCurRight = 0;
	for( sal_uInt16 nCur = 0; nCur < rRow.Count(); nCur++ )
	{
		SvLBoxEntry* pCur = (SvLBoxEntry*)rRow[ nCur ];
		if( !bGo && pCur == pStart )
			bGo = sal_True;

		SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pCur);
		// Massgebend (fuer das menschliche Auge) ist die Bitmap, da sonst
		// durch lange Texte der Eintrag stark springen kann
		const Rectangle& rBoundRect = GetBoundingRect( pCur, pViewData );
		Rectangle aCenterRect( CalcBmpRect( pCur, 0, pViewData ));
		if( bGo && !pViewData->IsEntryPosLocked() )
		{
			long nWidth = aCenterRect.GetSize().Width();
			Point aNewPos( AdjustAtGrid( aCenterRect, rBoundRect ) );
			while( aNewPos.X() < nCurRight )
				aNewPos.X() += nGridDX;
			if( aNewPos != rBoundRect.TopLeft() )
				SetEntryPosition( pCur, aNewPos );
			nCurRight = aNewPos.X() + nWidth;
		}
		else
		{
			nCurRight = rBoundRect.Right();
		}
	}
}

// Richtet Rect am Grid aus, garantiert jedoch nicht, dass die
// neue Pos. frei ist. Die Pos. kann fuer SetEntryPos verwendet werden.
// Das CenterRect beschreibt den Teil des BoundRects, der fuer
// die Berechnung des Ziel-Rechtecks verwendet wird.
Point SvImpIconView::AdjustAtGrid( const Rectangle& rCenterRect,
	const Rectangle& rBoundRect ) const
{
	Point aPos( rCenterRect.TopLeft() );
	Size aSize( rCenterRect.GetSize() );

	aPos.X() -= LROFFS_WINBORDER;
	aPos.Y() -= TBOFFS_WINBORDER;

	// align (ref ist mitte des rects)
	short nGridX = (short)((aPos.X()+(aSize.Width()/2)) / nGridDX);
	short nGridY = (short)((aPos.Y()+(aSize.Height()/2)) / nGridDY);
	aPos.X() = nGridX * nGridDX;
	aPos.Y() = nGridY * nGridDY;
	// hor. center
	aPos.X() += (nGridDX - rBoundRect.GetSize().Width() ) / 2;

	aPos.X() += LROFFS_WINBORDER;
	aPos.Y() += TBOFFS_WINBORDER;

	return aPos;
}


void SvImpIconView::SetTextMode( SvIconViewTextMode eMode, SvLBoxEntry* pEntry )
{
	if( !pEntry )
	{
		if( eTextMode != eMode )
		{
			if( eTextMode == ShowTextDontKnow )
				eTextMode = ShowTextShort;
			eTextMode = eMode;
			pView->Arrange();
		}
	}
	else
	{
		SvIcnVwDataEntry* pViewData = ICNVIEWDATA(pEntry);
		if( pViewData->eTextMode != eMode )
		{
			pViewData->eTextMode = eMode;
			pModel->InvalidateEntry( pEntry );
			AdjustVirtSize( pViewData->aRect );
		}
	}
}

SvIconViewTextMode SvImpIconView::GetTextMode( const SvLBoxEntry* pEntry,
	const SvIcnVwDataEntry* pViewData ) const
{
	if( !pEntry )
		return eTextMode;
	else
	{
		if( !pViewData )
			pViewData = ICNVIEWDATA(((SvLBoxEntry*)pEntry));
		return pViewData->GetTextMode();
	}
}

SvIconViewTextMode SvImpIconView::GetEntryTextModeSmart( const SvLBoxEntry* pEntry,
	const SvIcnVwDataEntry* pViewData ) const
{
	DBG_ASSERT(pEntry,"GetEntryTextModeSmart: Entry not set");
	if( !pViewData )
		pViewData = ICNVIEWDATA(((SvLBoxEntry*)pEntry));
	SvIconViewTextMode eMode = pViewData->GetTextMode();
	if( eMode == ShowTextDontKnow )
		return eTextMode;
	return eMode;
}

void SvImpIconView::ShowFocusRect( const SvLBoxEntry* pEntry )
{
	if( !pEntry )
		pView->HideFocus();
	else
	{
		Rectangle aRect ( CalcFocusRect( (SvLBoxEntry*)pEntry ) );
		pView->ShowFocus( aRect );
	}
}

IMPL_LINK(SvImpIconView, UserEventHdl, void*, EMPTYARG )
{
	nCurUserEvent = 0;
	AdjustScrollBars();
	Rectangle aRect;
	if( GetResizeRect(aRect) )
		PaintResizeRect( aRect );
	return 0;
}

void SvImpIconView::CancelUserEvent()
{
	if( nCurUserEvent )
	{
		Application::RemoveUserEvent( nCurUserEvent );
		nCurUserEvent = 0;
	}
}


