/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     2000/02/08
// RCS-ID:      $Id: pm_doc.cpp,v 1.27 2001/04/23 21:51:35 vlabella Exp $
// Copyright:   (c) Aleksandras Gluchovas
// Licence:   	GPL (version 2.0 or later)
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
    #pragma implementation "pm_doc.cpp"
    #pragma interface "pm_doc.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/pmf/pm_doc.h"

#include "wx/pmf/inf/textediting.h"

#include "wx/pmf/pm.h" // imports wxPMBootStrapService

#include "mimetypes.h"

/***** Implementation for class wxPMLayoutPrefs *****/

void wxPMWorkplaceListener::OnWorkplaceOpened( wxPMWorkplace& workplace )
{
    int breakPoint = 1;
}  // wxPMWorkplaceListener::OnWorkplaceOpened()


void wxPMWorkplaceListener::OnWorkplaceClosing( wxPMWorkplace& workplace )
{
    int breakPoint = 1;
}  // wxPMWorkplaceListener::OnWorkplaceClosing()


void wxPMWorkplaceListener::OnProjectAdded( wxPMProject& project )
{
    int breakPoint = 1;
}  // wxPMWorkplaceListener::OnProjectAdded()


void wxPMWorkplaceListener::OnProjectRemoved( wxPMProject& project )
{
    int breakPoint = 1;
}  // wxPMWorkplaceListener::OnProjectRemoved()


void wxPMWorkplaceListener::OnProjectActivated( wxPMProject& project, wxPMProject* pPrevProject )
{
    int breakPoint = 1;
}  // wxPMWorkplaceListener::OnProjectActivated()


void wxPMWorkplaceListener::OnFileAdded( wxPMFileInfo& file, wxPMProject* pToProject )
{
    int breakPoint = 1;
}  // wxPMWorkplaceListener::OnFileAdded()


void wxPMWorkplaceListener::OnFileRemoved( wxPMFileInfo& file, wxPMProject* pToProject )
{
    int breakPoint = 1;
}  // wxPMWorkplaceListener::OnFileRemoved()


void wxPMWorkplaceListener::OnViewActivated( wxPMView& view, wxPMView* pPrevView )
{
    int breakPoint = 1;
}  // wxPMWorkplaceListener::OnViewActivated()


/***** Implementation for class wxPMLayoutPreference *****/

IMPLEMENT_DYNAMIC_CLASS( wxPMLayoutPreference, wxObject )

wxPMLayoutPreference::wxPMLayoutPreference()
{}

void wxPMLayoutPreference::Serialize( wxConfigBase& cfg, const wxString& key, bool isLoading )

{ /* nothing by default */ }

/***** Implementation for class wxPMView *****/

IMPLEMENT_DYNAMIC_CLASS( wxPMView, wxView )

wxPMView::wxPMView()

	: m_pViewFrame( NULL ),
	  m_IsClosing( FALSE )
{
	SetRefData( (wxObjectRefData*)(wxPMService*)this );
}

wxPMView::~wxPMView()
{
	SetRefData( NULL );
}

wxPMMergableMenuChain* wxPMView::GetMergableMenuChain()
{
	return NULL; // no menus by default
}

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

void wxPMView::OnDraw( wxDC* pDc )

{ 
	pDc->DrawLine( 5,5, 500,500 );

	/* almost nothing by default*/ 
}

/** helper class **/

class MyDocMDIChildFrame : public wxDocMDIChildFrame
{
public:
	MyDocMDIChildFrame( wxDocument* doc, wxView* view, 
						wxMDIParentFrame* parent, wxWindowID id, 
						const wxString& title, 
						const wxPoint& pos = wxDefaultPosition, 
						const wxSize& size = wxDefaultSize, 
						long style = wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL, 
						const wxString& name = "frame")
		: wxDocMDIChildFrame( doc,view,parent,id,title,pos,size,style,name )
	{}

	void OnChar( wxKeyEvent& event )
	{
		if ( event.KeyCode() != WXK_CONTROL )

			wxPMBootSvc().TranslateKeyEvent( event );
	}

	void OnLeftDown( wxMouseEvent& event )
	{
		if ( wxWindow::FindFocus() != this )

			this->SetFocus();

		event.Skip();
	}

	void OnCommandRange( wxCommandEvent& event )
	{
		if ( event.m_eventType == wxEVT_COMMAND_BUTTON_CLICKED )

			event.m_eventType = wxEVT_COMMAND_MENU_SELECTED;

		if ( GetView()->ProcessEvent( event ) )
		
			event.Skip( FALSE );
		else
			event.Skip( TRUE );
	}

	void OnSetFocus( wxFocusEvent& event )
	{
		wxNode* pNode = GetChildren().First();

			if ( pNode )
			{
				//m_childView->Activate(true);

				((wxWindow*)pNode->GetData())->SetFocus();

				return;
			}

		event.Skip();
	}

	DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE( MyDocMDIChildFrame, wxDocMDIChildFrame )

	EVT_SET_FOCUS( MyDocMDIChildFrame::OnSetFocus )
	EVT_COMMAND_RANGE( 0, 65530, wxEVT_COMMAND_BUTTON_CLICKED, 
					   MyDocMDIChildFrame::OnCommandRange )
	EVT_COMMAND_RANGE( 0, 65530, wxEVT_COMMAND_MENU_SELECTED , 
					   MyDocMDIChildFrame::OnCommandRange )
	EVT_COMMAND_RANGE( 0, 65530, wxEVT_COMMAND_TEXT_UPDATED, 
					   MyDocMDIChildFrame::OnCommandRange )

	EVT_KEY_DOWN( MyDocMDIChildFrame::OnChar )
	//EVT_CHAR( MyDocMDIChildFrame::OnChar )

	EVT_LEFT_DOWN( MyDocMDIChildFrame::OnLeftDown )

END_EVENT_TABLE()

// FIXME:: should be made static member of wxPMView
static wxPoint _gLRU_DocPos( 5,5 );

bool wxPMView::OnCreate(wxDocument* doc, long flags)
{
  //// Make a child frame
	m_pViewFrame = 
		new MyDocMDIChildFrame(doc, this, 
							   wxPMBootSvc().GetMainWindow(), -1, 
							   "Child Frame",
							   _gLRU_DocPos, wxSize(500, 400), 
							   wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL );

	m_pViewFrame->SetFocus();

	_gLRU_DocPos.x += 20;
	_gLRU_DocPos.y += 20;

	if ( _gLRU_DocPos.x > 200 )
	{
		_gLRU_DocPos.x = 5;
		_gLRU_DocPos.y = 5;
	}

	return TRUE;
}

class MyDummyView : public wxPMView
{
public: virtual void Activate( bool activate ) {};
};

bool wxPMView::OnClose(bool deleteWindow)
{
	if ( !IsClosing() )
	{
		SetClosing( TRUE ); // set "reentrance flags" upon very first
							// attempt to close this view

		wxPMDocument* pDoc = (wxPMDocument*)GetDocument();
		wxPMDocManager* pDocMan = (wxPMDocManager*)pDoc->GetDocumentManager();

		wxList* pLst = pDoc->GetAllViews();

		wxNode* pFirst = pLst->GetFirst();
		wxNode* pLast = pLst->GetLast();
		size_t cnt = pLst->GetCount();

		if ( pFirst != NULL && pFirst == pLast ) // the last view closes the document
		{
			if ( pDocMan->CloseDocument( pDoc ) )
			{
				return TRUE;
			}
			else
			{
				SetClosing( FALSE ); // closing canceled, reset
									 // the "reentrance-flags" accordingly
				return FALSE;
			}
		}
		else
			return TRUE;
	}
	else
		// otherwise the closing already was in progress,
		// do not show any reasistance...

		return TRUE;
}

void wxPMView::OnActivateView(bool activate, wxView *activeView, wxView *deactiveView)
{
	wxPMBootSvc().SetStatusText( "" );
 
	if ( activate ) 
	{
		if ( deactiveView && deactiveView != this )
		{
			wxASSERT( activeView->IsKindOf( CLASSINFO(wxPMView) ) );

			wxPMView* pPMView = (wxPMView*)deactiveView;

			if ( pPMView->IsStarted() )

				wxPMBootSvc().StopService( pPMView );
		}

		if ( !IsStarted() )

			wxPMBootSvc().StartService( this );

		((wxPMDocument*)GetDocument())->OnActivatePMDocument();
		wxPMBootSvc().ActivateRelatedPanes( (wxPMDocument*)GetDocument(), TRUE );

		// FIXME:: might be excessive! this is due to some change in cvs-wxWin
		//		   where frame of child-document is not shown initiaily, not anymore..

		GetViewFrame()->Show( TRUE );
		GetViewFrame()->SetFocus();
	}
}

void wxPMView::Activate( bool activate )
{
	wxView::Activate( activate );
}

void wxPMView::SetClosing( bool closing )
{
	m_IsClosing = closing;
}

bool wxPMView::IsClosing()
{
	return m_IsClosing;
}

wxWindow* wxPMView::GetViewFrame()
{
	return m_pViewFrame;
}

wxWindow* wxPMView::GetViewClient()
{
	return NULL; // not client-window by default
}

/***** Implementation for class wxPMPane *****/

IMPLEMENT_DYNAMIC_CLASS( wxPMPane, wxEvtHandler )

wxPMPane::wxPMPane()

	: m_pDoc( NULL ),
	  m_pLayoutPref( NULL ),
	  m_pParentPane( NULL ),
	  m_IsShown( FALSE ),
	  m_PaneName( "unnamedPane" )
{
	// TBD:: comments
	SetRefData( (wxObjectRefData*)(wxPMService*)this );
}

wxPMPane::~wxPMPane()
{
	SetRefData( NULL );

	if ( m_pLayoutPref ) delete m_pLayoutPref;
}

void wxPMPane::SetParentPane( wxPMPane* pPane )
{
	m_pParentPane = pPane;
}

void wxPMPane::AddChildPane( wxPMPane* pPane )
{
	m_ChildPanes.Add( pPane );
	pPane->SetParentPane( this );
}

wxPMPane* wxPMPane::GetParentPane()
{
	return m_pParentPane;
}

PMPaneListT& wxPMPane::GetChildPanes()
{
	return m_ChildPanes;
}

void wxPMPane::SetPaneName( const wxString& name )
{
	m_PaneName = name;
}

const wxString& wxPMPane::GetPaneName()
{
	return m_PaneName;
}

void wxPMPane::ActivateChildPane( wxPMPane* pPane )
{
	/* nothing by default */
}

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

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

wxWindow* wxPMPane::GetPaneWindow()
{
	wxFAIL_MSG( "wxPMPane::GetPaneWindow() should be overridden by the derived classes!" );

	return NULL;
}

void wxPMPane::CreatePaneWindow( wxWindow* pParent )
{
	wxFAIL_MSG( "wxPMPane::CreatePaneWindow() should be overridden by the derived classes!" );
}

wxStringList* wxPMPane::GetDocumentTypes()
{
	return NULL;
}

wxStringList* wxPMPane::GetDocumentStates( const wxString& forDocType )
{
	return NULL;
}

void wxPMPane::SetDocument( wxPMDocument* pDoc )
{
	m_pDoc = pDoc;
}

wxPMDocument* wxPMPane::GetDocument()
{
	return m_pDoc;
}

void wxPMPane::OnDocumentActivated( wxPMDocument* pDoc )
{
}

void wxPMPane::OnDocumentDeactivated( wxPMDocument* pDoc )
{
}

void wxPMPane::OnClosingWorkplace()
{
}

wxPMLayoutPreference* wxPMPane::GetLayoutPefs()
{
	return m_pLayoutPref;
}

void wxPMPane::SetLayoutPrefs( wxPMLayoutPreference* pPrefs )
{
	m_pLayoutPref = pPrefs;
}

void wxPMPane::SetShown( bool shown )
{
	m_IsShown = shown;
}

bool wxPMPane::IsShown()
{
	return m_IsShown;
}

bool wxPMPane::SerializeState( wxPMSerializer& ser )
{
	return TRUE; // TBD:: serialize layout-preferences
}

/***** Implementation for class wxPMSimplePane *****/

IMPLEMENT_CLASS( wxPMSimplePane, wxPMPane )

wxPMSimplePane::wxPMSimplePane( wxWindow* wnd, wxPMLayoutPreference* layoutPrefs )

	: mpWnd( wnd ),
	  mpPrefs( layoutPrefs )
{}

void wxPMSimplePane::CreatePaneWindow( wxWindow* pParent )
{}

wxWindow* wxPMSimplePane::GetPaneWindow()
{
	return mpWnd;
}

wxPMLayoutPreference* wxPMSimplePane::GetLayoutPefs()
{
	return mpPrefs;
}

/***** Implementation for class wxPMDocument *****/

IMPLEMENT_DYNAMIC_CLASS( wxPMDocument, wxDocument )

wxPMDocument::wxPMDocument()

	: m_pParentDoc( NULL ),
	  m_pFile( NULL )
{
	SetRefData( (wxObjectRefData*)(wxPMService*)this );
}

wxPMDocument::~wxPMDocument()
{
	SetRefData( NULL );

	//WX_CLEAR_ARRAY( m_Subdocuments );
	// this should not be done here, intead wxPMDocManager
	// takes care of deleting docuemnt-objects, because deletion
	// of them in PM have similar complications as with objects of wxWindow

	if ( m_pFile ) delete m_pFile;
}

wxPMMergableMenuChain* wxPMDocument::GetMergableMenuChain()
{
	return NULL; // none by default
}

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

bool wxPMDocument::CanHaveFile()
{
	return TRUE;
}

void wxPMDocument::SetParentDocument( wxPMDocument* pParent )
{
	m_pParentDoc = pParent;
}

wxPMDocument* wxPMDocument::GetParentDocument()
{
	return m_pParentDoc;
}

void wxPMDocument::AddSubdocument( wxPMDocument* pSubdoc )
{
	pSubdoc->SetParentDocument( this );

	m_Subdocuments.Add( pSubdoc );

// Do not mark this as modified here, as when the project/workplace
// is initially opened, this would force it to be flagged as modified
//    Modify( TRUE );
}

void wxPMDocument::RemoveSubdocument( wxPMDocument* pSubdoc )
{
	pSubdoc->SetParentDocument( NULL );

	m_Subdocuments.Remove( pSubdoc );

// Do not mark this as modified here, as when the project/workplace
// is initially opened, this would force it to be flagged as modified
//    Modify( TRUE );
}

wxPMDocument* wxPMDocument::FindSubdocumentForFile( const wxPMFileInfo& finfo )
{
	// search documents recursively

    size_t i;
	for( i = 0; i != m_Subdocuments.Count(); ++i )
	{
		wxPMDocument* doc = m_Subdocuments[i];

		if ( doc->CanHaveFile() && doc->GetFileInfo() &&
			 *doc->GetFileInfo() == finfo 
		   )

		   return doc;

		wxPMDocument* pFound = m_Subdocuments[i]->FindSubdocumentForFile( finfo );

		if ( pFound ) return pFound;
	}

	return NULL;
}

void wxPMDocument::AssociateService( wxClassInfo* pSvcClass )
{
	m_ServiceClasses.Add( pSvcClass );
}

PMClassInfoListT& wxPMDocument::GetAssociatedServices()
{
	return m_ServiceClasses;
}

void wxPMDocument::OnClosingChildDocument( wxPMDocument* pChild, bool wasSaved )
{
	// nothing by default
}

void wxPMDocument::SetFileInfo( wxPMFileInfo* pFile )
{ 
	m_pFile = pFile;
}

wxPMFileInfo* wxPMDocument::GetFileInfo()

{ 
	wxASSERT_MSG( CanHaveFile() == TRUE, "Please, check CanHaveFile() before calling GetFile()!" );

	return m_pFile;
}

bool wxPMDocument::UseSerializer()
{
	return FALSE;
}

bool wxPMDocument::SavePMDocument( wxOutputStream& s )
{
	return TRUE;
}

bool wxPMDocument::LoadPMDocument( wxInputStream& s )
{
	return TRUE;
}

bool wxPMDocument::SerializePMDocument( wxPMSerializer& ser )
{
	return TRUE;
}

bool wxPMDocument::SerializeState( wxPMSerializer& ser )
{
	return TRUE;
}

bool wxPMDocument::SavePMDocument()
{
	return TRUE;
}

bool wxPMDocument::LoadPMDocument()
{
	return TRUE;
}

PMDocumentListT& wxPMDocument::GetSubdocuments()
{
	return m_Subdocuments;
}

wxList* wxPMDocument::GetAllViews()
{
	return &m_documentViews;
}

void wxPMDocument::OnActivatePMDocument()
{
	// nothing by default
}

wxPMView* wxPMDocument::GetFirstPMView()
{
	return wxDynamicCast( GetFirstView(), wxPMView );
}

wxString wxPMDocument::GetRelativePath( const wxString& absPath )
{
	// FOR NOW:: trivial and incorrect

	wxASSERT(m_pFile); // DBG::

	wxString path = m_pFile->GetPathOnly();

	return absPath.Mid( path.Length() + 1 );
}

wxString wxPMDocument::GetAbsolutePath( const wxString& relPath )
{
	wxASSERT(m_pFile); // DBG::

	// FOR NOW:: trivial and incorrect

	wxString path = m_pFile->GetPathOnly();

	return path + wxFILE_SEP_PATH + relPath;
}

/***** Implementation for class wxPMProject *****/

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

static wxCommandInfo _gProjectCommands[] = {

	wxCommandInfo( wxPMWorkplace::ID_MAKE_PROJECT_ACTIVE, "Set active project..." ), 
	wxCommandInfo( wxPMProject::ID_ADD_FILE,  "Insert &file(s)..." ), 
//	wxCommandInfo( wxPMProject::ID_ADD_FILES, "Insert files by &mask..." ), 
	wxCommandInfo( wxPMProject::ID_ADD_FILES_RECURSIVELY, "Insert files &recursively..." ), 

	wxCommandInfo( -1, "SEPARATOR" ),

	wxCommandInfo( wxPMProject::ID_PROJECT_SETTINGS, "&Settings..." ), 
	wxCommandInfo( -1, "SEPARATOR" ), 

	wxCommandInfo( wxPMWorkplace::ID_INSERT_PROJECT, "Insert project..." ), 
	wxCommandInfo( wxPMWorkplace::ID_NEW_PROJECT, "Insert new project..." ), 
	wxCommandInfo( 0, "" ) // terminator
};

static wxPMMergableMenuChain _gProjectChains[] = {

	wxPMMergableMenuChain( "/&Project/", _gProjectCommands ),

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

IMPLEMENT_DYNAMIC_CLASS( wxPMProject, wxPMDocument )

BEGIN_EVENT_TABLE( wxPMProject, wxDocument )

	EVT_MENU( wxPMWorkplace::ID_MAKE_PROJECT_ACTIVE, wxPMWorkplace::OnCmdMakeProjectActive )
	EVT_MENU( wxPMProject::ID_ADD_FILE,              wxPMProject::OnCmdAddFile )
//	EVT_MENU( wxPMProject::ID_ADD_FILES,             wxPMProject::OnCmdAddFiles )
	EVT_MENU( wxPMProject::ID_ADD_FILES_RECURSIVELY, wxPMProject::OnCmdFilesRecursively )

	EVT_MENU( wxPMProject::ID_PROJECT_SETTINGS,      wxPMProject::OnCmdProjectSettings )

	EVT_MENU( wxPMWorkplace::ID_INSERT_PROJECT,      wxPMWorkplace::OnCmdInsertProject )
	EVT_MENU( wxPMWorkplace::ID_NEW_PROJECT,         wxPMWorkplace::OnCmdNewProject )
END_EVENT_TABLE()

wxPMProject::wxPMProject()
	
	: wxPMDocument()
{}

wxPMProject::~wxPMProject()
{
	// subdocuments are not destroyed here!
}

wxPMMergableMenuChain* wxPMProject::GetMergableMenuChain()
{
	if ( GetParentDocument() &&
		 ((wxPMWorkplace*)GetParentDocument())->IsActiveProject( this ) )

		 // insert project-menus only when this project is currently active!
		return _gProjectChains;
	else
		return NULL;
}

bool wxPMProject::AddMultipleFiles( PMFileInfoListT& lst )
{
	wxString errors = "";
    size_t i;
	for( i = 0; i != lst.Count(); ++i )
	{
		wxPMFileInfo* pInfo = lst[i];

		if ( !pInfo->DetectType() )
		{
			errors += "\n" + pInfo->GetPath();

			delete pInfo;
			continue;
		}

		if ( ContainsFile( *pInfo ) )
		{
			errors += "\n" + pInfo->GetPath() + "[already included]";

			delete pInfo;
			continue;
		}

		m_Files.Add( lst[i] );		

		wxPMBootSvc().GetWorkplace()->NotifyFileAdded( *lst[i], this );

		// TBD:: mutiliple errros...
	}

	if ( errors.Length() != 0 )
	{
		wxMessageBox( "Type or location of the following file(s) could not be detected:\n" +
					  errors );

		return FALSE;
	}

	return TRUE;
}

bool wxPMProject::AddFile( wxPMFileInfo* pFile )
{
	PMFileInfoListT lst;
	lst.Add( pFile );

	Modify( TRUE );

	return AddMultipleFiles( lst );
}

bool wxPMProject::RemoveFile( wxPMFileInfo* pFile )
{
	// TBD:: error check

	wxPMBootSvc().GetWorkplace()->NotifyFileRemoved( *pFile, this );

	m_Files.Remove( pFile );

	delete pFile;

	Modify( TRUE );

	return TRUE;
}

PMFileInfoListT& wxPMProject::GetFiles()
{
	return m_Files;
}

void wxPMProject::FindFilesByMimeType( const wxString& mimeType, PMFileInfoListT& toLst )
{
    size_t i;
	for( i = 0; i != m_Files.Count(); ++i )
	{
		if ( m_Files[i]->GetType() == mimeType )
		
			toLst.Add( m_Files[i] );
	}
}

wxPMFileInfo* wxPMProject::FindFile( const wxString& shortFileName )
{
	if ( !shortFileName.Length() ) 

		return NULL;

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

#ifdef __WINDOWS__		
		if ( m_Files[i]->GetNameWithoutPath().CmpNoCase( shortFileName ) == 0 )
#else
		if ( m_Files[i]->GetNameWithoutPath() == shortFileName )
#endif

			return m_Files[i];
	
	return NULL;
}

bool wxPMProject::ContainsFile( const wxPMFileInfo& finfo )
{
    size_t i;
	for( i = 0; i != m_Files.Count(); ++i )
	{
		wxPMFileInfo& file = *m_Files[i];

		if ( *m_Files[i] == finfo )

			return TRUE;
	}

	return FALSE;
}

wxPMDocument* wxPMProject::FindDocumentByFile( const wxPMFileInfo& file )
{
	PMDocumentListT& lst = GetSubdocuments();

    unsigned int i;
	for( i = 0; i != lst.Count(); ++i )
	{
		wxPMDocument& doc = *(wxPMDocument*)lst[i];

		wxPMFileInfo* pFile = doc.GetFileInfo();

		if ( *pFile == file )

			return &doc;
	}
	
	return NULL;
}

bool wxPMProject::GetFileTextContent( const wxPMFileInfo& file, wxString& toStr )
{
	wxPMDocument* pDoc = FindDocumentByFile( file );

	if ( pDoc )
	{
		I_ContainingText* pInf = 
			(I_ContainingText*)pDoc->GetInterface( "I_ContainingText" );

		if ( pInf )

			return pInf->GetAllText( &toStr );
	}

	// otherwise, either there is not document open which corresponds to the
	// given file, or the document does not support interface for retrieving 
	// it's content in a text form

	wxPMFileLoader* pLoader = file.GetFileLoader();

	wxASSERT( pLoader ); // DBG::	

	bool ok = ::wxPMReadStreamToString( *pLoader->GetInputStream(), toStr );

	pLoader->Close();

	return ok;
}

bool wxPMProject::CanAcceptOtherFiles()
{
	return FALSE;
}

bool wxPMProject::OnCreate(const wxString& path, long flags)
{
	// overriden  in order to succeed without a view-class
	return TRUE;
}

bool wxPMProject::UseSerializer()
{
	return TRUE;
}

bool wxPMProject::SerializePMDocument( wxPMSerializer& ser )
{
	wxString docTypeName = this->GetDocumentTemplate()->GetDocumentName();

	ser.Exchange( "docTypeName", docTypeName );

	if ( ser.IsLoading() )
	{
		ser.SetPath( "Files" );
		long idx = 0; wxString name;

		bool ok = ser.GetFirstEntry( name, idx );

		while( ok )
		{
			wxString fname;
			ser.Exchange( name, fname );

			if (fname.length())
			{
				// Use absolute paths in the fileinfo until it is time
				// to save the document
				fname = wxPMGetAbsolutePath(wxPMBootSvc().GetWorkplace()->GetFileInfo()->GetPathOnly(), 
					                        fname);
			}

			m_Files.Add( new wxPMFileInfo( fname ) );

			ok = ser.GetNextEntry( name, idx );
		}

		ser.SetPath( ".." );
	}
	else
	{
		// Saving the Workspace

		if ( ser.Exists( "Files" ) ) 
		{		
			ser.DeleteGroup( "Files" );
		}

		ser.SetPath( "Files" );

        size_t i;
		for( i = 0; i != m_Files.Count(); ++i )
		{
			wxString fname = m_Files[i]->GetPath();

			// 'fname' is the absolute path (with drive/volume and path
			// from the root level of the drive/volume) when passed
			// in to wxPMGetRelativePath(), but needs to be changed
			// to a relative path before saving it in the project file 
			fname = wxPMGetRelativePath(wxPMBootSvc().GetWorkplace()->GetFileInfo()->GetPathOnly(), 
				                        fname);

			ser.Exchange( ser.IndexedKey( "file", i ), fname );
		}

		ser.SetPath( ".." );
	}

	return TRUE;
}

void wxPMProject::OnClosingChildDocument( wxPMDocument* pChild, bool wasSaved )
{
	if ( pChild->GetFileInfo() == NULL ) return;

	// automatically add new files upon closing the child-documents

	if ( !ContainsFile( *pChild->GetFileInfo() ) )
	
		AddFile( new wxPMFileInfo( *pChild->GetFileInfo() ) );
}

/*** command-event handlers ***/

void wxPMProject::OnCmdAddFile( wxCommandEvent& event )
{
    // Note that this returns the .WSP name as part of the dir, but the
    // wxFileDialog strips that for us
	//    wxString dir = GetFilename();
	//
	// commented out (by Aleks) because:
	// i want to insert files from my current working
	// directory (but not project's dir), which is 
	// initially path of the .WSP, but i
	// may change it any time, and i want that change to take effect!
	// that's why i added OnCmdChangeWorkingDirectoyr()
	// Also, the "stripping" doesn't work for 2.2.0 - opens up desktop-dir :(

    wxString dir = ::wxGetCwd();

#if wxCHECK_VERSION(2,3,0)
	wxFileDialog dlg( wxPMBootSvc().GetMainWindow(), 
					  "Insert file(s) into project", dir, "", "*.*", wxOPEN | wxMULTIPLE | wxCHANGE_DIR);
#else
	wxFileDialog dlg( wxPMBootSvc().GetMainWindow(), 
					  "Insert file(s) into project", dir, "", "*.*", wxOPEN | wxMULTIPLE);
#endif

	if ( dlg.ShowModal() == wxID_OK )
	{
        wxArrayString allSelectedFiles;
        dlg.GetFilenames(allSelectedFiles);
        size_t count = allSelectedFiles.GetCount();
        size_t n;
        for ( n = 0; n < count; n++ )
        {
			wxString fname = allSelectedFiles[n];

			fname = ::wxGetCwd() + wxFILE_SEP_PATH + fname;
		    wxPMFileInfo* pInfo = new wxPMFileInfo( fname );

		    AddFile( pInfo );
        }
	}
}  // wxPMProject::OnCmdAddFile()

/*
void wxPMProject::OnCmdAddFiles( wxCommandEvent& event )
{
	InsertFiles( FALSE );
}  // wxPMProject::OnCmdAddFiles()
*/

void wxPMProject::OnCmdFilesRecursively( wxCommandEvent& event )
{
	InsertFiles( TRUE );
}  // wxPMProject::OnCmdFilesRecursively()


void wxPMProject::OnCmdProjectSettings( wxCommandEvent& event )
{
	wxMessageBox( "OnCmdProjectSettings" );
}

/*** protected methods ***/

void wxPMProject::DoInsertFiles( bool recursive, 
								 wxString topDir, 
								 const wxString& mask,
								 wxStringList& fileList )
{
	// NOTE:: this chunk of code is converted from v-0.0.1 of wxStudio

	if ( topDir[ topDir.Length()-1 ] != '/' ) 
		
		topDir.Append( "/" );

	wxString path = ::wxFindFirstFile( topDir + mask, wxFILE );

	while( path != "" )
	{
		fileList.Add( path );
		
		path = wxFindNextFile();
	}

	if ( recursive )
	{
		path = wxFindFirstFile( topDir + "*", wxDIR );

		wxStringList dirs;

		while( path != "" )
		{
			size_t len = path.Length();

			if ( path[len-1] != '.' )

				dirs.Add( path );
			
			path = wxFindNextFile();
		}

        size_t i;
		for( i = 0; i != dirs.GetCount(); ++i )

			DoInsertFiles( TRUE, dirs[i], mask, fileList );
	}
}


void wxPMProject::InsertFiles( bool recursive )
{
	// TBD:: should be made static of wxPMProject class
	static wxString _gPMProject_LRUMask = "*";

	wxTextEntryDialog* wcardDlg = 
		new wxTextEntryDialog( wxPMBootSvc().GetMainWindow(), 
							   "Enter mask of files to insert", 
							   "Insert Files", _gPMProject_LRUMask );

	if ( wcardDlg->ShowModal() == wxID_OK )
	{
		_gPMProject_LRUMask =  wcardDlg->GetValue();

		wxDirDialog dirDlg( wxPMBootSvc().GetMainWindow(), 
							"Choose directory", wxGetWorkingDirectory() );

		if ( dirDlg.ShowModal() == wxID_OK )
		{
			wxString path = dirDlg.GetPath() + "/";

			wxString file = wxFindFirstFile( path + _gPMProject_LRUMask, wxFILE );

			wxStringList fileList;

			DoInsertFiles( recursive, path, _gPMProject_LRUMask, fileList );

			PMFileInfoListT lst;

            size_t i;
			for( i = 0; i != fileList.GetCount(); ++i )

				lst.Add( new wxPMFileInfo( fileList[i] ) );

			this->AddMultipleFiles( lst );
		}
	}
}

/***** Implementation for class wxPMTempProject *****/

IMPLEMENT_DYNAMIC_CLASS( wxPMTempProject, wxPMProject )

wxPMTempProject::wxPMTempProject()
	: wxPMProject()
{
	SetTitle( "Other files" );
}

wxPMTempProject::~wxPMTempProject()
{}

bool wxPMTempProject::CanHaveFile()
{
	return FALSE;
}

bool wxPMTempProject::CanAcceptOtherFiles()
{
	return TRUE;
}

void wxPMTempProject::AddSubdocument( wxPMDocument* pSubdoc )
{
	wxPMProject::AddSubdocument( pSubdoc );

	if ( pSubdoc->CanHaveFile() &&
		 pSubdoc->GetFileInfo() &&
		 !ContainsFile( *pSubdoc->GetFileInfo() )
	   )
	
		AddFile( new wxPMFileInfo( *pSubdoc->GetFileInfo() ) );
}

/***** Implementation for class wxPMWorkplace *****/

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

// mergable menus for wxPMWorkplace

static wxCommandInfo _gWorkplaceCommands[] = {

	wxCommandInfo( wxPMWorkplace::ID_SAVE_WORKPLACE, "Save workplace" ), 
	wxCommandInfo( wxPMWorkplace::ID_CLOSE_WORKPLACE, "Close workplace" ), 

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

static wxCommandInfo _gWorkplaceCommands1[] = {

//	wxCommandInfo( wxPMWorkplace::ID_INSERT_PROJECT, "Insert project..." ), 
//	wxCommandInfo( wxPMWorkplace::ID_NEW_PROJECT, "Insert new project..." ), 
//	wxCommandInfo( wxPMWorkplace::ID_MAKE_PROJECT_ACTIVE, "Set active project..." ), 

//	wxCommandInfo( -1, "SEPARATOR" ), 

//	wxCommandInfo( wxPMWorkplace::ID_CLOSE_WORKPLACE, "Close workplace" ), 
//	wxCommandInfo( wxPMWorkplace::ID_NEW_WORKPLACE, "New workplace..." ), 

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

static wxCommandInfo _gWorkplaceCommands2[] = {

	wxCommandInfo( wxPMWorkplace::ID_TIPS, "&Tips" ), 
	wxCommandInfo( 0, "" ) // terminator
};


static wxPMMergableMenuChain _gWorkplaceChains[] = {

	wxPMMergableMenuChain( "/&File/Open &workplace...", _gWorkplaceCommands ),
//	wxPMMergableMenuChain( "/&Workplace/", _gWorkplaceCommands1 ),
	wxPMMergableMenuChain( "/&Help/", _gWorkplaceCommands2 ),

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

IMPLEMENT_DYNAMIC_CLASS( wxPMWorkplace, wxPMDocument )

BEGIN_EVENT_TABLE( wxPMWorkplace, wxDocument )

	EVT_MENU( wxPMWorkplace::ID_INSERT_PROJECT,      wxPMWorkplace::OnCmdInsertProject )
	EVT_MENU( wxPMWorkplace::ID_NEW_PROJECT,         wxPMWorkplace::OnCmdNewProject )
	EVT_MENU( wxPMWorkplace::ID_MAKE_PROJECT_ACTIVE, wxPMWorkplace::OnCmdMakeProjectActive )
	EVT_MENU( wxPMWorkplace::ID_CLOSE_WORKPLACE,     wxPMWorkplace::OnCmdCloseWorkplace )
	EVT_MENU( wxPMWorkplace::ID_NEW_WORKPLACE,       wxPMWorkplace::OnCmdNewWorkplace )
	EVT_MENU( wxPMWorkplace::ID_TIPS,				 wxPMWorkplace::OnCmdTips )

END_EVENT_TABLE()

wxPMWorkplace::wxPMWorkplace()

	: m_pActivePrj( NULL ),
	  m_pLayoutAlg( NULL ),
      m_ActivePrjNo( 0 )
{
	//wxMessageBox( "DBG:: wxPMWorkplace object created!" );
}

wxPMWorkplace::~wxPMWorkplace()
{
	CleanUp();
}

bool wxPMWorkplace::CleanUp()
{
//	WX_CLEAR_ARRAY( m_Listeners );
	WX_CLEAR_ARRAY( m_Files );

    return TRUE;
}  // wxPMWorkplace::CleanUp()


bool wxPMWorkplace::OnCreate(const wxString& path, long flags)
{
	// overriden  in order to succeed without a view-class
	return TRUE;
}

wxPMMergableMenuChain* wxPMWorkplace::GetMergableMenuChain()
{
	return _gWorkplaceChains;
}

void wxPMWorkplace::SetLayoutAlgorithm( wxPMLayoutAlgorithm* pAlg )
{
	m_pLayoutAlg = pAlg;
}

wxPMLayoutAlgorithm* wxPMWorkplace::GetLayoutAlgorithm()
{
	return m_pLayoutAlg;
}

void wxPMWorkplace::SetActiveProject( wxPMProject* pPrj )
{
	if ( m_pActivePrj == pPrj )

		return; // should not be set twice

	wxPMProject* pPrevActive = m_pActivePrj;

	if ( pPrevActive )

		wxPMBootSvc().StopService( pPrevActive );

	m_pActivePrj = pPrj;

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

		if ( m_Subdocuments[i] == pPrj )

			m_ActivePrjNo = i;

	wxPMBootSvc().StartService( m_pActivePrj );

	wxPM_NOTIFY_TWO_ARG( OnProjectActivated, *pPrj, pPrevActive )

	if ( pPrj->CanHaveFile() && pPrj->GetFileInfo() )

		::wxSetWorkingDirectory(
			wxPMFileInfo::GetDirectoryFromPath( pPrj->GetFileInfo()->GetPath() ) );
}

wxPMProject* wxPMWorkplace::GetActiveProject()
{
	return m_pActivePrj;
}

bool wxPMWorkplace::IsActiveProject( wxPMProject* pPrj )
{
    if (m_pActivePrj)
        int breakpoint = 1;
    if (pPrj)
        int breakpoint = 1;

	return pPrj == m_pActivePrj;
}

void wxPMWorkplace::AddProject( wxPMProject* pPrj )
{
	AddSubdocument( pPrj );

	wxPM_NOTIFY_ONE_ARG( OnProjectAdded, *pPrj )

	if ( this->GetSubdocuments().Count() == 1 )

		SetActiveProject( pPrj );
}

void wxPMWorkplace::RemoveProject( wxPMProject* pPrj )
{
    wxASSERT( pPrj );

	wxPM_NOTIFY_ONE_ARG( OnProjectRemoved, *pPrj )

	RemoveSubdocument( pPrj );

    Modify( TRUE );
}

size_t wxPMWorkplace::GetProjectCount()
{
	return GetSubdocuments().Count();
}

wxPMProject& wxPMWorkplace::GetProject( size_t index )
{
	return *(wxPMProject*)GetSubdocuments()[index];
}

void wxPMWorkplace::UpdateUIForView( wxPMView* pView )
{
	// TBD::
}

void wxPMWorkplace::AddListener( wxPMWorkplaceListener* pListener )
{
	m_Listeners.Add( pListener );
}

void wxPMWorkplace::RemoveListener( wxPMWorkplaceListener* pListener )
{
	m_Listeners.Remove( pListener );
}

void wxPMWorkplace::OnClosingChildDocument( wxPMDocument* pChild, bool wasSaved )
{
	if ( pChild->CanHaveFile() == FALSE ||
		 pChild->GetFileInfo() == NULL )
		 return;

	m_Files.Add( new wxPMFileInfo( *pChild->GetFileInfo() ) );
}

void wxPMWorkplace::NotifyFileAdded( wxPMFileInfo& file, wxPMProject* pPrj )
{
	wxPM_NOTIFY_TWO_ARG( OnFileAdded, file, pPrj )
}

void wxPMWorkplace::NotifyFileRemoved( wxPMFileInfo& file, wxPMProject* pPrj )
{
	wxPM_NOTIFY_TWO_ARG( OnFileRemoved, file, pPrj )
}

bool wxPMWorkplace::UseSerializer()
{
	return TRUE;
}

bool wxPMWorkplace::SerializePMDocument( wxPMSerializer& ser )
{
	wxString docTypeName = this->GetDocumentTemplate()->GetDocumentName();

	ser.Exchange( "docTypeName", docTypeName );

	PMDocumentListT& subDocs = GetSubdocuments();

	if ( ser.IsLoading() )
	{
		long activeIdx = 0;

		ser.Exchange( "activeProjectIndex", activeIdx );

		long idx = 0; 
        wxString name, origName;
        long n = 0;

		ser.SetPath( "Projects" );

		bool ok = ser.GetFirstEntry( name, idx );
        origName = name;

        bool retry = FALSE;
		wxString fname;

		while( ok )
		{
            if (!retry)
		        ser.Exchange( name, fname );

			if (fname.length())
			{
				// Use absolute paths in the fileinfo until it is time
				// to save the document
				fname = wxPMGetAbsolutePath(wxPMBootSvc().GetWorkplace()->GetFileInfo()->GetPathOnly(), 
					                        fname);
			}

			wxPMFileInfo finfo( fname );

			wxPMProject* pPrj = wxPMBootSvc().GetDocManager()->OpenProject( finfo );

            if (pPrj)
            {
                if (retry)
                {
                    // Project was renamed/relocated
                    pPrj->Modify(TRUE);
                    Modify(TRUE);
                }

                retry = FALSE;
    			if ( n == activeIdx )
	    			this->SetActiveProject( pPrj );
            }
            else
            {
                retry = FALSE;
                // Project was not found or unable to be opened
                wxString dir = fname;

                // Project will either be removed/relocated
                // so the workplace is dirty
                Modify(TRUE);  

                // Check if the project location has been moved by giving
                // the user the chance to select the new project location/name
                wxString msgStr;
                msgStr.Printf("Unable to locate/open project:\n\n\t%s   \n\nDo you wish to search for and select the project's new location?\n(Answering NO will remove the project from the workplace)",fname.c_str());
                bool Ok = (wxMessageBox(msgStr,"PROJECT file missing/moved...",wxYES_NO|wxICON_QUESTION) == wxYES);
                if (Ok)
                {
                    wxString MaskStr;
                    MaskStr.Printf("*.%s",wxwEXT_PROJECT);
                    MaskStr.Replace(";",";*.");  // Just in case additional possible extensions are added

#if wxCHECK_VERSION(2,3,0)
                    wxFileDialog dlg( wxPMBootSvc().GetMainWindow(), 
				                      "Find existing project", dir, "", MaskStr, wxOPEN | wxCHANGE_DIR );
#else
                    wxFileDialog dlg( wxPMBootSvc().GetMainWindow(), 
				                      "Find existing project", dir, "", MaskStr, wxOPEN );
#endif
                    if ( dlg.ShowModal() == wxID_OK )
                    {
                        fname = dlg.GetPath();
                        retry = TRUE;
                        continue;
                    }
                }
            }

			ok = ser.GetNextEntry( name, idx );
			++n;
		}

		ser.SetPath( ".." );
	}
	else
	{
		long n = m_ActivePrjNo; 

		ser.Exchange( "activeProjectIndex", n );

		if ( ser.Exists( "Projects" ) )
            ser.DeleteGroup( "Projects" );

		ser.SetPath( "Projects" );

        size_t i;
		for( i = 0; i != m_Files.Count(); ++i )
		{
			wxString fname = m_Files[i]->GetPath();

			// 'fname' is the absolute path (with drive/volume and path
			// from the root level of the drive/volume) when passed
			// in to wxPMGetRelativePath(), but needs to be changed
			// to a relative path before saving it in the project file 
			fname = wxPMGetRelativePath(wxPMBootSvc().GetWorkplace()->GetFileInfo()->GetPathOnly(), 
				                        fname);

			ser.Exchange( ser.IndexedKey( "file", i ), fname );
		}

		ser.SetPath( ".." );
	}

	return TRUE;
} // wxPMWorkplace::SerializePMDocument()


/*** command-event handlers ***/

void wxPMWorkplace::OnCmdInsertProject( wxCommandEvent& event )
{
    // Note that this returns the .WSP name as part of the dir, but the
    // wxFileDialog strips that for us
    wxString dir = GetFilename();

    wxString MaskStr;
    MaskStr.Printf("*.%s",wxwEXT_PROJECT);
    MaskStr.Replace(";",";*.");

#if wxCHECK_VERSION(2,3,0)
	wxFileDialog dlg( wxPMBootSvc().GetMainWindow(), 
					  "Insert existing project", dir, "", MaskStr, wxOPEN | wxCHANGE_DIR );
#else
	wxFileDialog dlg( wxPMBootSvc().GetMainWindow(), 
					  "Insert existing project", dir, "", MaskStr, wxOPEN );
#endif

	if ( dlg.ShowModal() == wxID_OK )
	{
		wxPMDocManager& mgr = *wxPMBootSvc().GetDocManager();

        // Make sure the selected file is an existing PROJECT file
		if (mgr.CreatePMDocument( wxPMFileInfo( dlg.GetPath() ), 0, 
                        mgr.FindTemplateForDocClass( CLASSINFO(wxPMProject))))
        {
            Modify( TRUE );
        }
        else
        {
            wxString errmsgStr;
            errmsgStr.Printf("Selected file '%s' is not\na valid PROJECT file!",dlg.GetPath().c_str());
            wxMessageBox(errmsgStr.c_str(), "Invalid file selected", wxOK | wxICON_EXCLAMATION);
        }
	}
}  // wxPMWorkplace::OnCmdInsertProject()


void wxPMWorkplace::OnCmdNewProject( wxCommandEvent& event )
{
	wxPMDocManager& mgr = *wxPMBootSvc().GetDocManager();

	if (mgr.CreatePMDocument( wxPMFileInfo(), wxDOC_NEW, 
		    				  mgr.FindTemplateForDocClass( CLASSINFO(wxPMProject) ) ))
        Modify( TRUE );
    else
        wxMessageBox("Unable to create new PROJECT file!", "Failed", wxOK | wxICON_EXCLAMATION);
}  // wxPMWorkplace::OnCmdNewProject()


void wxPMWorkplace::OnCmdMakeProjectActive( wxCommandEvent& event )
{
    // Should de-highlight the previous active project, and then
    // highlight (bold the text of) the new selected project
}  // wxPMWorkplace::OnCmdMakeProjectActive()


void wxPMWorkplace::OnCmdCloseWorkplace( wxCommandEvent& event )
{
	wxPMBootSvc().GetDocManager()->CloseWorkplace();
}


void wxPMWorkplace::OnCmdNewWorkplace( wxCommandEvent& event )
{
	wxMessageBox( "TBD:: wxPMWorkplace::OnCmdNewWorkplace()" );
}


void wxPMWorkplace::OnCmdTips( wxCommandEvent& event )
{
	wxMessageBox( "There are no scrollbars in menu or widget editors,\n"
				  "use right-click + dragging to navigate around the canvas.\n\n"

				  "Editing of window-resources is not supported yet,\n"
				  "(empty canvas is shown instead)\n\n"

				  "To change source-generation rules, edit\n"
				  "config_srctempl.txt\n\n"

				  "Restart demo to apply changes made in config_* files\n\n"

				  "Alt+Backspace - to undo operation,\n"
				  "Ctrl+A - to redo operation,\n\n"

				  "If you have lost or closed some pane,\n"
				  "right-click on the very edge of main window\n"
				  "to bring it back (context menu will appear)"
				  );
}
