/**************************************************************
 * 
 * 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_dbaccess.hxx"
#ifndef DBAUI_QUERYTABLEVIEW_HXX
#include "JoinTableView.hxx"
#endif
#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif
#ifndef DBAUI_QUERYCONTROLLER_HXX
#include "querycontroller.hxx"
#endif
#ifndef DBAUI_JOINDESIGNVIEW_HXX
#include "JoinDesignView.hxx"
#endif
#ifndef _DBU_QRY_HRC_
#include "dbu_qry.hrc"
#endif
#ifndef DBAUI_TABLEWINDOW_HXX
#include "TableWindow.hxx"
#endif
//#ifndef DBAUI_QUERY_TABLEWINDOWDATA_HXX
//#include "QTableWindowData.hxx"
//#endif
#ifndef DBAUI_TABLEWINDOWLISTBOX_HXX
#include "TableWindowListBox.hxx"
#endif
#ifndef DBAUI_TABLECONNECTION_HXX
#include "TableConnection.hxx"
#endif
#ifndef DBAUI_TABLECONNECTIONDATA_HXX
#include "TableConnectionData.hxx"
#endif
#ifndef DBAUI_CONNECTIONLINE_HXX
#include "ConnectionLine.hxx"
#endif
#ifndef DBAUI_CONNECTIONLINEDATA_HXX
#include "ConnectionLineData.hxx"
#endif
#ifndef DBACCESS_UI_BROWSER_ID_HXX
#include "browserids.hxx"
#endif
#ifndef _URLBMK_HXX
#include <svl/urlbmk.hxx>
#endif
#ifndef _COM_SUN_STAR_SDBC_XDATABASEMETADATA_HPP_
#include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
#endif
#ifndef DBAUI_OQUERYMOVETABWINUNDOACT_HXX
#include "QueryMoveTabWinUndoAct.hxx"
#endif
#ifndef DBAUI_QUERYSIZETABWINUNDOACT_HXX
#include "QuerySizeTabWinUndoAct.hxx"
#endif
#ifndef _SV_SVAPP_HXX
#include <vcl/svapp.hxx>
#endif
#ifndef DBAUI_TABLEWINDOWDATA_HXX
#include "TableWindowData.hxx"
#endif
#ifndef DBACCESS_JACCESS_HXX
#include "JAccess.hxx"
#endif
#ifndef _COM_SUN_STAR_ACCESSIBILITY_XACCESSIBLE_HPP_
#include <com/sun/star/accessibility/XAccessible.hpp>
#endif
#ifndef _COM_SUN_STAR_ACCESSIBILITY_ACCESSIBLEROLE_HPP_
#include <com/sun/star/accessibility/AccessibleRole.hpp>
#endif
#include <com/sun/star/accessibility/AccessibleEventId.hpp>
#ifndef DBAUI_TOOLS_HXX
#include "UITools.hxx"
#endif
#include <cppuhelper/exc_hlp.hxx>
#include <tools/diagnose_ex.h>
#include <boost/bind.hpp>
#include <algorithm>
#include <functional>

using namespace dbaui;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::accessibility;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::lang;

#define LINE_SIZE			50
////////////////////////////////////////////////////////////////
// Konstanten fuer das Fensterlayout
#define TABWIN_SPACING_X	17
#define TABWIN_SPACING_Y	17

#define TABWIN_WIDTH_STD	120
#define TABWIN_HEIGHT_STD	120

DBG_NAME(OScrollWindowHelper)
OScrollWindowHelper::OScrollWindowHelper( Window* pParent) : Window( pParent)
	,m_aHScrollBar( this, WB_HSCROLL|WB_REPEAT|WB_DRAG )
	,m_aVScrollBar( this, WB_VSCROLL|WB_REPEAT|WB_DRAG )
	,m_pCornerWindow(new ScrollBarBox(this, WB_3DLOOK))
	,m_pTableView(NULL)
{
	DBG_CTOR(OScrollWindowHelper,NULL);

	//////////////////////////////////////////////////////////////////////
	// ScrollBars

	GetHScrollBar()->SetRange( Range(0, 1000) );
	GetVScrollBar()->SetRange( Range(0, 1000) );

	GetHScrollBar()->SetLineSize( LINE_SIZE );
	GetVScrollBar()->SetLineSize( LINE_SIZE );

	GetHScrollBar()->Show();
	GetVScrollBar()->Show();
	m_pCornerWindow->Show();

	// normally we should be SCROLL_PANE
	SetAccessibleRole(AccessibleRole::SCROLL_PANE);
}

// -----------------------------------------------------------------------------
OScrollWindowHelper::~OScrollWindowHelper()
{
	DBG_DTOR(OScrollWindowHelper,NULL);
	::std::auto_ptr<Window> aTemp(m_pCornerWindow);
	m_pCornerWindow = NULL;
	m_pTableView = NULL;	
}

// -----------------------------------------------------------------------------
void OScrollWindowHelper::setTableView(OJoinTableView* _pTableView)
{
	m_pTableView = _pTableView;
	//////////////////////////////////////////////////////////////////////
	// ScrollBars
	GetHScrollBar()->SetScrollHdl( LINK(m_pTableView, OJoinTableView, ScrollHdl) );
	GetVScrollBar()->SetScrollHdl( LINK(m_pTableView, OJoinTableView, ScrollHdl) );
}
// -----------------------------------------------------------------------------
void OScrollWindowHelper::resetRange(const Point& _aSize)
{
    Point aPos = PixelToLogic(_aSize);
    GetHScrollBar()->SetRange( Range(0, aPos.X() + TABWIN_SPACING_X) );
	GetVScrollBar()->SetRange( Range(0, aPos.Y() + TABWIN_SPACING_Y) );
}
//------------------------------------------------------------------------------
void OScrollWindowHelper::Resize()
{
	Window::Resize();

	Size aTotalOutputSize = GetOutputSizePixel();
	long nHScrollHeight = GetHScrollBar()->GetSizePixel().Height();
	long nVScrollWidth = GetVScrollBar()->GetSizePixel().Width();

	GetHScrollBar()->SetPosSizePixel(
		Point( 0, aTotalOutputSize.Height()-nHScrollHeight ),
		Size( aTotalOutputSize.Width()-nVScrollWidth, nHScrollHeight )
		);

	GetVScrollBar()->SetPosSizePixel(
		Point( aTotalOutputSize.Width()-nVScrollWidth, 0 ),
		Size( nVScrollWidth, aTotalOutputSize.Height()-nHScrollHeight )
		);

	m_pCornerWindow->SetPosSizePixel(
		Point( aTotalOutputSize.Width() - nVScrollWidth, aTotalOutputSize.Height() - nHScrollHeight),
		Size( nVScrollWidth, nHScrollHeight )
		);

	GetHScrollBar()->SetPageSize( aTotalOutputSize.Width() );
	GetHScrollBar()->SetVisibleSize( aTotalOutputSize.Width() );

	GetVScrollBar()->SetPageSize( aTotalOutputSize.Height() );
	GetVScrollBar()->SetVisibleSize( aTotalOutputSize.Height() );

	// adjust the ranges of the scrollbars if neccessary
	long lRange = GetHScrollBar()->GetRange().Max() - GetHScrollBar()->GetRange().Min();
	if (m_pTableView->GetScrollOffset().X() + aTotalOutputSize.Width() > lRange)
		GetHScrollBar()->SetRangeMax(m_pTableView->GetScrollOffset().X() + aTotalOutputSize.Width() + GetHScrollBar()->GetRange().Min());

	lRange = GetVScrollBar()->GetRange().Max() - GetVScrollBar()->GetRange().Min();
	if (m_pTableView->GetScrollOffset().Y() + aTotalOutputSize.Height() > lRange)
		GetVScrollBar()->SetRangeMax(m_pTableView->GetScrollOffset().Y() + aTotalOutputSize.Height() + GetVScrollBar()->GetRange().Min());

	m_pTableView->SetPosSizePixel(Point( 0, 0 ),Size( aTotalOutputSize.Width()-nVScrollWidth, aTotalOutputSize.Height()-nHScrollHeight ));
}
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
//==================================================================
// class OJoinTableView
//==================================================================

//const long WINDOW_WIDTH = 1000;
//const long WINDOW_HEIGHT = 1000;
DBG_NAME(OJoinTableView);
//------------------------------------------------------------------------------
OJoinTableView::OJoinTableView( Window* pParent, OJoinDesignView* pView )
    :Window( pParent,WB_BORDER )
	,DropTargetHelper(this)
    ,m_aDragOffset( Point(0,0) )
    ,m_aScrollOffset( Point(0,0) )
    ,m_pDragWin( NULL )
	,m_pSizingWin( NULL )
	,m_pSelectedConn( NULL )
	,m_bTrackingInitiallyMoved(sal_False)
    ,m_pLastFocusTabWin(NULL)
	,m_pView( pView )
	,m_pAccessible(NULL)
{
	DBG_CTOR(OJoinTableView,NULL);
	SetSizePixel( Size(1000, 1000) );

	InitColors();

	m_aDragScrollTimer.SetTimeoutHdl(LINK(this, OJoinTableView, OnDragScrollTimer));
}

//------------------------------------------------------------------------------
OJoinTableView::~OJoinTableView()
{
    DBG_DTOR(OJoinTableView,NULL);
    if( m_pAccessible )
    {
        m_pAccessible->clearTableView();
        m_pAccessible = NULL;
    }
    //////////////////////////////////////////////////////////////////////
    // Listen loeschen
    clearLayoutInformation();	
}
//------------------------------------------------------------------------------
IMPL_LINK( OJoinTableView, ScrollHdl, ScrollBar*, pScrollBar )
{
	//////////////////////////////////////////////////////////////////////
	// Alle Fenster verschieben
	ScrollPane( pScrollBar->GetDelta(), (pScrollBar == GetHScrollBar()), sal_False );

	return 0;
}
//------------------------------------------------------------------------------
void OJoinTableView::Resize()
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	Window::Resize();
	m_aOutputSize = GetSizePixel();

	// tab win positions may not be up-to-date
	if (m_aTableMap.empty())
		// no tab wins ...
		return;

	// we have at least one table so resize it
	m_aScrollOffset.X() = GetHScrollBar()->GetThumbPos();
	m_aScrollOffset.Y() = GetVScrollBar()->GetThumbPos();

	OTableWindow* pCheck = m_aTableMap.begin()->second;
	Point aRealPos = pCheck->GetPosPixel();
	Point aAssumedPos = pCheck->GetData()->GetPosition() - GetScrollOffset();

	if (aRealPos == aAssumedPos)
		// all ok
		return;

	OTableWindowMapIterator aIter = m_aTableMap.begin();
    OTableWindowMapIterator aEnd = m_aTableMap.end();
	for(;aIter != aEnd;++aIter)
	{
		OTableWindow* pCurrent = aIter->second;
		Point aPos(pCurrent->GetData()->GetPosition() - GetScrollOffset());
		pCurrent->SetPosPixel(aPos);
	}
}
//------------------------------------------------------------------------------
sal_uLong OJoinTableView::GetTabWinCount()
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	return m_aTableMap.size();
}

//------------------------------------------------------------------------------
bool OJoinTableView::RemoveConnection( OTableConnection* _pConn,sal_Bool _bDelete )
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	DeselectConn(_pConn);

	// to force a redraw
	_pConn->InvalidateConnection();

	m_pView->getController().removeConnectionData( _pConn->GetData() );

	m_vTableConnection.erase(
						::std::find(m_vTableConnection.begin(),m_vTableConnection.end(),_pConn) );

	modified();
	if ( m_pAccessible )
		m_pAccessible->notifyAccessibleEvent(	AccessibleEventId::CHILD,
												makeAny(_pConn->GetAccessible()),
												Any());
	if ( _bDelete )
	{
		delete _pConn;
	}

	return true;
}

//------------------------------------------------------------------------
OTableWindow* OJoinTableView::GetTabWindow( const String& rName )
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	OTableWindowMapIterator aIter = m_aTableMap.find(rName);

	return aIter == m_aTableMap.end() ? NULL : aIter->second;
}
// -----------------------------------------------------------------------------
TTableWindowData::value_type OJoinTableView::createTableWindowData(const ::rtl::OUString& _rComposedName
                                                                  ,const ::rtl::OUString& _sTableName
											                      ,const ::rtl::OUString& _rWinName)
{
    TTableWindowData::value_type pData( CreateImpl(_rComposedName, _sTableName,_rWinName) );
    OJoinDesignView* pParent = getDesignView();
    try
    {
        if ( !pData->init(pParent->getController().getConnection(),allowQueries()) )
        {
            if ( pData->isValid() )
                onNoColumns_throw();
            else
                pData.reset();
        }
    }
	catch ( const SQLException& )
	{
        ::dbaui::showError( ::dbtools::SQLExceptionInfo( ::cppu::getCaughtException() ),
            pParent, pParent->getController().getORB() );
	}
	catch( const WrappedTargetException& e )
	{
		SQLException aSql;
		if ( e.TargetException >>= aSql )
			::dbaui::showError( ::dbtools::SQLExceptionInfo( aSql ), pParent, pParent->getController().getORB() );
	}
    catch( const Exception& )
    {
    	DBG_UNHANDLED_EXCEPTION();
    }
    return pData;
}
// -----------------------------------------------------------------------------
OTableWindowData* OJoinTableView::CreateImpl(const ::rtl::OUString& _rComposedName
                                             ,const ::rtl::OUString& _sTableName
											 ,const ::rtl::OUString& _rWinName)
{
	return new OTableWindowData( NULL,_rComposedName,_sTableName, _rWinName );
}
//------------------------------------------------------------------------------
void OJoinTableView::AddTabWin(const ::rtl::OUString& _rComposedName, const ::rtl::OUString& rWinName, sal_Bool /*bNewTable*/)
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	OSL_ENSURE(_rComposedName.getLength(),"There must be a table name supplied!");

	TTableWindowData::value_type pNewTabWinData(createTableWindowData( _rComposedName, rWinName,rWinName ));

	//////////////////////////////////////////////////////////////////
	// Neues Fenster in Fensterliste eintragen
	OTableWindow* pNewTabWin = createWindow( pNewTabWinData );
	if ( pNewTabWin->Init() )
	{
		m_pView->getController().getTableWindowData()->push_back( pNewTabWinData);
		// when we already have a table with this name insert the full qualified one instead
		if(m_aTableMap.find(rWinName) != m_aTableMap.end())
			m_aTableMap[_rComposedName] = pNewTabWin;
		else
			m_aTableMap[rWinName] = pNewTabWin;

		SetDefaultTabWinPosSize( pNewTabWin );
		pNewTabWin->Show();

		modified();
		if ( m_pAccessible )
			m_pAccessible->notifyAccessibleEvent(	AccessibleEventId::CHILD,
													Any(),
													makeAny(pNewTabWin->GetAccessible()));
	}
	else
	{
		pNewTabWin->clearListBox();
		delete pNewTabWin;
	}
}

//------------------------------------------------------------------------------
void OJoinTableView::RemoveTabWin( OTableWindow* pTabWin )
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	//////////////////////////////////////////////////////////////////////
	// first delete all connections of this window to others
    bool bRemove = true;
    TTableWindowData::value_type pData = pTabWin->GetData();
	sal_Int32 nCount = m_vTableConnection.size();
	::std::vector<OTableConnection*>::reverse_iterator aIter = m_vTableConnection.rbegin();
	while(aIter != m_vTableConnection.rend() && bRemove)
	{
		OTableConnection* pTabConn = (*aIter);
		if(
			( pData == pTabConn->GetData()->getReferencingTable())		||
			( pData == pTabConn->GetData()->getReferencedTable())
        )
        {
		  bRemove = RemoveConnection( pTabConn ,sal_True);
          aIter = m_vTableConnection.rbegin();
        }
        else
            ++aIter;
	}

	//////////////////////////////////////////////////////////////////////
	// then delete the window itself
    if ( bRemove )
    {
	    if ( m_pAccessible )
		    m_pAccessible->notifyAccessibleEvent(	AccessibleEventId::CHILD,
												    makeAny(pTabWin->GetAccessible()),Any()
												    );

	    pTabWin->Hide();
        OJoinController& rController = m_pView->getController();
	    TTableWindowData::iterator aFind = ::std::find(rController.getTableWindowData()->begin(),rController.getTableWindowData()->end(),pData);
	    if(aFind != rController.getTableWindowData()->end())
	    {
		    rController.getTableWindowData()->erase(aFind);
		    rController.setModified(sal_True);
	    }

        String aWinName = pTabWin->GetWinName();
	    if(m_aTableMap.find(aWinName) != m_aTableMap.end())
		    m_aTableMap.erase( aWinName );
	    else
		    m_aTableMap.erase( pTabWin->GetComposedName() );

	    if (pTabWin == m_pLastFocusTabWin)
		    m_pLastFocusTabWin = NULL;

	    pTabWin->clearListBox();
	    delete pTabWin;

    }
	if ( (sal_Int32)m_vTableConnection.size() < (nCount-1) ) // if some connections could be removed
		modified();
}
namespace
{
	// -----------------------------------------------------------------------------
	sal_Bool isScrollAllowed( OJoinTableView* _pView,long nDelta, sal_Bool bHoriz)
	{
		sal_Bool bRet = sal_True;
		//////////////////////////////////////////////////////////////////////
		// adjust ScrollBar-Positions
		ScrollBar* pBar = _pView->GetVScrollBar();
		if( bHoriz )
			pBar = _pView->GetHScrollBar();

		long nOldThumbPos = pBar->GetThumbPos();
		long nNewThumbPos = nOldThumbPos + nDelta;
		if( nNewThumbPos < 0 )
			nNewThumbPos = 0;//	bRet = sal_False;
		else if( nNewThumbPos > pBar->GetRangeMax() )
			nNewThumbPos = pBar->GetRangeMax();// bRet = sal_False;

		if ( bHoriz )
		{
			if( nNewThumbPos == _pView->GetScrollOffset().X() )
				return sal_False;
		}
		else if ( nNewThumbPos == _pView->GetScrollOffset().Y() )
			return sal_False;

		return bRet;
	}
	// -----------------------------------------------------------------------------
	sal_Bool getMovementImpl(OJoinTableView* _pView,const Point& _rPoint,const Size& _rSize,long& _nScrollX,long& _nScrollY)
	{
		_nScrollY = _nScrollX = 0;
		// data about the tab win
		Point aUpperLeft = _rPoint;
		// normalize with respect to visibility
		aUpperLeft -= _pView->GetScrollOffset();
		//	aUpperLeft.Y() -= _pView->GetScrollOffset().Y();
		Point aLowerRight(aUpperLeft.X() + _rSize.Width(), aUpperLeft.Y() + _rSize.Height());

		// data about ourself
		Size aSize = _pView->getRealOutputSize(); //GetOutputSizePixel();

		sal_Bool bVisbile = sal_True;
		sal_Bool bFitsHor = (aUpperLeft.X() >= 0) && (aLowerRight.X() <= aSize.Width());
		sal_Bool bFitsVert= (aUpperLeft.Y() >= 0) && (aLowerRight.Y() <= aSize.Height());
		if (!bFitsHor || !bFitsVert)
		{
			// #100386# OJ
			if (!bFitsHor)
			{
				// ensure the visibility of the right border
				if ( aLowerRight.X() > aSize.Width() )
					_nScrollX = aLowerRight.X() - aSize.Width() + TABWIN_SPACING_X;

				// ensure the visibility of the left border (higher priority)
				//	if ( (aUpperLeft.X() - _nScrollX) < 0 )
				if ( aUpperLeft.X() < 0 )
					_nScrollX = aUpperLeft.X() - TABWIN_SPACING_X;
			}

			if (!bFitsVert)
			{
				// lower border
				if ( aLowerRight.Y() > aSize.Height() )
					_nScrollY = aLowerRight.Y() - aSize.Height() + TABWIN_SPACING_Y;
				// upper border
				//	if ( (aUpperLeft.Y() - _nScrollY) < 0 )
				if ( aUpperLeft.Y() < 0 )
					_nScrollY = aUpperLeft.Y() - TABWIN_SPACING_Y;
			}

			if ( _nScrollX ) // aSize.Width() > _rSize.Width() &&
				bVisbile = isScrollAllowed(_pView,_nScrollX, sal_True);

			if ( _nScrollY ) // aSize.Height() > _rSize.Height() &&
				bVisbile = bVisbile && isScrollAllowed(_pView,_nScrollY, sal_False);

			if ( bVisbile )
			{
				sal_Int32 nHRangeMax = _pView->GetHScrollBar()->GetRangeMax();
				sal_Int32 nVRangeMax = _pView->GetVScrollBar()->GetRangeMax();

				if ( aSize.Width() + _pView->GetHScrollBar()->GetThumbPos() + _nScrollX > nHRangeMax )
					bVisbile = sal_False;
				if ( bVisbile && aSize.Height() + _pView->GetVScrollBar()->GetThumbPos() + _nScrollY > nVRangeMax )
					bVisbile = sal_False;
			}
		}


		return bVisbile;
	}
} // end of ano namespace
// -----------------------------------------------------------------------------
sal_Bool OJoinTableView::isMovementAllowed(const Point& _rPoint,const Size& _rSize)
{
	long nX,nY;
	return getMovementImpl(this,_rPoint,_rSize,nX,nY);
}
//------------------------------------------------------------------------------
void OJoinTableView::EnsureVisible(const OTableWindow* _pWin)
{
	// data about the tab win
	TTableWindowData::value_type pData = _pWin->GetData();
	//	Point aUpperLeft = pData->GetPosition();
	EnsureVisible( pData->GetPosition() , pData->GetSize());
	Invalidate(INVALIDATE_NOCHILDREN);
}
//------------------------------------------------------------------------------
void OJoinTableView::EnsureVisible(const Point& _rPoint,const Size& _rSize)
{
	long nScrollX,nScrollY;

	if ( getMovementImpl(this,_rPoint,_rSize,nScrollX,nScrollY) )
	{
		sal_Bool bVisbile = sal_True;
		if (nScrollX)
			bVisbile = ScrollPane(nScrollX, sal_True, sal_True);

		if (nScrollY)
			bVisbile = bVisbile && ScrollPane(nScrollY, sal_False, sal_True);
	}
}

//------------------------------------------------------------------------------
void OJoinTableView::SetDefaultTabWinPosSize( OTableWindow* pTabWin )
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	//////////////////////////////////////////////////////////////////
	// Position bestimmen:
	// Das Fenster wird in Zeilen der Hoehe TABWIN_SPACING_Y+TABWIN_HEIGTH_STD aufgeteilt.
	// Dann wird fuer jede Zeile geprueft, ob noch Platz fuer ein weiteres Fenster ist.
	// Wenn kein Platz ist, wird die naechste Zeile ueberprueft.
	Size aOutSize = GetSizePixel();
	Point aNewPos( 0,0 );
	sal_uInt16 nRow = 0;
	sal_Bool bEnd = sal_False;
	while( !bEnd )
	{
		//////////////////////////////////////////////////////////////////
		// Neue Position auf Zeilenbeginn setzen
		aNewPos.X() = TABWIN_SPACING_X;
		aNewPos.Y() = (nRow+1) * TABWIN_SPACING_Y;

		//////////////////////////////////////////////////////////////////
		// Rectangle fuer die jeweilige Zeile bestimmen
		Rectangle aRowRect( Point(0,0), aOutSize );
		aRowRect.Top() = nRow * ( TABWIN_SPACING_Y + TABWIN_HEIGHT_STD );
		aRowRect.Bottom() = (nRow+1) * ( TABWIN_SPACING_Y + TABWIN_HEIGHT_STD );

		//////////////////////////////////////////////////////////////////
		// Belegte Bereiche dieser Zeile pruefen
		OTableWindow* pOtherTabWin;// = GetTabWinMap()->First();
		OTableWindowMapIterator aIter = m_aTableMap.begin();
		OTableWindowMapIterator aEnd = m_aTableMap.end();
	    for(;aIter != aEnd;++aIter)
		{
			pOtherTabWin = aIter->second;
			Rectangle aOtherTabWinRect( pOtherTabWin->GetPosPixel(), pOtherTabWin->GetSizePixel() );

			if(
				( (aOtherTabWinRect.Top()>aRowRect.Top()) && (aOtherTabWinRect.Top()<aRowRect.Bottom()) ) ||
				( (aOtherTabWinRect.Bottom()>aRowRect.Top()) && (aOtherTabWinRect.Bottom()<aRowRect.Bottom()) )
			  )
			{
				//////////////////////////////////////////////////////////////////
				// TabWin liegt in der Zeile
				if( aOtherTabWinRect.Right()>aNewPos.X() )
					aNewPos.X() = aOtherTabWinRect.Right() + TABWIN_SPACING_X;
			}
		}

		//////////////////////////////////////////////////////////////////
		// Ist in dieser Zeile noch Platz?
		if( (aNewPos.X()+TABWIN_WIDTH_STD)<aRowRect.Right() )
		{
			aNewPos.Y() = aRowRect.Top() + TABWIN_SPACING_Y;
			bEnd = sal_True;
		}
		else
		{
			if( (aRowRect.Bottom()+aRowRect.GetHeight()) > aOutSize.Height() )
			{
				// insert it in the first row
				sal_Int32 nCount = m_aTableMap.size() % (nRow+1);
				++nCount;
				aNewPos.Y() = nCount * TABWIN_SPACING_Y + (nCount-1)*CalcZoom(TABWIN_HEIGHT_STD);
				bEnd = sal_True;
			}
			else
				nRow++;

		}
	}

	//////////////////////////////////////////////////////////////////
	// Groesse bestimmen
	Size aNewSize( CalcZoom(TABWIN_WIDTH_STD), CalcZoom(TABWIN_HEIGHT_STD) );

	// check if the new position in inside the scrollbars ranges
	Point aBottom(aNewPos);
	aBottom.X() += aNewSize.Width();
	aBottom.Y() += aNewSize.Height();

	if(!GetHScrollBar()->GetRange().IsInside(aBottom.X()))
		GetHScrollBar()->SetRange( Range(0, aBottom.X()) );
	if(!GetVScrollBar()->GetRange().IsInside(aBottom.Y()))
		GetVScrollBar()->SetRange( Range(0, aBottom.Y()) );

	pTabWin->SetPosSizePixel( aNewPos, aNewSize );
}

//------------------------------------------------------------------------------
void OJoinTableView::DataChanged(const DataChangedEvent& rDCEvt)
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	if (rDCEvt.GetType() == DATACHANGED_SETTINGS)
	{
		// nehmen wir den worst-case an : die Farben haben sich geaendert, also
		// mich anpassen
		InitColors();
		Invalidate(INVALIDATE_NOCHILDREN);
		// durch das Invalidate werden auch die Connections neu gezeichnet, so dass die auch
		// gleich in den neuen Farben dargestellt werden
	}
}

//------------------------------------------------------------------------------
void OJoinTableView::InitColors()
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	// die Farben fuer die Darstellung sollten die Systemfarben sein
	StyleSettings aSystemStyle = Application::GetSettings().GetStyleSettings();
	SetBackground(Wallpaper(Color(aSystemStyle.GetDialogColor())));
}

//------------------------------------------------------------------------------
void OJoinTableView::BeginChildMove( OTableWindow* pTabWin, const Point& rMousePos  )
{
	DBG_CHKTHIS(OJoinTableView,NULL);

	if (m_pView->getController().isReadOnly())
		return;

	m_pDragWin = pTabWin;
	SetPointer(Pointer(POINTER_MOVE));
	Point aMousePos = ScreenToOutputPixel( rMousePos );
	m_aDragOffset = aMousePos - pTabWin->GetPosPixel();
	m_pDragWin->SetZOrder(NULL, WINDOW_ZORDER_FIRST);
	m_bTrackingInitiallyMoved = sal_False;
	StartTracking();
}

void OJoinTableView::NotifyTitleClicked( OTableWindow* pTabWin, const Point rMousePos )
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	DeselectConn(GetSelectedConn());
	BeginChildMove(pTabWin, rMousePos);
}

//------------------------------------------------------------------------------
void OJoinTableView::BeginChildSizing( OTableWindow* pTabWin, const Pointer& rPointer )
{
	DBG_CHKTHIS(OJoinTableView,NULL);

	if (m_pView->getController().isReadOnly())
		return;

	SetPointer( rPointer );
	m_pSizingWin = pTabWin;
	StartTracking();
}

//------------------------------------------------------------------------------
sal_Bool OJoinTableView::ScrollPane( long nDelta, sal_Bool bHoriz, sal_Bool bPaintScrollBars )
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	sal_Bool bRet = sal_True;

	//////////////////////////////////////////////////////////////////////
	// ScrollBar-Positionen anpassen
	if( bPaintScrollBars )
	{
		if( bHoriz )
		{
			long nOldThumbPos = GetHScrollBar()->GetThumbPos();
			long nNewThumbPos = nOldThumbPos + nDelta;
			if( nNewThumbPos < 0 )
			{
				nNewThumbPos = 0;
				bRet = sal_False;
			}
			if( nNewThumbPos > GetHScrollBar()->GetRange().Max() )
			{
				nNewThumbPos = GetHScrollBar()->GetRange().Max();
				bRet = sal_False;
			}
			GetHScrollBar()->SetThumbPos( nNewThumbPos );
			nDelta = GetHScrollBar()->GetThumbPos() - nOldThumbPos;
		}
		else
		{
			long nOldThumbPos = GetVScrollBar()->GetThumbPos();
			long nNewThumbPos = nOldThumbPos+nDelta;
			if( nNewThumbPos < 0 )
			{
				nNewThumbPos = 0;
				bRet = sal_False;
			}
			if( nNewThumbPos > GetVScrollBar()->GetRange().Max() )
			{
				nNewThumbPos = GetVScrollBar()->GetRange().Max();
				bRet = sal_False;
			}
			GetVScrollBar()->SetThumbPos( nNewThumbPos );
			nDelta = GetVScrollBar()->GetThumbPos() - nOldThumbPos;
		}
	}

	//////////////////////////////////////////////////////////////////////
	// Wenn ScrollOffset bereits an den Grenzen liegt, kein Neuzeichnen
	if( (GetHScrollBar()->GetThumbPos()==m_aScrollOffset.X()) &&
		(GetVScrollBar()->GetThumbPos()==m_aScrollOffset.Y()) )
		return sal_False;

	//////////////////////////////////////////////////////////////////////
	// ScrollOffset neu setzen
	if (bHoriz)
		m_aScrollOffset.X() = GetHScrollBar()->GetThumbPos();
	else
		m_aScrollOffset.Y() = GetVScrollBar()->GetThumbPos();

	//////////////////////////////////////////////////////////////////////
	// Alle Fenster verschieben
	OTableWindow* pTabWin;
	Point aPos;

	OTableWindowMapIterator aIter = m_aTableMap.begin();
	OTableWindowMapIterator aEnd = m_aTableMap.end();
	for(;aIter != aEnd;++aIter)
	{
		pTabWin = aIter->second;
		aPos = pTabWin->GetPosPixel();

		if( bHoriz )
			aPos.X() -= nDelta;
		else aPos.Y() -= nDelta;

		pTabWin->SetPosPixel( aPos );
	}

	Invalidate(); // INVALIDATE_NOCHILDREN

	return bRet;
}

//------------------------------------------------------------------------------
void OJoinTableView::Tracking( const TrackingEvent& rTEvt )
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	HideTracking();

	if (rTEvt.IsTrackingEnded())
	{
		if( m_pDragWin )
		{
			if (m_aDragScrollTimer.IsActive())
				m_aDragScrollTimer.Stop();

			//////////////////////////////////////////////////////////////////////
			// Position des Childs nach Verschieben anpassen
			//////////////////////////////////////////////////////////////////////
			// Fenster duerfen nicht aus Anzeigebereich herausbewegt werden
			Point aDragWinPos = rTEvt.GetMouseEvent().GetPosPixel() - m_aDragOffset;
			Size aDragWinSize = m_pDragWin->GetSizePixel();
			if( aDragWinPos.X() < 0 )
				aDragWinPos.X() = 0;
			if( aDragWinPos.Y() < 0 )
				aDragWinPos.Y() = 0;
			if( (aDragWinPos.X() + aDragWinSize.Width()) > m_aOutputSize.Width() )
				aDragWinPos.X() = m_aOutputSize.Width() - aDragWinSize.Width() - 1;
			if( (aDragWinPos.Y() + aDragWinSize.Height()) > m_aOutputSize.Height() )
				aDragWinPos.Y() = m_aOutputSize.Height() - aDragWinSize.Height() - 1;
			if( aDragWinPos.X() < 0 )
				aDragWinPos.X() = 0;
			if( aDragWinPos.Y() < 0 )
				aDragWinPos.Y() = 0;
			// TODO : nicht das Fenster neu positionieren, wenn es uebersteht, sondern einfach meinen Bereich erweitern


			//////////////////////////////////////////////////////////////////////
			// Fenster positionieren
			EndTracking();
			m_pDragWin->SetZOrder(NULL, WINDOW_ZORDER_FIRST);
			// erst mal testen, ob ich mich ueberhaupt bewegt habe
			// (das verhindert das Setzen des modified-Flags, wenn sich eigentlich gar nichts getan hat)
			TTableWindowData::value_type pData = m_pDragWin->GetData();
			if ( ! (pData && pData->HasPosition() && (pData->GetPosition() == aDragWinPos)))
			{
				// die alten logischen Koordinaten
				Point ptOldPos = m_pDragWin->GetPosPixel() + Point(GetHScrollBar()->GetThumbPos(), GetVScrollBar()->GetThumbPos());
				// neu positionieren
				m_pDragWin->SetPosPixel(aDragWinPos);
				TabWinMoved(m_pDragWin, ptOldPos);

				m_pDragWin->GrabFocus();
			}
			m_pDragWin = NULL;
			SetPointer(Pointer(POINTER_ARROW));
		}
		// else we handle the resizing
		else if( m_pSizingWin )
		{
			SetPointer( Pointer() );
			EndTracking();

			// die alten physikalischen Koordinaten

			Size szOld = m_pSizingWin->GetSizePixel();
			Point ptOld = m_pSizingWin->GetPosPixel();
			Size aNewSize(CalcZoom(m_aSizingRect.GetSize().Width()),CalcZoom(m_aSizingRect.GetSize().Height()));
			m_pSizingWin->SetPosSizePixel( m_aSizingRect.TopLeft(), aNewSize );
			TabWinSized(m_pSizingWin, ptOld, szOld);

			m_pSizingWin->Invalidate( m_aSizingRect );
			m_pSizingWin = NULL;
		}
	}
	else if (rTEvt.IsTrackingCanceled())
	{
		if (m_aDragScrollTimer.IsActive())
			m_aDragScrollTimer.Stop();
		EndTracking();
	}
	else
	{
		if( m_pDragWin )
		{
			m_ptPrevDraggingPos = rTEvt.GetMouseEvent().GetPosPixel();
			// an Fenstergrenzen scrollen
			ScrollWhileDragging();
		}

		if( m_pSizingWin )
		{
			Point aMousePos = rTEvt.GetMouseEvent().GetPosPixel();
			m_aSizingRect = m_pSizingWin->getSizingRect(aMousePos,m_aOutputSize);
			Update();
			ShowTracking( m_aSizingRect, SHOWTRACK_SMALL | SHOWTRACK_WINDOW );
		}
	}
}

//------------------------------------------------------------------------------
void OJoinTableView::ConnDoubleClicked( OTableConnection* /*pConnection*/ )
{
	DBG_CHKTHIS(OJoinTableView,NULL);
}

//------------------------------------------------------------------------------
void OJoinTableView::MouseButtonDown( const MouseEvent& rEvt )
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	GrabFocus();
	Window::MouseButtonDown(rEvt);
}

//------------------------------------------------------------------------------
void OJoinTableView::MouseButtonUp( const MouseEvent& rEvt )
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	Window::MouseButtonUp(rEvt);
	//////////////////////////////////////////////////////////////////////
	// Wurde eine Connection ausgewaehlt?
	if( !m_vTableConnection.empty() )
	{
		DeselectConn(GetSelectedConn());

		::std::vector<OTableConnection*>::iterator aIter = m_vTableConnection.begin();
        ::std::vector<OTableConnection*>::iterator aEnd = m_vTableConnection.end();
		for(;aIter != aEnd;++aIter)
		{
			if( (*aIter)->CheckHit(rEvt.GetPosPixel()) )
			{
				SelectConn((*aIter));

				// Doppelclick
				if( rEvt.GetClicks() == 2 )
					ConnDoubleClicked( (*aIter) );

				break;
			}
		}
	}
}

//------------------------------------------------------------------------------
void OJoinTableView::KeyInput( const KeyEvent& rEvt )
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	sal_uInt16 nCode = rEvt.GetKeyCode().GetCode();
	sal_Bool   bShift = rEvt.GetKeyCode().IsShift();
	sal_Bool   bCtrl = rEvt.GetKeyCode().IsMod1();

	if( !bCtrl && !bShift && (nCode==KEY_DELETE) )
	{
		if (GetSelectedConn())
			RemoveConnection( GetSelectedConn() ,sal_True);
	}
	else
		Window::KeyInput( rEvt );
}

//------------------------------------------------------------------------------
void OJoinTableView::DeselectConn(OTableConnection* pConn)
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	if (!pConn || !pConn->IsSelected())
		return;

	// die zugehoerigen Eitnraege in der ListBox des Tabellenfenster deselektieren
	OTableWindow* pWin = pConn->GetSourceWin();
	if (pWin && pWin->GetListBox())
		pWin->GetListBox()->SelectAll(sal_False);

	pWin = pConn->GetDestWin();
	if (pWin && pWin->GetListBox())
		pWin->GetListBox()->SelectAll(sal_False);

	pConn->Deselect();
	m_pSelectedConn = NULL;
}

//------------------------------------------------------------------------------
void OJoinTableView::SelectConn(OTableConnection* pConn)
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	DeselectConn(GetSelectedConn());

	pConn->Select();
	m_pSelectedConn = pConn;
	GrabFocus(); // has to be called here because a table window may still be focused

	// die betroffenene Eintraege in den Windows selektieren
	OTableWindow* pConnSource = pConn->GetSourceWin();
	OTableWindow* pConnDest = pConn->GetDestWin();
	if (pConnSource && pConnDest)
	{
		OTableWindowListBox* pSourceBox = pConnSource->GetListBox();
		OTableWindowListBox* pDestBox = pConnDest->GetListBox();
		if (pSourceBox && pDestBox)
		{
			pSourceBox->SelectAll(sal_False);
			pDestBox->SelectAll(sal_False);

			SvLBoxEntry* pFirstSourceVisible = pSourceBox->GetFirstEntryInView();
			SvLBoxEntry* pFirstDestVisible = pDestBox->GetFirstEntryInView();

			const ::std::vector<OConnectionLine*>* pLines = pConn->GetConnLineList();
			::std::vector<OConnectionLine*>::const_reverse_iterator aIter = pLines->rbegin();
			for(;aIter != pLines->rend();++aIter)
			{
				if ((*aIter)->IsValid())
				{
					SvLBoxEntry* pSourceEntry = pSourceBox->GetEntryFromText((*aIter)->GetData()->GetSourceFieldName());
					if (pSourceEntry)
					{
						pSourceBox->Select(pSourceEntry, sal_True);
						pSourceBox->MakeVisible(pSourceEntry);
					}

					SvLBoxEntry* pDestEntry = pDestBox->GetEntryFromText((*aIter)->GetData()->GetDestFieldName());
					if (pDestEntry)
					{
						pDestBox->Select(pDestEntry, sal_True);
						pDestBox->MakeVisible(pDestEntry);
					}

				}
			}

			if ((pFirstSourceVisible != pSourceBox->GetFirstEntryInView())
				|| (pFirstDestVisible != pDestBox->GetFirstEntryInView()))
				// es wurde gescrollt -> neu zeichnen
				Invalidate(INVALIDATE_NOCHILDREN);
		}
	}
}
//------------------------------------------------------------------------------
void OJoinTableView::Paint( const Rectangle& rRect )
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	DrawConnections( rRect );
}

//------------------------------------------------------------------------------
void OJoinTableView::InvalidateConnections()
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	//////////////////////////////////////////////////////////////////////
	// Die Joins zeichnen
	::std::for_each(m_vTableConnection.begin(),m_vTableConnection.end(),
		::std::mem_fun(& OTableConnection::InvalidateConnection));
}

//------------------------------------------------------------------------------
void OJoinTableView::DrawConnections( const Rectangle& rRect )
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	//////////////////////////////////////////////////////////////////////
	// Die Joins zeichnen
    ::std::for_each(m_vTableConnection.begin(),m_vTableConnection.end(),boost::bind( &OTableConnection::Draw, _1, boost::cref( rRect )));
	// zum Schluss noch mal die selektierte ueber alle anderen drueber
	if (GetSelectedConn())
		GetSelectedConn()->Draw( rRect );
}


//------------------------------------------------------------------------------
::std::vector<OTableConnection*>::const_iterator OJoinTableView::getTableConnections(const OTableWindow* _pFromWin) const
{
	return ::std::find_if(	m_vTableConnection.begin(),
							m_vTableConnection.end(),
							::std::bind2nd(::std::mem_fun(&OTableConnection::isTableConnection),_pFromWin));
}
// -----------------------------------------------------------------------------
sal_Int32 OJoinTableView::getConnectionCount(const OTableWindow* _pFromWin) const
{
	return ::std::count_if(	m_vTableConnection.begin(),
							m_vTableConnection.end(),
							::std::bind2nd(::std::mem_fun(&OTableConnection::isTableConnection),_pFromWin));
}
//------------------------------------------------------------------------------
sal_Bool OJoinTableView::ExistsAConn(const OTableWindow* pFrom) const
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	return getTableConnections(pFrom) != m_vTableConnection.end();
}
//------------------------------------------------------------------------
void OJoinTableView::ClearAll()
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	SetUpdateMode(sal_False);

	HideTabWins();

	// und das selbe mit den Connections
	::std::vector<OTableConnection*>::iterator aIter = m_vTableConnection.begin();
    ::std::vector<OTableConnection*>::iterator aEnd = m_vTableConnection.end();
	for(;aIter != aEnd;++aIter)
		RemoveConnection( *aIter ,sal_True);
	m_vTableConnection.clear();

	m_pLastFocusTabWin	= NULL;
	m_pSelectedConn		= NULL;

	// scroll to the upper left
	ScrollPane(-GetScrollOffset().X(), sal_True, sal_True);
	ScrollPane(-GetScrollOffset().Y(), sal_False, sal_True);
	Invalidate();
}

//------------------------------------------------------------------------
sal_Bool OJoinTableView::ScrollWhileDragging()
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	DBG_ASSERT(m_pDragWin != NULL, "OJoinTableView::ScrollWhileDragging darf nur waehrend Dragging eines Fensters aufgerufen werden !");

	// den Timer schon mal killen
	if (m_aDragScrollTimer.IsActive())
		m_aDragScrollTimer.Stop();

	Point aDragWinPos = m_ptPrevDraggingPos - m_aDragOffset;
	Size aDragWinSize = m_pDragWin->GetSizePixel();
	Point aLowerRight(aDragWinPos.X() + aDragWinSize.Width(), aDragWinPos.Y() + aDragWinSize.Height());

	if (!m_bTrackingInitiallyMoved && (aDragWinPos == m_pDragWin->GetPosPixel()))
		return sal_True;

	// Darstellungsfehler vermeiden (wenn bei aktivem TrackingRect gescrollt wird)
	HideTracking();

	sal_Bool bScrolling = sal_False;
	sal_Bool bNeedScrollTimer = sal_False;

	// An Fenstergrenzen scrollen
	// TODO : nur dann abfangen, wenn das Fenster komplett verschwinden wuerde (nicht, solange noch ein Pixel sichtbar ist)
	if( aDragWinPos.X() < 5 )
	{
		bScrolling = ScrollPane( -LINE_SIZE, sal_True, sal_True );
		if( !bScrolling && (aDragWinPos.X()<0) )
			aDragWinPos.X() = 0;

		// brauche ich weiteres (timergesteuertes) Scrolling ?
		bNeedScrollTimer = bScrolling && (aDragWinPos.X() < 5);
	}

	if( aLowerRight.X() > m_aOutputSize.Width() - 5 )
	{
		bScrolling = ScrollPane( LINE_SIZE, sal_True, sal_True ) ;
		if( !bScrolling && ( aLowerRight.X() > m_aOutputSize.Width() ) )
			aDragWinPos.X() = m_aOutputSize.Width() - aDragWinSize.Width();

		// brauche ich weiteres (timergesteuertes) Scrolling ?
		bNeedScrollTimer = bScrolling && (aLowerRight.X() > m_aOutputSize.Width() - 5);
	}

	if( aDragWinPos.Y() < 5 )
	{
		bScrolling = ScrollPane( -LINE_SIZE, sal_False, sal_True );
		if( !bScrolling && (aDragWinPos.Y()<0) )
			aDragWinPos.Y() = 0;

		bNeedScrollTimer = bScrolling && (aDragWinPos.Y() < 5);
	}

	if( aLowerRight.Y() > m_aOutputSize.Height() - 5 )
	{
		bScrolling = ScrollPane( LINE_SIZE, sal_False, sal_True );
		if( !bScrolling && ( (aDragWinPos.Y() + aDragWinSize.Height()) > m_aOutputSize.Height() ) )
			aDragWinPos.Y() =  m_aOutputSize.Height() - aDragWinSize.Height();

		bNeedScrollTimer = bScrolling && (aLowerRight.Y() > m_aOutputSize.Height() - 5);
	}

	// Timer neu setzen, wenn noch notwendig
	if (bNeedScrollTimer)
	{
		m_aDragScrollTimer.SetTimeout(100);
		m_aDragScrollTimer.Start();
	}

	// das DraggingRect neu zeichnen
	m_aDragRect = Rectangle(m_ptPrevDraggingPos - m_aDragOffset, m_pDragWin->GetSizePixel());
	Update();
	ShowTracking( m_aDragRect, SHOWTRACK_SMALL | SHOWTRACK_WINDOW );

	return bScrolling;
}

//------------------------------------------------------------------------
IMPL_LINK(OJoinTableView, OnDragScrollTimer, void*, EMPTYARG)
{
	ScrollWhileDragging();
	return 0L;
}
// -----------------------------------------------------------------------------
void OJoinTableView::invalidateAndModify(SfxUndoAction *_pAction)
{
	Invalidate(INVALIDATE_NOCHILDREN);
	m_pView->getController().addUndoActionAndInvalidate(_pAction);
}
//------------------------------------------------------------------------
void OJoinTableView::TabWinMoved(OTableWindow* ptWhich, const Point& ptOldPosition)
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	Point ptThumbPos(GetHScrollBar()->GetThumbPos(), GetVScrollBar()->GetThumbPos());
	ptWhich->GetData()->SetPosition(ptWhich->GetPosPixel() + ptThumbPos);

	invalidateAndModify(new OJoinMoveTabWinUndoAct(this, ptOldPosition, ptWhich));
}

//------------------------------------------------------------------------
void OJoinTableView::TabWinSized(OTableWindow* ptWhich, const Point& ptOldPosition, const Size& szOldSize)
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	ptWhich->GetData()->SetSize(ptWhich->GetSizePixel());
	ptWhich->GetData()->SetPosition(ptWhich->GetPosPixel());

	invalidateAndModify(new OJoinSizeTabWinUndoAct(this, ptOldPosition, szOldSize, ptWhich));
}

//------------------------------------------------------------------------------
sal_Bool OJoinTableView::IsAddAllowed()
{
	DBG_CHKTHIS(OJoinTableView,NULL);

	// nicht wenn Db readonly
	if (m_pView->getController().isReadOnly())
		return sal_False;

	try
	{
		Reference< XConnection> xConnection = m_pView->getController().getConnection();
		if(!xConnection.is())
			return sal_False;
		// nicht wenn schon zuviele Tabellen
		Reference < XDatabaseMetaData > xMetaData( xConnection->getMetaData() );

		sal_Int32 nMax = xMetaData.is() ? xMetaData->getMaxTablesInSelect() : 0;
		if (nMax && nMax <= (sal_Int32)m_aTableMap.size())
			return sal_False;
	}
	catch(SQLException&)
	{
		return sal_False;
	}

	// nicht wenn keine Joins moeglich
//	if (!GetDatabase()->IsCapable(SDB_CAP_JOIN) && nMax <= GetTabWinCount())
//		return sal_False;

	return sal_True;
}
// -----------------------------------------------------------------------------
void OJoinTableView::executePopup(const Point& _aPos,OTableConnection* _pSelConnection)
{
	PopupMenu aContextMenu( ModuleRes( RID_MENU_JOINVIEW_CONNECTION ) );
	switch (aContextMenu.Execute(this, _aPos))
	{
		case SID_DELETE:
			RemoveConnection( _pSelConnection ,sal_True);
			break;
		case ID_QUERY_EDIT_JOINCONNECTION:
			ConnDoubleClicked( _pSelConnection ); // is the same as double clicked
			break;
	}
}
//------------------------------------------------------------------------------
void OJoinTableView::Command(const CommandEvent& rEvt)
{
	DBG_CHKTHIS(OJoinTableView,NULL);

	sal_Bool bHandled = sal_False;

	switch (rEvt.GetCommand())
	{
		case COMMAND_CONTEXTMENU:
		{
			if( m_vTableConnection.empty() )
				return;

			OTableConnection* pSelConnection = GetSelectedConn();
			// when it wasn't a mouse event use the selected connection
			if (!rEvt.IsMouseEvent())
			{
				if( pSelConnection )
				{
					const ::std::vector<OConnectionLine*>* pLines = pSelConnection->GetConnLineList();
					::std::vector<OConnectionLine*>::const_iterator aIter = ::std::find_if(pLines->begin(),pLines->end(),::std::mem_fun(&OConnectionLine::IsValid));
					if( aIter != pLines->end() )
						executePopup((*aIter)->getMidPoint(),pSelConnection);
				}
			}
			else
			{
				DeselectConn(pSelConnection);

				const Point& aMousePos = rEvt.GetMousePosPixel();
				::std::vector<OTableConnection*>::iterator aIter = m_vTableConnection.begin();
                ::std::vector<OTableConnection*>::iterator aEnd = m_vTableConnection.end();
	            for(;aIter != aEnd;++aIter)
				{
					if( (*aIter)->CheckHit(aMousePos) )
					{
						SelectConn(*aIter);
						if(!getDesignView()->getController().isReadOnly() && getDesignView()->getController().isConnected())
							executePopup(rEvt.GetMousePosPixel(),*aIter);
						break;
					}
				}
			}
			bHandled = sal_True;
		}
	}
	if (!bHandled)
		Window::Command(rEvt);
}

//------------------------------------------------------------------------------
OTableConnection* OJoinTableView::GetTabConn(const OTableWindow* pLhs,const OTableWindow* pRhs,bool _bSupressCrossOrNaturalJoin,const OTableConnection* _rpFirstAfter) const
{
	OTableConnection* pConn = NULL;
	DBG_ASSERT(pRhs || pLhs, "OJoinTableView::GetTabConn : invalid args !");
		// only one NULL-arg allowed

	if ((!pLhs || pLhs->ExistsAConn()) && (!pRhs || pRhs->ExistsAConn()))
	{
		sal_Bool bFoundStart = _rpFirstAfter ? sal_False : sal_True;

		::std::vector<OTableConnection*>::const_iterator aIter = m_vTableConnection.begin();
        ::std::vector<OTableConnection*>::const_iterator aEnd = m_vTableConnection.end();
	    for(;aIter != aEnd;++aIter)
		{
			OTableConnection* pData = *aIter;

			if	(	(	(pData->GetSourceWin() == pLhs)
					&&	(	(pData->GetDestWin() == pRhs)
						||	(NULL == pRhs)
						)
					)
				||	(	(pData->GetSourceWin() == pRhs)
					&&	(	(pData->GetDestWin() == pLhs)
						||	(NULL == pLhs)
						)
					)
				)
			{
                if ( _bSupressCrossOrNaturalJoin )
                {
                    if ( supressCrossNaturalJoin(pData->GetData()) )
                        continue;
                }
				if (bFoundStart)
				{
					pConn = pData;
					break;
				}

				if (!pConn)
					// used as fallback : if there is no conn after _rpFirstAfter the first conn between the two tables
					// will be used
					pConn = pData;

				if (pData == _rpFirstAfter)
					bFoundStart = sal_True;
			}
		}
	}
	return pConn;
}

//------------------------------------------------------------------------------
long OJoinTableView::PreNotify(NotifyEvent& rNEvt)
{
	sal_Bool bHandled = sal_False;
	switch (rNEvt.GetType())
	{
		case EVENT_COMMAND:
		{
			const CommandEvent* pCommand = rNEvt.GetCommandEvent();
			if (pCommand->GetCommand() == COMMAND_WHEEL)
			{
				const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
				if (pData->GetMode() == COMMAND_WHEEL_SCROLL)
				{
					if (pData->GetDelta() > 0)
						ScrollPane(-10 * pData->GetScrollLines(), pData->IsHorz(), sal_True);
					else
						ScrollPane(10 * pData->GetScrollLines(), pData->IsHorz(), sal_True);
					bHandled = sal_True;
				}
			}
		}
		break;
		case EVENT_KEYINPUT:
		{
			if (m_aTableMap.empty())
				// no tab wins -> no conns -> no traveling
				break;

			const KeyEvent* pKeyEvent =	rNEvt.GetKeyEvent();
			if (!pKeyEvent->GetKeyCode().IsMod1())
			{
				switch (pKeyEvent->GetKeyCode().GetCode())
				{
					case KEY_TAB:
					{
						if (!HasChildPathFocus())
							break;

						sal_Bool bForward = !pKeyEvent->GetKeyCode().IsShift();
						// is there an active tab win ?
						OTableWindowMapIterator aIter = m_aTableMap.begin();
                        OTableWindowMapIterator aEnd = m_aTableMap.end();
						for(;aIter != aEnd;++aIter)
							if (aIter->second && aIter->second->HasChildPathFocus())
								break;

						OTableWindow* pNextWin = NULL;
						OTableConnection* pNextConn = NULL;

						if (aIter != m_aTableMap.end())
						{	// there is a currently active tab win
							// check if there is an "overflow" and we should select a conn instead of a win
							if (!m_vTableConnection.empty())
							{
								if ((aIter->second == m_aTableMap.rbegin()->second) && bForward)
									// the last win is active and we're travelling forward -> select the first conn
									pNextConn = *m_vTableConnection.begin();
								if ((aIter == m_aTableMap.begin()) && !bForward)
									// the first win is active an we're traveling backward -> select the last conn
									pNextConn = *m_vTableConnection.rbegin();
							}

							if (!pNextConn)
							{
								// no conn for any reason -> select the next or previous tab win
								if(bForward)
								{
									if ((aIter->second == m_aTableMap.rbegin()->second))
										pNextWin = m_aTableMap.begin()->second;
									else
									{
										++aIter;
										pNextWin = aIter->second;
									}
								}
								else
								{
									if (aIter == m_aTableMap.begin())
										pNextWin = m_aTableMap.rbegin()->second;
									else
									{
										--aIter;
										pNextWin = aIter->second;
									}
								}
							}
						}
						else
						{	// no active tab win -> travel the connections
							// find the currently selected conn within the conn list
							sal_Int32 i(0);
							for (   ::std::vector<OTableConnection*>::iterator connectionIter = m_vTableConnection.begin();
                                    connectionIter != m_vTableConnection.end();
                                    ++connectionIter, ++i
                                )
							{
								if ( (*connectionIter) == GetSelectedConn() )
									break;
							}
							if (i == sal_Int32(m_vTableConnection.size() - 1) && bForward)
								// the last conn is active and we're travelling forward -> select the first win
								pNextWin = m_aTableMap.begin()->second;
							if ((i == 0) && !bForward && !m_aTableMap.empty())
								// the first conn is active and we're travelling backward -> select the last win
								pNextWin = m_aTableMap.rbegin()->second;

							if (pNextWin)
								DeselectConn(GetSelectedConn());
							else
								// no win for any reason -> select the next or previous conn
								if (i < (sal_Int32)m_vTableConnection.size())
									// there is a currently active conn
									pNextConn = m_vTableConnection[(i + (bForward ? 1 : m_vTableConnection.size() - 1)) % m_vTableConnection.size()];
								else
								{	// no tab win selected, no conn selected
									if (!m_vTableConnection.empty())
										pNextConn = m_vTableConnection[bForward ? 0 : m_vTableConnection.size() - 1];
									else if (!m_aTableMap.empty())
									{
										if(bForward)
											pNextWin = m_aTableMap.begin()->second;
										else
											pNextWin = m_aTableMap.rbegin()->second;
									}
								}
						}

						// now select the object
						if (pNextWin)
						{
							if (pNextWin->GetListBox())
								pNextWin->GetListBox()->GrabFocus();
							else
								pNextWin->GrabFocus();
							EnsureVisible(pNextWin);
						}
						else if (pNextConn)
						{
							GrabFocus();
								// neccessary : a conn may be selected even if a tab win has the focus, in this case
								// the next travel would select the same conn again if we would not reset te focus ...
							SelectConn(pNextConn);
						}
					}
					break;
					case KEY_RETURN:
					{
						if (!pKeyEvent->GetKeyCode().IsShift() && GetSelectedConn() && HasFocus())
							ConnDoubleClicked(GetSelectedConn());
						break;
					}
				}
			}
		}
		break;
		case EVENT_GETFOCUS:
		{
			if (m_aTableMap.empty())
				// no tab wins -> no conns -> no focus change
				break;
			Window* pSource = rNEvt.GetWindow();
			if (pSource)
			{
				Window* pSearchFor = NULL;
				if (pSource->GetParent() == this)
					// it may be one of the tab wins
					pSearchFor = pSource;
				else if (pSource->GetParent() && (pSource->GetParent()->GetParent() == this))
					// it may be one of th list boxes of one of the tab wins
					pSearchFor = pSource->GetParent();

				if (pSearchFor)
				{
					OTableWindowMapIterator aIter = m_aTableMap.begin();
                    OTableWindowMapIterator aEnd = m_aTableMap.end();
					for(;aIter != aEnd;++aIter)
					{
						if (aIter->second == pSearchFor)
						{
							m_pLastFocusTabWin = aIter->second;
							break;
						}
					}
				}
			}
		}
		break;
	}

	if (!bHandled)
		return Window::PreNotify(rNEvt);
	return 1L;
}

//------------------------------------------------------------------------------
void OJoinTableView::GrabTabWinFocus()
{
	if (m_pLastFocusTabWin && m_pLastFocusTabWin->IsVisible())
	{
		if (m_pLastFocusTabWin->GetListBox())
			m_pLastFocusTabWin->GetListBox()->GrabFocus();
		else
			m_pLastFocusTabWin->GrabFocus();
	}
	else if (!m_aTableMap.empty() && m_aTableMap.begin()->second && m_aTableMap.begin()->second->IsVisible())
	{
		OTableWindow* pFirstWin = m_aTableMap.begin()->second;
		if (pFirstWin->GetListBox())
			pFirstWin->GetListBox()->GrabFocus();
		else
			pFirstWin->GrabFocus();
	}
}
// -----------------------------------------------------------------------------
void OJoinTableView::StateChanged( StateChangedType nType )
{
	Window::StateChanged( nType );

	if ( nType == STATE_CHANGE_ZOOM )
	{
		const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();

		Font aFont = rStyleSettings.GetGroupFont();
		if ( IsControlFont() )
			aFont.Merge( GetControlFont() );
		SetZoomedPointFont( aFont );

		OTableWindowMapIterator aIter = m_aTableMap.begin();
		OTableWindowMapIterator aEnd = m_aTableMap.end();
		for(;aIter != aEnd;++aIter)
		{
			aIter->second->SetZoom(GetZoom());
			Size aSize(CalcZoom(aIter->second->GetSizePixel().Width()),CalcZoom(aIter->second->GetSizePixel().Height()));
			aIter->second->SetSizePixel(aSize);
		}
		Resize();
	}
}
//------------------------------------------------------------------------------
void OJoinTableView::HideTabWins()
{
	DBG_CHKTHIS(OJoinTableView,NULL);
	SetUpdateMode(sal_False);

	OTableWindowMap* pTabWins = GetTabWinMap();
	if ( pTabWins )
	{
        // working on a copy because the real list will be cleared in inner calls
        OTableWindowMap aCopy(*pTabWins);
		OTableWindowMap::iterator aIter = aCopy.begin();
        OTableWindowMap::iterator aEnd = aCopy.end();
		for(;aIter != aEnd;++aIter)
			RemoveTabWin(aIter->second);
	}

	m_pView->getController().setModified(sal_True);

	SetUpdateMode(sal_True);

}
// -----------------------------------------------------------------------------
sal_Int8 OJoinTableView::AcceptDrop( const AcceptDropEvent& /*_rEvt*/ )
{
	return DND_ACTION_NONE;
}
// -----------------------------------------------------------------------------
sal_Int8 OJoinTableView::ExecuteDrop( const ExecuteDropEvent& /*_rEvt*/ )
{
	return DND_ACTION_NONE;
}
// -----------------------------------------------------------------------------
void OJoinTableView::dragFinished( )
{
}
//------------------------------------------------------------------------------
void OJoinTableView::StartDrag( sal_Int8 /*nAction*/, const Point& /*rPosPixel*/ )
{
}
// -----------------------------------------------------------------------------
void OJoinTableView::clearLayoutInformation()
{
	m_pLastFocusTabWin	= NULL;
	m_pSelectedConn		= NULL;
	//////////////////////////////////////////////////////////////////////
	// Listen loeschen
	OTableWindowMapIterator aIter = m_aTableMap.begin();
	OTableWindowMapIterator aEnd  = m_aTableMap.end();
	for(;aIter != aEnd;++aIter)
	{
		if ( aIter->second )
			aIter->second->clearListBox();
		::std::auto_ptr<Window> aTemp(aIter->second);
		aIter->second = NULL;
	}

	m_aTableMap.clear();

	::std::vector<OTableConnection*>::const_iterator aIter2 = m_vTableConnection.begin();
    ::std::vector<OTableConnection*>::const_iterator aEnd2 = m_vTableConnection.end();
	for(;aIter2 != aEnd2;++aIter2)
		delete *aIter2;

	m_vTableConnection.clear();
}
// -----------------------------------------------------------------------------
void OJoinTableView::lookForUiActivities()
{
}
// -----------------------------------------------------------------------------
void OJoinTableView::LoseFocus()
{
	DeselectConn(GetSelectedConn());
	Window::LoseFocus();
}
// -----------------------------------------------------------------------------
void OJoinTableView::GetFocus()
{
	Window::GetFocus();
	if ( !m_aTableMap.empty() && !GetSelectedConn() )
		GrabTabWinFocus();
}
// -----------------------------------------------------------------------------
Reference< XAccessible > OJoinTableView::CreateAccessible()
{
	m_pAccessible = new OJoinDesignViewAccess(this);
	return m_pAccessible;
}
// -----------------------------------------------------------------------------
void OJoinTableView::modified()
{
	OJoinController& rController = m_pView->getController();
	rController.setModified( sal_True );
	rController.InvalidateFeature(ID_BROWSER_ADDTABLE);
	rController.InvalidateFeature(SID_RELATION_ADD_RELATION);
}
// -----------------------------------------------------------------------------
void OJoinTableView::addConnection(OTableConnection* _pConnection,sal_Bool _bAddData)
{
	if ( _bAddData )
	{
#if OSL_DEBUG_LEVEL > 0
		TTableConnectionData* pTabConnDataList = m_pView->getController().getTableConnectionData();
		OSL_ENSURE( ::std::find(pTabConnDataList->begin(),pTabConnDataList->end(),_pConnection->GetData()) == pTabConnDataList->end(),"Data already in vector!");
#endif
		m_pView->getController().getTableConnectionData()->push_back(_pConnection->GetData());
	}
	m_vTableConnection.push_back(_pConnection);
	_pConnection->RecalcLines();
	_pConnection->InvalidateConnection();

	modified();
	if ( m_pAccessible )
		m_pAccessible->notifyAccessibleEvent(	AccessibleEventId::CHILD,
												Any(),
												makeAny(_pConnection->GetAccessible()));
}
// -----------------------------------------------------------------------------
bool OJoinTableView::allowQueries() const
{
    return true;
}
// -----------------------------------------------------------------------------
void OJoinTableView::onNoColumns_throw()
{
    OSL_ENSURE( false, "OTableWindow::onNoColumns_throw: cannot really handle this!" );
    throw SQLException();
}
//------------------------------------------------------------------------------
bool OJoinTableView::supressCrossNaturalJoin(const TTableConnectionData::value_type& ) const
{
    return false;
}
