//####COPYRIGHTBEGIN####
//                                                                          
// ----------------------------------------------------------------------------
// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.
//
// This program is part of the eCos host tools.
//
// This program is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the Free 
// Software Foundation; either version 2 of the License, or (at your option) 
// any later version.
// 
// This program is distributed in the hope that it will be useful, but WITHOUT 
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
// more details.
// 
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 
// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// ----------------------------------------------------------------------------
//                                                                          
//####COPYRIGHTEND####
#include "stdafx.h"

#include "ConfigToolDoc.h"

#ifdef PLUGIN
  #include "CTMain.h"
  #include "Project.h"
  #include "ProjectManager.h"
#else
  #include "MainFrm.h"
#endif

#include "CTUtils.h"
#include "CdlPackagesDialog.h"
#include "ConfigItem.h"
#include "ConfigTool.h"
#include "ControlView.h"
#include "FailingRulesDialog.h"
#include "FolderDialog.h"
#include "IdleMessage.h"
#include "MLTView.h"
#include "NotePage.h"
#include "RegionGeneralPage.h"
#include "RegKeyEx.h"
#include "RulesView.h"
#include "SectionGeneralPage.h"
#include "SectionRelocationPage.h"

#include <htmlhelp.h>
#include <shlobj.h>

#define INCLUDEFILE <string>
#include "IncludeSTL.h"
#define INCLUDEFILE "build.hxx"
#include "IncludeSTL.h"

#ifdef _DEBUG
  #define new DEBUG_NEW
  #undef THIS_FILE
  static char THIS_FILE[] = __FILE__;
#endif

const CFileName &CConfigToolDoc::DefaultExternalBrowser()
{
  static bool bFirstTime=true;
  if(bFirstTime){
    const CFileName strFile(CFileName::GetTempPath()+_T("index.html"));
    CFile f;
    if(f.Open(strFile,CFile::modeCreate|CFile::modeWrite)){
      f.Close();
      bool rc=((int)FindExecutable(strFile,_T("."),m_strDefaultExternalBrowser.GetBuffer(MAX_PATH))>32);
      m_strDefaultExternalBrowser.ReleaseBuffer();
      if(!rc){
        m_strDefaultExternalBrowser=_T("");
      }
      ::DeleteFile(strFile);
    }
    bFirstTime=false;
  }
  return m_strDefaultExternalBrowser;
}

CFileName CConfigToolDoc::m_strDefaultExternalBrowser;

void CConfigToolDoc::CheckRadios()
{
  for(int nItem=0;nItem<ItemCount();nItem++){
    CConfigItem *pItem=Item(nItem);
    if(pItem->HasRadio () && pItem==pItem->FirstRadio()){
      CString strMsg;
      CConfigItem *pFirstSet=NULL;
      for(CConfigItem *pSibItem=pItem;pSibItem;pSibItem=pSibItem->NextRadio()){
        if(pSibItem->IsEnabled ()){
          if(pFirstSet){
            strMsg+=_T(" ");
            strMsg+=pSibItem->Macro ();
          } else {
            pFirstSet=pSibItem;
          }
        }
      }
      if(!strMsg.IsEmpty()){
        CConfigTool::Log(_T("%s, multiple radio buttons are set: %s%s"),
          pItem->Macro(),pFirstSet->Macro(),strMsg);
      } else if(!pFirstSet){
        CConfigTool::Log(_T("%s, no radio buttons are set"),pItem->Macro());
      }
    }
  }
}

// Find the ConfigItem referencing the given CdlValuable
CConfigItem * CConfigToolDoc::Find (CdlValuable v)
{
  for(int nItem=0;nItem<ItemCount();nItem++){
    CConfigItem *pItem=Item(nItem);
    if(v==pItem->GetCdlValuable()){
      return pItem;
    }
  }
  return NULL;
}

CConfigItem * CConfigToolDoc::Find(const CString & strWhat,WhereType where)
{
  for(int nItem=0;nItem<ItemCount();nItem++){
    CConfigItem *pItem=Item(nItem);
    if(0==pItem->StringValue(where).Compare(strWhat)){
      return pItem;
    }
  }
  return NULL;
}

void CConfigToolDoc::OnMLTNewRegion() 
{
  // create the property sheet
  
  CPropertySheet prsRegionSheet (IDS_NEW_REGION_SHEET_TITLE, CConfigTool::GetMLTView());
  
  // add the property pages
  
  CRegionGeneralPage prpGeneral;
  CNotePage prpNote;
  
  prsRegionSheet.AddPage (&prpGeneral);
  prsRegionSheet.AddPage (&prpNote);
  
  // setup initial values
  
  prpGeneral.m_strRegionName = _T("");
  prpGeneral.m_dwRegionStartAddress = 0;
  prpGeneral.m_dwRegionSize = 0;
  
  // show property sheet
  
  if (prsRegionSheet.DoModal () == IDOK)
  {
    if (int nErrorCode = MemoryMap.create_memory_region (CUtils::UnicodeToStdStr(prpGeneral.m_strRegionName), prpGeneral.m_dwRegionStartAddress, prpGeneral.m_dwRegionSize, (prpGeneral.m_bRegionReadOnly ? read_only : read_write), CUtils::UnicodeToStdStr(prpNote.m_strNote)))
      ErrorBox (IDS_ERR_MEMMAP_REGION_CREATE, IDS_ERR_MEMMAP_BASE + nErrorCode);
    else // no errors
    {
      SetModifiedFlag ();
      UpdateAllViews (NULL, MemLayoutChanged);
    }
  }		
}

void CConfigToolDoc::ErrorBox (UINT uIDSIntro, UINT uIDSError)
{
  CString strIntro, strError;
  strIntro.LoadString (uIDSIntro);
  strError.LoadString (uIDSError);
  AfxMessageBox (strIntro + _T("\n\n") + strError);
}

void CConfigToolDoc::OnMLTNewSection() 
{
  // create the property sheet
  
  CPropertySheet prsSectionSheet (IDS_NEW_SECTION_SHEET_TITLE, CConfigTool::GetMLTView());
  
  // add the property pages
  
  CSectionGeneralPage prpGeneral;
  CSectionRelocationPage prpRelocation;
  CNotePage prpNote;
  
  prsSectionSheet.AddPage (&prpGeneral);
  prsSectionSheet.AddPage (&prpRelocation);
  prsSectionSheet.AddPage (&prpNote);
  
  // initialise for a new section
  
  prpGeneral.m_bNameLinkerDefined = TRUE;
  prpGeneral.m_strNameLinker = _T("");
  prpRelocation.m_bRelocates = FALSE;
  prpRelocation.m_bNewSection = TRUE;
  
  // show property sheet
  
  if (prsSectionSheet.DoModal () == IDOK)
  {
    mem_anchor initial_anchor, final_anchor;
    mem_address initial_address;
    CString strInitialRelativeName = _T("");
    
    if (prpGeneral.m_bFinalAbsolute) // final location is an absolute location
      final_anchor = absolute;
    else // final location is a relative location
      final_anchor = relative;
    
    if (prpRelocation.m_bRelocates) // if the section relocates
    {
      if (prpRelocation.m_bInitialAbsolute) // initial location is an absolute location
      {
        initial_anchor = absolute;
      }
      else // initial location is a relative location
      {
        strInitialRelativeName = prpRelocation.m_strInitialRelativeName;
        initial_anchor = relative;
      }
      initial_address = prpRelocation.m_dwInitialAddress;
    }
    else
    {
      strInitialRelativeName = prpGeneral.m_strFinalRelativeName;
      initial_anchor = final_anchor;
      initial_address = prpGeneral.m_dwFinalAddress;
    }
    
    
    if (int nErrorCode = MemoryMap.create_memory_section (CUtils::UnicodeToStdStr(prpGeneral.m_bNameLinkerDefined ? prpGeneral.m_strNameLinker : prpGeneral.m_strNameUser), prpGeneral.m_dwSectionSize, prpGeneral.m_dwAlignment, initial_anchor, CUtils::UnicodeToStdStr(strInitialRelativeName), initial_address, final_anchor, CUtils::UnicodeToStdStr(prpGeneral.m_strFinalRelativeName), prpGeneral.m_dwFinalAddress, prpRelocation.m_bRelocates, false, prpGeneral.m_bNameLinkerDefined, CUtils::UnicodeToStdStr(prpNote.m_strNote)))
      ErrorBox (IDS_ERR_MEMMAP_SECTION_CREATE, IDS_ERR_MEMMAP_BASE + nErrorCode);
    else // no errors
    {
      SetModifiedFlag ();
      UpdateAllViews (NULL, MemLayoutChanged);
    }
  }		
}

void CConfigToolDoc::OnMLTDelete() 
{
  if (strSelectedSection != _T("")) // a section is selected
  {
    if (MemoryMap.delete_memory_section (CUtils::UnicodeToStdStr(strSelectedSection)))
    {
      strSelectedSection = _T("");
      SetModifiedFlag ();
      UpdateAllViews (NULL, MemLayoutChanged);
    }
    else
      AfxMessageBox (_T("Could not delete memory section.\n\nMake sure there are no other sections defined relative to this one."));
  }
  else if (strSelectedRegion != _T("")) // a region is selected
  {
    if (MemoryMap.delete_memory_region (CUtils::UnicodeToStdStr(strSelectedRegion)))
    {
      strSelectedRegion = _T("");
      SetModifiedFlag ();
      UpdateAllViews (NULL, MemLayoutChanged);
    }
    else
      AfxMessageBox (_T("Could not delete memory region.\n\nMake sure it is not in use.")); // FIXME
  }
}

void CConfigToolDoc::ShowRegionProperties()
{
  // create the property sheet
  
  CString strTitle;
  strTitle.LoadString (IDS_REGION_PROPERTIES_SHEET_TITLE);
  CPropertySheet prsRegionSheet (strSelectedRegion + _T(" - ") + strTitle, CConfigTool::GetMLTView());
  
  // add the property pages
  
  CRegionGeneralPage prpGeneral;
  CNotePage prpNote;
  
  prsRegionSheet.AddPage (&prpGeneral);
  prsRegionSheet.AddPage (&prpNote);
  
  // set up property sheet values
  
  mem_type type;
  std::string note;
  MemoryMap.get_memory_region (CUtils::UnicodeToStdStr(strSelectedRegion), &prpGeneral.m_dwRegionStartAddress, &prpGeneral.m_dwRegionSize, &type, &note);
  prpGeneral.m_strRegionName = strSelectedRegion;
  prpGeneral.m_bRegionReadOnly = (type == read_only);
  prpNote.m_strNote = note.c_str ();
  
  // show property sheet
  
  if (prsRegionSheet.DoModal () == IDOK)
  {
    if (int nErrorCode = MemoryMap.edit_memory_region (CUtils::UnicodeToStdStr(strSelectedRegion), CUtils::UnicodeToStdStr(prpGeneral.m_strRegionName), prpGeneral.m_dwRegionStartAddress, prpGeneral.m_dwRegionSize, (prpGeneral.m_bRegionReadOnly ? read_only : read_write), CUtils::UnicodeToStdStr(prpNote.m_strNote)))
      ErrorBox (IDS_ERR_MEMMAP_REGION_MODIFY, IDS_ERR_MEMMAP_BASE + nErrorCode);
    else // no errors
    {
      SetModifiedFlag ();
      UpdateAllViews (NULL, MemLayoutChanged);
    }
  }		
}

void CConfigToolDoc::ShowSectionProperties()
{
  // create the property sheet
  
  CString strTitle;
  strTitle.LoadString (IDS_SECTION_PROPERTIES_SHEET_TITLE);
  CPropertySheet prsSectionSheet (strSelectedSection + _T(" - ") + strTitle, CConfigTool::GetMLTView());
  
  // add the property pages
  
  CSectionGeneralPage prpGeneral;
  CSectionRelocationPage prpRelocation;
  CNotePage prpNote;
  
  prsSectionSheet.AddPage (&prpGeneral);
  prsSectionSheet.AddPage (&prpRelocation);
  prsSectionSheet.AddPage (&prpNote);
  
  // setup initial values
  
  std::list <mem_section>::iterator section = MemoryMap.find_memory_section (CUtils::UnicodeToStdStr(strSelectedSection));
  prpGeneral.m_bNameLinkerDefined = (section->linker_defined);
  if (prpGeneral.m_bNameLinkerDefined)
    prpGeneral.m_strNameLinker = section->name.c_str ();
  else
  {
    prpGeneral.m_strNameUser = section->name.c_str ();
    prpGeneral.m_dwSectionSize = section->size;
  }
  
  prpGeneral.m_bFinalAbsolute = (section->final_location->anchor == absolute);
  if (prpGeneral.m_bFinalAbsolute)
    prpGeneral.m_dwFinalAddress = section->final_location->address;
  else
  {
    prpGeneral.m_strFinalRelativeName = MemoryMap.find_preceding_section (section, false)->name.c_str ();
    prpGeneral.m_dwAlignment = section->alignment;
  }
  
  prpRelocation.m_bRelocates = section->relocates;
  if (prpRelocation.m_bRelocates)
  {
    prpRelocation.m_bInitialAbsolute = (section->initial_location->anchor == absolute);
    if (prpRelocation.m_bInitialAbsolute)
      prpRelocation.m_dwInitialAddress = section->initial_location->address;
    else
      prpRelocation.m_strInitialRelativeName = MemoryMap.find_preceding_section (section, true)->name.c_str ();
  }
  
  prpNote.m_strNote = section->note.c_str ();
  
  // show property sheet
  
  if (prsSectionSheet.DoModal () == IDOK)
  {
    mem_anchor initial_anchor, final_anchor;
    mem_address initial_address;
    CString strInitialRelativeName = _T("");
    
    if (prpGeneral.m_bFinalAbsolute) // final location is an absolute location
      final_anchor = absolute;
    else // final location is a relative location
      final_anchor = relative;
    
    if (prpRelocation.m_bRelocates) // if the section relocates
    {
      if (prpRelocation.m_bInitialAbsolute) // initial location is an absolute location
      {
        initial_anchor = absolute;
      }
      else // initial location is a relative location
      {
        strInitialRelativeName = prpRelocation.m_strInitialRelativeName;
        initial_anchor = relative;
      }
      initial_address = prpRelocation.m_dwInitialAddress;
    }
    else
    {
      strInitialRelativeName = prpGeneral.m_strFinalRelativeName;
      initial_anchor = final_anchor;
      initial_address = prpGeneral.m_dwFinalAddress;
    }
    
    
    if (int nErrorCode = MemoryMap.edit_memory_section (CUtils::UnicodeToStdStr(strSelectedSection), CUtils::UnicodeToStdStr(prpGeneral.m_bNameLinkerDefined ? prpGeneral.m_strNameLinker : prpGeneral.m_strNameUser), prpGeneral.m_dwSectionSize, prpGeneral.m_dwAlignment, initial_anchor, CUtils::UnicodeToStdStr(strInitialRelativeName), initial_address, final_anchor, CUtils::UnicodeToStdStr(prpGeneral.m_strFinalRelativeName), prpGeneral.m_dwFinalAddress, prpRelocation.m_bRelocates, false, prpGeneral.m_bNameLinkerDefined, CUtils::UnicodeToStdStr(prpNote.m_strNote)))
      ErrorBox (IDS_ERR_MEMMAP_SECTION_MODIFY, IDS_ERR_MEMMAP_BASE + nErrorCode);
    else // no errors
    {
      SetModifiedFlag ();
      UpdateAllViews (NULL, MemLayoutChanged);
    }
  }		
}

void CConfigToolDoc::OnMLTProperties() 
{
  if (strSelectedRegion != _T(""))
    ShowRegionProperties ();
  else if (strSelectedSection != _T(""))
    ShowSectionProperties ();
}

// Choose a default Hal.  Do this using clues the installer may have helpfully left behind (PR 18050)
// or else if there is only one possible choice, by choosing that one :-).
/*
void CConfigToolDoc::ChooseDefaultHal()
{
bool bFound=false;
// Has the installer told us?
LPCTSTR pszRegPath=_T("SOFTWARE\\Red Hat\\eCos\\1.2.8");
HKEY hKey;
if(ERROR_SUCCESS==RegOpenKeyEx (HKEY_LOCAL_MACHINE, pszRegPath, 0L, KEY_READ, &hKey)){
DWORD dwSizePath=MAX_PATH;
CString str;
PTCHAR psz=str.GetBuffer(dwSizePath);
if(ERROR_SUCCESS==RegQueryValueEx(hKey, _T("Default Architecture"), NULL, NULL, (LPBYTE)psz, &dwSizePath)){
str.MakeLower();
for(int nItem=0;nItem<ItemCount();nItem++){
CConfigItem *pItem=Item(nItem);
if(pItem->m_pTarget){
CString strTarget(pItem->m_pTarget->Name());
strTarget.MakeLower();
if(-1!=str.Find(strTarget)){
bFound=true;
dwSizePath=MAX_PATH;
if(ERROR_SUCCESS==::RegQueryValueEx(hKey, _T("Default Build Tools Path"), NULL, NULL, (LPBYTE)psz, &dwSizePath)){
pItem->m_pTarget->SetBinDir(psz);
for(CConfigItem *pSib=pItem->FirstRadio();pSib;pSib=pSib->NextRadio()){
pSib->SetValue((ItemIntegerType) (pSib==pItem));
}
//                            pItem->EnableAncestors();
}
break;
}
}
}
}
RegCloseKey(hKey);
}

  if(!bFound){
  // No - choose the Hal according to toolchain
  for(int nItem=0;nItem<ItemCount();nItem++){
  CConfigItem *pItem=Item(nItem);
  if(pItem->m_pTarget){
  CString strTarget(pItem->m_pTarget->Name());
  strTarget.MakeLower();
  for(int j=0;j<m_arstrToolChainPaths.GetSize();j++){
  CString strTools(m_arstrToolChainPaths[j]);
  strTools.MakeLower();
  if(-1!=strTools.Find(strTarget)){
  for(CConfigItem *pSib=pItem->FirstRadio();pSib;pSib=pSib->NextRadio()){
  pSib->SetValue((ItemIntegerType) (pSib==pItem));
  }
  return;
  }
  }
  }
  }
  }
  }
*/

bool CConfigToolDoc::SetValue (CConfigItem &ti, double dValue, CdlTransaction transaction/*=NULL*/)
{
  ASSERT (ti.Type () == CConfigItem::Double);
  
  // test if the new double value is in range
  const CdlValuable valuable = ti.GetCdlValuable();
  CdlListValue list_value;
  CdlEvalContext context (NULL, ti.GetCdlItem (), ti.GetCdlItem ()->get_property (CdlPropertyId_LegalValues));
  valuable->get_legal_values ()->eval (context, list_value);
  if (! list_value.is_member (dValue))
  {
    if (dValue==valuable->get_double_value(CdlValueSource_Current) || IDNO == CUtils::MessageBoxFT (MB_YESNO, _T("%s is not a legal value for %s.\n\nDo you want to use this value anyway?"),
      CUtils::DoubleToStr (dValue), ti.Macro ()))
      return false;
  }
  
  if (! ti.SetValue (dValue,transaction))
    return false;
  
  SetModifiedFlag ();
  return true;
}
bool CConfigToolDoc::SetValue(CConfigItem &ti,const CString &strValue, CdlTransaction transaction/*=NULL*/)
{
  // warn the user if a modified memory layout is about to be discarded
  if (MemoryMap.map_modified () && (ti.Macro () == _T("CYG_HAL_STARTUP")) &&
    (IDCANCEL == CUtils::MessageBoxFT (MB_OKCANCEL, _T("Changes to the current memory layout will be lost."))))
    return false;
  
  bool rc=false;
  switch(ti.Type()){
		case CConfigItem::None:
      break;
    case CConfigItem::Enum:
    case CConfigItem::String:
      rc=ti.SetValue(strValue,transaction);
      break;
    case CConfigItem::Integer:
      {
        ItemIntegerType n;
        rc=CUtils::StrToItemIntegerType(strValue,n) && SetValue(ti,n,transaction);
      }
      break;
    case CConfigItem::Double:
      {
        double dValue;
        rc = CUtils::StrToDouble (strValue, dValue) && SetValue (ti, dValue,transaction);
      }
      break;
    default:
      ASSERT(FALSE);
      break;
      
  }
  if(rc){
    SetModifiedFlag();
    UpdateAllViews (NULL, CConfigToolDoc::ValueChanged, (CObject *)&ti);
  }
  return rc;
}

// Look in registry to match prefixes CYGPKG_HAL_ and CYGHWR_HAL_:
//
bool CConfigToolDoc::GetRunPlatform(CString &strTarget)
{
  CRegKeyEx k1(HKEY_CURRENT_USER,_T("Software\\Red Hat\\eCos\\Platforms"), KEY_READ);
  CString strKey;
  for(int i=0;k1.QueryKey(i,strKey);i++){
    CRegKeyEx k2((HKEY)k1,strKey,KEY_READ);
    // Subkeys' names are the target image names [one of which we aim to return]
    // Subkeys's values of interest are:
    //      Prefix  String
    //      Sim     DWORD  [optional]
    //      Macro   String 
    DWORD dwSim=0;
    CString strPrefix,strMacro;
    k2.QueryValue(_T("Sim"),dwSim);
    k2.QueryValue(_T("Prefix"),strPrefix);
    k2.QueryValue(_T("Macro"),strMacro);

    CConfigItem *pItem=Find(_T("CYGPKG_HAL_")+strMacro);
    if(pItem && pItem->IsEnabled()){
      // We have found what we are looking for - probably
      pItem=Find(_T("CYGHWR_HAL_")+strMacro+_T("_STARTUP"));
      if(pItem){
        // Two platforms apply - choose this one only if the ram startup attribute fits
        if( (dwSim==0) == (0==pItem->StringValue().CompareNoCase(_T("ram"))) ) {
          strTarget=strKey;
        }
      } else {
        // Only one platform applies
        strTarget=strKey;
      }
      
    }
  }
  return !strTarget.IsEmpty();
}

bool CConfigToolDoc::SetEnabled(CConfigItem &ti, bool bEnabled, CdlTransaction transaction/*=NULL*/)
{
  const bool bStatus = ti.SetEnabled (bEnabled,transaction);
  
  if (bStatus) {
    SetModifiedFlag();
    UpdateAllViews (NULL, CConfigToolDoc::ValueChanged, (CObject *) &ti);
  }
  return bStatus;
}

void CConfigToolDoc::AddContents (const CdlContainer container, CConfigItem *pParent)
{
  
  // determine the container contents
  
  const std::vector<CdlNode>& contents = container->get_contents ();
  std::vector<CdlNode>::const_iterator node_i;
  for (node_i = contents.begin (); node_i != contents.end (); node_i++)
  {
    const CdlNode node = * node_i;
    const CdlPackage pkg = dynamic_cast<CdlPackage> (node);
    const CdlComponent comp = dynamic_cast<CdlComponent> (node);
    const CdlOption opt = dynamic_cast<CdlOption> (node);
    const CdlContainer contnr = dynamic_cast<CdlContainer> (node);
    
    // if the node in the container is a package, component or option
    // then it is visible and should be added to the tree
    if  (0 != pkg) // the node is a package
    {
      CConfigItem * pItem = AddItem (pkg, pParent); // add the package
      AddContents (pkg, pItem); // add the package contents
    }
    else if (0 != comp) // the node is a component
    {
      CConfigItem * pItem = AddItem (comp, pParent); // add the component
      AddContents (comp, pItem); // add the component contents
    }
    else if (0 != opt) // the node is an option
      AddItem (opt, pParent); // add the option
    
    else if (0 != contnr) // if the node is a container
      AddContents (contnr, pParent); // add the container contents
    
    // ignore nodes of any other class
  }
}

// Three hack functions.

const CString CConfigToolDoc::CurrentPlatform()
{
/*
for(int nItem=0;nItem<ItemCount();nItem++){
CConfigItem *pItem=Item(nItem);
if(pItem->IsEnabled() && !pItem->Platform().IsEmpty()){
return pItem->Platform();
}
}
  */
  return _T("");
}

// a trivial CDL parse error handler
void CConfigToolDoc::CdlParseErrorHandler (std::string message)
{
  CConfigToolDoc *pDoc=CConfigTool::GetConfigToolDoc();
  pDoc->m_strCdlErrorMessage = message.c_str ();
  TRACE (_T("CdlParseError: %s\n"), pDoc->m_strCdlErrorMessage);
  CConfigTool::Log (CUtils::StripExtraWhitespace (pDoc->m_strCdlErrorMessage)); // display the message in the output window
};

// a trivial CDL parse warning handler
void CConfigToolDoc::CdlParseWarningHandler (std::string message)
{
  CConfigToolDoc *pDoc=CConfigTool::GetConfigToolDoc();
  pDoc->m_strCdlErrorMessage = message.c_str ();
  TRACE (_T("CdlParseWarning: %s\n"), pDoc->m_strCdlErrorMessage);
  CConfigTool::Log (CUtils::StripExtraWhitespace (pDoc->m_strCdlErrorMessage)); // display the message in the output window
};

// a trivial CDL load error handler
void CConfigToolDoc::CdlLoadErrorHandler (std::string message)
{
  CConfigToolDoc *pDoc=CConfigTool::GetConfigToolDoc();
  pDoc->m_strCdlErrorMessage = message.c_str ();
  TRACE (_T("CdlLoadError: %s\n"), pDoc->m_strCdlErrorMessage);
  CConfigTool::Log (CUtils::StripExtraWhitespace (pDoc->m_strCdlErrorMessage)); // display the message in the output window
};

// a trivial CDL load warning handler
void CConfigToolDoc::CdlLoadWarningHandler (std::string message)
{
  CConfigToolDoc *pDoc=CConfigTool::GetConfigToolDoc();
  pDoc->m_strCdlErrorMessage = message.c_str ();
  TRACE (_T("CdlLoadWarning: %s\n"), pDoc->m_strCdlErrorMessage);
  CConfigTool::Log (CUtils::StripExtraWhitespace (pDoc->m_strCdlErrorMessage)); // display the message in the output window
};

CdlInferenceCallbackResult CConfigToolDoc::CdlGlobalInferenceHandler(CdlTransaction transaction)
{
  CConfigToolDoc *pDoc=CConfigTool::GetConfigToolDoc();
  pDoc->m_ConflictsOutcome=NotDone;  // prepare for the case that there are no solutions
  CdlInferenceCallbackResult rc=CdlInferenceCallbackResult_Continue;
  /*
  std::list<CdlConflict>conflicts1(transaction->get_global_conflicts_with_solutions());
  const std::vector<CdlConflict>&conflicts2=transaction->get_resolved_conflicts();
	for (unsigned int i = 0; i < conflicts2.size (); i++) {
		conflicts1.push_back(conflicts2 [i]);
	}
  if(conflicts1.size()>0){
    CFailingRulesDialog dlg(conflicts1,transaction,&pDoc->m_arConflictsOfInterest);
    rc=(IDOK==dlg.DoModal())?CdlInferenceCallbackResult_Continue:CdlInferenceCallbackResult_Cancel;
    pDoc->m_ConflictsOutcome=(CdlInferenceCallbackResult_Continue==rc)?OK:Cancel;
  }
  */
  const std::list<CdlConflict>& conflicts=pDoc->GetCdlConfig()->get_all_conflicts();  
  CFailingRulesDialog dlg(conflicts,transaction,&pDoc->m_arConflictsOfInterest);
  rc=(IDOK==dlg.DoModal())?CdlInferenceCallbackResult_Continue:CdlInferenceCallbackResult_Cancel;
  pDoc->m_ConflictsOutcome=(CdlInferenceCallbackResult_Continue==rc)?OK:Cancel;

  return rc;
}

CdlInferenceCallbackResult CConfigToolDoc::CdlInferenceHandler (CdlTransaction transaction)
{
  CConfigToolDoc *pDoc=CConfigTool::GetConfigToolDoc();
  CdlInferenceCallbackResult rc=CdlInferenceCallbackResult_Continue;
  const std::list<CdlConflict>&conflicts=transaction->get_new_conflicts();
  if((pDoc->m_nRuleChecking&Immediate) && conflicts.size()>0){
    if(pDoc->m_nRuleChecking&SuggestFixes){
      std::list<CdlConflict> s_conflicts;
      for (std::list<CdlConflict>::const_iterator conf_i= conflicts.begin (); conf_i != conflicts.end (); conf_i++) { // for each conflict
        if((*conf_i)->has_known_solution()){
          s_conflicts.push_back(*conf_i);
        }
      }
      if(s_conflicts.size()>0){
        CFailingRulesDialog dlg(s_conflicts,transaction);
        return(IDOK==dlg.DoModal()?CdlInferenceCallbackResult_Continue:CdlInferenceCallbackResult_Cancel);
      }
    } 
    CString strMsg;
    if(1==conflicts.size()){
      strMsg=(_T("There is 1 unresolved conflict."));
    } else {
      strMsg.Format(_T("There are %d unresolved conflict%s."),conflicts.size(),1==conflicts.size()?_T(""):_T("s"));
    }
    rc=(IDYES==CUtils::MessageBoxFT(MB_YESNO|MB_DEFBUTTON2,_T("%s  Make the change anyway?"),strMsg))?CdlInferenceCallbackResult_Continue:CdlInferenceCallbackResult_Cancel;
  }
  return rc;
}


// a CDL transaction handler to refresh the configuration tree
void CConfigToolDoc::CdlTransactionHandler (const CdlTransactionCallback & data)
{
static int nNesting=0;
TRACE(_T("Transaction handler: nesting level=%d\n"),nNesting++);
  CConfigToolDoc *pDoc=CConfigTool::GetConfigToolDoc();
  std::vector<CdlValuable>::const_iterator val_i;
  std::vector<CdlNode>::const_iterator node_i;
  std::list<CdlConflict>::const_iterator conf_i;
  CControlView *pControlView=CConfigTool::GetControlView();
  for (val_i = data.value_changes.begin(); val_i != data.value_changes.end(); val_i++)
  {
    const CString strName((*val_i)->get_name().c_str());
    TRACE(_T("%s %s : value change\n"), CString ((*val_i)->get_class_name().c_str()), strName);
    pControlView->Refresh(strName);
    if (strName==_T("CYGHWR_MEMORY_LAYOUT")){               // the memory layout has changed...
      pDoc->SwitchMemoryLayout (false); // ...so display a new one
    }
  }
  for (node_i = data.active_changes.begin(); node_i != data.active_changes.end(); node_i++)
  {
    const CString strName((*node_i)->get_name().c_str());
    TRACE(_T("%s %s : this has become active or inactive\n"), CString ((*node_i)->get_class_name().c_str()),
      CString ((*node_i)->get_name().c_str()));
    if (! dynamic_cast<CdlInterface> (*node_i)){ // if not an interface node
      pControlView->Refresh(strName);
    }
  }
  for (val_i = data.legal_values_changes.begin(); val_i != data.legal_values_changes.end(); val_i++)
  {
    const CString strName((*node_i)->get_class_name().c_str());
    TRACE(_T("%s %s : the legal_values list has changed, a new widget may be needed.\n"),
      CString ((*val_i)->get_class_name().c_str()), strName);               
  }
  
  for (val_i = data.value_source_changes.begin(); val_i != data.value_source_changes.end(); val_i++)
  {
    const CString strName((*val_i)->get_name().c_str());
    CdlValueSource source = (*val_i)->get_source();
    TRACE(_T("%s %s : the value source has changed to %s\n"),
      CString ((*val_i)->get_class_name().c_str()), strName,
      CString ((CdlValueSource_Default  == source) ? "default"  :
    (CdlValueSource_Inferred == source) ? "inferred" :
    (CdlValueSource_Wizard   == source) ? "wizard"   : "user"));
    pControlView->Refresh (strName);
  }

  pDoc->UpdateFailingRuleCount();
nNesting--;
}

// Three hack functions.

const CString CConfigToolDoc::CurrentMemoryLayout ()
{
  const CConfigItem * pItem = Find (_T("CYGHWR_MEMORY_LAYOUT"));
#ifdef _DEBUG
  if(NULL==pItem){
    TRACE(_T("Warning - CurrentMemoryLayout() returning NULL\n"));
  }
#endif
  return pItem ? pItem->StringValue () : _T("");
}

const CString CConfigToolDoc::CurrentTestingIdentifier ()
{
  // use the CDL target as the default testing identifier
  // override in the forthcoming testing identifier dialog as necessary
  return GetCdlConfig()->get_hardware ().c_str ();

//  const CConfigItem * pItem = Find (_T("CYGTST_TESTING_IDENTIFIER"));
//#ifdef _DEBUG
//  if(NULL==pItem){
//    TRACE(_T("Warning - CurrentTestingIdentifier() returning NULL\n"));
//  }
//#endif
//  return pItem ? pItem->StringValue () : _T("");
}

const CString CConfigToolDoc::CurrentStartup()
{
  const CConfigItem * pItem = Find (_T("CYG_HAL_STARTUP"));
#ifdef _DEBUG
  if(NULL==pItem){
    TRACE(_T("Warning - CurrentStartup() returning NULL\n"));
  }
#endif
  return pItem ? pItem->StringValue () : _T("");
  
  /*
  for(int nItem=0;nItem<ItemCount();nItem++){
		CConfigItem *pItem=Item(nItem);
    const CString strName(pItem->Name());
    if(0==strName.Compare(_T("Startup type")) && pItem->IsEnabled()){
    return pItem->StringValue();
    }
    }
    return _T("ram"); // FIXME - assume ram startup for now if no startup item
  */
}

void CConfigToolDoc::UpdateFailingRuleCount()
{
  int nCount=0;
  if (GetCdlConfig ()){
    // if configuration information
    
    // calculate the number of conflicts
    nCount =
      //	        GetCdlConfig ()->get_structural_conflicts ().size () +    ignore for now
      GetCdlConfig ()->get_all_conflicts ().size ();
    
    // update the conflicts view
    if (CConfigTool::GetRulesView ()) {
      CConfigTool::GetRulesView ()->FillRules ();
    }
  }
  if(CConfigTool::GetMain()){
    CConfigTool::GetMain()->SetFailRulePane(nCount);
  }
}

void CConfigToolDoc::LogConflicts (const std::list<CdlConflict> & conflicts)
{
  std::list<CdlConflict>::const_iterator conf_i;
  for (conf_i = conflicts.begin (); conf_i != conflicts.end (); conf_i++) // for each conflict
  {
    CString strExplain = (* conf_i)->get_explanation ().c_str (); // get the conflict explanation
    CConfigTool::Log (CUtils::StripExtraWhitespace (strExplain)); // display the message in the output window
  }
}

CString CConfigToolDoc::GetPackageName (const CString & strAlias)
{
  const std::vector<std::string> & packages = m_CdlPkgData->get_packages ();
  std::vector<std::string>::const_iterator package_i;
  for (package_i = packages.begin (); package_i != packages.end (); package_i++)
  {
    const std::vector<std::string> & aliases = m_CdlPkgData->get_package_aliases (* package_i);
    CString strPackageAlias = aliases [0].c_str ();
    if (aliases.size () && (0 == strAlias.Compare (strPackageAlias)))
      return package_i->c_str ();
  }
  return _T("");
}

void CConfigToolDoc::DeleteContents() 
{
  TRACE(_T("###DeleteContents()\n"));
#ifndef PLUGIN
  CDocument::DeleteContents();
#endif
  
  RemoveAllItems();
  TRACE(_T("###DeleteContents done\n"));
}

void CConfigToolDoc::SelectPackages ()
{
  CCdlPackagesDialog dlg;
  // This map holds the CConfigItem pointers for the packages loaded before the dialog is invoked.
  // We cannot use Find(), which traverses all items - potentially those that have been removed
  CMapStringToPtr arLoadedPackages;

  // generate the contents of the add/remove list boxes
  const std::vector<std::string> & packages = m_CdlPkgData->get_packages ();
  std::vector<std::string>::const_iterator package_i;
  for (package_i = packages.begin (); package_i != packages.end (); package_i++)
  {
    //		if (! m_CdlPkgData->is_hardware_package (* package_i)) // do not list hardware packages
    {
      const std::vector<std::string> & aliases = m_CdlPkgData->get_package_aliases (* package_i);
      CString strMacroName = package_i->c_str ();
      
      // use the first alias (if any) as the package identifier
      CString strPackageName = aliases.size () ? aliases [0].c_str () : strMacroName;
      CConfigItem * pItem = Find (strMacroName);
      if (pItem) // if the package is loaded
      {
        arLoadedPackages.SetAt(strMacroName,pItem);
        // pass the currently selected package version string to the dialog box
        const CdlValuable valuable = pItem->GetCdlValuable();
        dlg.Insert (strPackageName, TRUE, NULL, valuable ? (LPCTSTR) CString (valuable->get_value ().c_str ()) : NULL);
      }
      else
      {
        // pass version string of the most latest version to the dialog box
        dlg.Insert (strPackageName, FALSE, NULL, (LPCTSTR) CString (m_CdlPkgData->get_package_versions (* package_i) [0].c_str ()));
      }
    }
  }
  
  if (IDOK == dlg.DoModal ())
  {
    bool bChanged = false; // until proved otherwise

    // determine whether each package has changed loaded/unloaded state
    for (package_i = packages.begin (); package_i != packages.end (); package_i++)
      //			if (! m_CdlPkgData->is_hardware_package (* package_i)) // do not check hardware packages
    {
      const std::vector<std::string> & aliases = m_CdlPkgData->get_package_aliases (* package_i);
      CString strMacroName = package_i->c_str ();
      
      // use the first alias (if any) as the package identifier
      CString strPackageName = aliases.size () ? aliases [0].c_str () : strMacroName;
      
      CConfigItem *pItem=NULL;
      bool bPreviouslyLoaded=arLoadedPackages.Lookup(strMacroName,(void *&)pItem);
      bool bNowLoaded=dlg.IsAdded (strPackageName);
      
      // unload packages which are no longer required before
      // loading new ones to avoid potential duplicate macro definitions
      if (! bNowLoaded && bPreviouslyLoaded){
        // The package was loaded but should now be unloaded:
        bChanged|=pItem->Unload();
      } else if (bNowLoaded) {// if the package should be loaded
        const CString strVersion(dlg.GetVersion (strPackageName));
        if (bPreviouslyLoaded) { // if the package is not already loaded
          bChanged|=pItem->ChangeVersion(strVersion);
        } else {
          // the package was not loaded but should now be loaded
          TRACE (_T("Loading package %s\n"), strMacroName);
          try
          {
            GetCdlConfig()->load_package (CUtils::UnicodeToStdStr(strMacroName), CUtils::UnicodeToStdStr (strVersion), CConfigToolDoc::CdlParseErrorHandler, CConfigToolDoc::CdlParseWarningHandler);
            bChanged=true;
          }
          catch (CdlStringException exception)
          {
            CUtils::MessageBoxF(_T("Error loading package %s:\n\n%s"), strMacroName, CString (exception.get_message ().c_str ()));
          }
          catch (...)
          {
            CUtils::MessageBoxF(_T("Error loading package %s."), strMacroName);
          }
        }
      }				
    }
    
    if (bChanged) {// at least one package was loaded, unloaded or changed version
      SetModifiedFlag();
      RegenerateData();
    }
  }
}

bool CConfigToolDoc::SetValue(CConfigItem &ti,ItemIntegerType nValue, CdlTransaction transaction/*=NULL*/)
{
  switch(ti.Type()){
		case CConfigItem::Enum:
    case CConfigItem::Integer:
      //		case CConfigItem::Boolean:
      //		case CConfigItem::Radio:
      break;
    case CConfigItem::None:
    case CConfigItem::String:
    default:
      ASSERT(FALSE);
      break;
  }
  
  bool rc=false;
  bool bChangingMemmap=MemoryMap.map_modified () && ((ti.Macro ().Compare (_T ("CYG_HAL_STARTUP")) == 0));
  
  if(nValue==ti.Value()){
    return true;
  }
  
  // test if the new integer value is in range
  if (CConfigItem::Integer == ti.Type ())
 	{
    const CdlValuable valuable = ti.GetCdlValuable();
    CdlListValue list_value;
    CdlEvalContext context (NULL, ti.GetCdlItem (), ti.GetCdlItem ()->get_property (CdlPropertyId_LegalValues));
    valuable->get_legal_values ()->eval (context, list_value);
    if (! list_value.is_member ((cdl_int) nValue))
    {
      if (nValue==(ItemIntegerType) valuable->get_integer_value (CdlValueSource_Current) || IDNO == CUtils::MessageBoxFT (MB_YESNO, _T("%s is not a legal value for %s.\n\nDo you want to use this value anyway?"),
        CUtils::IntToStr (nValue, CConfigTool::GetConfigToolDoc ()->m_bHex), ti.Macro ()))
        goto Exit;
    };
 	}
  
  //    if(!ti.CanSetValue(nValue))
  //	{
  //        if(bInteractive){
  //			CString strExplanation;
  //			if(CConfigItem::Integer==ti.Type()){
  //				strExplanation.Format(_T(" [value must lie in the range %d..%d]"),
  //					CUtils::IntToStr(ti.Min(),m_bHex), 
  //                    CUtils::IntToStr(ti.Max()),m_bHex);
  //			}
  //			CUtils::MessageBoxF(
  //				_T("Cannot set '%s' to %s%s"),
  //				ti.ItemNameOrMacro(), CUtils::IntToStr(nValue,m_bHex), strExplanation);
  //        }
  //		goto Exit;
  //	}
  
  // warn the user if the current memory layout has been changed and will be lost
  // this will happen when the layout has been modified and the target-platform-startup is changed
  
  if (bChangingMemmap && IDCANCEL==CUtils::MessageBoxFT(MB_OKCANCEL,_T("Changes to the current memory layout will be lost."))){
    goto Exit;
  }
  
  // Save state
  if(!ti.SetValue(nValue,transaction)){
    // CanSetValue above should have caught this
    CUtils::MessageBoxF(_T("Cannot set '%s' to %d"),ti.ItemNameOrMacro(), nValue);
    goto Exit;
  } 
  
  rc=true;
Exit:
  if(rc){
    SetModifiedFlag();
    UpdateFailingRuleCount ();
    UpdateAllViews (NULL, CConfigToolDoc::ValueChanged, (CObject *)&ti);
    // switch to new memory layout when target, platform or startup changes
    // but ignore cases where a target or platform is being deactivated (PR 19363)
    
    //		if (((CurrentTarget () != pOldTarget) || (CurrentPlatform () != strOldPlatform) || (ti.Name ().Compare (_T ("Startup type")) == 0)) &&
    //			((ti.Type () != CConfigItem::Radio) || (nValue)))
    //			SwitchMemoryLayout ((CurrentTarget () != pOldTarget) || (CurrentPlatform () != strOldPlatform));
  }
  return rc;
}

bool CConfigToolDoc::OpenRepository(LPCTSTR pszRepository/*=NULL*/,bool bPromptInitially/*=false*/)
{
  if(!m_bRepositoryOpen){
    CWaitCursor wait;
    CIdleMessage IM(_T("Opening repository"));
    UpdateFailingRuleCount();
    
    CFileName strNewRepository;
    while(!m_bRepositoryOpen){
      
      if(bPromptInitially){
        CFolderDialog dlg(/*BOOL bAllowCreation=*/false);
        dlg.m_strDesc=_T("Please specify the root of the eCos repository tree.");
        dlg.m_strTitle=_T("Choose folder for eCos repository");
        dlg.m_strFolder=strNewRepository;
        CConfigTool::DismissSplash();
        if(IDCANCEL==dlg.DoModal()){
          return false;
        }
        strNewRepository=dlg.m_strFolder;
      } else {
        // Use what came in as parameter or what was found in registry
        strNewRepository=pszRepository?pszRepository:m_strRepository;
        bPromptInitially=true;
      }
      
      IM.Set (_T("Opening repository ") + (CString) strNewRepository);
      
      CdlPackagesDatabase NewCdlPkgData = NULL;
      CdlInterpreter      NewCdlInterp  = NULL;
      CdlConfiguration    NewCdlConfig  = NULL;
      CFileName strNewPackagesDir;
      
      EnableCallbacks(false); // disable transaction callbacks until the config tree is regenerated
      
      if(OpenRepository(strNewRepository,NewCdlPkgData,NewCdlInterp,NewCdlConfig,strNewPackagesDir)){
        // Success
        
        // select the "default" template if it exists
        // otherwise select the first available template
        std::string default_template = "default";
        if (! NewCdlPkgData->is_known_template (default_template))
        {
          const std::vector<std::string>& templates = NewCdlPkgData->get_templates ();
          if (templates.size () != 0)
            default_template = templates [0];
        }
        
        m_template_version = "";
        try
        {
          const std::vector<std::string>& template_versions = NewCdlPkgData->get_template_versions (default_template);
          NewCdlConfig->set_template (default_template, template_versions [0], &CdlParseErrorHandler, &CdlParseWarningHandler);
          m_template_version = template_versions [0];
        }
        catch (CdlStringException exception) {
          CUtils::MessageBoxF(_T("Error loading package template '%s':\n\n%s"), CString (default_template.c_str ()), CString (exception.get_message ().c_str ()));
        }
        catch (...) {
          CUtils::MessageBoxF(_T("Error loading package template '%s'."), CString (default_template.c_str ()));
        }
        
        // check the configuration
        ASSERT (NewCdlConfig->check_this (cyg_extreme));
        
        // use the new package database, interpreter and configuration
        deleteZ(m_CdlConfig); // delete the previous configuration
        deleteZ(m_CdlInterp); // delete the previous interpreter
        deleteZ(m_CdlPkgData); // delete the previous package database
        
        m_CdlPkgData = NewCdlPkgData;
        m_CdlInterp  = NewCdlInterp;
        m_CdlConfig  = NewCdlConfig;
        
        // save the repository location
        
        SetRepository(strNewRepository);
        m_strPackagesDir = strNewPackagesDir;
        
#ifndef PLUGIN
        // clear the previously specified document file name (if any),
        // OnNewDocument() calls DeleteContents() so must be called
        // before AddAllItems()
        CDocument::OnNewDocument ();
#endif
        
        // generate the CConfigItems from their CDL descriptions
        AddAllItems ();
        
        m_bRepositoryOpen=true;
      } else {
        // failure
        deleteZ(NewCdlConfig);
        deleteZ(NewCdlInterp);
        deleteZ(NewCdlPkgData);
        
      }
      
      // install a transaction handler callback function now that the tree exists
      EnableCallbacks(true);
    }
    
    }
    return m_bRepositoryOpen;
}

// Helper fn for namesake above
bool CConfigToolDoc::OpenRepository (const CFileName strNewRepository,CdlPackagesDatabase &NewCdlPkgData,CdlInterpreter &NewCdlInterp,CdlConfiguration &NewCdlConfig,CFileName &strNewPackagesDir)
{
  bool rc=false;
  TRACE(_T("###Open repository %s\n"),strNewRepository);
  if(!strNewRepository.IsEmpty()){
    // Now strNewRepository is guaranteed non-empty, but does it exist?
    if(!strNewRepository.IsDir()) {
      CUtils::MessageBoxF(_T("Cannot open repository - the folder %s does not exist"), strNewRepository);
    } else {
    
      // Ok so it exists, but does it look right?
      strNewPackagesDir=strNewRepository+_T("ecc");
      if(!strNewPackagesDir.IsDir()){
        strNewPackagesDir=strNewRepository+_T("packages");
      }
    
      if(!strNewPackagesDir.IsDir()){
        // Don't mention the ecc\ attempt
        CUtils::MessageBoxF(_T("%s does not seem to be a source repository: the folder %s does not exist"),
          strNewRepository,strNewPackagesDir);
      } else {
      
        const CFileName strDatabase = strNewPackagesDir + _T("ecos.db");
        if(!strDatabase.Exists()) {
          CUtils::MessageBoxF(_T("%s does not seem to be a source repository: %s does not exist"), strNewRepository, strDatabase);
        } else {
        
          // create a CDL repository, interpreter and configuration
        
          try {// create a new package database, interpreter and configuration
            NewCdlPkgData = CdlPackagesDatabaseBody::make (CUtils::UnicodeToStdStr(strNewPackagesDir));
            NewCdlInterp = CdlInterpreterBody::make ();
            NewCdlConfig = CdlConfigurationBody::make ("eCos", NewCdlPkgData, NewCdlInterp);
          }
          catch (CdlStringException exception) {
            CUtils::MessageBoxF(_T("Error opening eCos repository:\n\n%s"), CString (exception.get_message ().c_str ()));
            return false;
          }
          catch (...) {
            CUtils::MessageBoxF(_T("Error opening eCos repository."));
            return false;
          }
        
          // select the default target if specified in the registry
          // otherwise select the first available target
          std::string default_hardware = CUtils::UnicodeToStdStr (GetDefaultHardware ());
          if (! NewCdlPkgData->is_known_target (default_hardware)) {
            const std::vector<std::string>& targets = NewCdlPkgData->get_targets ();
            if (targets.size () == 0){
              CUtils::MessageBoxF (_T("Error opening eCos repository:\n\nno hardware templates"));
              return false;
            } else {
              default_hardware = targets [0];
            }
          }
        
          try {
            m_strCdlErrorMessage = _T("");
            NewCdlConfig->set_hardware (default_hardware, &CdlParseErrorHandler, &CdlParseWarningHandler);
          }
          catch (CdlStringException exception) {
            if (m_strCdlErrorMessage.IsEmpty ())
            {
              CUtils::MessageBoxF (_T("Error loading the default hardware template '%s':\n\n%s"), CString (default_hardware.c_str ()), CString (exception.get_message ().c_str ()));
            }
            else // report the most recent parser message in the message box since there may be no output pane in which to view it
            {
              CUtils::MessageBoxF (_T("Error loading the default hardware template '%s':\n\n%s\n\nParser message:\n\n%s"), CString (default_hardware.c_str ()), CString (exception.get_message ().c_str ()), m_strCdlErrorMessage);
            }
            return false;
          }
          catch (...) {
            CUtils::MessageBoxF (_T("Error loading the default hardware template '%s':\n\n%s"), CString (default_hardware.c_str ()), m_strCdlErrorMessage);
            return false;
          }
          rc=true;
        }
      }
    }
  }
  return rc;
}    

void CConfigToolDoc::CloseRepository()
{
  if(m_bRepositoryOpen){
    // delete the libCDL objects with the document
    EnableCallbacks(false); // first disable the transaction handler
    deleteZ(m_CdlConfig);
    deleteZ(m_CdlInterp);
    deleteZ(m_CdlPkgData);
    m_bRepositoryOpen=false;
  }
}

void CConfigToolDoc::SelectTemplate (std::string NewTemplate, std::string NewTemplateVersion)
{
  if ((NewTemplate != m_CdlConfig->get_template()) || (NewTemplateVersion != m_template_version)){

    CWaitCursor wait; // this may take a little while
    RemoveAllItems();
    m_template_version = "";
    try
    {
      m_CdlConfig->set_template (NewTemplate, NewTemplateVersion, CdlParseErrorHandler, CdlParseWarningHandler);
      m_template_version = NewTemplateVersion;
    }
    catch (CdlStringException exception)
    {
      CUtils::MessageBoxF(_T("Error loading package template '%s':\n\n%s"), CString (NewTemplate.c_str ()), CString (exception.get_message ().c_str ()));
    }
    catch (...)
    {
      CUtils::MessageBoxF(_T("Error loading package template '%s'."), CString (NewTemplate.c_str ()));
    }
    RegenerateData();  

    if (!GetPathName().IsEmpty()){ // not a new document
      CopyMLTFiles (); // copy new MLT files to the build tree as necessary
    }
    SetModifiedFlag();
  }
}

void CConfigToolDoc::RegenerateData()
{
  CWaitCursor wait; // This may take a little while
  AddAllItems (); // regenerate all the config items since the topology may have changed
  if (m_strLinkerScriptFolder.IsEmpty ())
  {
    CUtils::MessageBoxF(_T("The eCos linker script macro CYGBLD_LINKER_SCRIPT is not defined."));
  }
  if (m_strMemoryLayoutFolder.IsEmpty ())
  {
    CUtils::MessageBoxF(_T("The eCos memory layout macro CYGHWR_MEMORY_LAYOUT is not defined."));
  }
  SwitchMemoryLayout (true); // the hardware template may have changed
  UpdateBuildInfo();
  CConfigTool::GetControlView()->SelectItem(Item(0));
}

void CConfigToolDoc::SelectHardware (std::string NewTemplate)
{
  const std::string OldTemplate=m_CdlConfig->get_hardware();
  if (NewTemplate != OldTemplate){
    RemoveAllItems();

    try
    {
      m_CdlConfig->set_hardware (NewTemplate, CdlParseErrorHandler, CdlParseWarningHandler);
    }
    catch (CdlStringException exception)
    {
      CUtils::MessageBoxF(_T("Error loading hardware template '%s':\n\n%s"), CString (NewTemplate.c_str ()), CString (exception.get_message ().c_str ()));
      m_CdlConfig->set_hardware (OldTemplate, CdlParseErrorHandler, CdlParseWarningHandler);
    }
    catch (...)
    {
      CUtils::MessageBoxF(_T("Error loading hardware template '%s'."), CString (NewTemplate.c_str ()));
      m_CdlConfig->set_hardware (OldTemplate, CdlParseErrorHandler, CdlParseWarningHandler);
    }
  
    RegenerateData();
    //EnableCallbacks(true); // re-enable the transaction handler
    if (!GetPathName().IsEmpty()){
      CopyMLTFiles (); // copy new MLT files to the build tree as necessary
    }
    SetModifiedFlag();
  }
}

bool CConfigToolDoc::SaveMemoryMap()
{
  const CString strSuffix(_T("mlt_") + CurrentMemoryLayout ());
  const CFileName strMLTInstallPkgconfDir(InstallTree() + _T("include\\pkgconf"));
  bool rc=false;
  if(strMLTInstallPkgconfDir.CreateDirectory(true)){
    const CString strMLTInstallBase(strMLTInstallPkgconfDir+CFileName(strSuffix));
    const CFileName strMLTDir (MLTDir());
    if(strMLTDir.CreateDirectory (true)){
      const CString strMLTBase (strMLTDir + CFileName (strSuffix));
      TRACE(_T("Saving memory layout to %s\n"), strMLTBase + _T(".mlt"));
      if(MemoryMap.save_memory_layout (strMLTBase + _T(".mlt"))){
        TRACE(_T("Exporting memory layout to %s\n"), strMLTInstallPkgconfDir);
        rc=MemoryMap.export_files (strMLTInstallBase + _T(".ldi"), strMLTInstallBase + _T(".h"));
      }
    }
  }    
  return rc;
}

bool CConfigToolDoc::CopyMLTFiles()
{
  // copy default MLT files for the selected target/platform from the repository if they do not already exist

  TRACE (_T("Looking for MLT files at %s\n"), PackagesDir() + m_strMemoryLayoutFolder + _T("include\\pkgconf\\mlt_*.*"));
  const CFileName strInstallDestination(InstallTree () + _T("include\\pkgconf"));
  const CFileName strMLTDestination (MLTDir ());
  TRACE (_T("Copying .ldi and .h files to %s\n"), strInstallDestination);
  TRACE (_T("Copying .mlt files to %s\n"), strMLTDestination);
  bool rc=strInstallDestination.CreateDirectory (true) && strMLTDestination.CreateDirectory (true);
  if(rc){
    CFileFind ffFileFind;
    BOOL bLastFile = ffFileFind.FindFile (PackagesDir() + m_strMemoryLayoutFolder + _T("\\include\\pkgconf\\mlt_*.*"));
    while (bLastFile)
    {
      bLastFile = ffFileFind.FindNextFile ();
      if (_T(".mlt") == ffFileFind.GetFileName ().Right (4)) // if a .mlt file
      {
        if (! CFileName (strMLTDestination, CFileName (ffFileFind.GetFileName ())).Exists ())
        {
          if(!CUtils::CopyFile (ffFileFind.GetFilePath (), strMLTDestination + CFileName (ffFileFind.GetFileName ()))){
            return false; // message already emitted
          }
        }
      }
      else // a .h or .ldi file
      {
        if (! CFileName (strInstallDestination, CFileName (ffFileFind.GetFileName ())).Exists () && 
          !CUtils::CopyFile (ffFileFind.GetFilePath (), strInstallDestination + CFileName (ffFileFind.GetFileName ()))){
          return false; // message already emitted
        }
      }
    }
  }
  return rc; //FIXME
}

CString CConfigToolDoc::GetDefaultHardware ()
{
  CString strKey = _T("SOFTWARE\\Red Hat\\eCos");
  CString strVersionKey = _T("");
  CString rc = _T("");
  TCHAR pszBuffer [MAX_PATH + 1];
  HKEY hKey;
  LONG lStatus;
  
  // get the greatest eCos version subkey
  if (ERROR_SUCCESS == RegOpenKeyEx (HKEY_LOCAL_MACHINE, strKey, 0L, KEY_READ, &hKey))
  {
    DWORD dwIndex = 0;
    while (ERROR_SUCCESS == RegEnumKey (hKey, dwIndex++, (LPTSTR) pszBuffer, sizeof (pszBuffer)))
    {
      if (strVersionKey.Compare (pszBuffer) < 0)
        strVersionKey = pszBuffer;
    }
    RegCloseKey (hKey);
    if (! strVersionKey.IsEmpty ())
    {
      TRACE (_T("CConfigToolDoc::GetDefaultHardware(): version subkey = '%s'\n"), strVersionKey);
      
      // get the default hardware value
      strKey +=  _T("\\") + strVersionKey;
      if (ERROR_SUCCESS == RegOpenKeyEx (HKEY_LOCAL_MACHINE, strKey, 0L, KEY_READ, &hKey))
      {
        DWORD dwBufferSize = sizeof (pszBuffer);
        lStatus = RegQueryValueEx (hKey, _T("Default Hardware"), NULL, NULL, (LPBYTE) pszBuffer, &dwBufferSize);
        RegCloseKey (hKey);
        if (ERROR_SUCCESS == lStatus)
        {
          TRACE (_T("CConfigToolDoc::GetDefaultHardware(): default hardware = '%s'\n"), pszBuffer);
          rc=pszBuffer;
        }
      }
    }
  }
  return rc;
}

CConfigItem * CConfigToolDoc::AddItem (const CdlUserVisible vitem, CConfigItem * pParent)
{
  CConfigItem * pItem = new CConfigItem (pParent, vitem);

  m_arItem.Add(pItem);
  pItem->m_strDesc = CUtils::StripExtraWhitespace (CString (vitem->get_description ().c_str ()));

  if (vitem->get_name () == "CYGHWR_MEMORY_LAYOUT")
  {
    ASSERT (m_strMemoryLayoutFolder.IsEmpty ());
    m_strMemoryLayoutFolder = vitem->get_owner ()->get_directory().c_str ();
    m_strMemoryLayoutFolder.Replace(_TCHAR('/'),_TCHAR('\\'));
    TRACE (_T("Found memory layout folder: %s\n"), m_strMemoryLayoutFolder);
  }
  
  if (vitem->get_name () == "CYGBLD_LINKER_SCRIPT")
  {
    ASSERT (m_strLinkerScriptFolder.IsEmpty ());
    m_strLinkerScriptFolder = vitem->get_owner ()->get_directory().c_str ();
    m_strLinkerScriptFolder.Replace(_TCHAR('/'),_TCHAR('\\'));
    TRACE (_T("Found linker script folder: %s\n"), m_strLinkerScriptFolder);
    
    // the CDL hardware template name will eventually become the target name,
    // but for now we must deduce the target name from the linker script file name
    
    const CdlValuable valuable = dynamic_cast<CdlValuable> (vitem);
    CFileName strLinkerScript (m_strPackagesDir, m_strLinkerScriptFolder, CString (valuable->get_value ().c_str ()));
    strLinkerScript.Replace (_TCHAR('/'), _TCHAR('\\'));
    if(!strLinkerScript.Exists ()){
        CConfigTool::Log(_T("%s does not exist\n"),strLinkerScript);
    }
    TRACE (_T("Target '%s' selected\n"), strLinkerScript.Tail ().Root (), pItem->Macro());
    //CFileName strBinDir = AfxGetApp () -> GetProfileString (CUtils::LoadString (IDS_KEY_TOOLS_DIR), pTarget->Name (), _T(""));
    //if (! strBinDir.IsEmpty () && strBinDir.IsDir ())
    //{
    //  pTarget->SetBinDir (strBinDir);
    //}
  }	
  
  //TRACE(_T("Created new item from cdl: "));
  //pItem->DumpItem();
  return pItem;
}

void CConfigToolDoc::AddAllItems ()
{
  // remove any old items and packages
  DeleteContents ();
  if(NULL!=CConfigTool::GetControlView()){ // may not be the case for plugin

    // add the root item
    CConfigItem * pItem = new CConfigItem (NULL, NULL);
    m_arItem.Add(pItem);
    pItem->m_strDesc = _T("The root node for all configurable items");
  
    // add the remaining items using their CDL descriptions
    m_strMemoryLayoutFolder = _T("");
    m_strLinkerScriptFolder = _T("");
    AddContents (m_CdlConfig, FirstItem ());
  
    // check that exactly one radio button in each group is enabled
    CheckRadios ();
  
    // update the rules (conflicts) view
    UpdateFailingRuleCount ();
  
    if(NULL==CConfigTool::GetRulesView() || !CConfigTool::GetRulesView()->IsWindowVisible()){
      // log all conflicts
      //	LogConflicts (m_CdlConfig->get_structural_conflicts ()); // relating to package availability - ignore for now
      LogConflicts (m_CdlConfig->get_all_conflicts ());
    }

    CConfigTool::GetControlView()->SelectItem(Item(0));
    UpdateAllViews (NULL, SelChanged, (CObject *)Item(0)); // refresh the display
    if(ItemCount()>0){
      CConfigTool::GetControlView()->GetTreeCtrl().Expand(Item(0)->HItem(),TVE_EXPAND);
    }
    CConfigTool::GetControlView()->SetFocus();
  }
}

const CFileName CConfigToolDoc::CurrentLinkerScript()
{
  const CConfigItem * pItem = Find (_T("CYGBLD_LINKER_SCRIPT"));
  return pItem ? CFileName (m_strPackagesDir, m_strLinkerScriptFolder, pItem->StringValue ()) : _T("");
}

bool CConfigToolDoc::GenerateHeaders()
{
// Copy non-config header files from the repository to the
// install tree for plugin only. The make system used by
// the standalone config tool does this for us.
#ifdef PLUGIN
  typedef std::vector<CdlBuildInfo_Loadable> EntriesArray;
  const EntriesArray &arEntries=BuildInfo().entries;
  typedef std::vector<CdlBuildInfo_Header> HeaderArray;
  for(EntriesArray::size_type j=0;j<arEntries.size();j++){
    const CdlBuildInfo_Loadable &e=arEntries[j];
    const CFileName strDir(CUtils::WPath(e.directory));
    const HeaderArray &arHeaders=e.headers;
    for(HeaderArray::size_type i=0;i<arHeaders.size();i++){
      const CdlBuildInfo_Header &h=arHeaders[i];
      const CFileName strSource(PackagesDir()+strDir+CUtils::WPath(h.source));
      const CFileName strDest(HeadersDir()/*+CUtils::WPath(e.directory)*/+CUtils::WPath(h.destination));
    
      if(!strDest.Head().CreateDirectory(true)){
        CUtils::MessageBoxF(_T("Failed to create %s - %s"),strDest.Head(),CUtils::GetLastErrorMessageString());
        return false;
      }
      if(!CUtils::CopyFile(strSource,strDest)){ 
        return false; // message already emitted
      }
    }
  }
#endif

  // Generate headers
  try {
    CFileName strPkfConfDir(InstallTree()+_T("include\\pkgconf"));
    if(!strPkfConfDir.CreateDirectory()){
      CUtils::MessageBoxF(_T("Failed to create %s - %s"),strPkfConfDir,CUtils::GetLastErrorMessageString());
      return false;
    }
    GetCdlConfig()->generate_config_headers(CUtils::UnicodeToStdStr(strPkfConfDir.ShortName()));
  }
  catch (CdlInputOutputException e) {
    const CString strMsg(e.get_message().c_str());
    TRACE(_T("!!! Exception thrown calling generate_config_headers - %s"),strMsg);
    CUtils::MessageBoxF(_T("Failed to generate header files - %s"),strMsg);
    return false;
  }
  return true;
}

const CFileName CConfigToolDoc::MLTDir ()
{
	CString strPathName (GetPathName ());
	ASSERT (! strPathName.IsEmpty ());
	return strPathName.Left (strPathName.ReverseFind (_TCHAR('.'))) + _T("_mlt");
}

bool CConfigToolDoc::UpdateBuildInfo(bool bFirstTime)
{
  try {
    GetCdlConfig()->get_build_info(m_BuildInfo);
    #ifdef PLUGIN
    // FIXME: this means anything not mentioned by AddFile in our scan below will be removed - including user-added items
    m_peCosProject->ClearItemFlags();    
    m_peCosProject->UpdateeCosProject(bFirstTime);
    m_peCosProject->RemoveAllUnflaggedItems();
    #else
    UNUSED_ALWAYS(bFirstTime);
    generate_build_tree (GetCdlConfig(), CUtils::UnicodeToStdStr(BuildTree()), CUtils::UnicodeToStdStr(InstallTree()));
    #endif
    return true;
  }
  catch(...){
    return false;
  }
}

void CConfigToolDoc::RemoveAllItems()
{
  for(int nItem=0;nItem<ItemCount();nItem++){
    CConfigItem *pItem=Item(nItem);
    deleteZ(pItem);
  }
  m_arItem.RemoveAll();

  UpdateAllViews(NULL,Clear,0);
}

void CConfigToolDoc::EnableCallbacks (bool bEnable/*=true*/)
{
  CdlTransactionCallback::set_callback_fn(bEnable?&CdlTransactionHandler:0);
  CdlTransactionBody::set_inference_callback_fn(bEnable?&CdlInferenceHandler:0);
  CdlTransactionBody::set_inference_override(CdlValueSource_Invalid);
}

int CConfigToolDoc::GetTestExeNames (CFileNameArray &arTestExes,CFileNameArray &arMissing)
{
  arTestExes.RemoveAll();
  arMissing.RemoveAll();
  
  typedef std::vector<CdlBuildInfo_Loadable> EntriesArray;
  const EntriesArray &arEntries=BuildInfo().entries;
  for(EntriesArray::size_type j=0;j<arEntries.size();j++){
    const CdlBuildInfo_Loadable &e=arEntries[j];
    CStringArray ar;
    int n=CUtils::Chop(CString(get_tests(GetCdlConfig(),e).c_str()),ar);
    for(int i=0;i<n;i++){
      CFileName strFile;
      strFile.Format(_T("%s\\tests\\%s\\%s.exe"),InstallTree(),CString(e.directory.c_str()),ar[i]); 
      strFile.Replace(_TCHAR('/'),_TCHAR('\\'));
      if(strFile.Exists()){
        arTestExes.Add(strFile);
      } else {
        arMissing.Add(strFile);
      }
    }
  }
  return arTestExes.GetSize();
}

CConfigToolDoc::GlobalConflictOutcome CConfigToolDoc::ResolveGlobalConflicts(CPtrArray *parConflictsOfInterest)
{
  m_ConflictsOutcome=NotDone;
  m_arConflictsOfInterest.RemoveAll();
  if(parConflictsOfInterest){
    m_arConflictsOfInterest.Copy(*parConflictsOfInterest);
  }
  CdlInferenceCallback fn=CdlTransactionBody::get_inference_callback_fn();
  CdlTransactionBody::set_inference_callback_fn(CdlGlobalInferenceHandler);
  GetCdlInterpreter()->get_toplevel()->resolve_all_conflicts();
  CdlTransactionBody::set_inference_callback_fn(fn);
  if(NotDone==m_ConflictsOutcome){
    // No solutions were available, but we'll run the dialog anyway
    const std::list<CdlConflict>& conflicts=GetCdlConfig()->get_all_conflicts();  
    CFailingRulesDialog dlg(conflicts,NULL,&m_arConflictsOfInterest);
    m_ConflictsOutcome=(IDOK==dlg.DoModal())?OK:Cancel;
  }
  return m_ConflictsOutcome;
}

bool CConfigToolDoc::CheckConflictsBeforeSave()
{
  if(GetCdlInterpreter()->get_toplevel()->get_all_conflicts().size()>0){
    if(Deferred&m_nRuleChecking){
      if((SuggestFixes&m_nRuleChecking)&&(Cancel==ResolveGlobalConflicts())){
        return false;
      }
      int nConflicts=GetCdlInterpreter()->get_toplevel()->get_all_conflicts().size();
      switch(nConflicts){
      case 0:
        break;
      case 1:
        if(IDNO==CUtils::MessageBoxFT(MB_YESNO|MB_DEFBUTTON2,_T("There is 1 unresolved conflict.  Save anyway?"))){
          return false;
        }
      default:
        if(IDNO==CUtils::MessageBoxFT(MB_YESNO|MB_DEFBUTTON2,_T("There are %d unresolved conflicts.  Save anyway?"),nConflicts)){
          return false;
        }
      }
    }
  }
  return true;
}

BOOL CConfigToolDoc::IsModified() 
{ 
  return m_bModified || MemoryMap.map_modified();
}

void CConfigToolDoc::OnFileExport()
{
  CFileDialog dlg (FALSE, _T("ecm"), NULL, OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY, _T("eCos Minimal Configuration (*.ecm)|*.ecm||"), AfxGetMainWnd ());
  TCHAR szBuffer [MAX_PATH * 16] = _T("");
  dlg.m_ofn.lpstrFile = szBuffer;
  dlg.m_ofn.nMaxFile = MAX_PATH * 16;
  dlg.m_ofn.lpstrTitle = _T("Export eCos Minimal Configuration");
  if (IDOK == dlg.DoModal ()) {
    try {
      TRACE (_T("Exporting eCos minimal configuration '%s'\n"), dlg.GetPathName ());
      m_CdlConfig->save (CUtils::UnicodeToStdStr (dlg.GetPathName ()), /* minimal = */ true);
    }
    catch (CdlStringException exception) {
      CUtils::MessageBoxF (_T("Error exporting eCos minimal configuration:\n\n%s"), CString (exception.get_message ().c_str ()));
    }
    catch (...) {
      CUtils::MessageBoxF (_T("Error exporting eCos minimal configuration."));
    }
  }
}

void CConfigToolDoc::OnFileImport()
{
  CFileDialog dlg (TRUE, NULL, NULL, OFN_PATHMUSTEXIST | OFN_HIDEREADONLY, _T("eCos Minimal Configurations (*.ecm)|*.ecm||"), AfxGetMainWnd ());
  TCHAR szBuffer [MAX_PATH * 16] = _T("");
  dlg.m_ofn.lpstrFile = szBuffer;
  dlg.m_ofn.nMaxFile = MAX_PATH * 16;
  dlg.m_ofn.lpstrTitle = _T("Import eCos Minimal Configuration");
  if (IDOK == dlg.DoModal ()) {
    try {
      TRACE (_T("Importing eCos minimal configuration '%s'\n"), dlg.GetPathName ());
      m_CdlConfig->add (CUtils::UnicodeToStdStr (dlg.GetPathName ()), CConfigToolDoc::CdlParseErrorHandler, CConfigToolDoc::CdlParseWarningHandler);
    }
    catch (CdlStringException exception) {
      CUtils::MessageBoxF (_T("Error importing eCos minimal configuration:\n\n%s"), CString (exception.get_message ().c_str ()));
    }
    catch (...) {
      CUtils::MessageBoxF (_T("Error importing eCos minimal configuration."));
    }

    CWaitCursor wait;
    AddAllItems (); // regenerate all the config items since the topology may have changed
    if (m_strLinkerScriptFolder.IsEmpty ()) {
      CUtils::MessageBoxF (_T("The eCos linker script macro CYGBLD_LINKER_SCRIPT is not defined."));
    }
    if (m_strMemoryLayoutFolder.IsEmpty ()) {
      CUtils::MessageBoxF (_T("The eCos memory layout macro CYGHWR_MEMORY_LAYOUT is not defined."));
    }
    SwitchMemoryLayout (true); // the hardware template may have changed
    UpdateBuildInfo ();
    CConfigTool::GetControlView()->SelectItem (Item (0));
    SetModifiedFlag ();
  }
}

bool CConfigToolDoc::QualifyDocURL(CString &strURL)
{
  if(-1==strURL.Find(_T("://"))){
    // Relative URL - qualify it as a file URL
    strURL=DocBase()+CFileName(strURL);
    strURL.Replace(_TCHAR('/'),_TCHAR('\\'));
    strURL=_T("file://")+strURL;
  }

  if(0==strURL.Find(_T("file://"))){
    CFileName strFile(strURL.Right(strURL.GetLength()-7));
    int nIndex=strFile.ReverseFind(_TCHAR('#'));
    if(-1!=nIndex){
      strFile=strFile.Left(nIndex);
    }
    strFile.Replace(_TCHAR('/'),_TCHAR('\\'));
    if(!strFile.Exists()){
      CUtils::MessageBoxF(_T("Cannot locate the file %s"),strFile);
      return false;
    }
  }
  return true;
}

bool CConfigToolDoc::NewMemoryLayout (const CString &strPrefix)
{
  CFileName strFileName = CurrentLinkerScript ();
  TRACE(_T("Reading linker-defined memory sections from %s\n"), strFileName);
  MemoryMap.new_memory_layout (); // delete the old memory layout regardless
  if (! strFileName.IsEmpty ())
    MemoryMap.import_linker_defined_sections (strFileName); // read the linker-defined section names from the repository (failure is silent)

  CString strMemoryLayoutFileName = strPrefix + _T("\\mlt_") + CurrentMemoryLayout () + _T(".mlt");
  TRACE(_T("Reading memory layout from %s\n"), strMemoryLayoutFileName);
  MemoryMap.load_memory_layout (strMemoryLayoutFileName); // load the new memory layout (failure is silent)
  strSelectedSection = _T("");
  strSelectedRegion = _T("");
  UpdateAllViews (NULL, MemLayoutChanged);
  return true; // FIXME
}

void CConfigToolDoc::SetPathName( LPCTSTR pszPath, BOOL bAddToMRU /*= TRUE*/ )
{
  if(_TCHAR('\0')==*pszPath){ // called like this after failed saves to put things back as they were
	  // CDocument::SetPathName would assert given an empty string
	  m_strPathName = _T("");
#ifndef PLUGIN
    SetTitle(_T("Untitled"));
#endif
    m_strBuildTree   = _T("");
    m_strInstallTree = _T("");
  } else {
#ifdef PLUGIN
    m_strPathName = CFileName (pszPath);
#else
    CDocument::SetPathName (pszPath, bAddToMRU);
#endif
    CString strFolder (pszPath);
    strFolder = strFolder.Left (strFolder.ReverseFind (_TCHAR('.'))); // extract folder from file path
    m_strBuildTree   = CFileName(strFolder + _T("_build")); 
    m_strInstallTree = CFileName(strFolder + _T("_install")); 
  }
}

bool CConfigToolDoc::ShowHtmlHelp (LPCTSTR pszURL)
{

  HWND hwndCaller=AfxGetMainWnd()->CWnd::GetDesktopWindow()->GetSafeHwnd();
  bool rc=false;
  const CFileName strFile(HTMLHelpLinkFileName());
  CStdioFile f;
  if(!CFileName(CConfigTool::strHelpFile).Exists()) {
    CUtils::MessageBoxF(_T("Cannot display help - %s does not exist\n"),CConfigTool::strHelpFile);
  } else if(!f.Open(strFile,CFile::typeText|CFile::modeCreate|CFile::modeWrite)){
    CUtils::MessageBoxF(_T("Cannot display help - error creating %s\n"),strFile);
  } else {
    CString str;
    str.Format(_T("<meta HTTP-EQUIV=\"refresh\" CONTENT=\"0;URL=%s\">"),pszURL);
    f.WriteString(str);
    f.Close();
    if(0==HtmlHelp(hwndCaller, CConfigTool::strHelpFile, HH_DISPLAY_TOPIC, 0)){
      CUtils::MessageBoxF(_T("Cannot display %s"),pszURL);
    } else {
      // FIXME: Do this the first time only?
      HH_WINTYPE WinType;
      HWND wnd;
      HH_WINTYPE *pWinType=NULL;
      wnd = HtmlHelp(hwndCaller, CConfigTool::strHelpFile+_T(">mainwin"), HH_GET_WIN_TYPE, (DWORD) &pWinType);
      WinType=*pWinType;
      WinType.hwndCaller=hwndCaller;
      WinType.fsWinProperties|=HHWIN_PROP_TRACKING;
      WinType.idNotify = ID_HHNOTIFICATION;
      wnd = HtmlHelp(hwndCaller, CConfigTool::strHelpFile, HH_SET_WIN_TYPE, (DWORD) &WinType);
      rc=true;
    }
    //::DeleteFile(strFile);
  }
  return rc;
}

const CString CConfigToolDoc::HTMLHelpLinkFileName()
{
  return CFileName(CConfigTool::strHelpFile).Head()+"link2.htm";
}
