/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     2001/03/28
// RCS-ID:      $Id: codecompl_svc.cpp,v 1.5 2001/04/25 23:40:50 georgetasker Exp $
// Copyright:   (c) Aleksandras Gluchovas (alex@soften.ktu.lt)
// Licence:   	GPL (version 2.0 or later)
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
    #pragma implementation "classfinder_svc.cpp"
    #pragma interface "classfinder_svc.h"
#endif

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif
#include "wx/wspp/rescrolw.h"
#include "codecompl_svc.h"

static const wxSize _gComplPanelSize( 270, 150 ); // default

/***** Implementation for class wxPMCodeCompletionService *****/

IMPLEMENT_DYNAMIC_CLASS( wxPMCodeCompletionService, wxPMSimpleService )

BEGIN_EVENT_TABLE( wxPMCodeCompletionService, wxPMSimpleService )

END_EVENT_TABLE()

wxPMCodeCompletionService::wxPMCodeCompletionService()
{
}

wxPMCodeCompletionService::~wxPMCodeCompletionService()
{
}

// overriden methods of wxPMService

bool wxPMCodeCompletionService::Start()
{
	return TRUE;
}

bool wxPMCodeCompletionService::Stop()
{
	return TRUE;
}

wxPMMergableMenuChain* wxPMCodeCompletionService::GetMergableMenuChain()
{
	return NULL;
}

/***** Implementation for class wxsTypingMonitor *****/

BEGIN_EVENT_TABLE( wxsTypingMonitor, wxEvtHandler )

END_EVENT_TABLE()

wxsTypingMonitor::wxsTypingMonitor( wxPMCodeCompletionService* svc, wxPMDocument* doc )

	: mpSvc( svc ),
	  mpDoc( doc ),
	  mIsEnabled( FALSE )
{
}

void wxsTypingMonitor::OnChar( wxKeyEvent& event )
{
}

void wxsTypingMonitor::OnKeyDown( wxKeyEvent& event )
{
}

void wxsTypingMonitor::Enable( bool enable )
{
}

/***** Implementation for class TCtxListPinPainter *****/

wxBitmap TCtxListPinPainter::mBmps[11];

#define WXS_ICON( name ) wxPMBmpMgr().FindBitmap( "code##name" )

TCtxListPinPainter::TCtxListPinPainter()

	: TPinPainterBase( CTX_LIST_PIN_TC ),
	  mBkBrush( wxColour( 255,255,255 ), wxSOLID )
{
	if ( mBmps[0].Ok() ) return;

	mBmps[CLASS_IMAGE]		  = WXS_ICON(class);
	mBmps[PRIV_MTD_IMAGE]	  = WXS_ICON(priv_mtd);
	mBmps[PROT_MTD_IMAGE]	  = WXS_ICON(prot_mtd);
	mBmps[PUB_MTD_IMAGE]	  = WXS_ICON(pub_mtd);
	mBmps[PRIV_VAR_IMAGE]	  = WXS_ICON(priv_var);
	mBmps[PROT_VAR_IMAGE]	  = WXS_ICON(prot_var);
	mBmps[PUB_VAR_IMAGE]	  = WXS_ICON(pub_var);
	mBmps[PUB_PURE_MDT_IMAGE] = WXS_ICON(pub_pure_mtd);
	mBmps[PRIV_MTD_DEF_IMAGE] = WXS_ICON(priv_mtd_def);
	mBmps[PROT_MTD_DEF_IMAGE] = WXS_ICON(prot_mtd_def);
	mBmps[PUB_MTD_DEF_IMAGE]  = WXS_ICON(pub_mtd_def);
}

void TCtxListPinPainter::DrawPin( TPinBase* pPin, wxTextEditorView& view, wxDC& dc, 
								  const wxPoint& pos, const wxSize& dim )
{
	CtxListPin* pMyPin = (CtxListPin*)pPin;

	wxBitmap& bmp = mBmps[ pMyPin->mIconNo ];

	dc.DrawBitmap( bmp, pos.x, pos.y, FALSE );

	int w = bmp.GetWidth();
	int h = bmp.GetHeight();

	// fill in gaps around image

	if ( w < dim.x )
	{
		dc.SetPen( *wxTRANSPARENT_PEN );
		dc.SetBrush( mBkBrush );

		dc.DrawRectangle( pos.x + w, pos.y,
			              dim.x - w + 1, dim.y + 1 );
	}
	
	if ( h < dim.y )
	{
		if ( !(w < dim.x) )
		{
			dc.SetPen( *wxTRANSPARENT_PEN );
			dc.SetBrush( mBkBrush );
		}

		dc.DrawRectangle( pos.x, pos.y + h,
			              w + 1, dim.y - h + 1);
	}
}

/***** Implementation for class ContextListBox *****/

BEGIN_EVENT_TABLE( ContextListBox, wxTextEditorView )

	EVT_CHAR    ( ContextListBox::OnChar    )
	EVT_KEY_DOWN( ContextListBox::OnKeyDown )
	EVT_KILL_FOCUS( ContextListBox::OnKillFocus )
	EVT_LEFT_DCLICK( ContextListBox::OnDblClick )

END_EVENT_TABLE()

static int ctx_visibility_to_var_image_no( int flags, int type, bool isDef = TRUE )
{
	if ( flags & SP_FLAG_VIS_PRIVATE )
	{
		if ( type == SP_CTX_ATTRIBUTE )
			return TCtxListPinPainter::PRIV_VAR_IMAGE;
		else
			return isDef ? TCtxListPinPainter::PRIV_MTD_DEF_IMAGE
						 : TCtxListPinPainter::PRIV_MTD_IMAGE;
	}			
	else
	if ( flags & SP_FLAG_VIS_PROTECTED )
	{
		if ( type == SP_CTX_ATTRIBUTE )
			return TCtxListPinPainter::PROT_VAR_IMAGE;
		else
			return isDef ? TCtxListPinPainter::PROT_MTD_DEF_IMAGE
						 : TCtxListPinPainter::PROT_MTD_IMAGE;
	}
	else
	{
		if ( type == SP_CTX_ATTRIBUTE )
			return TCtxListPinPainter::PUB_VAR_IMAGE;
		else
			return isDef ? TCtxListPinPainter::PUB_MTD_DEF_IMAGE
						 : TCtxListPinPainter::PUB_MTD_IMAGE;
	}
}

ContextListBox::ContextListBox( wxWindow*     parent, 
							          wxWindowID    id,
									  MMemberListT& ctxList,
									  spFile*       pFile )

	: wxTextEditorView( parent, id, new wxTextEditorModel(), 0 ),
	  mpFile( pFile ),
	  mPopupMode( FALSE )
{
	mCtxList = ctxList;

	SetLineTrackingMode( TRUE, wxColour( 0,255,255 ) );

	mpModel->AddView( this );
	mpModel->SetActiveView( this );

	AddPinPainter( new TCtxListPinPainter() );

	wxString text;

	size_t i = 0;
	for( ; i != mCtxList.Count(); ++i )
	{
		if ( i != 0 ) text += '\n';

		spContext* pCtx = mCtxList[i];
		int iconNo = -1;
		
		if ( pCtx->GetType() == SP_CTX_CLASS )
	
			text += pCtx->GetName();
		else
		if ( pCtx->GetType() == SP_CTX_ATTRIBUTE )
		{
			spAttribute* pAttr = (spAttribute*)pCtx;
	
			text += pCtx->GetName() + " : " + pAttr->mType;
		}
		else
		if ( pCtx->GetType() == SP_CTX_OPERATION )
		{
			spOperation* pOp = (spOperation*)pCtx;

			text += pOp->FormatSignature( TRUE, TRUE, FALSE, FALSE, TRUE );

			if ( pOp->mRetType.length() )

				text += " : " + pOp->mRetType;
		}
	}

	mpModel->InsertText( TPosition( 0,0 ), text.c_str(), text.length() );

	for( i = 0; i != mCtxList.Count(); ++i )
	{
		if ( i != 0 ) text += '\n';

		spContext* pCtx = mCtxList[i];
		int iconNo = -1;
		
		if ( pCtx->GetType() == SP_CTX_CLASS )
		
			iconNo = TCtxListPinPainter::CLASS_IMAGE;
		else
		if ( pCtx->GetType() == SP_CTX_ATTRIBUTE )
		
			iconNo = ctx_visibility_to_var_image_no( pCtx->mFlags, pCtx->GetType() );
		else
		if ( pCtx->GetType() == SP_CTX_OPERATION )
		{
			spOperation* pOp = (spOperation*)pCtx;

			if ( pOp->mFlags & SP_FLAG_IS_DEFINITION  ||
				 pOp->mScope.length() != 0 )
			
				iconNo = ctx_visibility_to_var_image_no( pCtx->mFlags, pCtx->GetType(), TRUE );
			else
				iconNo = ctx_visibility_to_var_image_no( pCtx->mFlags, pCtx->GetType(), FALSE );
		}

		mpModel->AddPin( new CtxListPin( i, iconNo ) );
	}

	SetMaxColumns( 250 );

	// FIXME:: wxTextEditorView's bug??
	
	SetRowsPerPage( GetRowsPerPage() - 1 );
}

ContextListBox::~ContextListBox()
{
	/*
	if ( mpFile ) 
		
		delete mpFile;
	else
	{
		for( size_t i = 0; i != mCtxList.Count(); ++i )

			delete mCtxList[i];
	}
	*/
}

void ContextListBox::SetPopupMode( bool on )
{
	mPopupMode = on;
}

#define ALPHA_NUM( ch ) ( ( ch >= 'a' && ch <= 'z' ) || \
	                      ( ch >= 'A' && ch <= 'Z' ) || \
						  ( ch >= '0' && ch <= '9' ) || \
						  ( ch == '_' ) )

#define UPPER_CHAR( ch ) ( ( ch >= 'a' && ch <='z' ) ? ch - 'a' + 'A' : ch )

void ContextListBox::OnChar( wxKeyEvent& event )
{
	if ( IsClipboardCmd( event ) )
	{
		event.Skip();
		return;
	}

	if ( !event.ControlDown() &&
		 !event.AltDown() )
	{
			if ( event.m_keyCode == WXK_BACK )
			{
				if ( mName.length() != 0 ) 
				
					mName.erase( mName.length() -1, 1 );
				else
				{
					OnCloseList( event );
					return;
				}

				OnKeyReceived( event );
			}
			else
			{
				if ( ALPHA_NUM( event.m_keyCode ) )

					mName += event.m_keyCode;

				OnKeyReceived( event );
			}

			if ( ALPHA_NUM( event.m_keyCode ) )

				mpModel->OnGotoLine( FindClosestMatch(), 0 );
	}

	// do not let it propagate to wxTextEditorView
}

void ContextListBox::OnKeyDown( wxKeyEvent& event )
{
	if ( IsClipboardCmd( event ) )
	{
		event.Skip();
		return;
	}

	if ( !event.ControlDown() &&
		 !event.AltDown()     &&
		 !event.ShiftDown() )
	{
		if ( event.m_keyCode == WXK_LEFT ) 
		
			OnCloseList( event );
		else
		if ( event.m_keyCode == WXK_RIGHT ) 
		
			OnCloseList( event );
		else
		if ( event.m_keyCode == WXK_UP ) 
		
			mpModel->OnMoveUp();
		else
		if ( event.m_keyCode == WXK_DOWN ) 
		
			mpModel->OnMoveDown();
		else
		if ( event.m_keyCode == WXK_HOME ) 
		
			mpModel->OnStartOfText();
		else
		if ( event.m_keyCode == WXK_END ) 
		
			mpModel->OnEndOfText();
		else
		if ( event.m_keyCode == WXK_NEXT ) 
		
			mpModel->OnPageDown();
		else
		if ( event.m_keyCode == WXK_PRIOR ) 
		
			mpModel->OnPageUp();
		else
		if ( event.m_keyCode == WXK_DELETE )

			OnCloseList( event );
		else
		if ( event.m_keyCode == WXK_ESCAPE )

			OnCloseList( event );	
		else
		if ( ( event.m_keyCode >= WXK_START && 
			   event.m_keyCode != WXK_SHIFT )
			  && mPopupMode
		   )
		   OnCloseList( event );
	}
	else
	{
		if ( event.m_keyCode == WXK_INSERT && mPopupMode )

			OnCloseList( event );	
		else
		if ( !event.ShiftDown() )

			OnCloseList( event );
	}
}

void ContextListBox::OnKillFocus( wxFocusEvent& event )
{
	OnFocusKilled();
}

void ContextListBox::OnDblClick( wxMouseEvent& event )
{
	wxKeyEvent key;
	key.m_keyCode = WXK_RETURN;
	key.m_altDown     = FALSE;
	key.m_controlDown = FALSE;
	key.m_shiftDown   = FALSE;

	OnChar( key );
}

int ContextListBox::FindClosestMatch()
{
	size_t closest = 0, maxMatches = 0;

	wxString& s2 = mName;

	for( size_t i = 0; i != mCtxList.Count(); ++i )
	{
		wxString& s1 = mCtxList[i]->GetName();
		
		size_t len = wxMin( s1.length(), s2.length() );

		char c1 = UPPER_CHAR(s1[0u]);
		char c2 = UPPER_CHAR(s2[0u]);

		size_t n;
		for( n = 0; n != len && 

			 UPPER_CHAR(s1[n]) == UPPER_CHAR(s2[n]) ; ++n );

		if ( n > maxMatches )
		{
			closest = i;
			maxMatches = n;
		}
	}

	return closest;
}

spContext* ContextListBox::GetMatchingContext()
{
	return mCtxList[ mpModel->GetCursor().mRow ];
}

/***** Implementation for class AutoComplPanel *****/

BEGIN_EVENT_TABLE( AutoComplPanel, wxPanel )

END_EVENT_TABLE()

AutoComplPanel::AutoComplPanel( wxPMDocument* doc,
							    wxsTypingMonitor* monitor,
							    MMemberListT& ctxList,
							    const wxPoint& pos,
							    const wxSize&  Count )

	: wxPanel( doc->GetFirstPMView()->GetViewFrame(), -1, 
			   pos, Count, wxSUNKEN_BORDER ),
	  mpCtxBox( NULL  ),
	  mpArgBox( NULL ),
	  mpMonitor( monitor ),
	  mpDoc( doc )
{
	SetBackgroundColour( wxColour( 255,255,255 ) );

	monitor->Enable( FALSE );

	mCtxList = ctxList;

	mpCtxBox = new AutoComplBox( this, ctxList, NULL );

	int w = 0, h = 0;
	GetClientSize( &w, &h );

	mpCtxBox->SetPopupMode( TRUE );
	mpCtxBox->SetSize( 0,0, w,h );
	mpCtxBox->SetFocus();

	//mpFile->GetEditor().HoldCursor( TRUE );

	mpArgBox     = NULL;
	mArgHintMode = FALSE;
}

AutoComplPanel::~AutoComplPanel()
{
	//wxMessageBox("AutoComplPanel destoryed!");

	mpMonitor->Enable( TRUE );

	for( size_t i = 0; i != mCtxList.Count(); ++i )

		delete mCtxList[i];

	//TX::
	//mpFile->GetEditor().HoldCursor( FALSE );
}

bool AutoComplPanel::CalcPanelPosition( bool      above,
									    wxSize&   size,
									    wxPoint&  pos )
{
	wxSize charSz = mpInfTextEdit->GetCharacterSize();

	int w = 0, h = 0;
/*
	I_EditableText*    pp;
	pp->GetTextWidgetWindow();
*/
	mpInfTextEdit->GetTextWidgetWindow();

	int line=0, col=0, x=0, y=0;
	mpInfContText->GetCursorPosition( &line, &col );

	mpInfTextEdit->TextPosToScreenPos( line, col, &x, &y );
	pos.x = x;
	pos.y = y;

	// first, decide whether it's enough space above or
	// below (as requested), and change this setting if
	// it's really necessary

	int spcAbove = pos.y - charSz.y;
	int spcBelow = h - pos.y;


	if ( above && 
		 spcAbove < size.y && spcBelow > spcAbove )

		 above = FALSE;
	else
	if ( !above &&
		 spcBelow < size.y && spcAbove > spcBelow )

		 above = TRUE;
		
	// clip child's dimensions if necessary

	if ( above &&
		 spcAbove < size.y )

		 size.y -= size.y - spcAbove;
	else
	if ( !above &&
		 spcBelow < size.y )
		 
		 size.y -= size.y - spcBelow;

	// position vertically

	if ( above )
	
		pos.y -= charSz.y + size.y; 

	// position horizontally

	if ( pos.x + size.x > w )
	{
		if ( size.x > w )
		{
			size.x = w;
			pos.x = 0;
		}
		else
			pos.x = w - size.x;
	}

	int cut = 0; // FOR NOW::

	if ( pos.x + size.x >= h ) 
	
		size.x -= cut;

	if ( pos.y == 0 )
	{
		pos.y  += cut;
		size.y -= cut;
	}

	if ( pos.y + size.y >= h )
	
		size.y -= cut;

	return above;
}

void AutoComplPanel::StoreScreenPos()
{
	int line=0, col=0;
	mpInfContText->GetCursorPosition( &line, &col );

	mPrevCursorLine = line;
	mpInfTextEdit->GetPagePosition( &mPrevPageLine, &mPrevPageCol );
}

// overriden methods of ContextListBox

void AutoComplPanel::OnCloseList( wxKeyEvent& key )
{
	mpInfTextEdit->GetTextWidgetWindow()->ProcessEvent( key );

	if ( mpCtxBox ) 
		mpCtxBox->SetEnabled( FALSE );

	Destroy();
}

void AutoComplPanel::OnKeyReceived( wxKeyEvent& key )
{
	if ( !mArgHintMode && key.m_keyCode == WXK_RETURN )
	{
		CompleteName();
		return;
	}

	mpInfTextEdit->GetTextWidgetWindow()
		->GetEventHandler()
			->ProcessEvent( key );

	if ( !mArgHintMode && !ALPHA_NUM( key.m_keyCode ) )
	{
		if ( key.m_keyCode == '(' &&
			 MethodPresent( mpCtxBox->GetTypedName() ) )
		{
			if ( !TrySwitching() ) 
			{
				mpCtxBox->SetEnabled( FALSE );
				Destroy();
				return;
			}
		}
		if ( key.m_keyCode != WXK_BACK )
		{
			mpCtxBox->SetEnabled( FALSE );
			Destroy();
			return;
		}
	}

	// popup-window refreshing logic

	// FIXME:: messy...

	{
		int line=0, col=0, pgLine, pgCol;
		mpInfContText->GetCursorPosition( &line, &col );
		mpInfTextEdit->GetPagePosition( &pgLine, &pgCol );

		if ( mPrevCursorLine != line || key.m_keyCode == WXK_RETURN ||
			 pgLine != mPrevPageLine ||
			 pgCol  != mPrevPageCol  )
		{
			if ( !( mIsAbove && 
					pgLine == mPrevPageLine &&
					pgCol  == mPrevPageCol )
			   )
			{
				Show( FALSE );
				RepositionPanel();
				Show( TRUE );
			}
		}

	}
}

void AutoComplPanel::OnFocusKilled()
{
	Destroy();
}

void AutoComplPanel::OnSize( wxSizeEvent& event )
{
	if ( !mpCtxBox ) return;

	int w = 0, h = 0;
	GetClientSize( &w, &h );

	if ( mArgHintMode )

		mpArgBox->SetSize( 0,0, w,h );
	else
		mpCtxBox->SetSize( 0,0, w,h );
}

/*** protected methods ***/

bool AutoComplPanel::MethodPresent( const wxString& name )
{
	for( size_t i = 0; i != mCtxList.Count(); ++i )

		if ( mCtxList[i]->GetType() == SP_CTX_OPERATION &&
		     mCtxList[i]->GetName() == name ) return TRUE;

	return FALSE;
}

void AutoComplPanel::FindOverloads( MMemberListT& ops, const wxString& name )
{
	for( size_t i = 0; i != mCtxList.Count(); ++i )

		if ( mCtxList[i]->GetType() == SP_CTX_OPERATION && 
		     mCtxList[i]->GetName() == name ) 

			ops.Add( mCtxList[i] );
}

void AutoComplPanel::CompleteName()
{
	spContext* pMember = mpCtxBox->GetMatchingContext();

	wxString fullName = pMember->GetName();
	wxString typed    = mpCtxBox->GetTypedName();

	size_t i = 0;
	size_t min = wxMin( fullName.length(), typed.length() );

	while( i < min && typed[i] == fullName[i] )
		++i;

	int line = 0, col = 0;
	mpInfContText->GetCursorPosition( &line, &col );

	if ( i < typed.length() )
	{
		// truncate unmatched trailing characters

		int dif = typed.length() - i;

		mpInfTextEdit->DeleteRenage( line, col - dif, line, col );
		col -= dif;
	}

	if ( i < fullName.length() )
	{
		// append missing characters

		int dif = fullName.length() - i;

		wxString ins( fullName, i, dif );

		mpInfTextEdit->InsertText( ins, line, col );

		col += dif;
	}

	mpInfTextEdit->SetCursorPosition( line, col );

	if ( pMember->GetType() == SP_CTX_OPERATION )
	{
		// QUCIK-N-DIRTY::  use InsertText() instead of ProcessEvent()

		wxEvtHandler* pHandler = mpInfTextEdit->GetTextWidgetWindow()->GetEventHandler();

		wxKeyEvent key( wxEVT_CHAR );
		key.m_altDown     = FALSE;key.m_controlDown = FALSE;
		key.m_metaDown    = FALSE;key.m_shiftDown   = FALSE;
		key.m_keyCode     = '(';

		pHandler->ProcessEvent( key );

		MMemberListT ops;
		FindOverloads( ops, fullName );

		if ( pMember->GetMembers().Count() == 0 &&
			 ops.Count() == 1 
		   )
		{
			key.m_keyCode = ')';
			pHandler->ProcessEvent( key );
		}
		else
		{
			SwitchToHintMode( ops );
			return;
		}
	}

	mpCtxBox->SetEnabled( FALSE );
	Destroy();
}

void AutoComplPanel::RepositionPanel()
{
	wxPoint pos;
	wxSize  size = ( mArgHintMode ) ? mpArgBox->GetBoxSize()
									: _gComplPanelSize;

	if ( mArgHintMode )
	{
		size.y += 10;
		size.x += 10;
	}

	mIsAbove = AutoComplPanel::CalcPanelPosition( TRUE, size, pos );

	this->SetSize( pos.x, pos.y, size.x, size.y );

	StoreScreenPos();
}

void AutoComplPanel::SwitchToHintMode( MMemberListT& ops )
{
	wxSize charSize = mpCtxBox->GetCharacterSize();
	mpCtxBox->SetEnabled( FALSE );
	mpCtxBox->Destroy();
	mpCtxBox = NULL;
	mArgHintMode = TRUE;

	int w = 0, h = 0;
	mpInfTextEdit->GetTextWidgetWindow()->GetClientSize( &w, &h );

	mpArgBox = new ArgListBox( this, ops, w, charSize );

	RepositionPanel();

	GetClientSize( &w, &h );
	mpArgBox->SetSize( 0,0, w,h );
	mpArgBox->SetFocus();

	mpInfTextEdit->HoldCursor( TRUE );
}

bool AutoComplPanel::TrySwitching()
{
	MMemberListT ops;
	FindOverloads( ops, mpCtxBox->GetTypedName() );

	if ( ops.Count() > 1 || 
		 ops[0]->GetMembers().Count() != 0 )
	{
		 SwitchToHintMode( ops );
		 return TRUE;
	}
	else
		return FALSE;
}

/***** Implementation for class AutoComplBox *****/

AutoComplBox::AutoComplBox( AutoComplPanel* panel, 
							MMemberListT&   ctxList,
							spFile*         file )

	: ContextListBox( panel, -1, ctxList, file ),
	  mpPanel( panel ),
	  mEnabled( TRUE )
{}

void AutoComplBox::OnCloseList( wxKeyEvent& key ) 
{ 
	if ( mEnabled ) mpPanel->OnCloseList( key ); 
}

void AutoComplBox::OnKeyReceived( wxKeyEvent& key ) 
{ 
	if ( mEnabled ) mpPanel->OnKeyReceived( key ); 
}

void AutoComplBox::OnFocusKilled() 
{ 
	if ( mEnabled ) mpPanel->OnFocusKilled(); 
}

void AutoComplBox::SetEnabled( bool enabled ) 
{ 
	mEnabled = enabled; 
}

/***** Implementation for class ArgListBox *****/

BEGIN_EVENT_TABLE( ArgListBox, wxTextEditorView )

	EVT_CHAR      ( ArgListBox::OnChar      )
	EVT_KEY_DOWN  ( ArgListBox::OnKeyDown   )
	EVT_KILL_FOCUS( ArgListBox::OnKillFocus )

END_EVENT_TABLE()


ArgListBox::ArgListBox( AutoComplPanel* pPanel,
						MMemberListT&      mtdList,
						int                maxWidth,
						const wxSize&      charDim )

	: wxTextEditorView( pPanel, -1, new wxTextEditorModel(), 0 ),
	  mpPanel( pPanel ),
	  mName( "(" )
{
	wxString text;

	maxWidth /= charDim.x;

	int actualMaxWidth = 0;

	int curLine =  0;
	int curWidth = 0;

	for( size_t i = 0; i != mtdList.Count(); ++i )
	{
		spOperation* pOp = (spOperation*)mtdList[i];

		if ( i != 0 )
		{
			text += '\n';
			++curLine;
		}

		curWidth = 0;

		text += '(';
		++curWidth;

		for( size_t k = 0; k != pOp->GetMembers().Count(); ++k )
		{
			wxString parText;

			spParameter* pPar = (spParameter*)pOp->GetMembers()[k];

			parText += pPar->mType;
			parText += ' ';
			parText += pPar->mName;
			
			if ( pPar->mInitVal.length() )
			{
				parText += " = ";
				parText += pPar->mInitVal;
			}

			if ( k > 0 && curWidth + parText.length() + 2 >= maxWidth )
			{
				actualMaxWidth = wxMax( curWidth + 1, actualMaxWidth );

				curWidth = parText.length();
				++curLine;

				text += ",\n" + parText;
			}
			else
			{
				if ( k > 0 ) 
				{
					text += ", ";
					curWidth += 2;
				}

				text += parText;
				curWidth += parText.length();
			}
		}

		text += ')';
		++curWidth;

		actualMaxWidth = wxMax( curWidth, actualMaxWidth );
	}

	SetMargins( 0,0, 0,0 );

	SetBackgroundColour( wxColour( 224,224,224 ) );

	SetColours( wxColour( 224,224,224 ), // normalBkCol 
				wxColour(   0,  0,128 ), // selectedBkCol
				wxColour( 255,255,255 ) // selectedTextCol
			  );

	EnableScrollbars( FALSE );
	EnableCursor( FALSE );

	mBoxDim.y = ( curLine + 1 ) * charDim.y;
	mBoxDim.x = actualMaxWidth * charDim.x;

	mpModel->InsertText( TPosition(0,0), text.c_str(), text.length() );

	Activate();
}

void ArgListBox::OnChar( wxKeyEvent& event )
{
	if ( IsClipboardCmd( event ) )
	{
		event.Skip();
		return;
	}

	if ( !event.ControlDown() &&
		 !event.AltDown() )
	{
		if ( event.m_keyCode == WXK_BACK )
		{
			if ( mName.length() != 0 ) 
			
				mName.erase( mName.length() -1, 1 );

			mpPanel->OnKeyReceived( event );
		}
		else
		{
			mName += event.m_keyCode;
		}

		int level = 0;
		for( size_t i = 0; i != mName.length(); ++i )
		{
			if ( mName[i] == '(' ) ++level;
			if ( mName[i] == ')' ) --level;
		}

		if ( level == 0 ) 
			
			mpPanel->OnCloseList( event );
		else
		{
			if ( event.m_keyCode != WXK_BACK )

				mpPanel->OnKeyReceived( event );
		}
	}
	else
		mpPanel->OnCloseList( event );
}

void ArgListBox::OnKeyDown( wxKeyEvent& event )
{
	if ( IsClipboardCmd( event ) )
	{
		event.Skip();
		return;
	}

	if ( event.ControlDown() ||
		 event.AltDown() )

		 mpPanel->OnCloseList( event );
	else
	if ( event.m_keyCode == WXK_ESCAPE )

		mpPanel->OnCloseList( event );
	else
	if ( event.m_keyCode >= WXK_START && 
	     event.m_keyCode != WXK_SHIFT &&
		 event.m_keyCode != WXK_RETURN 
	   )

		mpPanel->OnCloseList( event );
}

void ArgListBox::OnKillFocus( wxFocusEvent& event )
{
	mpPanel->OnFocusKilled();
}
