/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     2000/02/08
// RCS-ID:      
// Copyright:   (c) Aleksandras Gluchovas
// Licence:   	GPL (version 2.0 or later)
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
    #pragma implementation "pm_docmgr.cpp"
    #pragma interface "pm_docmgr.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_docmgr.h"

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

#include "mimetypes.h"

/***** Implementation for class wxPMDocumentListener *****/

void wxPMDocumentListener::OnDocumentOpened( wxPMDocument& doc, bool created )
{}

void wxPMDocumentListener::OnDocumentClosed( wxPMDocument& doc, bool saved )
{}

bool wxPMDocumentListener::OnClosingDocument( wxPMDocument& doc, wxOutputStream* s, wxPMSerializer* ser )
{ return TRUE; }

bool wxPMDocumentListener::OnOpeningDocument( wxPMDocument& doc, wxInputStream* s, wxPMSerializer* ser )
{ return TRUE; }

/***** Implementation for class wxPMDocManager *****/

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

// mergable menus for wxPMDocManager 

static wxCommandInfo _gDocManagerCommands[] = {

	wxCommandInfo( wxID_NEW,                  "&New", "New document", "Ctrl+N" ), 
	wxCommandInfo( wxID_OPEN,                 "&Open...", "Opens a file", "Ctrl+O" ), 
	wxCommandInfo( wxPMDocManager::ID_OPEN_URL, "Open &URL...", "Opens a file from URL (disk only)", "" ), 
	wxCommandInfo( wxID_CLOSE,                "&Close" ), 
	wxCommandInfo( wxPMDocManager::ID_CLOSE_ALL, "Close all" ), 
	wxCommandInfo( -1,                        "SEPARATOR" ), 
	wxCommandInfo( wxPMDocManager::ID_OPEN_WORKPLACE, "Open &workplace..." ), 
//	wxCommandInfo( wxPMDocManager::ID_SAVE_WORKPLACE, "Save workplace..." ), 
//	wxCommandInfo( wxPMDocManager::ID_CLOSE_WORKPLACE, "Close workplace..." ), 
//	wxCommandInfo( wxPMDocManager::ID_OPEN_PROJECT, "Open &Project..." ), 
	wxCommandInfo( -1,                        "SEPARATOR" ), 
	wxCommandInfo( wxID_SAVE,                 "&Save", "Save document", "Ctrl+S" ), 
	wxCommandInfo( wxID_SAVEAS,               "Save &as..." ), 
	wxCommandInfo( wxPMDocManager::ID_SAVE_AS_URL, "Save as URL..." ), 
	wxCommandInfo( wxPMDocManager::ID_SAVE_ALL,    "Save al&l" ), 

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

static wxPMMergableMenuChain _gDocManChains[] = {

	wxPMMergableMenuChain( "/&File/", _gDocManagerCommands ),

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


BEGIN_EVENT_TABLE( wxPMDocManager, wxDocManager )

	EVT_MENU( wxID_NEW,	  wxPMDocManager::OnNew )
	EVT_MENU( wxID_OPEN,  wxPMDocManager::OnOpenFile )
	EVT_MENU( wxID_CLOSE, wxPMDocManager::OnCloseFile )
	EVT_MENU( wxPMDocManager::ID_OPEN_URL,		 wxPMDocManager::OnOpenURL )
	EVT_MENU( wxPMDocManager::ID_CLOSE_ALL,      wxPMDocManager::OnCloseAll )
	EVT_MENU( wxPMDocManager::ID_OPEN_WORKPLACE, wxPMDocManager::OnOpenWorkplace )
	EVT_MENU( wxPMDocManager::ID_SAVE_WORKPLACE, wxPMDocManager::OnSaveWorkplace )
	EVT_MENU( wxPMDocManager::ID_CLOSE_WORKPLACE, wxPMDocManager::OnCloseWorkplace )
//	EVT_MENU( wxPMDocManager::ID_OPEN_PROJECT,   wxPMDocManager::OnOpenProject )
	EVT_MENU( wxID_SAVE,   wxPMDocManager::OnSave )
	EVT_MENU( wxID_SAVEAS, wxPMDocManager::OnSaveAs )
	EVT_MENU( wxPMDocManager::ID_SAVE_AS_URL,	   wxPMDocManager::OnSaveAs )
	EVT_MENU( wxPMDocManager::ID_SAVE_ALL,         wxPMDocManager::OnSaveAll )

END_EVENT_TABLE()

wxPMDocManager::wxPMDocManager( long flags, bool initialize )

	: wxDocManager( flags, initialize )
{}

wxPMDocManager::~wxPMDocManager()
{
}

wxPMMergableMenuChain* wxPMDocManager::GetMergableMenuChain()
{
	return _gDocManChains;
}

// actual "performers" of commands

void wxPMDocManager::NewProject( wxPMFileInfo& location, wxDocTemplate& templ )
{
}


void wxPMDocManager::NewWorkplace( wxPMFileInfo& location, wxDocTemplate& templ )
{
}


wxPMProject* wxPMDocManager::OpenProject( const wxPMFileInfo& location )
{
    wxPMProject *pProject = (wxPMProject*)CreatePMDocument( location, wxDOC_SILENT );
    if (!pProject)
    {
        int breakpoint = 1;
    }

	return pProject;
}  // wxPMDocManager::OpenProject()


wxPMWorkplace* wxPMDocManager::OpenWorkplace( const wxPMFileInfo& location )
{
    bool success = FALSE;
    wxPMWorkplace *pWorkplace = (wxPMWorkplace*)CreatePMDocument( location, wxDOC_SILENT );
    if (!pWorkplace)
    {
        // Could not open the WORKPLACE

        // Check if the project location has been moved by giving
        // the user the chance to select the new project location/name
        int breakpoint = 1;
    }

	return pWorkplace;
}  // wxPMDocManager::OpenWorkplace()


wxPMDocument* wxPMDocManager::OpenDocument( const wxPMFileInfo& finfo, 
										    wxPMDocument* pParentDoc,
											bool activate )
{
	if ( pParentDoc )
	{
        // Check if document is already open, and if it is, just
        // switch focus to that document
		wxPMDocument* pDoc = pParentDoc->FindSubdocumentForFile( finfo );

		if ( pDoc )
		{
			if ( activate ) 
				
				ActivateDocument( pDoc );

			return pDoc;
		}
	}

    // Document has not been opened yet, so we must create its views/etc.
    bool success = FALSE;
    wxPMDocument *pDocument = CreatePMDocument( finfo, wxDOC_SILENT, NULL, pParentDoc, activate );
    if (!pDocument)
    {
        // Could not open the document

        // Check if the document location has been moved by giving
        // the user the chance to select the new project location/name
        int breakpoint = 1;
    }

	return pDocument;
}  // wxPMDocManager::OpenDocument()


void wxPMDocManager::ActivateDocument( wxPMDocument* pDoc )
{
	// TBD::
	pDoc->OnActivatePMDocument();

	wxView* pView = pDoc->GetFirstView();

	if ( pView )
	{
		pView->Activate( TRUE );

		if ( pView->GetFrame() )
		{
			pView->GetFrame()->Raise();
			pView->GetFrame()->SetFocus();
		}
	}
}  // wxPMDocManager::ActivateDocument()


void wxPMDocManager::ActivatePMView( wxPMView* pView )
{
	// TBD::
}  // wxPMDocManager::ActivatePMView()


bool wxPMDocManager::CloseDocument( wxPMDocument* pDoc )
{
	PMClassInfoListT docsToSkip;
	return DoSaveDocumentRecursively( pDoc, docsToSkip, TRUE );
}  // wxPMDocManager::CloseDocument()


bool wxPMDocManager::SaveDocument( wxPMDocument* pDoc )
{
	PMClassInfoListT docsToSkip;
	return DoSaveDocumentRecursively( pDoc, docsToSkip, FALSE );
}  // wxPMDocManager::SaveDocument()


bool wxPMDocManager::CloseWorkplace()
{
	wxASSERT( wxPMBootSvc().HasWorkplace() ); // DBG:;

	return CloseDocument( wxPMBootSvc().GetWorkplace() );
}  // wxPMDocManager::CloseWorkplace()


/*** event handlers ***/

void wxPMDocManager::OnNew( wxCommandEvent& event )
{
	const wxEventTable* pTbl = wxDocManager::GetEventTable();

	// let the wxDocManager handle this first
	SearchEventTable((wxEventTable&)*pTbl, event);
}

void wxPMDocManager::OnOpenFile( wxCommandEvent& event )
{
	const wxEventTable* pTbl = wxDocManager::GetEventTable();

	// let the wxDocManager handle this first
	SearchEventTable((wxEventTable&)*pTbl, event);
}

// TBD:: should be made static of wxPMDocManager  class

static wxString _gLRU_URLText = "";

void wxPMDocManager::OnOpenURL( wxCommandEvent& event )
{
	wxTextEntryDialog dlg( wxPMBootSvc().GetMainWindow(),
						   "Please enter an URL for the file to be opened",
						   "Open file from URL", _gLRU_URLText);

	if ( dlg.ShowModal() == wxID_OK )
	{
		_gLRU_URLText = dlg.GetValue();

		if ( dlg.GetValue().Length() == 0 ) 
		{
			wxMessageBox( "URL cannot be empty!" );

			return;
		}

		wxPMFileInfo finfo(  dlg.GetValue() );

		if ( !finfo.DetectType() )
		{
			wxMessageBox( "Sorry, but the location/type of the specified file is not recognized!"
						  "\nMake sure you have given protocol prefix, e.g. HTTP:// or FTP://"
                          "\n(Currently can only open local files, so use a full path name)");

			return;
		}

		CreatePMDocument( finfo, wxDOC_SILENT );
	}
}

void wxPMDocManager::OnOpenWorkplace( wxCommandEvent& event )
{
	// FIXME:: what about extention of the workplace as we
	//		   do not know it's exact type at this moment...?

    wxString MaskStr;
    MaskStr.Printf("*.%s",wxwEXT_WORKPLACE);
    MaskStr.Replace(";",";*.");  // Just in case additional possible extensions are added

    wxString cwd = ::wxGetCwd();
#if wxCHECK_VERSION(2,3,0)
	wxFileDialog dlg( wxPMBootSvc().GetMainWindow(), 
					  "Open existing workplace", cwd, "", MaskStr, wxOPEN | wxCHANGE_DIR );
#else
	wxFileDialog dlg( wxPMBootSvc().GetMainWindow(), 
					  "Open existing workplace", cwd, "", MaskStr, wxOPEN );
#endif

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

		CreatePMDocument( wxPMFileInfo( dlg.GetPath() ), 0, 
					  FindTemplateForDocClass( CLASSINFO(wxPMWorkplace) ) );
	}
}


void wxPMDocManager::OnSaveWorkplace( wxCommandEvent& event )
{
}


void wxPMDocManager::OnOpenProject( wxCommandEvent& event )
{
	// FIXME:: what about extention of the project as we
	//		   do not know it's exact type at this moment...?

    wxString cwd = ::wxGetCwd();
    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(), 
					  "Insert existing project", cwd, "", MaskStr, wxOPEN | wxCHANGE_DIR );
#else
	wxFileDialog dlg( wxPMBootSvc().GetMainWindow(), 
					  "Insert existing project", cwd, "", MaskStr, wxOPEN );
#endif

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

		mgr.CreatePMDocument( wxPMFileInfo( dlg.GetPath() ), 0, 
							  mgr.FindTemplateForDocClass( CLASSINFO(wxPMProject) ) );
	}
}

void wxPMDocManager::OnCloseFile( wxCommandEvent& event )
{
	wxPMDocument* pDoc = (wxPMDocument*)GetCurrentDocument();

	wxASSERT( pDoc ); // DBG::

	if (pDoc)
		CloseDocument( pDoc );

}

void wxPMDocManager::OnCloseAll( wxCommandEvent& event )
{
	// closing all starts from workplace
	wxPMDocument* pDoc = wxPMBootSvc().GetWorkplace();

	wxASSERT( pDoc ); // DBG::

	if (pDoc)
		CloseDocument( pDoc );

//    event.Enable( (doc != (wxDocument*) NULL) );
}

void wxPMDocManager::OnSave( wxCommandEvent& event )
{
	wxPMDocument* pDoc = (wxPMDocument*)GetCurrentDocument();

	wxASSERT( pDoc ); // DBG::

	if (!pDoc)
		return;

	if ( SaveDocument( pDoc ) )
	{
		if ( pDoc->CanHaveFile()  )

			wxPMBootSvc().SetStatusText( pDoc->GetFileInfo()->GetPath() + " saved" );
		else
			wxPMBootSvc().SetStatusText( "Document saved" );
	}
}

void wxPMDocManager::OnSaveAs( wxCommandEvent& event )
{
	// TBD::
}

void wxPMDocManager::OnSaveAsURL( wxCommandEvent& event )
{
	// TBD::
}

void wxPMDocManager::OnSaveAll( wxCommandEvent& event )
{
	// Make sure at least _something_ is open before allowing save
	if (!wxPMBootSvc().GetWorkplace())
		return;

	// saving all starts from workplace
	if ( SaveDocument( wxPMBootSvc().GetWorkplace() ) )

		wxPMBootSvc().SetStatusText( "All documents saved" );
}

void wxPMDocManager::OnCloseWorkplace( wxCommandEvent& event )
{
	CloseWorkplace();
}

void wxPMDocManager::AddListener( wxPMDocumentListener* listener )
{
	m_Listeners.Add( listener );
}

void wxPMDocManager::RemoveListener( wxPMDocumentListener* listener )
{
	m_Listeners.Remove( listener );
}

wxPMDocument* wxPMDocManager::CreatePMDocument( const wxPMFileInfo& finfo, long flags,
												wxDocTemplate* pSingleTemplate,
												wxPMDocument* pParentDoc,
												bool activate
											  )
{
	// pasted & heavily code of wxDocManager::CreateDocument(..)

	//////////////////////////////////////////
	// Part 1 : creation of document object
	//////////////////////////////////////////

	wxPMDocument *pDoc = NULL;

	wxDocTemplate **templates = new wxDocTemplate *
		[ pSingleTemplate ? 1 : m_templates.Number() ];

	int i;
	int n = pSingleTemplate ? 1 : 0;

	if ( pSingleTemplate == NULL )
	{
		for (i = 0; i < m_templates.Number(); i++)
		{
			wxDocTemplate *temp = (wxDocTemplate *)(m_templates.Nth(i)->Data());
			if (temp->IsVisible())
			{
				templates[n] = temp;
				n ++;
			}
		}
	}
	else
	{
		templates[0] = pSingleTemplate;
	}

	if (n == 0)
	{
		delete[] templates;
		return NULL;
	}

	bool isLoading = !(flags & wxDOC_NEW);

	// New document: user chooses a template, unless there's only one.

	if ( isLoading == FALSE ) // if creating a new document
	{
		wxString title;

		if (n == 1)
		{
			wxDocTemplate *temp = templates[0];
			delete[] templates;
			pDoc = (wxPMDocument*)temp->CreateDocument( finfo.GetPath(), flags);

			if (pDoc)
			{
				wxASSERT( pDoc->IsKindOf( CLASSINFO(wxPMDocument) ) ); // DBG::
				title = pDoc->GetTitle();
	
				pDoc->SetDocumentName(temp->GetDocumentName());
				pDoc->OnNewDocument();
			}
			else 
				return NULL;
		}
		else
		{
#if wxCHECK_VERSION(2,3,0)
			wxDocTemplate *temp = SelectDocumentType(templates, n, TRUE);
#else
			wxDocTemplate *temp = SelectDocumentType(templates, n);
#endif
			delete[] templates;
			if (temp)
			{
				pDoc = (wxPMDocument*)temp->CreateDocument( finfo.GetPath(), flags);

				if (pDoc)
				{
					wxASSERT( pDoc->IsKindOf( CLASSINFO(wxPMDocument) ) ); // DBG::
					title = pDoc->GetTitle();

					pDoc->SetDocumentName(temp->GetDocumentName());
				}
				else
					return NULL;
			}
			else
				return NULL;
		}

		pDoc->OnNewDocument();

		if ( title != "" )

			pDoc->SetTitle( title ); // preserve title if it was set
									 // prior to OnNewDocument();

		pDoc->Modify( TRUE ); // all new documents are automatically made "dirty"
	}

	wxPMSerializer* pSer = NULL;
	wxPMFileInfo finfo2( finfo.GetPath() );

	if ( isLoading )
	{
		//////////////////////////////////////
		// otherwise, OPEN existing document
		//////////////////////////////////////

		wxDocTemplate *temp = (wxDocTemplate *) NULL;

		if ( pSingleTemplate )

			temp = pSingleTemplate ;
		else
		{
			if (flags & wxDOC_SILENT)
				temp = FindTemplateForPath( finfo2.GetPath() );
			else
			{
				wxString path = finfo2.GetPath();

				temp = SelectDocumentPath(templates, n, path, flags);

				if ( temp != NULL ) 
				{
					finfo2.SetPath( path );

					temp = FindTemplateForPath( path );
				}
			}
		}
		
		delete[] templates;
		
		if (temp)
		{
			// document with empty-type indicates that it's exact type
			// should be determined by reading the "docTypeName" entry of 
			// their human-readable storage file

			if ( temp->GetDocumentName() == "" )
			{
				wxFileConfig* pCfgImpl = 
					new wxFileConfig( "someAppName","someVendor", finfo2.GetPath(),"",
									  wxCONFIG_USE_LOCAL_FILE );

				pSer = new wxPMSerializer( pCfgImpl, TRUE );

				wxString docTypeName;

				pSer->Exchange( "docTypeName", docTypeName );

				temp = FindTemplateForDocType( docTypeName );

				if ( temp == NULL )
				{
					wxMessageBox( "Unrecognized internal document type: " + docTypeName );

					return NULL;
				}

                // Unknown file type
                if (docTypeName == "")
                    return NULL;
			}

			pDoc = (wxPMDocument*)temp->CreateDocument( finfo2.GetPath(), flags);

			if (pDoc)
			{
				wxASSERT( pDoc->IsKindOf( CLASSINFO(wxPMDocument) ) ); // DBG::

				pDoc->SetDocumentName(temp->GetDocumentName());

				wxString title = finfo2.GetNameWithoutExtension();

				pDoc->SetTitle( title );

				pDoc->SetFileInfo( new wxPMFileInfo( finfo2.GetPath() ) );

				wxPMView* pView = (wxPMView*)pDoc->GetFirstView();

				if ( pView )

					pView->GetViewFrame()->SetTitle( /*pDoc->GetTitle()*/finfo2.GetNameWithoutPath() );
			}
			else
				return NULL;
		}
		else
			return NULL;
	}

	/////////////////////////////////////////
	// Part 2: peparing the document object
	/////////////////////////////////////////

	if ( pDoc->IsKindOf( CLASSINFO(wxPMProject) ) )
	{
		if ( wxPMBootSvc().HasWorkplace() )

			wxPMBootSvc().GetWorkplace()->AddProject( (wxPMProject*)pDoc );
		else
			DoCreateWorkplace( (wxPMProject*)pDoc );
	}
	else
	if ( pDoc->IsKindOf( CLASSINFO(wxPMWorkplace) ) )
	{
		if ( wxPMBootSvc().HasWorkplace() )
		{
			wxMessageBox( "Cannot create new workplace, while the current one is open" );

			delete pDoc; // implicitely deletes it's node
			return NULL;
		}

		wxPMBootSvc().SetWorkplace( (wxPMWorkplace*)pDoc );
	}
	else
	// otherwise, all the regular documents (direct children of active wxPMProject)
	{
		if ( pParentDoc )
		{
			pParentDoc->AddSubdocument( pDoc );
		}
		else
		if ( pDoc->GetParentDocument() == NULL )
		{
			// check if we have a workplace 
			
			if ( wxPMBootSvc().HasWorkplace() == FALSE )

				CreateProjectByClass( CLASSINFO(wxPMTempProject) );

			// first, try inserting new/opened document into the active project
			
			wxPMProject* pTargetPrj = wxPMBootSvc().GetWorkplace()->GetActiveProject();
			
			if ( pTargetPrj == NULL )
				
				// if above fails, then into the project which is meant to
				// accept "Other files"
				
				pTargetPrj = FindProjectForOtherFiles();
			
			if ( pTargetPrj == NULL )
            {				
				// create one if does not exist yet
    			pTargetPrj = CreateProjectByClass( CLASSINFO(wxPMTempProject) );
            }

            bool m = (m_docs.Member(pTargetPrj) != NULL);  
			
			pTargetPrj->AddSubdocument( pDoc );
		}
		// TBD::
	}

	/////////////////////////////////////////
	// Part 3: loading contents if any
	/////////////////////////////////////////

	if ( isLoading )
	{
        bool ok = TRUE;
		if ( !pDoc->CanHaveFile() )
		{
			if ( !pDoc->LoadPMDocument() )
			{
				wxMessageBox( "Error loading child-document" );

				return NULL;
			}
		}
		else
		if ( pDoc->UseSerializer() )
		{
			if ( !pSer )
			{
				wxFileConfig* pCfgImpl = 
					new wxFileConfig( "someAppName","someVendor", finfo2.GetPath(),"",
									  wxCONFIG_USE_LOCAL_FILE );

				pSer = new wxPMSerializer( pCfgImpl, TRUE );
			}

			bool ok = pDoc->SerializePMDocument( *pSer );

			if ( ok )
			
				// listener-specific saving are done after document has stored itself
				// itself successfully

				wxPM_NOTIFY_THREE_ARG( OnOpeningDocument, *pDoc, NULL, pSer );

			delete pSer;

			if ( !ok )
			{
				wxMessageBox( "Error loading document from file: " + finfo2.GetPath() );

           		if ( pParentDoc &&
                     !pDoc->IsKindOf( CLASSINFO(wxPMProject) ) &&
	                 !pDoc->IsKindOf( CLASSINFO(wxPMWorkplace) ) )
		        {
                    DoCloseDocument(pDoc, TRUE);
//			        pParentDoc->RemoveSubdocument( pDoc );
//                    delete pDoc;
                    return NULL;
		        }

//glt - trying to not have open of workplace fail if a file is not found
//glt                return NULL;
			}
		}
		else
		{
			// otherwise stream-based loading

            ok = wxFileExists(finfo2.GetPath());
            if (!ok)
            {
                int glt = 1;  // breakpoint

				wxString pParentPath;
				pParentPath = "";
				if (pParentDoc)
					pParentPath = pParentDoc->GetFilename();

                wxString possiblePath;
                possiblePath.Printf("%s/%s",
                        ::wxPathOnly( pParentPath ),
                        finfo2.GetNameWithoutPath().c_str());

                if (wxFileExists(possiblePath))
                {
                    // ask if user wants to use this file
                    wxString msgStr;
                    msgStr.Printf("Unable to locate/open file:\n\n\t%s   \n\nA file with this same name exists in the project's directory:\n\n\t%s   \n\nDo you wish to use this file in place of the missing file?\n(Answering NO will allow you to browse for the missing file)",finfo2.GetPath().c_str(),possiblePath.c_str());
                    ok = (wxMessageBox(msgStr,"File missing/moved...",wxYES_NO|wxICON_QUESTION) == wxYES);

                    if (ok)
                    {
                        finfo2.SetPath(possiblePath);
                        pDoc->SetFilename(possiblePath);
                    }
                }

                if (!ok)
                {
                    // Must find new path/location of this file, or
                    // delete the reference to the file
                    wxString msgStr, titleStr;
                    msgStr.Printf("Do you wish to search for and select the file's new location?\n(Answering NO will remove the file from the project)");
                    titleStr.Printf("Looking for '%s':",finfo2.GetNameWithoutPath().c_str());
                    bool Ok = (wxMessageBox(msgStr.c_str(),titleStr,wxYES_NO|wxICON_QUESTION) == wxYES);
                    if (Ok)
                    {

						wxString p,f,e;
						wxSplitPath(finfo2.GetPath(),&p,&f,&e);

                        wxString dir = finfo2.GetDirectoryFromPath(finfo2.GetPath());
                        wxString MaskStr;
                        MaskStr.Printf("*.%s",e.c_str());
                        MaskStr.Replace(";",";*.");  // Just in case additional possible extensions are added

#if wxCHECK_VERSION(2,3,0)
                        wxFileDialog dlg( wxPMBootSvc().GetMainWindow(), 
				                          titleStr, dir.c_str(), "", MaskStr, wxOPEN | wxCHANGE_DIR );
#else
                        wxFileDialog dlg( wxPMBootSvc().GetMainWindow(), 
				                          titleStr, dir.c_str(), "", MaskStr, wxOPEN );
#endif
                        if ( dlg.ShowModal() == wxID_OK )
                        {
							possiblePath = dlg.GetPath();
                            finfo2.SetPath(possiblePath);
                            pDoc->SetFilename(possiblePath);
                            ok = TRUE;
                        }
                    }
                }
            }

            if (ok)
            {
			    wxPMFileLoader* pLoader = finfo2.GetFileLoader();

			    wxInputStream* pStm = pLoader->GetInputStream();

			    ok = pDoc->LoadPMDocument( *pStm );

				if ( ok )
			
					// listener-specific saving are done after document has stored itself
					// itself successfully

					wxPM_NOTIFY_THREE_ARG( OnOpeningDocument, *pDoc, pStm, NULL );

			    pLoader->Close();
            }

			if ( !ok )
			{
				wxMessageBox( "Error loading document from file: " + finfo2.GetPath() );

           		if ( pParentDoc &&
                     !pDoc->IsKindOf( CLASSINFO(wxPMProject) ) &&
	                 !pDoc->IsKindOf( CLASSINFO(wxPMWorkplace) ) )
		        {
                    DoCloseDocument(pDoc, TRUE);
//                    pParentDoc->RemoveSubdocument( pDoc );
//                    delete pDoc;
                    return NULL;
		        }

//glt - trying to not have open of workplace fail if a file is not found
//glt                return NULL;
			}
		}

        if (ok)
		    AddFileToHistory( finfo2.GetPath() );
	}

	////////////////////////////////////////////////
	// Part 4: registration of document's services
	////////////////////////////////////////////////

	// document is itself a service

	wxPMBootSvc().AddService( pDoc );
	
	wxNode* pNode = pDoc->GetAllViews()->First();

	if ( pNode == NULL && !pDoc->IsStarted() )

		wxPMBootSvc().StartService( pDoc ); // auto-start document service
											// if it has no views initially

	else
		// otherwise it's view will start document's service when activated
		while( pNode )
		{
			wxPMView* pView = (wxPMView*)pNode->GetData();

			// it's views are services too

			wxPMBootSvc().AddService( pView );

			// view's service starts itself later, via OnActivateView(...)
			//wxPMBootSvc().StartService( pView );

			pNode = pNode->GetNext();
		}

	// any other associated services are started upon creation

	StartAssociatedServices( *pDoc );

	if ( activate )
		
		ActivateDocument( pDoc );

	wxPM_NOTIFY_TWO_ARG( OnDocumentOpened, *pDoc, flags != wxDOC_SILENT );

	return pDoc;
}

wxDocument* wxPMDocManager::CreateDocument(const wxString& path, long flags )
{
	wxPMFileInfo finfo( path );

	return CreatePMDocument( finfo, flags );
}

wxDocTemplate* wxPMDocManager::FindTemplateForPath(const wxString& path)
{
    wxDocTemplate *theTemplate = (wxDocTemplate *) NULL;

    // Find the template which this extension corresponds to
    int i;


    for (i = 0; i < m_templates.Number(); i++)
    {
        wxDocTemplate *temp = (wxDocTemplate *)m_templates.Nth(i)->Data();
        if ( temp->GetDocumentName() == "" && temp->FileMatchesTemplate(path) )
        {
            theTemplate = temp;
            break;
        }
    }

	if ( theTemplate )
        return theTemplate;

    for (i = 0; i < m_templates.Number(); i++)
    {
        wxDocTemplate *temp = (wxDocTemplate *)m_templates.Nth(i)->Data();
        if ( temp->FileMatchesTemplate(path) )
        {
            theTemplate = temp;
            break;
        }
    }
    return theTemplate;
}

/*** protected methods ***/

wxPMProject* wxPMDocManager::FindProjectForOtherFiles()
{
	wxPMWorkplace& wp = *wxPMBootSvc().GetWorkplace();

    size_t i;
	for( i = 0; i != wp.GetProjectCount(); ++i )

		if ( wp.GetProject(i).CanAcceptOtherFiles() )

			return &wp.GetProject(i);

	return NULL;
}

// to work around "protection" of m_docClassInfo of wxDocTemplate class
class MyHelperDocTemplate : public wxDocTemplate {
public:
	MyHelperDocTemplate();   // bk it was complaining about lack of default constructor
    wxClassInfo* GetDocClass() { return m_docClassInfo; }
};

wxDocTemplate* wxPMDocManager::FindTemplateForDocClass( wxClassInfo* pDocClass )
{
	// FOR NOW:: assume that there's only one template for workplace
	//           the very first is selected if there are more

	wxNode* pNode = m_templates.First();

	while( pNode )
	{
		MyHelperDocTemplate* pTempl = (MyHelperDocTemplate*)pNode->GetData();

		if ( pTempl->GetDocClass() && pTempl->GetDocClass()->IsKindOf( pDocClass ) )

			return pTempl;

		pNode = pNode->GetNext();
	}

	return NULL;
}

wxDocTemplate* wxPMDocManager::FindTemplateForDocType( const wxString& docTypeName )
{
	wxNode* pNode = m_templates.First();

	while( pNode )
	{
		MyHelperDocTemplate* pTempl = (MyHelperDocTemplate*)pNode->GetData();

		if ( pTempl->GetDocumentName() == docTypeName )

			return pTempl;

		pNode = pNode->GetNext();
	}

	return NULL;
}

wxDocTemplate* wxPMDocManager::FindTemplateForDocument( wxPMDocument* pDoc )
{
	return FindTemplateForDocClass( pDoc->GetClassInfo() );
}

wxClassInfo* wxPMDocManager::GetDocClass( wxDocTemplate* pTempl )
{
	return ((MyHelperDocTemplate*)pTempl)->GetDocClass();
}

bool wxPMDocManager::DoCreateWorkplace( wxPMProject* pFirstPrj )
{
	wxDocTemplate* pWkpTempl = FindTemplateForDocClass( CLASSINFO(wxPMWorkplace) );

	// cach possible bugs in "client code"

	wxASSERT_MSG( pWkpTempl != NULL, "Template for workplace document not found!" );

	wxPMWorkplace* wp = (wxPMWorkplace*)pWkpTempl->CreateDocument( "", 0 );

	wxPMBootSvc().SetWorkplace( wp );

	if ( pFirstPrj )
	{
		wp->SetTitle( pFirstPrj->GetTitle() );

		// FOR NOW:: assumed that project already have it's file assigned

		if ( pFirstPrj->CanHaveFile() && pFirstPrj->GetFileInfo() != NULL )
		{
			wxPMFileInfo* pWpFile = new wxPMFileInfo( *pFirstPrj->GetFileInfo() );

			pWpFile->ChangeExtension( pWkpTempl->GetDefaultExtension() );

			wp->SetFileInfo( pWpFile );
		}

		wp->AddProject( pFirstPrj );
	}

	wp->Modify( TRUE );

	wxPMBootSvc().AddService( wp );
	wxPMBootSvc().StartService( wp );

	StartAssociatedServices( *wp );

	return TRUE;
}

wxPMProject* wxPMDocManager::CreateProjectByClass( wxClassInfo* pPrjClass )
{
	wxDocTemplate* pTempl = FindTemplateForDocClass( pPrjClass );

	if ( !pTempl ) return NULL;

	wxPMProject* pPrj = (wxPMProject*)pTempl->CreateDocument( "", 0 );

	wxPMBootSvc().AddService( pPrj );

	if ( !wxPMBootSvc().HasWorkplace() )
	
		DoCreateWorkplace( pPrj ); // creates workplace around the first project
	else
		wxPMBootSvc().GetWorkplace()->AddProject( pPrj );

	return pPrj;
}

void wxPMDocManager::StartAssociatedServices( wxPMDocument& doc )
{
	PMClassInfoListT& lst = doc.GetAssociatedServices();

    size_t i;
	for( i = 0; i != lst.Count(); ++i )
	{
		wxObject* pObj = lst[i]->CreateObject();
		wxPMService* pSvc = (wxPMService*)pObj->GetRefData();

		wxASSERT_MSG( pSvc != NULL, "wxPMService should be declared as dynamic class!" );

		wxPMBootSvc().AddService( pSvc );
		wxPMBootSvc().StartService( pSvc );
	}
}

void wxPMDocManager::StopAssociatedServices( wxPMDocument& doc )
{
	PMClassInfoListT& ascLst = doc.GetAssociatedServices();
	PMServiceListT matchedLst;

	size_t i;
	for( i = 0; i != ascLst.Count(); ++i )
	
		wxPMBootSvc().FindServicesByClass( ascLst[i], matchedLst, FALSE );

	for( i = 0; i != matchedLst.Count(); ++i )
	{
		wxPMBootSvc().StopService( matchedLst[i] );
		wxPMBootSvc().RemoveService( matchedLst[i] );
	}
}

bool wxPMDocManager::DoSaveDocumentRecursively( wxPMDocument* pDoc, 
												PMClassInfoListT& docsToSkip,
												bool closeFlag )
{
	wxASSERT(pDoc); //DBG::

	PMDocumentListT lst = pDoc->GetSubdocuments();

	// sub-documents are closed/saved first

    size_t i;
	for( i = 0; i != lst.Count(); ++i )
	{
		if ( !DoSaveDocumentRecursively( lst[i], docsToSkip, closeFlag ) )

			return FALSE;
	}

	if ( !pDoc->IsModified() )
	{
		if ( closeFlag )
            return DoCloseDocument( pDoc, FALSE );

		return TRUE;
	}

	// for workplace and project objects "save-yes/no?" dialog is not shown
	// (to save the hassle..)

	if ( closeFlag /*&& 
		 !pDoc->IsKindOf( CLASSINFO(wxPMWorkplace) ) &&
		 !pDoc->IsKindOf( CLASSINFO(wxPMProject) )*/
	   )
	{
		// while closing we ask if the mods. should be saved

        wxString titleStr, tempStr;
        titleStr.Printf("Save changes to %s ?",pDoc->GetTitle().c_str());

        tempStr.Printf("'%s'",pDoc->GetTitle().c_str());
    	if (pDoc->IsKindOf( CLASSINFO(wxPMWorkplace)))
        {
            tempStr.Printf("%s '%s'",_T("WORKPLACE"),pDoc->GetTitle().c_str());
        }
        else if (pDoc->IsKindOf( CLASSINFO(wxPMProject)))
        {
            tempStr.Printf("%s '%s'",_T("PROJECT"),pDoc->GetTitle().c_str());
        }
        titleStr.Printf("Save changes to %s?",tempStr.c_str());

		wxMessageDialog dlg( wxPMBootSvc().GetMainWindow(), 
                             titleStr.c_str(),
							 wxPMBootSvc().GetAppTitle(),
							 wxYES_NO | wxCANCEL | wxICON_QUESTION );

		int result = dlg.ShowModal();

		if ( result == wxID_NO )

			return DoCloseDocument( pDoc, FALSE );
		else
		if ( result == wxID_CANCEL )

			return FALSE;

		// otherwise go on with saving of changes
	}

	if ( pDoc->CanHaveFile() == FALSE ) 
	{
		pDoc->SavePMDocument(); // "stream-less" serialization
								// for documents without storage-file
	}
	else
	{
		wxPMFileInfo* pFile = pDoc->GetFileInfo();

		if ( !pFile && 
			  pDoc->IsKindOf( CLASSINFO(wxPMWorkplace) ) &&
			  pDoc->GetSubdocuments().Count() == 1 )
		{
			// if workplace's file is not defined yet, it will
			// be automatically saved to directory where it's
			// first (and only) project is located

			wxDocTemplate* pWkpTempl = FindTemplateForDocument( pDoc );

			wxPMDocument* pFirstPrj = pDoc->GetSubdocuments()[0];

			wxASSERT( pFirstPrj->GetFileInfo() ); // DBG::

			wxPMFileInfo* pWpFile = new wxPMFileInfo( *pFirstPrj->GetFileInfo() );

			pWpFile->ChangeExtension( pWkpTempl->GetDefaultExtension() );

			pDoc->SetFileInfo( pWpFile );

			pDoc->SetTitle( pFirstPrj->GetTitle() );
		}

		if ( !pFile )
		{
			wxDocTemplate* pTempl = pDoc->GetDocumentTemplate();

			wxString fname = pDoc->GetTitle() + "." + pTempl->GetDefaultExtension();

#if wxCHECK_VERSION(2,3,0)
			wxFileDialog dlg( wxPMBootSvc().GetMainWindow(), "Save as", 
							  wxGetCwd(), fname, "", wxSAVE | wxOVERWRITE_PROMPT | wxCHANGE_DIR );
#else
			wxFileDialog dlg( wxPMBootSvc().GetMainWindow(), "Save as", 
							  wxGetCwd(), fname, "", wxSAVE | wxOVERWRITE_PROMPT );
#endif

			if ( dlg.ShowModal() == wxID_OK )
			{
				wxString path = dlg.GetPath();

				pFile = new wxPMFileInfo( path );

				pDoc->SetFileInfo( pFile );
			}
			else
				return FALSE;
		}
		
		if ( pDoc->UseSerializer() )
		{
			// FOR NOW:: file-loaders are not used for config-based persistance
			//           (because current wxFileConfig does not accept streams,
			//			 instead it manages disk-IO himself)

			wxPMFileInfo::CreateDirsForPath( pFile->GetPath() );

			wxFileConfig* pCfgImpl = 
				new wxFileConfig( "someAppName","someVendor", pFile->GetPath(),"",
								  wxCONFIG_USE_LOCAL_FILE );

			wxPMSerializer* pSer = new wxPMSerializer( pCfgImpl, FALSE );

			bool ok = pDoc->SerializePMDocument( *pSer );

			if ( ok )
			
				// listener-specific saving are done after document has stored itself
				// itself successfully

				wxPM_NOTIFY_THREE_ARG( OnClosingDocument, *pDoc, NULL, pSer );

			pSer->Flush();
			delete pSer;

			if ( !ok ) return FALSE;
		}
		else
		{
			wxPMFileLoader* pLoader = pFile->GetFileLoader();

			wxASSERT( pLoader->CreateDirsForPath() );

			wxASSERT_MSG( pLoader, "Cannot create loader for file " + 
								   pFile->GetPath() + "!" ); // DBG::

			wxOutputStream* pOut = pLoader->GetOutputStream();

			bool ok = pDoc->SavePMDocument( *pOut );

			if ( ok )
			
				// listener-specific saving are done after document has stored itself
				// itself successfully

				wxPM_NOTIFY_THREE_ARG( OnClosingDocument, *pDoc, pOut, NULL );

			pOut->Sync();
			pLoader->Close();

			if ( !ok ) return FALSE;
		}
	}

	// here we get after any type of serialization has been successfuly finished

    if (!pDoc->IsKindOf( CLASSINFO(wxPMWorkplace) ))
//		 !pDoc->IsKindOf( CLASSINFO(wxPMProject) )
    {
	    pDoc->Modify( FALSE );
    }

	if ( closeFlag )

		return DoCloseDocument( pDoc, TRUE );

	return TRUE;
}

bool wxPMDocManager::DoCloseDocument( wxPMDocument* pDoc, bool wasSaved )
{
	wxPMBootSvc().ActivateRelatedPanes( pDoc, FALSE );

	// we get to this point, after modifications to document
	// either have been saved or discarded, thus all that's left to
	// do is to close all it's views and "garbage-collect" the 
	// document object

	bool m = (m_docs.Member( pDoc ) != NULL);

	StopAssociatedServices( *pDoc );

	wxPMBootSvc().StopService( pDoc );
	wxPMBootSvc().DetachService( pDoc );

	if ( pDoc->GetParentDocument() != NULL )
	{
		pDoc->GetParentDocument()->OnClosingChildDocument( pDoc, wasSaved );
		pDoc->GetParentDocument()->RemoveSubdocument( pDoc );
	}

	wxNode* pNode = pDoc->GetAllViews()->First();

	pDoc->OnCloseDocument(); // "standard" notification of wx/docview

	wxPM_NOTIFY_TWO_ARG( OnDocumentClosed, *pDoc, wasSaved );

	wxView* pCurView = GetCurrentView();

	while( pNode )
	{
		wxPMView* pView = (wxPMView*)pNode->GetData();

		if ( pView == pCurView )

			ActivateView( pCurView, FALSE, FALSE );

		if ( pView->IsStarted() )

			wxPMBootSvc().StopService( pView );

		wxPMBootSvc().DetachService( pView );

		// all it does, is sets m_currentView to NULL to prevent
		// crashes while accessing it later during idle events

		ActivateView( pView, FALSE, TRUE );

		pNode = pNode->GetNext();

		if ( pView->IsClosing() == FALSE ) // have closed the view's frame yet?
		{
			pView->SetClosing( TRUE ); // this prevents a call to close-doc twice,
									   // which may result by request comming
									   // from frame of the view while OnClose(...)

			wxASSERT( pView->GetViewFrame() ); // DBG::

			pView->GetViewFrame()->Close(); // closing of frame will finish the job
		}
	}

	if ( m_docs.Member( pDoc ) && 
		 pDoc->GetAllViews()->First() == NULL ) 

		 // still on the list and no more views pointing to it?
	{
		if ( pDoc->IsKindOf( CLASSINFO(wxPMWorkplace) ) )
		{
			wxPMBootSvc().SetWorkplace( NULL );
		}

		delete pDoc; // is done only when it's views haven't destroyed it
					 // (e.g. doc have had no views at all)
	}

	// NOTE:: in the current docview-midi arch., an instance of view is 
	//		  deleted by it's corresponding frame-window, therefore we
	//		  aren't doing this here in wxPMDocManager

	return TRUE;
}
