/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_dbaccess.hxx"

#ifndef _DBAUI_INDEXDIALOG_HXX_
#include "indexdialog.hxx"
#endif
#ifndef _DBU_DLG_HRC_
#include "dbu_dlg.hrc"
#endif
#ifndef _DBA_DBACCESS_HELPID_HRC_
#include "dbaccess_helpid.hrc"
#endif
#ifndef _DBAUI_INDEXDIALOG_HRC_
#include "indexdialog.hrc"
#endif
#ifndef _DBAUI_INDEXFIELDSCONTROL_HXX_
#include "indexfieldscontrol.hxx"
#endif
#ifndef _DBAUI_INDEXCOLLECTION_HXX_
#include "indexcollection.hxx"
#endif
#ifndef _SV_MSGBOX_HXX 
#include <vcl/msgbox.hxx>
#endif
#ifndef _COM_SUN_STAR_SDB_SQLCONTEXT_HPP_
#include <com/sun/star/sdb/SQLContext.hpp>
#endif
#ifndef DBAUI_TOOLS_HXX
#include "UITools.hxx"
#endif
#ifndef _SVTOOLS_IMGDEF_HXX
#include <svtools/imgdef.hxx>
#endif
#ifndef DBACCESS_UI_BROWSER_ID_HXX
#include "browserids.hxx"
#endif
#ifndef _CONNECTIVITY_DBTOOLS_HXX_
#include <connectivity/dbtools.hxx>
#endif
//......................................................................
namespace dbaui
{
//......................................................................

	using namespace ::com::sun::star::uno;
	using namespace ::com::sun::star::container;
	using namespace ::com::sun::star::sdbc;
	using namespace ::com::sun::star::sdb;
	using namespace ::com::sun::star::lang;
	using namespace ::dbtools;

	//==================================================================
	//= helper
	//==================================================================
	//------------------------------------------------------------------
	sal_Bool operator ==(const OIndexField& _rLHS, const OIndexField& _rRHS)
	{
		return	(_rLHS.sFieldName == _rRHS.sFieldName)
			&&	(_rLHS.bSortAscending == _rRHS.bSortAscending);
	}

	//------------------------------------------------------------------
	sal_Bool operator !=(const OIndexField& _rLHS, const OIndexField& _rRHS)
	{
		return !(_rLHS == _rRHS);
	}

	//------------------------------------------------------------------
	sal_Bool operator ==(const IndexFields& _rLHS, const IndexFields& _rRHS)
	{
		if (_rLHS.size() != _rRHS.size())
			return sal_False;

		ConstIndexFieldsIterator aLeft = _rLHS.begin();
        ConstIndexFieldsIterator aLeftEnd = _rLHS.end();
		ConstIndexFieldsIterator aRight = _rRHS.begin();
		for	(; aLeft != aLeftEnd; ++aLeft, ++aRight)
		{
			if (*aLeft != *aRight)
				return sal_False;
		}

		return sal_True;
	}

	//------------------------------------------------------------------
	sal_Bool operator !=(const IndexFields& _rLHS, const IndexFields& _rRHS)
	{
		return !(_rLHS == _rRHS);
	}

	//==================================================================
	//= DbaIndexList
	//==================================================================
	//------------------------------------------------------------------
	DbaIndexList::DbaIndexList(Window* _pParent, const ResId& _rId)
		:SvTreeListBox(_pParent, _rId)
		,m_bSuspendSelectHdl(sal_False)
	{
	}

	extern sal_Bool isCharOk(sal_Unicode _cChar,sal_Bool _bFirstChar,sal_Bool _bUpperCase,const ::rtl::OUString& _sAllowedChars);
	//------------------------------------------------------------------
	sal_Bool DbaIndexList::EditedEntry( SvLBoxEntry* _pEntry, const String& _rNewText )
	{
		// first check if this is valid SQL92 name
		if ( isSQL92CheckEnabled(m_xConnection) )
		{
			Reference<XDatabaseMetaData> xMeta = m_xConnection->getMetaData();
			if ( xMeta.is() )
			{
				::rtl::OUString sNewName(_rNewText);
				::rtl::OUString sAlias = ::dbtools::convertName2SQLName(sNewName,xMeta->getExtraNameCharacters());
				if ( ( xMeta->supportsMixedCaseQuotedIdentifiers() )
						? 
						sAlias != sNewName 
						: 
				!sNewName.equalsIgnoreAsciiCase(sAlias))
					return sal_False;
			}
		}

		if (!SvTreeListBox::EditedEntry(_pEntry, _rNewText))
			return sal_False;

		String sOldText = GetEntryText(_pEntry);
		SvTreeListBox::SetEntryText(_pEntry, _rNewText);

		sal_Bool bValid = sal_True;
		if (m_aEndEditHdl.IsSet())
			bValid = (0 != m_aEndEditHdl.Call(_pEntry));

		if (bValid)
			return sal_True;

		SvTreeListBox::SetEntryText(_pEntry, sOldText);

		return sal_False;
	}

	//------------------------------------------------------------------
	void DbaIndexList::enableSelectHandler()
	{
		DBG_ASSERT(m_bSuspendSelectHdl, "DbaIndexList::enableSelectHandler: invalid call (this is not cumulative)!");
		m_bSuspendSelectHdl = sal_False;
	}

	//------------------------------------------------------------------
	void DbaIndexList::disableSelectHandler()
	{
		DBG_ASSERT(!m_bSuspendSelectHdl, "DbaIndexList::enableSelectHandler: invalid call (this is not cumulative)!");
		m_bSuspendSelectHdl = sal_True;
	}

	//------------------------------------------------------------------
	void DbaIndexList::SelectNoHandlerCall( SvLBoxEntry* _pEntry )
	{
		disableSelectHandler();
		Select(_pEntry, sal_True);
		enableSelectHandler();
	}

	//------------------------------------------------------------------
	sal_Bool DbaIndexList::Select( SvLBoxEntry* pEntry, sal_Bool _bSelect )
	{
		sal_Bool bReturn = SvTreeListBox::Select(pEntry, _bSelect);

		if (m_aSelectHdl.IsSet() && !m_bSuspendSelectHdl && _bSelect)
			m_aSelectHdl.Call(this);

		return bReturn;
	}

	//==================================================================
	//= DbaIndexDialog
	//==================================================================
DBG_NAME(DbaIndexDialog)
//------------------------------------------------------------------
	DbaIndexDialog::DbaIndexDialog(	Window* _pParent, const Sequence< ::rtl::OUString >& _rFieldNames, 
									const Reference< XNameAccess >& _rxIndexes, 
									const Reference< XConnection >& _rxConnection,
									const Reference< XMultiServiceFactory >& _rxORB,sal_Int32 _nMaxColumnsInIndex)
		:ModalDialog( _pParent, ModuleRes(DLG_INDEXDESIGN))
		,m_xConnection(_rxConnection)
		,m_aGeometrySettings(E_DIALOG, ::rtl::OUString::createFromAscii("dbaccess.tabledesign.indexdialog"))
		,m_aActions							(this, ModuleRes(TLB_ACTIONS))
		,m_aIndexes							(this, ModuleRes(CTR_INDEXLIST))
		,m_aIndexDetails					(this, ModuleRes(FL_INDEXDETAILS))
		,m_aDescriptionLabel				(this, ModuleRes(FT_DESC_LABEL))
		,m_aDescription						(this, ModuleRes(FT_DESCRIPTION))
		,m_aUnique							(this, ModuleRes(CB_UNIQUE))
		,m_aFieldsLabel						(this, ModuleRes(FT_FIELDS))
		,m_pFields(new IndexFieldsControl	(this, ModuleRes(CTR_FIELDS),_nMaxColumnsInIndex,::dbtools::getBooleanDataSourceSetting( m_xConnection, "AddIndexAppendix" )))
		,m_aClose							(this, ModuleRes(PB_CLOSE))
		,m_aHelp							(this, ModuleRes(HB_HELP))
		,m_pIndexes(NULL)
		,m_pPreviousSelection(NULL)
		,m_bEditAgain(sal_False)
		,m_xORB(_rxORB)
	{
        DBG_CTOR(DbaIndexDialog,NULL);

		FreeResource();

		m_aActions.SetSelectHdl(LINK(this, DbaIndexDialog, OnIndexAction));

		m_aIndexes.SetSelectHdl(LINK(this, DbaIndexDialog, OnIndexSelected));
		m_aIndexes.SetEndEditHdl(LINK(this, DbaIndexDialog, OnEntryEdited));
		m_aIndexes.SetSelectionMode(SINGLE_SELECTION);
		m_aIndexes.SetHighlightRange();
		m_aIndexes.setConnection(m_xConnection);

		m_pFields->Init(_rFieldNames);

		setToolBox(&m_aActions);

		m_pIndexes = new OIndexCollection();
		try
		{
			m_pIndexes->attach(_rxIndexes);
		}
		catch(SQLException& e)
		{
			::dbaui::showError(SQLExceptionInfo(e),_pParent,_rxORB);
		}
		catch(Exception&)
		{
			OSL_ENSURE(sal_False, "DbaIndexDialog::DbaIndexDialog: could not retrieve basic information from the UNO collection!");
		}

		fillIndexList();

		m_aUnique.SetClickHdl(LINK(this, DbaIndexDialog, OnModified));
		m_pFields->SetModifyHdl(LINK(this, DbaIndexDialog, OnModified));

		m_aClose.SetClickHdl(LINK(this, DbaIndexDialog, OnCloseDialog));

		// get our most recent geometry settings
//		if (m_aGeometrySettings.Exists())
//		{
//			Point aPos;
//			m_aGeometrySettings.GetPosition(aPos.X(), aPos.Y());
//			SetPosPixel(aPos);
//		}

		// if all of the indexes have an empty description, we're not interested in displaying it
		Indexes::const_iterator aCheck;

		for (	aCheck = m_pIndexes->begin();
				aCheck != m_pIndexes->end();
				++aCheck
			)
		{
			if (aCheck->sDescription.getLength())
				break;
		}

		if (aCheck == m_pIndexes->end())
		{
			sal_Int32 nMoveUp = m_aUnique.GetPosPixel().Y() - m_aDescriptionLabel.GetPosPixel().Y();

			// hide the controls which are necessary for the description
			m_aDescription.Hide();
			m_aDescriptionLabel.Hide();

			// move other controls up
			Point aPos = m_aUnique.GetPosPixel();
			aPos.Y() -= nMoveUp;
			m_aUnique.SetPosPixel(aPos);

			aPos = m_aFieldsLabel.GetPosPixel();
			aPos.Y() -= nMoveUp;
			m_aFieldsLabel.SetPosPixel(aPos);

			aPos = m_pFields->GetPosPixel();
			aPos.Y() -= nMoveUp;
			m_pFields->SetPosPixel(aPos);

			// and enlarge the fields list
			Size aSize = m_pFields->GetSizePixel();
			aSize.Height() += nMoveUp;
			m_pFields->SetSizePixel(aSize);
		}
	}

	//------------------------------------------------------------------
	void DbaIndexDialog::updateToolbox()
	{
		m_aActions.EnableItem(ID_INDEX_NEW, !m_aIndexes.IsEditingActive());

		SvLBoxEntry* pSelected = m_aIndexes.FirstSelected();
		sal_Bool bSelectedAnything = NULL != pSelected;
		

		if (pSelected)
		{
			// is the current entry modified?
			Indexes::const_iterator aSelectedPos = m_pIndexes->begin() + reinterpret_cast<sal_IntPtr>(pSelected->GetUserData());
			m_aActions.EnableItem(ID_INDEX_SAVE, aSelectedPos->isModified() || aSelectedPos->isNew());
			m_aActions.EnableItem(ID_INDEX_RESET, aSelectedPos->isModified() || aSelectedPos->isNew());
            bSelectedAnything = bSelectedAnything && !aSelectedPos->bPrimaryKey;
		}
		else
		{
			m_aActions.EnableItem(ID_INDEX_SAVE, sal_False);
			m_aActions.EnableItem(ID_INDEX_RESET, sal_False);
		}
        m_aActions.EnableItem(ID_INDEX_DROP, bSelectedAnything);
		m_aActions.EnableItem(ID_INDEX_RENAME, bSelectedAnything);
	}

	//------------------------------------------------------------------
	void DbaIndexDialog::fillIndexList()
	{
		sal_Bool bHiContrast = GetSettings().GetStyleSettings().GetHighContrastMode();
		Image aPKeyIcon(ModuleRes( bHiContrast ? IMG_PKEYICON_SCH : IMG_PKEYICON));
		// fill the list with the index names
		m_aIndexes.Clear();
		Indexes::iterator aIndexLoop = m_pIndexes->begin();
		Indexes::iterator aEnd = m_pIndexes->end();
		for (; aIndexLoop != aEnd; ++aIndexLoop)
		{
			SvLBoxEntry* pNewEntry = NULL;
			if (aIndexLoop->bPrimaryKey)
				pNewEntry = m_aIndexes.InsertEntry(aIndexLoop->sName, aPKeyIcon, aPKeyIcon);
			else
				pNewEntry = m_aIndexes.InsertEntry(aIndexLoop->sName);

			pNewEntry->SetUserData(reinterpret_cast< void* >(sal_Int32(aIndexLoop - m_pIndexes->begin())));
		}

		OnIndexSelected(&m_aIndexes);
	}

	//------------------------------------------------------------------
	DbaIndexDialog::~DbaIndexDialog( )
	{
		setToolBox(NULL);
		delete m_pIndexes;
		delete m_pFields;

		// save our geometry settings
//		Point aPos = GetPosPixel();
//		m_aGeometrySettings.SetPosition(aPos.X(), aPos.Y());
	
        DBG_DTOR(DbaIndexDialog,NULL);
    }

	//------------------------------------------------------------------
	sal_Bool DbaIndexDialog::implCommit(SvLBoxEntry* _pEntry)
	{
		DBG_ASSERT(_pEntry, "DbaIndexDialog::implCommit: invalid entry!");

		Indexes::iterator aCommitPos = m_pIndexes->begin() + reinterpret_cast<sal_IntPtr>(_pEntry->GetUserData());

		// if it's not a new index, remove it
		// (we can't modify indexes, only drop'n'insert)
		if (!aCommitPos->isNew())
			if (!implDropIndex(_pEntry, sal_False))
				return sal_False;

		// create the new index
		SQLExceptionInfo aExceptionInfo;
		try
		{
			m_pIndexes->commitNewIndex(aCommitPos);
		}
		catch(SQLContext& e) { aExceptionInfo = SQLExceptionInfo(e); }
		catch(SQLWarning& e) { aExceptionInfo = SQLExceptionInfo(e); }
		catch(SQLException& e) { aExceptionInfo = SQLExceptionInfo(e); }

		// reflect the new selection in the toolbox
		updateToolbox();

		if (aExceptionInfo.isValid())
			showError(aExceptionInfo, this, m_xORB);
		else
		{
			m_aUnique.SaveValue();
			m_pFields->SaveValue();
		}

		return !aExceptionInfo.isValid();
	}

	//------------------------------------------------------------------
	void DbaIndexDialog::OnNewIndex()
	{
		// commit the current entry, if necessary
		if (!implCommitPreviouslySelected())
			return;

		// get a new unique name for the new index
		String sNewIndexName;
		const String sNewIndexNameBase(ModuleRes(STR_LOGICAL_INDEX_NAME));
		sal_Int32 i;

		for ( i = 1; i < 0x7FFFFFFF; ++i )
		{
			sNewIndexName = sNewIndexNameBase;
			sNewIndexName += String::CreateFromInt32(i);
			if (m_pIndexes->end() == m_pIndexes->find(sNewIndexName))
				break;
		}
		if ((i>0x7FFFFFFF) || (i<0))
		{
			DBG_ERROR("DbaIndexDialog::OnNewIndex: no free index name found!");
			// can't do anything ... of course we try another base, but this could end with the same result ...
			return;
		}

		SvLBoxEntry* pNewEntry = m_aIndexes.InsertEntry(sNewIndexName);
		m_pIndexes->insert(sNewIndexName);

		// update the user data on the entries in the list box:
		// they're iterators of the index collection, and thus they have changed when removing the index
		for (SvLBoxEntry* pAdjust = m_aIndexes.First(); pAdjust; pAdjust = m_aIndexes.Next(pAdjust))
		{
			Indexes::iterator aAfterInsertPos = m_pIndexes->find(m_aIndexes.GetEntryText(pAdjust));
			DBG_ASSERT(aAfterInsertPos != m_pIndexes->end(), "DbaIndexDialog::OnNewIndex: problems with on of the entries!");
			pAdjust->SetUserData(reinterpret_cast< void* >(sal_Int32(aAfterInsertPos - m_pIndexes->begin())));
		}

		// select the entry and start in-place editing
		m_aIndexes.SelectNoHandlerCall(pNewEntry);
		OnIndexSelected(&m_aIndexes);
		m_aIndexes.EditEntry(pNewEntry);
		updateToolbox();
	}

	//------------------------------------------------------------------
	void DbaIndexDialog::OnDropIndex(sal_Bool _bConfirm)
	{
		// the selected index
		SvLBoxEntry* pSelected = m_aIndexes.FirstSelected();
		DBG_ASSERT(pSelected, "DbaIndexDialog::OnDropIndex: invalid call!");
		if (pSelected)
		{
			// let the user confirm the drop
			if (_bConfirm)
			{
				String sConfirm(ModuleRes(STR_CONFIRM_DROP_INDEX));
				sConfirm.SearchAndReplaceAscii("$name$", m_aIndexes.GetEntryText(pSelected));
				QueryBox aConfirm(this, WB_YES_NO, sConfirm);
				if (RET_YES != aConfirm.Execute())
					return;
			}

			// do the drop
			implDropIndex(pSelected, sal_True);

			// reflect the new selection in the toolbox
			updateToolbox();
		}
	}

	//------------------------------------------------------------------
	sal_Bool DbaIndexDialog::implDropIndex(SvLBoxEntry* _pEntry, sal_Bool _bRemoveFromCollection)
	{
		// do the drop
		Indexes::iterator aDropPos = m_pIndexes->begin() + reinterpret_cast<sal_IntPtr>(_pEntry->GetUserData());
		DBG_ASSERT(aDropPos != m_pIndexes->end(), "DbaIndexDialog::OnDropIndex: did not find the index in my collection!");

		SQLExceptionInfo aExceptionInfo;
		sal_Bool bSuccess = sal_False;
		try
		{
			if (_bRemoveFromCollection)
				bSuccess = m_pIndexes->drop(aDropPos);
			else
				bSuccess = m_pIndexes->dropNoRemove(aDropPos);
		}
		catch(SQLContext& e) { aExceptionInfo = SQLExceptionInfo(e); }
		catch(SQLWarning& e) { aExceptionInfo = SQLExceptionInfo(e); }
		catch(SQLException& e) { aExceptionInfo = SQLExceptionInfo(e); }

		if (aExceptionInfo.isValid())
			showError(aExceptionInfo, this, m_xORB);
		else if (bSuccess && _bRemoveFromCollection)
		{
			SvLBoxTreeList* pModel = m_aIndexes.GetModel();

			m_aIndexes.disableSelectHandler();
			pModel->Remove(_pEntry);
			m_aIndexes.enableSelectHandler();

			// update the user data on the entries in the list box:
			// they're iterators of the index collection, and thus they have changed when removing the index
			for (SvLBoxEntry* pAdjust = m_aIndexes.First(); pAdjust; pAdjust = m_aIndexes.Next(pAdjust))
			{
				Indexes::iterator aAfterDropPos = m_pIndexes->find(m_aIndexes.GetEntryText(pAdjust));
				DBG_ASSERT(aAfterDropPos != m_pIndexes->end(), "DbaIndexDialog::OnDropIndex: problems with on of the remaining entries!");
				pAdjust->SetUserData(reinterpret_cast< void* >(sal_Int32(aAfterDropPos - m_pIndexes->begin())));
			}

			// if the remvoved entry was the selected on ...
			if (m_pPreviousSelection == _pEntry)
				m_pPreviousSelection = NULL;

			// the Remove automatically selected another entry (if possible), but we disabled the calling of the handler
			// to prevent that we missed something ... call the handler directly
			OnIndexSelected(&m_aIndexes);
		}

		return !aExceptionInfo.isValid();
	}

	//------------------------------------------------------------------
	void DbaIndexDialog::OnRenameIndex()
	{
		// the selected index
		SvLBoxEntry* pSelected = m_aIndexes.FirstSelected();
		DBG_ASSERT(pSelected, "DbaIndexDialog::OnRenameIndex: invalid call!");

		// save the changes made 'til here
		// Upon leaving the edit mode, the control will be re-initialized with the
		// settings from the current entry
		implSaveModified(sal_False);

		m_aIndexes.EditEntry(pSelected);
		updateToolbox();
	}

	//------------------------------------------------------------------
	void DbaIndexDialog::OnSaveIndex()
	{
		// the selected index
#if OSL_DEBUG_LEVEL > 0
		SvLBoxEntry* pSelected = m_aIndexes.FirstSelected();
		OSL_ENSURE( pSelected, "DbaIndexDialog::OnSaveIndex: invalid call!" );
#endif

		implCommitPreviouslySelected();
		updateToolbox();
	}

	//------------------------------------------------------------------
	void DbaIndexDialog::OnResetIndex()
	{
		// the selected index
		SvLBoxEntry* pSelected = m_aIndexes.FirstSelected();
		DBG_ASSERT(pSelected, "DbaIndexDialog::OnResetIndex: invalid call!");

		Indexes::iterator aResetPos = m_pIndexes->begin() + reinterpret_cast<sal_IntPtr>(pSelected->GetUserData());

		if (aResetPos->isNew())
		{
			OnDropIndex(sal_False);
			return;
		}
		
		SQLExceptionInfo aExceptionInfo;
		try
		{
			m_pIndexes->resetIndex(aResetPos);
		}
		catch(SQLContext& e) { aExceptionInfo = SQLExceptionInfo(e); }
		catch(SQLWarning& e) { aExceptionInfo = SQLExceptionInfo(e); }
		catch(SQLException& e) { aExceptionInfo = SQLExceptionInfo(e); }

		if (aExceptionInfo.isValid())
			showError(aExceptionInfo, this, m_xORB);
		else
			m_aIndexes.SetEntryText(pSelected, aResetPos->sName);

		updateControls(pSelected);
		updateToolbox();
	}

	//------------------------------------------------------------------
	IMPL_LINK( DbaIndexDialog, OnIndexAction, ToolBox*, /*NOTINTERESTEDIN*/ )
	{
		sal_uInt16 nClicked = m_aActions.GetCurItemId();
		switch (nClicked)
		{
			case ID_INDEX_NEW:
				OnNewIndex();
				break;
			case ID_INDEX_DROP:
				OnDropIndex();
				break;
			case ID_INDEX_RENAME:
				OnRenameIndex();
				break;
			case ID_INDEX_SAVE:
				OnSaveIndex();
				break;
			case ID_INDEX_RESET:
				OnResetIndex();
				break;
		}
		return 0L;
	}

	//------------------------------------------------------------------
	IMPL_LINK( DbaIndexDialog, OnCloseDialog, void*, /*NOTINTERESTEDIN*/ )
	{
		if (m_aIndexes.IsEditingActive())
		{
			DBG_ASSERT(!m_bEditAgain, "DbaIndexDialog::OnCloseDialog: somebody was faster than hell!");
				// this means somebody entered a new name, which was invalid, which cause us to posted us an event,
				// and before the event arrived the user clicked onto "close". VERY fast, this user ....
			m_aIndexes.EndEditing(sal_False);
			if (m_bEditAgain)
				// could not commit the new name (started a new - asynchronous - edit trial)
				return 1L;
		}

		// the currently selected entry
		const SvLBoxEntry* pSelected = m_aIndexes.FirstSelected();
		DBG_ASSERT(pSelected == m_pPreviousSelection, "DbaIndexDialog::OnCloseDialog: inconsistence!");

		sal_Int32 nResponse = RET_NO;
		if (pSelected)
		{
			// the descriptor
			Indexes::const_iterator aSelected = m_pIndexes->begin() + reinterpret_cast<sal_IntPtr>(pSelected->GetUserData());
			
			if (aSelected->isModified() || aSelected->isNew())
			{
				QueryBox aQuestion(this, ModuleRes(QUERY_SAVE_CURRENT_INDEX));
				nResponse = aQuestion.Execute();
			}
		}

		switch (nResponse)
		{
			case RET_YES:
				if (!implCommitPreviouslySelected())
					return 1L;
				break;
			case RET_NO:
				break;
			default:
				return 1L;
		}

		EndDialog(RET_OK);

		return 0L;
	}

	//------------------------------------------------------------------
	IMPL_LINK( DbaIndexDialog, OnEditIndexAgain, SvLBoxEntry*, _pEntry )
	{
		m_bEditAgain = sal_False;
		m_aIndexes.EditEntry(_pEntry);
		return 0L;
	}

	//------------------------------------------------------------------
	IMPL_LINK( DbaIndexDialog, OnEntryEdited, SvLBoxEntry*, _pEntry )
	{
		Indexes::iterator aPosition = m_pIndexes->begin() + reinterpret_cast<sal_IntPtr>(_pEntry->GetUserData());
		
		DBG_ASSERT(aPosition >= m_pIndexes->begin() && aPosition < m_pIndexes->end(),
			"DbaIndexDialog::OnEntryEdited: invalid entry!");

		String sNewName = m_aIndexes.GetEntryText(_pEntry);

		Indexes::const_iterator aSameName = m_pIndexes->find(sNewName);
		if ((aSameName != aPosition) && (m_pIndexes->end() != aSameName))
		{
			String sError(ModuleRes(STR_INDEX_NAME_ALREADY_USED));
			sError.SearchAndReplaceAscii("$name$", sNewName);
			ErrorBox aError(this, WB_OK, sError);
			aError.Execute();

			updateToolbox();
			m_bEditAgain = sal_True;
			PostUserEvent(LINK(this, DbaIndexDialog, OnEditIndexAgain), _pEntry);
			return 0L;
		}

		aPosition->sName = sNewName;

		// rename can be done by a drop/insert combination only
		if (aPosition->isNew())
		{
			updateToolbox();
			// no commitment needed here ....
			return 1L;
		}

		if (aPosition->sName != aPosition->getOriginalName())
		{
			aPosition->setModified(sal_True);
			updateToolbox();
		}

		return 1L;
	}

	//------------------------------------------------------------------
	sal_Bool DbaIndexDialog::implSaveModified(sal_Bool _bPlausibility)
	{
		if (m_pPreviousSelection)
		{
			// try to commit the previously selected index
			if (m_pFields->IsModified() && !m_pFields->SaveModified())
				return sal_False;

			Indexes::iterator aPreviouslySelected = m_pIndexes->begin() + reinterpret_cast<sal_IntPtr>(m_pPreviousSelection->GetUserData());

			// the unique flag
			aPreviouslySelected->bUnique = m_aUnique.IsChecked();
			if (m_aUnique.GetSavedValue() != m_aUnique.GetState())
				aPreviouslySelected->setModified(sal_True);

			// the fields
			m_pFields->commitTo(aPreviouslySelected->aFields);
			if (m_pFields->GetSavedValue() != aPreviouslySelected->aFields)
				aPreviouslySelected->setModified(sal_True);

			// plausibility checks
			if (_bPlausibility && !implCheckPlausibility(aPreviouslySelected))
				return sal_False;
		}

		return sal_True;
	}

	//------------------------------------------------------------------
	sal_Bool DbaIndexDialog::implCheckPlausibility(const ConstIndexesIterator& _rPos)
	{
		// need at least one field
		if (0 == _rPos->aFields.size())
		{
			ErrorBox aError(this, ModuleRes(ERR_NEED_INDEX_FIELDS));
			aError.Execute();
			m_pFields->GrabFocus();
			return sal_False;
		}

		// no double fields
		DECLARE_STL_STDKEY_SET( String, StringBag );
		StringBag aExistentFields;
		for (	ConstIndexFieldsIterator aFieldCheck = _rPos->aFields.begin();
				aFieldCheck != _rPos->aFields.end();
				++aFieldCheck
			)
		{
			if (aExistentFields.end() != aExistentFields.find(aFieldCheck->sFieldName))
			{
				// a column is specified twice ... won't work anyway, so prevent this here and now
				String sMessage(ModuleRes(STR_INDEXDESIGN_DOUBLE_COLUMN_NAME));
				sMessage.SearchAndReplaceAscii("$name$", aFieldCheck->sFieldName);
				ErrorBox aError(this, WB_OK, sMessage);
				aError.Execute();
				m_pFields->GrabFocus();
				return sal_False;
			}
			aExistentFields.insert(aFieldCheck->sFieldName);
		}

		return sal_True;
	}

	//------------------------------------------------------------------
	sal_Bool DbaIndexDialog::implCommitPreviouslySelected()
	{
		if (m_pPreviousSelection)
		{
			Indexes::iterator aPreviouslySelected = m_pIndexes->begin() + reinterpret_cast<sal_IntPtr>(m_pPreviousSelection->GetUserData());

			if (!implSaveModified())
				return sal_False;

			// commit the index (if necessary)
			if (aPreviouslySelected->isModified() && !implCommit(m_pPreviousSelection))
				return sal_False;
		}

		return sal_True;
	}

	//------------------------------------------------------------------
	IMPL_LINK( DbaIndexDialog, OnModified, void*, /*NOTINTERESTEDIN*/ )
	{
		DBG_ASSERT(m_pPreviousSelection, "DbaIndexDialog, OnModified: invalid call!");
		Indexes::iterator aPosition = m_pIndexes->begin() + reinterpret_cast<sal_IntPtr>(m_pPreviousSelection->GetUserData());

		aPosition->setModified(sal_True);
		updateToolbox();

		return 1L;
	}

	//------------------------------------------------------------------
	void DbaIndexDialog::updateControls(const SvLBoxEntry* _pEntry)
	{
		if (_pEntry)
		{
			// the descriptor of the selected index
			Indexes::const_iterator aSelectedIndex = m_pIndexes->begin() + reinterpret_cast<sal_IntPtr>(_pEntry->GetUserData());

			// fill the controls
			m_aUnique.Check(aSelectedIndex->bUnique);
			m_aUnique.Enable(!aSelectedIndex->bPrimaryKey);
			m_aUnique.SaveValue();

			m_pFields->initializeFrom(aSelectedIndex->aFields);
			m_pFields->Enable(!aSelectedIndex->bPrimaryKey);
			m_pFields->SaveValue();

			m_aDescription.SetText(aSelectedIndex->sDescription);
			m_aDescription.Enable(!aSelectedIndex->bPrimaryKey);

			m_aDescriptionLabel.Enable(!aSelectedIndex->bPrimaryKey);
		}
		else
		{
			m_aUnique.Check(sal_False);
			m_pFields->initializeFrom(IndexFields());
			m_aDescription.SetText(String());
		}
	}

	//------------------------------------------------------------------
	IMPL_LINK( DbaIndexDialog, OnIndexSelected, DbaIndexList*, /*NOTINTERESTEDIN*/ )
	{
		m_aIndexes.EndSelection();

		if (m_aIndexes.IsEditingActive())
			m_aIndexes.EndEditing(sal_False);

		// commit the old data
		if (m_aIndexes.FirstSelected() != m_pPreviousSelection)
		{	// (this call may happen in case somebody ended an in-place edit with 'return', so we need to check this before committing)
			if (!implCommitPreviouslySelected())
			{
				m_aIndexes.SelectNoHandlerCall(m_pPreviousSelection);
				return 1L;
			}
		}

		sal_Bool bHaveSelection = (NULL != m_aIndexes.FirstSelected());

		// disable/enable the detail controls
		m_aIndexDetails.Enable(bHaveSelection);
		m_aUnique.Enable(bHaveSelection);
		m_aDescriptionLabel.Enable(bHaveSelection);
		m_aFieldsLabel.Enable(bHaveSelection);
		m_pFields->Enable(bHaveSelection);

		SvLBoxEntry* pNewSelection = m_aIndexes.FirstSelected();
		updateControls(pNewSelection);
		if (bHaveSelection)
			m_aIndexes.GrabFocus();

		m_pPreviousSelection = pNewSelection;

		updateToolbox();
		return 0L;
	}
	// -----------------------------------------------------------------------------
	void DbaIndexDialog::StateChanged( StateChangedType nType )
	{
		ModalDialog::StateChanged( nType );

		if ( nType == STATE_CHANGE_CONTROLBACKGROUND )
		{
			// Check if we need to get new images for normal/high contrast mode
			checkImageList();
		}
		else if ( nType == STATE_CHANGE_TEXT )
		{
			// The physical toolbar changed its outlook and shows another logical toolbar!
			// We have to set the correct high contrast mode on the new tbx manager.
			//	pMgr->SetHiContrast( IsHiContrastMode() );
			checkImageList();
		}
	}
	// -----------------------------------------------------------------------------
	void DbaIndexDialog::DataChanged( const DataChangedEvent& rDCEvt )
	{
		ModalDialog::DataChanged( rDCEvt );

		if ((( rDCEvt.GetType() == DATACHANGED_SETTINGS	)	||
			( rDCEvt.GetType() == DATACHANGED_DISPLAY	))	&&
			( rDCEvt.GetFlags() & SETTINGS_STYLE		))
		{
			// Check if we need to get new images for normal/high contrast mode
			checkImageList();
		}
	}
	//------------------------------------------------------------------
	ImageList DbaIndexDialog::getImageList(sal_Int16 _eBitmapSet,sal_Bool _bHiContast) const
	{
		sal_Int16 nN = IMG_INDEX_DLG_SC;
		sal_Int16 nH = IMG_INDEX_DLG_SCH;
		if ( _eBitmapSet == SFX_SYMBOLS_SIZE_LARGE )	 	 
		{
			nN = IMG_INDEX_DLG_LC;
			nH = IMG_INDEX_DLG_LCH;
		} // if ( _eBitmapSet == SFX_SYMBOLS_LARGE )
		return ImageList(ModuleRes( _bHiContast ? nH : nN ));
	}
	//------------------------------------------------------------------
	void DbaIndexDialog::resizeControls(const Size& _rDiff)
	{
		// we use large images so we must change them
		Size aTbNewSize = m_aActions.GetSizePixel();
		if ( _rDiff.Width() || _rDiff.Height() )
		{
			Size aDlgSize = GetSizePixel();
			// adjust size of dlg
			SetSizePixel(Size(aDlgSize.Width() + _rDiff.Width(),
							  aDlgSize.Height() + _rDiff.Height())	
						);
			Size aIndexSize = m_aIndexes.GetSizePixel();
			m_aIndexes.SetPosSizePixel(m_aIndexes.GetPosPixel() + Point(0,_rDiff.Height()),
									Size(aIndexSize.Width() + _rDiff.Width(),
									     aIndexSize.Height()));

			//now move the rest to the left side
			Point aMove(_rDiff.Width(),_rDiff.Height());
			m_aIndexDetails.SetPosPixel(m_aIndexDetails.GetPosPixel() + aMove);
			m_aDescriptionLabel.SetPosPixel(m_aDescriptionLabel.GetPosPixel() + aMove);
			m_aDescription.SetPosPixel(m_aDescription.GetPosPixel() + aMove);
			m_aUnique.SetPosPixel(m_aUnique.GetPosPixel() + aMove);
			m_aFieldsLabel.SetPosPixel(m_aFieldsLabel.GetPosPixel() + aMove);
			OSL_ENSURE(m_pFields,"NO valid fields!");
			m_pFields->SetPosPixel(m_pFields->GetPosPixel() + aMove);
			m_aClose.SetPosPixel(m_aClose.GetPosPixel() + aMove);
			m_aHelp.SetPosPixel(m_aHelp.GetPosPixel() + aMove);

			Invalidate();
		}
	}

//......................................................................
}	// namespace dbaui
//......................................................................

