/////////////////////////////////////////////////////////////////////////////
// Name:        log.cpp
// Purpose:     Assorted wxLogXXX functions, and wxLog (sink for logs)
// Author:      Vadim Zeitlin
// Modified by:
// Created:     29/01/98
// RCS-ID:      $Id$
// Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
// Licence:     wxWindows license
/////////////////////////////////////////////////////////////////////////////

// ============================================================================
// declarations
// ============================================================================

// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#ifdef __GNUG__
  #pragma implementation "log.h"
#endif

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

#ifdef __BORLANDC__
  #pragma hdrstop
#endif

// wxWindows
#ifndef WX_PRECOMP
  #include  <wx/app.h>
  #include  <wx/string.h>
  #include  <wx/intl.h>

  #include  <wx/generic/msgdlgg.h>
  #include  <wx/filedlg.h>
  #include  <wx/textctrl.h>
#endif //WX_PRECOMP

#include  <wx/file.h>
#include  <wx/textfile.h>
#include  <wx/log.h>

// other standard headers
#include  <errno.h>
#include  <stdlib.h>
#include  <time.h>

#ifdef  __WINDOWS__
  #include  <windows.h>
#else   //Unix
  #include  <signal.h>
#endif  //Win/Unix

// ----------------------------------------------------------------------------
// non member functions
// ----------------------------------------------------------------------------

// define this to enable wrapping of log messages
//#define LOG_PRETTY_WRAP

#ifdef  LOG_PRETTY_WRAP
  static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz);
#endif

// ============================================================================
// implementation
// ============================================================================

// ----------------------------------------------------------------------------
// implementation of Log functions
//
// NB: unfortunately we need all these distinct functions, we can't make them
//     macros and not all compilers inline vararg functions.
// ----------------------------------------------------------------------------

// log functions can't allocate memory (LogError("out of memory...") should
// work!), so we use a static buffer for all log messages
#define LOG_BUFFER_SIZE   (4096)

// static buffer for error messages (@@@ MT-unsafe)
static char s_szBuf[LOG_BUFFER_SIZE];

// generic log function
void wxLogGeneric(wxLogLevel level, wxTString strFormat, ...)
{
  if ( wxLog::GetActiveTarget() != NULL ) {
    va_list argptr;
    va_start(argptr, strFormat);
    vsprintf(s_szBuf, strFormat, argptr);
    va_end(argptr);

    wxLog::OnLog(level, s_szBuf);
  }
}

#define IMPLEMENT_LOG_FUNCTION(level)                             \
  void wxLog##level(wxTString strFormat, ...)                     \
  {                                                               \
    if ( wxLog::GetActiveTarget() != NULL ) {                     \
      va_list argptr;                                             \
      va_start(argptr, strFormat);                                \
      vsprintf(s_szBuf, strFormat, argptr);                       \
      va_end(argptr);                                             \
                                                                  \
      wxLog::OnLog(wxLOG_##level, s_szBuf);                       \
    }                                                             \
  }

IMPLEMENT_LOG_FUNCTION(FatalError)
IMPLEMENT_LOG_FUNCTION(Error)
IMPLEMENT_LOG_FUNCTION(Warning)
IMPLEMENT_LOG_FUNCTION(Message)
IMPLEMENT_LOG_FUNCTION(Info)
IMPLEMENT_LOG_FUNCTION(Status)

// same as info, but only if 'verbose' mode is on
void wxLogVerbose(wxTString strFormat, ...)
{
  wxLog *pLog = wxLog::GetActiveTarget();
  if ( pLog != NULL && pLog->GetVerbose() ) {
    va_list argptr;
    va_start(argptr, strFormat);
    vsprintf(s_szBuf, strFormat, argptr);
    va_end(argptr);

    wxLog::OnLog(wxLOG_Info, s_szBuf);
  }
}

// debug functions
#ifdef __DEBUG__
#define IMPLEMENT_LOG_DEBUG_FUNCTION(level)                       \
  void wxLog##level(const char *szFormat, ...)                    \
  {                                                               \
    if ( wxLog::GetActiveTarget() != NULL ) {                     \
      va_list argptr;                                             \
      va_start(argptr, szFormat);                                 \
      vsprintf(s_szBuf, szFormat, argptr);                        \
      va_end(argptr);                                             \
                                                                  \
      wxLog::OnLog(wxLOG_##level, s_szBuf);                       \
    }                                                             \
  }

  void wxLogTrace(wxTraceMask mask, const char *szFormat, ...)
  {
    wxLog *pLog = wxLog::GetActiveTarget();

    // we check that all of mask bits are set in the current mask, so
    // that wxLogTrace(wxTraceRefCount | wxTraceOle) will only do something
    // if both bits are set.
    if ( pLog != NULL && (pLog->GetTraceMask() & mask == mask) ) {
      va_list argptr;
      va_start(argptr, szFormat);
      vsprintf(s_szBuf, szFormat, argptr);
      va_end(argptr);

      wxLog::OnLog(wxLOG_Trace, s_szBuf);
    }
  }

#else // release
  #define IMPLEMENT_LOG_DEBUG_FUNCTION(level)
#endif

IMPLEMENT_LOG_DEBUG_FUNCTION(Debug)
IMPLEMENT_LOG_DEBUG_FUNCTION(Trace)

// wxLogSysError: one uses the last error code, for other  you must give it
// explicitly

// common part of both wxLogSysError
void wxLogSysErrorHelper(long lErrCode)
{
  char szErrMsg[LOG_BUFFER_SIZE / 2];
  sprintf(szErrMsg, _(" (error %ld: %s)"), lErrCode, wxSysErrorMsg(lErrCode));
  strncat(s_szBuf, szErrMsg, WXSIZEOF(s_szBuf) - strlen(s_szBuf));

  wxLog::OnLog(wxLOG_Error, s_szBuf);
}

void WXDLLEXPORT wxLogSysError(wxTString strFormat, ...)
{
  va_list argptr;
  va_start(argptr, strFormat);
  vsprintf(s_szBuf, strFormat, argptr);
  va_end(argptr);

  wxLogSysErrorHelper(wxSysErrorCode());
}

void WXDLLEXPORT wxLogSysError(long lErrCode, wxTString strFormat, ...)
{
  va_list argptr;
  va_start(argptr, strFormat);
  vsprintf(s_szBuf, strFormat, argptr);
  va_end(argptr);

  wxLogSysErrorHelper(lErrCode);
}

// ----------------------------------------------------------------------------
// wxLog class implementation
// ----------------------------------------------------------------------------

wxLog::wxLog()
{
  m_bHasMessages = FALSE;
  m_bVerbose     = FALSE;
  m_szTimeFormat = "[%d/%b/%y %H:%M:%S] ";
  m_ulTraceMask  = (wxTraceMask)0;        // -1 to set all bits
}

wxLog *wxLog::GetActiveTarget() 
{ 
  if ( !ms_bInitialized ) {
    // prevent infinite recursion if someone calls wxLogXXX() from
    // wxApp::CreateLogTarget()
    ms_bInitialized = TRUE;

    #ifdef  WX_TEST_MINIMAL
      ms_pLogger = new wxLogStderr;
    #else
      // ask the application to create a log target for us
      ms_pLogger = wxTheApp->CreateLogTarget();
    #endif

    // do nothing if it fails - what can we do?
  }

  return ms_pLogger; 
}

wxLog *wxLog::SetActiveTarget(wxLog *pLogger)
{ 
  // flush the old messages before changing
  if ( ms_pLogger != NULL )
    ms_pLogger->Flush();

  ms_bInitialized = TRUE;

  wxLog *pOldLogger = ms_pLogger; 
  ms_pLogger = pLogger; 
  return pOldLogger; 
}

void wxLog::DoLog(wxLogLevel level, const char *szString)
{
  wxString str;

  // prepend a timestamp if not disabled
  if ( !IsEmpty(m_szTimeFormat) ) {
    char szBuf[128];
    time_t timeNow;
    struct tm *ptmNow;

    time(&timeNow);
    ptmNow = localtime(&timeNow);

    strftime(szBuf, WXSIZEOF(szBuf), m_szTimeFormat, ptmNow);
    str = szBuf;
  }

  switch ( level ) {
    case wxLOG_FatalError:
      DoLogString(str << _("Fatal error: ") << szString);
      DoLogString(_("Program aborted."));
      Flush();
      abort();
      break;

    case wxLOG_Error:
      DoLogString(str << _("Error: ") << szString);
      break;

    case wxLOG_Warning:
      DoLogString(str << _("Warning: ") << szString);
      break;

    case wxLOG_Info:
      if ( GetVerbose() )
    case wxLOG_Message:
        DoLogString(str + szString);
      // fall through

    case wxLOG_Status:
      // nothing to do
      break;

    case wxLOG_Trace:
    case wxLOG_Debug:
      #ifdef __DEBUG__
        #ifdef  __WIN32__
          // in addition to normal logging, also send the string to debugger
          // (don't prepend "Debug" here: it will go to debug window anyhow)
          ::OutputDebugString(str + szString + "\n\r");
        #endif  //Win32
        DoLogString(str << (level == wxLOG_Trace ? "Trace" : "Debug")
                        << ": " << szString);
      #endif
            
      break;

    default:
      wxFAIL_MSG("unknown log level in wxLog::DoLog");
  }
}

void wxLog::DoLogString(const char *szString)
{
  wxFAIL_MSG("DoLogString must be overrided if it's called.");
}

void wxLog::Flush()
{
  // do nothing
}

// ----------------------------------------------------------------------------
// wxLogStderr class implementation
// ----------------------------------------------------------------------------

wxLogStderr::wxLogStderr(FILE *fp)
{
  if ( fp == NULL )
    m_fp = stderr;
  else
    m_fp = fp;
}

void wxLogStderr::DoLogString(const char *szString)
{
  fputs(szString, m_fp);
  fputc('\n', m_fp);
  fflush(m_fp);
}

// ----------------------------------------------------------------------------
// wxLogStream implementation
// ----------------------------------------------------------------------------

wxLogStream::wxLogStream(ostream *ostr)
{
  if ( ostr == NULL )
    m_ostr = &cerr;
  else
    m_ostr = ostr;
}

void wxLogStream::DoLogString(const char *szString)
{
  (*m_ostr) << szString << endl << flush;
}

// ----------------------------------------------------------------------------
// wxLogTextCtrl implementation
// ----------------------------------------------------------------------------
wxLogTextCtrl::wxLogTextCtrl(wxTextCtrl *pTextCtrl)
// @@@ TODO: in wxGTK wxTextCtrl doesn't derive from streambuf
#ifndef __GTK__
             : wxLogStream(new ostream(pTextCtrl))
#endif //GTK
{
}

wxLogTextCtrl::~wxLogTextCtrl()
{
  #ifndef __GTK__
    delete m_ostr;
  #endif //GTK
}

// ----------------------------------------------------------------------------
// wxLogGui implementation
// ----------------------------------------------------------------------------

#ifndef   WX_TEST_MINIMAL

wxLogGui::wxLogGui()
{
  m_bErrors = FALSE;
}

void wxLogGui::Flush()
{
  if ( !m_bHasMessages )
    return;

  // @@@ ugly...
  
  // concatenate all strings (but not too many to not overfill the msg box)
  wxString str;
  uint nLines    = 0, 
       nMsgCount = m_aMessages.Count();

  // start from the most recent message
  for ( uint n = nMsgCount; n > 0; n-- ) {
    // for Windows strings longer than this value are wrapped (NT 4.0)
    const uint nMsgLineWidth = 156;

    nLines += (m_aMessages[n - 1].Len() + nMsgLineWidth - 1) / nMsgLineWidth;

    if ( nLines > 25 )  // don't put too many lines in message box
      break;

    str << m_aMessages[n - 1] << "\n";
  }

  if ( m_bErrors ) {
    wxMessageBox(str, _("Error"), wxOK | wxICON_EXCLAMATION);
  }
  else {
    wxMessageBox(str, _("Information"), wxOK | wxICON_INFORMATION);
  }

  // no undisplayed messages whatsoever
  m_bHasMessages =
  m_bErrors      = FALSE;
  m_aMessages.Empty();
}

// the default behaviour is to discard all informational messages if there
// are any errors/warnings.
void wxLogGui::DoLog(wxLogLevel level, const char *szString)
{
  switch ( level ) {
    case wxLOG_Info:
      if ( GetVerbose() )
    case wxLOG_Message:
        if ( !m_bErrors ) {
          m_aMessages.Add(szString);
          m_bHasMessages = TRUE;
        }
      break;

    case wxLOG_Status:
      {
        // find the top window and set it's status text if it has any
        wxWindow *pWin = wxTheApp->GetTopWindow();
        if ( pWin != NULL && pWin->IsKindOf(CLASSINFO(wxFrame)) ) {
          wxFrame *pFrame = (wxFrame *)pWin;
          pFrame->SetStatusText(szString);
        }
      }
      break;

    case wxLOG_Trace:
    case wxLOG_Debug:
      #ifdef __DEBUG__
        #ifdef  __WIN32__
          OutputDebugString(szString);
          OutputDebugString("\n\r");
        #else   //!WIN32
          // send them to stderr
          fprintf(stderr, level == wxLOG_Trace ? "Trace: %s\n" 
                                               : "Debug: %s\n", szString);
          fflush(stderr);
        #endif  // WIN32
      #endif
      break;

    case wxLOG_FatalError:
      // show this one immediately
      wxMessageBox(szString, "Fatal error", wxICON_HAND);
      break;

    case wxLOG_Error:
    case wxLOG_Warning:
      // discard earlier informational messages if this is the 1st error
      if ( !m_bErrors ) {
        m_aMessages.Empty();
        m_bHasMessages = TRUE;
        m_bErrors = TRUE;
      }

      m_aMessages.Add(szString);
      break;
    
    default:
      wxFAIL_MSG("unknown log level in wxLogGui::DoLog");
  }
}

// ----------------------------------------------------------------------------
// wxLogWindow implementation
// ----------------------------------------------------------------------------

// log frame class
class wxLogFrame : public wxFrame
{
public:
  // ctor
  wxLogFrame(const char *szTitle);

  // menu callbacks
  void OnClose(wxCommandEvent& event);
  void OnCloseWindow(wxCloseEvent& event);
  void OnSave (wxCommandEvent& event);
  void OnClear(wxCommandEvent& event);

  // accessors
  wxTextCtrl *TextCtrl() const { return m_pTextCtrl; }

private:
  enum
  {
    Menu_Close = 100,
    Menu_Save,
    Menu_Clear
  };

  wxTextCtrl *m_pTextCtrl;

  DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(wxLogFrame, wxFrame)
  // wxLogWindow menu events
  EVT_MENU(Menu_Close, wxLogFrame::OnClose)
  EVT_MENU(Menu_Save,  wxLogFrame::OnSave)
  EVT_MENU(Menu_Clear, wxLogFrame::OnClear)

  EVT_CLOSE(wxLogFrame::OnCloseWindow)
END_EVENT_TABLE() 

wxLogFrame::wxLogFrame(const char *szTitle)
          : wxFrame(NULL, -1, szTitle)
{
  // we don't want to be a top-level frame because it would prevent the
  // application termination when all other frames are closed
  wxTopLevelWindows.DeleteObject(this);

  // @@ kludge: wxSIMPLE_BORDER is simply to prevent wxWindows from creating
  //    a rich edit control instead of a normal one we want
  m_pTextCtrl = new wxTextCtrl(this, -1, wxEmptyString, wxDefaultPosition,
                               wxDefaultSize,
                               wxSIMPLE_BORDER |
                               wxTE_MULTILINE  |
                               wxHSCROLL       |
                               wxTE_READONLY);
  /*
  m_pTextCtrl->SetEditable(FALSE);
  m_pTextCtrl->SetRichEdit(FALSE);
  */

  // create menu
  wxMenuBar *pMenuBar = new wxMenuBar;
  wxMenu *pMenu = new wxMenu;
  pMenu->Append(Menu_Save,  "&Save...");
  pMenu->Append(Menu_Clear, "C&lear");
  pMenu->AppendSeparator();
  pMenu->Append(Menu_Close, "&Close");
  pMenuBar->Append(pMenu, "&Log");
  SetMenuBar(pMenuBar);

  // @@ what about status bar? needed (for menu prompts)?
}

void wxLogFrame::OnClose(wxCommandEvent& event)
{
  // just hide the window
  Show(FALSE);
}

void wxLogFrame::OnCloseWindow(wxCloseEvent& event)
{
  // just hide the window
  Show(FALSE);
}

void wxLogFrame::OnSave(wxCommandEvent& event)
{
  // get the file name
  // -----------------
  const char *szFileName = wxSaveFileSelector("log", "txt", "log.txt");
  if ( szFileName == NULL ) {
    // cancelled
    return;
  }

  // open file
  // ---------
  wxFile file;
  bool bOk;
  if ( wxFile::Exists(szFileName) ) {
    bool bAppend;
    wxString strMsg;
    strMsg.Printf(_("Append log to file '%s' "
                    "(choosing [No] will overwrite it)?"), szFileName);
    switch ( wxMessageBox(strMsg, "Question", wxYES_NO | wxCANCEL) ) {
      case wxYES:
        bAppend = TRUE;
        break;

      case wxNO:
        bAppend = FALSE;
        break;

      case wxCANCEL:
        return;

      default:
        wxFAIL_MSG("invalid message box return value");
    }

    if ( bAppend ) {
      bOk = file.Open(szFileName, wxFile::write_append);
    }
    else {
      bOk = file.Create(szFileName, TRUE /* overwrite */);
    }
  }
  else {
    bOk = file.Create(szFileName);
  }

  // retrieve text and save it
  // -------------------------
  
#ifdef __GTK__
  // @@@@ TODO: no GetNumberOfLines and GetLineText in wxGTK yet
  wxLogError("Sorry, this function is not implemented under GTK");
#else
  int nLines = m_pTextCtrl->GetNumberOfLines();
  for ( int nLine = 0; bOk && nLine < nLines; nLine++ ) {
    bOk = file.Write(m_pTextCtrl->GetLineText(nLine) + wxTextFile::GetEOL());
  }
#endif //GTK
  
  if ( bOk )
    bOk = file.Close();

  if ( !bOk ) {
    wxLogError("Can't save log contents to file.");
    return;
  }
}

void wxLogFrame::OnClear(wxCommandEvent& event)
{
  m_pTextCtrl->Clear();
}

wxLogWindow::wxLogWindow(const wxTString& strTitle, bool bShow)
{
  m_pOldLog = wxLog::GetActiveTarget();
  m_pLogFrame = new wxLogFrame(strTitle);
  
  if ( bShow )
    m_pLogFrame->Show(TRUE);
}

void wxLogWindow::Show(bool bShow)
{
  m_pLogFrame->Show(bShow);
}

void wxLogWindow::DoLog(wxLogLevel level, const char *szString)
{
  // first let the previous logger show it
  if ( m_pOldLog != NULL ) {
    // @@@ why can't we access protected wxLog method from here (we derive 
    // from wxLog)? gcc gives "DoLog is protected in this context", what
    // does this mean? Anyhow, the cast is harmless and let's us do what
    // we want.
    ((wxLogWindow *)m_pOldLog)->DoLog(level, szString);
  }
  
  // and this will format it nicely and call our DoLogString()
  wxLog::DoLog(level, szString);
}

void wxLogWindow::DoLogString(const char *szString)
{
  // put the text into our window
  wxTextCtrl *pText = m_pLogFrame->TextCtrl();

  // remove selection (WriteText is in fact ReplaceSelection)
  #ifdef __WINDOWS__
    long nLen = pText->GetLastPosition();
    pText->SetSelection(nLen, nLen);
  #endif // Windows

  pText->WriteText(szString);
  pText->WriteText("\n"); // "\n" ok here (_not_ "\r\n")

  // ensure that the line can be seen
  // @@@ TODO
}

wxLogWindow::~wxLogWindow()
{
  m_pLogFrame->Close(TRUE);
}

#endif  //WX_TEST_MINIMAL

// ============================================================================
// Global functions/variables
// ============================================================================

// ----------------------------------------------------------------------------
// static variables
// ----------------------------------------------------------------------------
wxLog      *wxLog::ms_pLogger      = NULL;
bool        wxLog::ms_bInitialized = FALSE;

// ----------------------------------------------------------------------------
// stdout error logging helper
// ----------------------------------------------------------------------------

// helper function: wraps the message and justifies it under given position
// (looks more pretty on the terminal). Also adds newline at the end.
//
// @@ this is now disabled until I find a portable way of determining the
//    terminal window size (ok, I found it but does anybody really cares?)
#ifdef    LOG_PRETTY_WRAP
static void wxLogWrap(FILE *f, const char *pszPrefix, const char *psz)
{
  size_t nMax = 80; // @@@@
  size_t nStart = strlen(pszPrefix);
  fputs(pszPrefix, f);

  size_t n;
  while ( *psz != '\0' ) {
    for ( n = nStart; (n < nMax) && (*psz != '\0'); n++ )
      putc(*psz++, f);

    // wrapped?
    if ( *psz != '\0' ) {
      /*putc('\n', f);*/
      for ( n = 0; n < nStart; n++ )
        putc(' ', f);

      // as we wrapped, squeeze all white space
      while ( isspace(*psz) )
        psz++;
    }
  }

  putc('\n', f);
}
#endif  //LOG_PRETTY_WRAP

// ----------------------------------------------------------------------------
// error code/error message retrieval functions
// ----------------------------------------------------------------------------

// get error code from syste
unsigned long wxSysErrorCode()
{
  #ifdef  __WINDOWS__
    #ifdef  __WIN32__
      return ::GetLastError();
    #else   //WIN16
      // @@@@ what to do on Windows 3.1?
      return 0;
    #endif  //WIN16/32
  #else   //Unix
    return errno;
  #endif  //Win/Unix
}

// get error message from system
const char *wxSysErrorMsg(unsigned long nErrCode)
{
  if ( nErrCode == 0 )
    nErrCode = wxSysErrorCode();

  #ifdef  __WINDOWS__
    #ifdef  __WIN32__
      static char s_szBuf[LOG_BUFFER_SIZE / 2];

      // get error message from system
      LPVOID lpMsgBuf;
      FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                    NULL, nErrCode, 
                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                    (LPTSTR)&lpMsgBuf,
                    0, NULL);

      // copy it to our buffer and free memory
      strncpy(s_szBuf, (const char *)lpMsgBuf, WXSIZEOF(s_szBuf) - 1);
      s_szBuf[WXSIZEOF(s_szBuf) - 1] = '\0';
      LocalFree(lpMsgBuf);

      // returned string is capitalized and ended with '\r\n' - bad
      s_szBuf[0] = (char)tolower(s_szBuf[0]);
      size_t len = strlen(s_szBuf);
      if ( len > 0 ) {
        // truncate string
        if ( s_szBuf[len - 2] == '\r' )
          s_szBuf[len - 2] = '\0';
      }

      return s_szBuf;
    #else   //Win16
      // TODO @@@@
      return NULL;
    #endif // Win16/32
  #else   // Unix
    return strerror(nErrCode);
  #endif  // Win/Unix
}

// ----------------------------------------------------------------------------
// debug helper
// ----------------------------------------------------------------------------

#ifdef  __DEBUG__

// this function is called when an assert fails
void wxOnAssert(const char *szFile, int nLine, const char *szMsg)
{
  // this variable can be set to true to suppress "assert failure" messages
  static s_bNoAsserts = FALSE;

  char szBuf[LOG_BUFFER_SIZE];
  sprintf(szBuf, _("Assert failed in file %s at line %d"), szFile, nLine);
  if ( szMsg != NULL ) {
    strcat(szBuf, ": ");
    strcat(szBuf, szMsg);
  }
  else {
    strcat(szBuf, ".");
  }

  // send it to the normal log destination
  wxLogDebug(szBuf);

  if ( !s_bNoAsserts ) {
    strcat(szBuf, _("\nDo you want to stop the program?"
                    "\nYou can also choose [Cancel] to suppress "
                    "further warnings."));

    switch ( wxMessageBox(szBuf, _("Debug"),
                          wxYES_NO | wxCANCEL | wxICON_STOP ) ) {
      case wxYES:
        #ifdef __WINDOWS__
          DebugBreak();
        #else // Unix
          raise(SIGTRAP);
        #endif // Win/Unix
        break;

      case wxCANCEL:
        s_bNoAsserts = TRUE;
        break;
        
      //case wxNO: nothing to do
    }
  }
}

#endif  //DEBUG

