/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     2000/02/08
// RCS-ID:      $Id: pm.cpp,v 1.20 2001/04/20 10:17:11 georgetasker Exp $
// Copyright:   (c) Aleksandras Gluchovas
// Licence:   	GPL (version 2.0 or later)
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
    #pragma implementation "pm.cpp"
    #pragma interface "pm.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 "build.h"  // wxWorkshop PROGRAM_xxxxx constants

#include "wx/pmf/pm.h"
#include "wx/pmf/wsAboutDialog.h"
//#include <winsvc.h> causing errors

#include "wx/dirdlg.h"

/***** Implementation for class wxPMBootstrapServiceListener *****/

void wxPMBootstrapServiceListener::OnServiceStarted( wxPMService& service )
{}

void wxPMBootstrapServiceListener::OnServiceStopped( wxPMService& service )
{}

void wxPMBootstrapServiceListener::OnPaneShown( wxPMPane& pane )
{}

void wxPMBootstrapServiceListener::OnPaneHidden( wxPMPane& pane )
{}


/***** Implementation for class PMServiceAssociationInfo *****/

PMServiceAssociationInfo::~PMServiceAssociationInfo()
{
	for( size_t i = 0; i != mData.Count(); ++i )
	
		delete (wxObject*)mData[i];
}

/***** Implementation for class wxPMBootstrapService *****/

// TBD:: should be made statics of wxPMBootstrapService class

// mergable menus for bootstrap service

static wxCommandInfo _gBootstrapCommands1[] = 
	{
		//glt	wxCommandInfo( -1, "SEPARATOR" ),
		//glt	wxCommandInfo( wxPMBootstrapService::ID_BOOTSTRAP_ON_ABOUT, "Abou&t" ), 
		//glt	wxCommandInfo( wxPMBootstrapService::ID_BOOTSTRAP_CHANGE_DIR, "&Change Working Directory..." ), 
		//glt	wxCommandInfo( -1, "SEPARATOR" ),
		wxCommandInfo( wxPMBootstrapService::ID_BOOTSTRAP_ON_EXIT, "E&xit", "", "Alt+X" ), 

		wxCommandInfo( 0, "" ) // terminator
	};


static wxCommandInfo _gBootstrapCommands2[] =  // EDIT
	{
		wxCommandInfo( wxPMBootstrapService::ID_BOOTSTRAP_ON_ABOUT, "Dummy" ), 

		wxCommandInfo( 0, "" ) // terminator
	};


static wxCommandInfo _gBootstrapCommands3[] = // VIEW
	{
		wxCommandInfo( 0, "" ) // terminator
	};


static wxCommandInfo _gBootstrapCommands4[] = // PROJECT
	{
		wxCommandInfo( 0, "" ) // terminator
	};


static wxCommandInfo _gBootstrapCommands5[] = // BUILD
	{
		wxCommandInfo( 0, "" ) // terminator
	};


static wxCommandInfo _gBootstrapCommands6[] = // EVENTS
	{
		wxCommandInfo( 0, "" ) // terminator
	};


static wxCommandInfo _gBootstrapCommands7[] =   // OPTIONS
	{
		wxCommandInfo( wxPMBootstrapService::ID_BOOTSTRAP_OPTIONS_EDITOR_PREFS, "&Editor preferences..." ), 
		wxCommandInfo( -1, "SEPARATOR" ),
		wxCommandInfo( wxPMBootstrapService::ID_BOOTSTRAP_OPTIONS_DIRECTORIES, "&Directories..." ), 
		wxCommandInfo( wxPMBootstrapService::ID_BOOTSTRAP_OPTIONS_CHANGE_CWD, "&Change current directory..." ), 
		wxCommandInfo( -1, "SEPARATOR" ),
		wxCommandInfo( wxPMBootstrapService::ID_BOOTSTRAP_OPTIONS_KEYBINDINGS, "&Keyboard bindings..." ), 

		wxCommandInfo( 0, "" ) // terminator
	};


static wxCommandInfo _gBootstrapCommands8[] = {

	wxCommandInfo( wxPMBootstrapService::ID_BOOTSTRAP_ON_ABOUT, "&About", "", "Alt+A" ), 
	wxCommandInfo( 0, "" ) // terminator
};


static wxPMMergableMenuChain _gBootstrapChains[] = {

	wxPMMergableMenuChain( "/&File/",	_gBootstrapCommands1 ),  // first chain
	wxPMMergableMenuChain( "/&Edit/",	_gBootstrapCommands2 ),
	wxPMMergableMenuChain( "/&View/",	_gBootstrapCommands3 ),
	wxPMMergableMenuChain( "/&Project/",_gBootstrapCommands4 ),
	wxPMMergableMenuChain( "/&Build/",	_gBootstrapCommands5 ),
//	wxPMMergableMenuChain( "/E&vents/", _gBootstrapCommands6 ),
	wxPMMergableMenuChain( "/&Options/",_gBootstrapCommands7 ),
	wxPMMergableMenuChain( "/&Help/",	_gBootstrapCommands8 ),

	wxPMMergableMenuChain( "", NULL ) // terminator
};

BEGIN_EVENT_TABLE( wxPMBootstrapService, wxEvtHandler )

	EVT_CUSTOM_RANGE( wxEVT_COMMAND_MENU_SELECTED,  500, 5000, wxPMBootstrapService::OnMenuEvent )
	EVT_CUSTOM_RANGE( wxEVT_COMMAND_BUTTON_CLICKED, 500, 5000, wxPMBootstrapService::OnButtonEvent )
	EVT_CUSTOM_RANGE( wxEVT_UPDATE_UI, 500, 5000, wxPMBootstrapService::OnUpdateUIEvent )

	EVT_MENU( wxPMBootstrapService::ID_BOOTSTRAP_ON_EXIT,  wxPMBootstrapService::OnExit )
	EVT_MENU( wxPMBootstrapService::ID_BOOTSTRAP_ON_ABOUT, wxPMBootstrapService::OnAbout )
	EVT_MENU( wxPMBootstrapService::ID_BOOTSTRAP_ON_DUMMY, wxPMBootstrapService::OnDummy )
	EVT_IDLE( wxPMBootstrapService::OnIdle )
	EVT_MENU( wxPMBootstrapService::ID_BOOTSTRAP_OPTIONS_EDITOR_PREFS,	wxPMBootstrapService::OnOptionsEditorPrefs )
	EVT_MENU( wxPMBootstrapService::ID_BOOTSTRAP_OPTIONS_DIRECTORIES,	wxPMBootstrapService::OnOptionsDirectories )
	EVT_MENU( wxPMBootstrapService::ID_BOOTSTRAP_OPTIONS_CHANGE_CWD,	wxPMBootstrapService::OnOptionsChangeCWD )
	EVT_MENU( wxPMBootstrapService::ID_BOOTSTRAP_OPTIONS_KEYBINDINGS,	wxPMBootstrapService::OnOptionsKeyBindings )
//	EVT_MENU( wxPMBootstrapService::ID_BOOTSTRAP_CHANGE_DIR,  wxPMBootstrapService::OnChangeDir )

END_EVENT_TABLE()


wxPMBootstrapService::wxPMBootstrapService( wxDocMDIParentFrame* pMainWnd, 
										    wxPMDocManager* pDocMgr )

	: m_pWorkplace( NULL ),
	  m_pMainWnd( pMainWnd ),
	  m_pDocMgr( pDocMgr ),
	  m_RecreatePending( FALSE ),
	  m_AppTitle( "Default-PMF-Application-Title" ),
	  m_MenuItemsHash( wxKEY_INTEGER ),
	  m_PerServiceDataHash( wxKEY_INTEGER ),
	  m_UniqueInstHash( wxKEY_STRING )
{
	wxChar *buf = ::wxGetWorkingDirectory();
	mStartupDir = buf;

	delete buf;

	SetRefData( (wxObjectRefData*)(wxPMService*)this );

	wxSetPMBootstrapSvc( this );
}

wxPMBootstrapService::~wxPMBootstrapService()
{
	wxSetPMBootstrapSvc( NULL );

	if ( &wxPMBootSvc() != NULL )

		CleanUp();

	SetRefData( NULL );
}

bool wxPMBootstrapService::CleanUp()
{
	if ( this->IsStarted() && !this->Stop() )

		return FALSE; // veto!

	RemoveAllServices();

	wxSetPMBootstrapSvc( NULL );

	//if ( m_pDocMgr ) delete m_pDocMgr;
	m_pDocMgr = NULL;

	if ( &wxPMBmpMgr() != NULL )
	{
		delete &wxPMBmpMgr();

		wxSetPMBitmapManager( NULL );
	}

	return TRUE;
}

wxDocMDIParentFrame* wxPMBootstrapService::GetMainWindow()
{
	return m_pMainWnd;
}

wxKeyBinder& wxPMBootstrapService::GetKeyBinder()
{
	return m_KeyBinder;
}

void wxPMBootstrapService::TranslateKeyEvent( wxKeyEvent& event )
{
	m_KeyBinder.OnChar( event );
}

wxPMDocManager* wxPMBootstrapService::GetDocManager()
{
	return m_pDocMgr;
}

bool wxPMBootstrapService::Start()
{
	wxASSERT( this->IsStarted() == FALSE ); // DBG::

	wxASSERT_MSG( m_pMainWnd, "SetMainWindow() should be called before wxPMBootstrapService::Init()!" );

	wxASSERT_MSG( m_pDocMgr, "PM-doc-manager should be set before starting wxPMBootstrapService!" );

	// event receiver chain:
	//
	//	first:	 wxKeyBinder
	//	then:	 wxPMBootstrapService
	//  finally: wxWindow (app's main window)

#if 0
	m_pMainWnd->SetEventHandler( &m_KeyBinder );
	m_KeyBinder.SetNextHandler( this );
	this->SetNextHandler( m_pMainWnd );
#else
	//m_pMainWnd->PushEventHandler( &m_KeyBinder );
	m_pMainWnd->PushEventHandler( this );
	m_KeyBinder.SetNextHandler( this );
#endif

	MergeMenuChains( m_pDocMgr->GetMergableMenuChain(), m_pDocMgr );

	MergeMenuChains( this->GetMergableMenuChain(), this );

	this->SetIsStarted( TRUE );

	return TRUE;
}

bool wxPMBootstrapService::Stop()
{
	wxASSERT( this->IsStarted() ); // DBG::

	if ( HasWorkplace() )
	{
		if ( !m_pDocMgr->CloseWorkplace() )

			return FALSE; // veto Stop() request
	}

	StopAllServices();

	if ( m_pMainWnd ) 
	{
		m_pMainWnd->PopEventHandler(); // first pop keybinder

		m_pMainWnd->SetEventHandler( m_pMainWnd ); // then remove ourselves from chain
	}

	RemoveMenuChains( this->GetMergableMenuChain() );

	this->SetIsStarted( FALSE );

	return TRUE;
}

wxPMMergableMenuChain* wxPMBootstrapService::GetMergableMenuChain()
{
	return _gBootstrapChains;
}

wxObject* wxPMBootstrapService::GetWxObject()
{
	return (wxObject*)this;
}

void wxPMBootstrapService::SetStatusText( const wxString& message )
{
	if ( m_pMainWnd->GetStatusBar() )

		m_pMainWnd->GetStatusBar()->SetStatusText( message );
}

void wxPMBootstrapService::AddListener( wxPMBootstrapServiceListener* pListener )
{
	m_Listeners.Add( pListener );
}

void wxPMBootstrapService::RemoveListener( wxPMBootstrapServiceListener* pListener )
{
	m_Listeners.Remove( pListener );
}

const wxString& wxPMBootstrapService::GetStartupDir()
{
	return mStartupDir;
}

void wxPMBootstrapService::SetWorkplace( wxPMWorkplace* wp )
{
	m_pWorkplace = wp;
}

wxPMWorkplace* wxPMBootstrapService::GetWorkplace()
{
	return m_pWorkplace;
}
								
void wxPMBootstrapService::AddService( wxPMService* pSvc )
{
	m_Services.Add( pSvc );
}

wxPMService* wxPMBootstrapService::FindServiceByClass( wxClassInfo* pExactSvcClass )
{
    size_t i;
	for( i = 0;i != m_Services.Count(); ++i )
    {
		if ( m_Services[i]->GetWxObject()->GetClassInfo() == pExactSvcClass &&
			 m_Services[i]->IsStarted() )
			return m_Services[i];
    }

	return NULL;
}

void wxPMBootstrapService::StartService( wxPMService* pSvc )
{
	wxObject* pObj = pSvc->GetWxObject();

	// only wxEvtHandler-derivatives may receive events and thuse
	// have their own menus added

	if ( pObj->IsKindOf( CLASSINFO(wxEvtHandler) ) 
	   )
	{
		MergeMenuChains( pSvc->GetMergableMenuChain(), (wxEvtHandler*)pObj );
	}

	pSvc->SetIsStarted( TRUE );
	pSvc->Start();

	if ( pSvc->GetWxObject()->IsKindOf( CLASSINFO( wxPMPane ) ) )
	{
		wxPMPane* pPane = (wxPMPane*)pSvc->GetWxObject();

		if ( pPane->GetDocumentTypes() == NULL )

			ShowPane( pPane );
	}

	wxPM_NOTIFY_ONE_ARG( OnServiceStarted, *pSvc );
}

void wxPMBootstrapService::StopService( wxPMService* pSvc )
{
	if ( !pSvc->IsStarted() )
		return;

	wxObject* pObj = pSvc->GetWxObject();

	if ( pObj->IsKindOf( CLASSINFO(wxEvtHandler) ) )
	
		RemoveMenuChains( pSvc->GetMergableMenuChain() );

	if ( pSvc->GetWxObject()->IsKindOf( CLASSINFO( wxPMPane ) ) )
	{
		wxPMPane* pPane = (wxPMPane*)pSvc->GetWxObject();

		if ( pPane->IsShown() )

			HidePane( pPane );
	}

	pSvc->Stop();
	pSvc->SetIsStarted( FALSE );

	wxPM_NOTIFY_ONE_ARG( OnServiceStopped, *pSvc );
}

void wxPMBootstrapService::StopAllServices()
{
    size_t i;
	for( i = 0; i != m_Services.Count(); ++i )
    {
		if ( m_Services[i]->IsStarted() )
			StopService( m_Services[i] );
    }
}

void wxPMBootstrapService::RemoveAllServices()
{
	// FIXME:: O(^N) performance!

	PMServiceListT lst = m_Services;

    size_t i;
	for( i = 0; i != lst.Count(); ++i )
	
		RemoveService( lst[i] );
	
	m_Services.Clear();
}

void wxPMBootstrapService::RemoveService( wxPMService* pSvc )
{
	if ( pSvc->IsStarted() )
		StopService( pSvc );

	m_Services.Remove( pSvc );
	delete pSvc->GetWxObject();

	// step 1: remove all data of this service associated to
	// other services 

	PMServiceAssociationInfo* pInfo =
		(PMServiceAssociationInfo*)m_PerServiceDataHash.Get( (long)pSvc );

	if ( pInfo )
	{
		m_PerServiceDataHash.Delete( (long)pSvc );

		delete pInfo;
	}

	// step 2: remove all data-associations made by other
	// services to this service

	// FIXME:: performance..

	m_PerServiceDataHash.BeginFind();

	wxNode* pNode = NULL;

	while( pNode = m_PerServiceDataHash.Next() )
	{
		PMServiceAssociationInfo* pInfo = 
			(PMServiceAssociationInfo*)pNode->GetData();

		for( size_t i = 0; i != pInfo->mToServices.Count(); ++i )
		{
			if ( pInfo->mToServices[i] == pSvc )
			{
				delete pInfo->mData[i];

				pInfo->mToServices.Remove(i);
				pInfo->mData.Remove(i);
				break;
			}
		}
	}
}

void wxPMBootstrapService::DetachService( wxPMService* pSvc )
{
	m_Services.Remove( pSvc );
}

void wxPMBootstrapService::FindServicesByClass( wxClassInfo* pSvcClass,
											    PMServiceListT& toLst, 
												bool machSubclasses )
{
    size_t i;
	for( i = 0; i != m_Services.Count(); ++i )
	{
		wxPMService* pSvc = m_Services[i];
		wxObject* pObj = pSvc->GetWxObject();

		bool match = ( machSubclasses ) ? pObj->IsKindOf( pSvcClass )
									    : pObj->GetClassInfo() == pSvcClass;

		if ( match )
            toLst.Add( pSvc );
	}
}

bool wxPMBootstrapService::AssociateData( wxPMService& fromService, 
										  wxPMService& toService,
										  wxObject* pData )
{
	PMServiceAssociationInfo* pInfo = 
		(PMServiceAssociationInfo*)m_PerServiceDataHash.Get( (long)&fromService );

	if ( !pInfo )
	{
		pInfo = new PMServiceAssociationInfo();

		m_PerServiceDataHash.Put( (long)&fromService, pInfo );
	}

	// assertain if we do not already have association with given 'toService'

	for( size_t i = 0; i != pInfo->mToServices.Count(); ++i )

		if ( pInfo->mToServices[i] == &toService )
		{
			// FOR NOW:: failing...
			wxFAIL_MSG( "Data is already associated with the given 'toService'" );
			return FALSE;
		}

	pInfo->mToServices.Add( &toService );
	pInfo->mData.Add( (void*)pData );

	return TRUE;
}

wxObject* wxPMBootstrapService::GetAssociatedData( wxPMService& fromService, 
											  	   wxPMService& toService )
{
	PMServiceAssociationInfo* pInfo = 
		(PMServiceAssociationInfo*)m_PerServiceDataHash.Get( (long)&fromService );

	if ( !pInfo ) 
		return NULL;

	for( size_t i = 0; i != pInfo->mToServices.Count(); ++i )

		if ( pInfo->mToServices[i] == &toService )

			return (wxObject*)pInfo->mData[i];

	return NULL;
}

bool wxPMBootstrapService::RegisterUniqueServiceInstance( wxPMService* pSvc, const wxString& interfaceName )
{
	if ( m_UniqueInstHash.Get( interfaceName ) )
		return FALSE;

	m_UniqueInstHash.Put( interfaceName, (wxObject*)(void*)pSvc );

	wxASSERT(m_UniqueInstHash.Get( interfaceName ) == (void*)pSvc); // DBG::
	return TRUE;

	// TBD:: auto-unregister all all unique instances
	//		 when coresponding service is stopped
}

bool wxPMBootstrapService::UnregisterUniqueServiceInstance( wxPMService* pSvc, const wxString& interfaceName )
{
	if ( m_UniqueInstHash.Get( interfaceName ) != (void*)pSvc )
		return FALSE;

	m_UniqueInstHash.Delete( interfaceName );

	wxASSERT(m_UniqueInstHash.Get( interfaceName ) != (void*)pSvc); // DBG::
	return TRUE;
}

wxPMService* wxPMBootstrapService::FindUniqueServiceInstance( const wxString& byInterfaceName )
{
	return (wxPMService*)(void*)m_UniqueInstHash.Get( byInterfaceName );
}

void wxPMBootstrapService::ShowPane( wxPMPane* pPane )
{
	pPane->SetShown( TRUE );

	wxPM_NOTIFY_ONE_ARG( OnPaneShown, *pPane );
}

void wxPMBootstrapService::HidePane( wxPMPane* pPane )
{
	pPane->SetShown( FALSE );

	wxPM_NOTIFY_ONE_ARG( OnPaneHidden, *pPane );
}
	 
void wxPMBootstrapService::RemoveMenuChains( wxPMMergableMenuChain* pChains )
{
	if ( pChains == NULL ) 
        return;

	while( pChains->m_pCommands != NULL )

		DoRemoveChain( pChains++ );

	m_RecreatePending = TRUE;
}

void wxPMBootstrapService::MergeMenuChains( wxPMMergableMenuChain* pChains, wxEvtHandler* pOwner )
{
	if ( pChains == NULL )
        return;

	while( pChains->m_pCommands != NULL )

		DoMergeChain( pChains++, pOwner );

	m_RecreatePending = TRUE;
}

/*** event handlers ***/

class MyApp;
DECLARE_APP(MyApp);

void wxPMBootstrapService::OnExit( wxCommandEvent& event )
{
	if (!Stop())
        return;

	// slightly more "powerful" E&xit alg.

	m_pMainWnd->Destroy();

	delete this;

	wxApp* app = (wxApp*)&wxGetApp();
	wxYield();

	app->ExitMainLoop();
}


void wxPMBootstrapService::OnOptionsChangeCWD( wxCommandEvent& event )
{
	wxDirDialog dlg( wxPMBootSvc().GetMainWindow(), "Choose a directory", ::wxGetCwd() );

	wxString cwd = ::wxGetCwd();

	if ( dlg.ShowModal() == wxID_OK )
	{
		cwd = dlg.GetPath();
		SetStatusText( "Working directory changed to " + cwd );
	}

	wxSetWorkingDirectory( cwd );
}  // wxPMBootstrapService::OnOptionsChangeCWD()

void wxPMBootstrapService::OnDummy( wxCommandEvent& event )
{
}


void wxPMBootstrapService::OnAbout( wxCommandEvent& event )
{
	wsAboutDialog dlgAbout(GetMainWindow());
	dlgAbout.Center(wxBOTH);
    dlgAbout.ShowModal();

	/*wxString aboutStr, titleStr;
    aboutStr.Printf("%s %s\n\n%s\n\n%s",PROGRAM_NAME, PROGRAM_VERSION,
                                    PROGRAM_COPYRIGHT,
                                    PROGRAM_WEBSITE);
    titleStr.Printf("%s %s", _T("About"), PROGRAM_NAME);
	wxMessageBox( aboutStr.c_str(), titleStr.c_str() );*/
}


void wxPMBootstrapService::OnOptionsEditorPrefs( wxCommandEvent& event )
{
}  // wxPMBootstrapService::OnOptionsEditorPrefs()


void wxPMBootstrapService::OnOptionsDirectories( wxCommandEvent& event )
{
}  // wxPMBootstrapService::OnOptionsDirectories()


void wxPMBootstrapService::OnOptionsKeyBindings( wxCommandEvent& event )
{
	wxKeyConfigDialog dlg( GetMainWindow(), &m_KeyBinder );

	dlg.ShowModal();

	RegenerateCommandIds();

	m_RecreatePending = TRUE;
}  // wxPMBootstrapService::OnOptionsKeyBindings()


void wxPMBootstrapService::OnIdle( wxIdleEvent& event )
{
	if ( m_RecreatePending )
	{
		m_RecreatePending = FALSE;

		m_MenuItemsHash.Clear();

		RegenerateCommandIds();

		m_KeyBinder.CreateMenuBar( m_pMainWnd );

		m_KeyBinder.UpdateKeyAssignments();
	}

	event.Skip(); // allow others do their tasks too
}

void wxPMBootstrapService::OnMenuEvent( wxEvent& event )
{
	DoTranslateEvent( event );
}

void wxPMBootstrapService::OnButtonEvent( wxEvent& event )
{
	DoTranslateEvent( event );
}

void wxPMBootstrapService::OnUpdateUIEvent( wxEvent& event )
{
	DoTranslateEvent( event );
}

int wxPMBootstrapService::RemoveServicesByClass( wxClassInfo* pSvcClass )
{
	size_t nFound = 0;
	PMServiceListT lst = m_Services;

    size_t i;
	for( i = 0; i != lst.Count(); ++i )
    {
		if ( lst[i]->GetWxObject()->GetClassInfo() == pSvcClass )
		{
			RemoveService( lst[i] );
			++nFound;
		}
    }

	return nFound;
}

bool wxPMBootstrapService::StartServicesByClass( wxClassInfo* pSvcClass )
{
	wxObject* pObj = pSvcClass->CreateObject();

	wxPMService* pSvc = (wxPMService*)pObj->GetRefData();

	AddService( pSvc );

	this->StartService( pSvc );

	return TRUE;
}

void wxPMBootstrapService::SetAppTitle( const wxString& appTitle )
{
	m_AppTitle = appTitle;
}

wxString& wxPMBootstrapService::GetAppTitle()
{
	return m_AppTitle;
}

void wxPMBootstrapService::ActivateRelatedPanes( wxPMDocument* pDoc, bool activate )
{
	wxString docType = pDoc->GetDocumentName();

    size_t i;
	for( i = 0; i != m_Services.Count(); ++i )
	{
		wxPMService* pSvc = m_Services[i];

		if ( pSvc->IsStarted() &&
			 pSvc->GetWxObject()->IsKindOf( CLASSINFO( wxPMPane ) ) )
		{
			wxPMPane* pPane = (wxPMPane*)pSvc->GetWxObject();

			wxStringList* pDocTypes = pPane->GetDocumentTypes();

			if ( pDocTypes )
			{
                size_t k;
				for( k = 0; k != pDocTypes->GetCount(); ++k )
				{
					// FIXME:: messy!

					if ( (*pDocTypes)[k] == docType )
					{
						if ( activate && !pPane->IsShown() )
						{
							ShowPane( pPane );
							break;
						}
						else
						if ( !activate && pPane->IsShown() )
						{
							HidePane( pPane );
							break;
						}

						if ( ( activate && pPane->IsShown() ) ||
							 ( !activate && !pPane->IsShown() ) )

							 break;
					}
				}

				if ( k == pDocTypes->GetCount() &&
					 pPane->IsShown() && activate )
				{
					 HidePane( pPane );
				}
			}
		}
	}
}
					 
/*** protected methods ***/

void wxPMBootstrapService::DoTranslateEvent( wxEvent& event )
{
	if ( m_RecreatePending ) return;

	wxCommandInfo* pItem = 
		(wxCommandInfo*)m_MenuItemsHash.Get( (long)event.GetId() );

	//wxASSERT_MSG( pItem != NULL, "wxCommandInfo object for the given event not found! not registered yet?" );

	if ( pItem == NULL )
	{
		event.Skip();
		return;
	}

	wxPMMenuItemData* pData = (wxPMMenuItemData*)pItem->GetUserData();

	// FIXME:: hack... how to pass the original id?? (needed in some cases)

	int id = event.GetId();

	//event.n = (char*)event.GetId();

	event.SetId( pData->m_ActualId ); // change id to that requested by it's owner

	if ( pData->m_pReceiver )

		pData->m_pReceiver->ProcessEvent( event );
}

wxCommandInfo* wxPMBootstrapService::FindItemByName( const wxString& path )
{
	return DoFindItemByName( path, m_KeyBinder.GetGroups() );
}

wxCommandInfo* wxPMBootstrapService::DoFindItemByName( const char* path, wxCommandInfoListT& lst )
{
	if ( path[0] == '/' ) ++path;

	if ( path[0] == '\0' ) return NULL;

	char* cur = (char*)(const char*)path;

	while( *cur && *cur != '/' ) ++cur;
	char tmp = *cur;

	*cur = '\0'; // terminate the first part temporarely

    size_t i;
	for( i = 0; i != lst.Count(); ++i )
	{
		if ( lst[i]->GetTitle() == path )
		{
			if ( tmp == '\0' )
			{
				*cur = tmp;
				return lst[i];
			}

			wxCommandInfo* pFound = NULL;

			if ( lst[i]->HasSubcommands() )
			
				// recurse to the next part of the menu-path
				pFound = DoFindItemByName( cur+1, *lst[i]->GetSubcommands() );

			if ( pFound )
			{
				*cur = tmp; // restore 

				return pFound;
			}
		}
	}

	*cur = tmp; // restore 

	return NULL;
}

void wxPMBootstrapService::DoMergeChain( wxPMMergableMenuChain* pChain,
									    wxEvtHandler* pReceiver )
{
	wxCommandInfo* pParent = NULL;
	wxCommandInfoListT* pLst = NULL;
	size_t insPos = 0;

	wxCommandInfo* pPred = FindItemByName( pChain->m_PredecessorPath );

	if ( pPred ) 
	{
		pParent = pPred->GetParent();

		wxASSERT(pParent);

		pLst = pParent->GetSubcommands();
		insPos = (size_t)pLst->Index( pPred ); ++insPos; // after
	}
	else // insert at the end of the siblings list, if Predecessor was not found
	{
		pLst = CreateTreePath( StripLastPathItem( pChain->m_PredecessorPath ), pParent );
		insPos = pLst->Count();
	}

	wxCommandInfo* pCommand = pChain->m_pCommands;

	while( pCommand->GetTitle() != "" )
	{

		wxPMMenuItemData* pPMData = new wxPMMenuItemData();
		pPMData->m_ActualId  = pCommand->GetId();
		pPMData->m_pReceiver = pReceiver;

		wxASSERT( pParent ); // DBG::

		wxCommandGroup* pNewItem = new wxCommandGroup();
		pCommand->Prepare();
		pCommand->AssignInfoTo( pNewItem );

		pNewItem->SetUserData( pPMData );
		pNewItem->SetCmdParent( pParent );

		pLst->Insert( pNewItem, insPos++ );

		++pCommand;
	}
}

void wxPMBootstrapService::DoRemoveChain( wxPMMergableMenuChain* pChain )
{
	wxCommandInfo* pCommand = pChain->m_pCommands;

	wxString path = StripLastPathItem( pChain->m_PredecessorPath );

	// remove matching items in one branch

	while( pCommand->GetTitle() != "" )
	{
		RemoveItemByPath( path + "/" + pCommand->GetTitle() );

		++pCommand;
	}

	// remove empty owned menu-path components ("those implicitely created by PMF")

	wxCommandInfo* pItem = FindItemByName( path );

	while( pItem && pItem->HasSubcommands() == FALSE )
	{
		wxCommandInfo* pParent = pItem->GetParent();

		if ( pParent )
            pItem->GetParent()->GetSubcommands()->Remove( pItem );
        else 
            m_KeyBinder.GetGroups().Remove( pItem );

		m_MenuItemsHash.Delete( (long)pItem->GetId() );
		delete pItem;

		pItem = pParent;
	}
}

void wxPMBootstrapService::MutateCommandToGroup( wxCommandInfoListT& lst, size_t cmdIdx )
{
	// our parent item is instance of wxCommandInfo, but
	// we need wxCommandInfoGroup for adding child-items,
	// do the mutation now

	wxCommandGroup* pGroup = new wxCommandGroup();
	wxCommandInfo* pCmd = lst[cmdIdx];

	pCmd->AssignInfoTo( pGroup );

	lst[cmdIdx] = pGroup;

	delete pCmd;
}

wxCommandInfoListT* wxPMBootstrapService::CreateTreePath( const wxString& path,
														  wxCommandInfo*& pParent )
{
	wxCommandInfoListT* pLst = &m_KeyBinder.GetGroups();

	size_t depth = 0; pParent = NULL;

	// search for existing components of given menu-path

	while( pLst )
	{
		wxString name = GetPathItem( path, depth );
        bool found = FALSE;

		if ( name == "" )
            break;

        size_t i;
		for( i = 0; i != pLst->Count(); ++i )
		{
			if ( (*pLst)[i]->GetTitle() == name )
			{
				/*
				if ( GetPathItem( path, depth+1 ) == "" &&
					 (*pLst)[i]->GetSubcommands() == NULL )
				{
					// if there are more path components required
					// after the current one, and the current one
					// is not a command-group (i.e. single command)
					// then convert it's type "on-the-fly"

					MutateCommandToGroup( *pLst, i );
				}
				*/

				pParent = (*pLst)[i];
				pLst = (*pLst)[i]->GetSubcommands(); found = TRUE;
				break;
			}
		}

		if ( !found )
		{
			// append missing path components

			while( name != "" )
			{
				wxPMMenuItemData* pPMData = new wxPMMenuItemData();
				pPMData->m_pReceiver = NULL;  // TBD:: really noone will receive it?
				pPMData->m_ActualId  = -1;

				wxCommandGroup* pCommand = 
					new wxCommandGroup( name );

				pCommand->SetId( 1033 );

				pCommand->SetUserData( pPMData );

				pLst->Add( pCommand );
				pCommand->SetCmdParent( pParent );
	
				// go deeper
				pLst    = pCommand->GetSubcommands();
				pParent = pCommand;

				name = GetPathItem( path, ++depth );
			}

			break;
		}
		else
			++depth;
	} 

	return pLst;
}

wxString wxPMBootstrapService::StripLastPathItem( const wxString& treePath )
{
	if ( treePath.Length() == 0 )
        return treePath;

	// must use int rather than size_t, as size_t is unsigned,
	// and can never go below 0 in the test below
	int i = treePath.Length() - 1;

	while( i >= 0 && treePath[i] != '/' )
        --i;

	if ( i == 1 || i == 0 )
        return ""; // single "/" left as root, strip it

	return wxString( treePath, 0, i );
}

wxString wxPMBootstrapService::GetPathItem( const wxString& treePath, size_t i )
{
	size_t n = 0, len = treePath.Length();

	while( n < len )
	{
		if ( treePath[n] == '/' )
            ++n;

		size_t start = n;

		while( n < len && treePath[n] != '/' )
            ++n;

		if ( i == 0 )

			return wxString( treePath, start, n - start );

		++n; --i;
	}

	return "";
}

void wxPMBootstrapService::RemoveItemByPath( const wxString& treePath )
{
	wxCommandInfo* pItem = FindItemByName( treePath );

	if ( pItem == NULL )
        return;

	if ( pItem->HasSubcommands() )
        return; // cannot remove parent item when children are present

	if ( pItem->GetParent() )
        pItem->GetParent()->GetSubcommands()->Remove( pItem );
    else 
        m_KeyBinder.GetGroups().Remove( pItem );

	m_MenuItemsHash.Delete( (long)pItem->GetId() );

	delete pItem;
}

void wxPMBootstrapService::DoRegenerateCommandIds( wxCommandInfoListT& commands, int& nextId )
{
    size_t i;
	for( i = 0; i != commands.Count(); ++i )
	{
		wxCommandInfo& cmd = *commands[i];

		m_MenuItemsHash.Put( nextId, (wxObject*)&cmd );

		cmd.SetId( nextId++ );

		if ( cmd.HasSubcommands() )
		
			DoRegenerateCommandIds( *cmd.GetSubcommands(), nextId );
	}
}

void wxPMBootstrapService::RegenerateCommandIds()
{
	m_MenuItemsHash.Clear();

	int nextId = 500;

	DoRegenerateCommandIds( m_KeyBinder.GetGroups(), nextId );
}

/* global access-point to the instance of bootstrap service */

static wxPMBootstrapService* _gBootstrapObj = NULL;

wxPMBootstrapService& wxGetPMBootstrapSvc()
{
	if(!_gBootstrapObj)
		wxLogWarning(_T("Be careful not to use it this way: wxPMBootSvc().whatever()!")); // DBG::

	return *_gBootstrapObj;
}

void wxSetPMBootstrapSvc( wxPMBootstrapService* pSvc )
{
	_gBootstrapObj = pSvc;
}

wxPMBootstrapService& wxPMBootSvc()
{
	if(!_gBootstrapObj)
		wxLogWarning(_T("Be careful not to use it this way: wxPMBootSvc().whatever()!")); // DBG::

	return *_gBootstrapObj;
}
