text: /**********************************************************************
  
    NyqBench.cpp
  
    Leland Lucius
  
  **********************************************************************/
  
  #include "Audacity.h"
  
  #include <wx/defs.h>
  
  #include <wx/aboutdlg.h>
  #include <wx/filedlg.h>
  #include <wx/font.h>
  #include <wx/fontdlg.h>
  #include <wx/menu.h>
  #include <wx/msgdlg.h>
  #include <wx/settings.h>
  #include <wx/sizer.h>
  #include <wx/splitter.h>
  #include <wx/statbox.h>
  #include <wx/textctrl.h>
  #include <wx/toolbar.h>
  
  #include "AudioIOBase.h"
  #include "CommonCommandFlags.h"
  #include "LabelTrack.h"
  #include "ModuleManager.h"
  #include "Prefs.h"
  #include "Project.h"
  #include "ShuttleGui.h"
  #include "effects/EffectManager.h"
  #include "effects/EffectUI.h"
  #include "effects/nyquist/Nyquist.h"
  #include "../images/AudacityLogo.xpm"
  #include "../../src/commands/CommandContext.h"
  #include "../../src/commands/CommandManager.h"
  #include "widgets/AudacityMessageBox.h"
  
  #include "NyqBench.h"
  
  #include <iostream>
  #include <ostream>
  #include <sstream>
  
  //
  // Images are from the Tango Icon Gallery
  // http://tango.freedesktop.org/Tango_Icon_Gallery
  //
  #include "images/document-new-small.xpm"
  #include "images/document-open-small.xpm"
  #include "images/document-save-as-small.xpm"
  #include "images/document-save-small.xpm"
  #include "images/edit-clear-small.xpm"
  #include "images/edit-copy-small.xpm"
  #include "images/edit-cut-small.xpm"
  #include "images/edit-delete-small.xpm"
  #include "images/edit-find-small.xpm"
  #include "images/edit-paste-small.xpm"
  #include "images/edit-redo-small.xpm"
  #include "images/edit-select-all-small.xpm"
  #include "images/edit-undo-small.xpm"
  #include "images/go-top-small.xpm"
  #include "images/go-up-small.xpm"
  #include "images/go-previous-small.xpm"
  #include "images/go-next-small.xpm"
  #include "images/system-search-small.xpm"
  #include "images/media-playback-start-small.xpm"
  #include "images/media-playback-stop-small.xpm"
  
  #include "images/document-new-large.xpm"
  #include "images/document-open-large.xpm"
  #include "images/document-save-as-large.xpm"
  #include "images/document-save-large.xpm"
  #include "images/edit-clear-large.xpm"
  #include "images/edit-copy-large.xpm"
  #include "images/edit-cut-large.xpm"
  #include "images/edit-delete-large.xpm"
  #include "images/edit-find-large.xpm"
  #include "images/edit-paste-large.xpm"
  #include "images/edit-redo-large.xpm"
  #include "images/edit-select-all-large.xpm"
  #include "images/edit-undo-large.xpm"
  #include "images/go-top-large.xpm"
  #include "images/go-up-large.xpm"
  #include "images/go-previous-large.xpm"
  #include "images/go-next-large.xpm"
  #include "images/system-search-large.xpm"
  #include "images/media-playback-start-large.xpm"
  #include "images/media-playback-stop-large.xpm"
  
  #if defined(__WXMSW__)
  #include <wx/init.h>
  #  if defined(__WXDEBUG__)
  #     define D "d"
  #  else
  #     define D ""
  #  endif
  #  if wxCHECK_VERSION(3, 1, 0)
  #     define V "31"
  #  elif wxCHECK_VERSION(3, 0, 0)
  #     define V "30"
  #  else
  #     define V "28"
  #  endif
  
  #  pragma comment(lib, "wxbase" V "u" D)
  #  pragma comment(lib, "wxbase" V "u" D "_net")
  #  pragma comment(lib, "wxmsw"  V "u" D "_adv")
  #  pragma comment(lib, "wxmsw"  V "u" D "_core")
  #  pragma comment(lib, "wxmsw"  V "u" D "_html")
  #  pragma comment(lib, "wxpng"        D)
  #  pragma comment(lib, "wxzlib"       D)
  #  pragma comment(lib, "wxjpeg"       D)
  #  pragma comment(lib, "wxtiff"       D)
  
  #  undef V
  #  undef D
  
  #endif //(__WXMSW__)
  /*
  There are several functions that can be used in a GUI module.
  
  //#define versionFnName   "GetVersionString"
  If the version is wrong, the module will be rejected.
  That is it will be loaded and then unloaded.
  
  //#define ModuleDispatchName "ModuleDispatch"
  The most useful function.  See the example in this 
  file.  It has several cases/options in it.
  
  //#define scriptFnName    "RegScriptServerFunc"
  This function is run from a non gui thread.  It was originally 
  created for the benefit of mod-script-pipe.
  
  //#define mainPanelFnName "MainPanelFunc"
  This function is the hijacking function, to take over Audacity
  and replace the main project window with our own wxFrame.
  
  */
  
  namespace {
  CommandHandlerObject &findme(AudacityProject&)
  {
     return *NyqBench::GetBench();
  }
  
  void RegisterMenuItems()
  {
    // Get here only after the module version check passes
     using namespace MenuTable;
     static AttachedItem sAttachment{ wxT("Tools"),
        ( FinderScope( findme ), Section( wxT("NyquistWorkBench"),
           Command( wxT("NyqBench"), XO("&Nyquist Workbench..."),
              static_cast<CommandFunctorPointer>(&NyqBench::ShowNyqBench),
              AudioIONotBusyFlag())
        ) )
     };
  }
  }
  
  extern "C"
  {
     static NyqBench *gBench = NULL;
  
     #ifdef _MSC_VER
        #define DLL_API _declspec(dllexport)
     #else
        #define DLL_API __attribute__ ((visibility("default")))
     #endif
  
     extern DLL_API const wxChar * GetVersionString();
     // GetVersionString
     // REQUIRED for the module to be accepted by Audacity.
     // Without it Audacity will see a version number mismatch.
     const wxChar * GetVersionString()
     {
        // For now, the versions must match exactly for Audacity to 
        // agree to load the module.
        return AUDACITY_VERSION_STRING;
     }
  
     extern int DLL_API ModuleDispatch(ModuleDispatchTypes type);
     // ModuleDispatch
     // is called by Audacity to initialize/terminate the module
     int ModuleDispatch(ModuleDispatchTypes type){
        switch (type){
           case ModuleInitialize:
              RegisterMenuItems();
              break;
           case AppQuiting: {
              //It is perfectly OK for gBench to be NULL.
              //Can happen if the menu item was never invoked.
              //wxASSERT(gBench != NULL);
              if (gBench) {
                 // be sure to do this while gPrefs still exists:
                 gBench->SavePrefs();
                 gBench->Destroy();
                 gBench = NULL;
              }
           }
           break;
           default:
           break;
        }
        return 1;
     }
  };
  
  //----------------------------------------------------------------------------
  // NyqTextCtrl
  //----------------------------------------------------------------------------
  
  BEGIN_EVENT_TABLE(NyqTextCtrl, wxTextCtrl)
  #if defined(__WXMAC__)
     EVT_KEY_DOWN(NyqTextCtrl::OnKeyDown)
  #endif
     EVT_KEY_UP(NyqTextCtrl::OnKeyUp)
     EVT_CHAR(NyqTextCtrl::OnChar)
     EVT_UPDATE_UI(wxID_ANY, NyqTextCtrl::OnUpdate)
  END_EVENT_TABLE()
  
  NyqTextCtrl::NyqTextCtrl(wxWindow *parent,
                           wxWindowID id,
                           const wxString &value,
                           const wxPoint & pos,
                           const wxSize & size,
                           int style)
  :  wxTextCtrl(parent, id, value, pos, size, style)
  {
     mLastCaretPos = -1;
     mLeftParen = -1;
     mRightParen = -1;
  
     mOn.SetTextColour(*wxRED);
     mOff.SetTextColour(*wxBLACK);
  }
  
  void NyqTextCtrl::SetFocusFromKbd()
  {
  #if defined(__WXMSW__)
     // We do this to prevent wxMSW from selecting all text when the
     // user tabs into the text controls.
     wxWindowBase::SetFocusFromKbd();
  #else
     wxTextCtrl::SetFocusFromKbd();
  #endif
  }
  
  void NyqTextCtrl::MarkDirty()
  {
     wxTextCtrl::MarkDirty();
     FindParens();
  }
  
  void NyqTextCtrl::OnChar(wxKeyEvent & e)
  {
  	e.Skip();
  
     // Hide any previously highlighted parens
     if (mLeftParen >= 0) {
  #if defined(__WXMSW__)
        Freeze(); // Prevents selection flashing on Windows
  #endif
  
        SetStyle(mLeftParen, mLeftParen + 1, mOff);
        SetStyle(mLeftParens[mLeftParen], mLeftParens[mLeftParen] + 1, mOff);
        mLeftParen = -1;
        mRightParen = -1;
  
  #if defined(__WXMSW__)
        Thaw(); // Prevents selection flashing on Windows
  #endif
     }
  }
  
  #if defined(__WXMAC__REMOVED_UNTIL_ITS_PROVEN_THAT_IT_IS_STILL_NEEDED)
  #include <wx/mac/uma.h>
  
  // This is hackage to correct a problem on Leopard where the
  // caret jumps up two lines when the up arrow is pressed and
  // the caret is at the beginning of the line.
  void NyqTextCtrl::OnKeyDown(wxKeyEvent & e)
  {
     e.Skip();
     if (UMAGetSystemVersion() >= 0x1050) {
        if (e.GetKeyCode() == WXK_UP && e.GetModifiers() == 0) {
           long x;
           long y;
     
           PositionToXY(GetInsertionPoint(), &x, &y);
           if (x == 0 && y > 1) {
              y--;
              SetInsertionPoint(XYToPosition(x, y) - 1);
              e.Skip(false);
           }
        }
     }
  }
  #endif
  
  void NyqTextCtrl::OnKeyUp(wxKeyEvent & e)
  {
     e.Skip();
  
     int pos = GetInsertionPoint();
     int lpos = wxMax(0, pos - 1);
  
     wxString text = GetRange(lpos, pos);
  
     if (text[0] == wxT('(')) {
        wxLongToLongHashMap::const_iterator left = mLeftParens.find(lpos);
        if (left != mLeftParens.end()) {
           Colorize(lpos, left->second);
        }
     }
     else if (text[0] == wxT(')')) {
        wxLongToLongHashMap::const_iterator right = mRightParens.find(lpos);
        if (right != mRightParens.end()) {
           Colorize(right->second, lpos);
        }
     }
  }
  
  void NyqTextCtrl::OnUpdate(wxUpdateUIEvent & e)
  {
     int pos = GetInsertionPoint();
  
     if (pos != mLastCaretPos) {
        int lpos = wxMax(0, pos - 1);
     
        wxString text = GetRange(lpos, pos);
        if (text.Length() > 0) {
           if (text[0] == wxT('(')) {
              wxLongToLongHashMap::const_iterator left = mLeftParens.find(lpos);
              if (left != mLeftParens.end()) {
                 Colorize(lpos, left->second);
              }
           }
           else if (text[0] == wxT(')')) {
              wxLongToLongHashMap::const_iterator right = mRightParens.find(lpos);
              if (right != mRightParens.end()) {
                 Colorize(right->second, lpos);
              }
           }
        }
  
        mLastCaretPos = pos;
     }
  }
  
  void NyqTextCtrl::GoMatch()
  {
     MoveCursor(mRightParen, mLeftParen);
  }
  
  void NyqTextCtrl::GoTop()
  {
     wxLongToLongHashMap::const_iterator it;
     long first = -1;
     long second = -1;
  
     if (mLeftParen != -1) {
        for (it = mLeftParens.begin(); it != mLeftParens.end(); it++) {
           if (mLeftParen > it->first && mLeftParen < it->second) {
              if (first == -1 || it->first < first) {
                 first = it->first;
                 second = it->second;
              }
           }
        }
     }
  
     if (first != -1) {
        MoveCursor(first, second);
     }
  }
  
  void NyqTextCtrl::GoUp()
  {
     wxLongToLongHashMap::const_iterator it;
     long first = -1;
     long second = -1;
  
     if (mLeftParen != -1) {
        for (it = mLeftParens.begin(); it != mLeftParens.end(); it++) {
           if (mLeftParen > it->first && mLeftParen < it->second) {
              if (first == -1 || it->first > first) {
                 first = it->first;
                 second = it->second;
              }
           }
        }
     }
  
     if (first != -1) {
        MoveCursor(first, second);
     }
  }
  
  void NyqTextCtrl::GoPrev()
  {
     wxLongToLongHashMap::const_iterator it;
     long first = -1;
     long second = -1;
  
     if (mLeftParen != -1) {
        for (it = mLeftParens.begin(); it != mLeftParens.end(); it++) {
           if (it->first < mLeftParen && it->first >= first) {
              first = it->first;
              second = it->second;
           }
        }
     }
  
     if (first != -1) {
        MoveCursor(first, second);
     }
  }
  
  void NyqTextCtrl::GoNext()
  {
     wxLongToLongHashMap::const_iterator it;
     long first = -1;
     long second = -1;
  
     if (mLeftParen != -1) {
        for (it = mLeftParens.begin(); it != mLeftParens.end(); it++) {
           if (it->first > mLeftParen && (first == -1 || it->first < first)) {
              first = it->first;
              second = it->second;
           }
        }
     }
  
     if (first != -1) {
        MoveCursor(first, second);
     }
  }
  
  void NyqTextCtrl::MoveCursor(long first, long second)
  {
     int pos = GetInsertionPoint();
     int lpos = wxMax(0, pos - 1);
  
     wxString text = GetRange(lpos, pos);
  
     if (text[0] == wxT('(')) {
        SetInsertionPoint(first + 1);
        Colorize(first, second);
     }
     else if (text[0] == wxT(')')) {
        SetInsertionPoint(second + 1);
        Colorize(first, second);
     }
  }
  
  void NyqTextCtrl::Colorize(long left, long right)
  {
     // Hide any previously highlighted parens
     if (mLeftParen >= 0) {
  #if defined(__WXMSW__)
        Freeze(); // Prevents selection flashing on Windows
  #endif
  
        SetStyle(mLeftParen, mLeftParen + 1, mOff);
        SetStyle(mLeftParens[mLeftParen], mLeftParens[mLeftParen] + 1, mOff);
        mLeftParen = -1;
        mRightParen = -1;
  
  #if defined(__WXMSW__)
        Thaw(); // Prevents selection flashing on Windows
  #endif
     }
  
     mLeftParen = left;
     mRightParen = right;
  
     if (mLeftParen != -1) {
        SetStyle(mLeftParen, mLeftParen + 1, mOn);
        SetStyle(mRightParen, mRightParen + 1, mOn);
  
        SetStyle(mLeftParen + 1, mLeftParen + 1, mOff);
        SetStyle(mRightParen + 1, mRightParen + 1, mOff);
     }
  }
  
  void NyqTextCtrl::FindParens()
  {
     wxString text = GetValue();
     bool inquotes = false;
     wxArrayInt stack;
     long len = (long) text.Length();
     long pos;
  
     mLeftParens.clear();
     mRightParens.clear();
  
     for (pos = 0; pos < len; pos++) {
        wxChar c = text[pos];
        switch (c)
        {
           case wxT('"'):
              inquotes = !inquotes;
           break;
  
           case wxT(';'):
              if (!inquotes) {
                 pos = (long)text.find(wxT('\n'), pos);
                 if (pos == (long)wxString::npos) {
                    pos = len;
                 }
              }
           break;
  
           case wxT('#'):
              if (!inquotes) {
                 long ndx = pos + 1;
                 if (ndx < len && text[(int)ndx] == wxT('|')) {
                    // Shamelessly stolen from xlread.c/pcomment()
                    wxChar lastch = -1;
                    int n = 1;
                
                    /* look for the matching delimiter (and handle nesting) */
                    while (n > 0 && ++pos < len) {
                       wxChar ch = text[(int)pos];
                       if (lastch == '|' && ch == '#') {
                          --n;
                          ch = -1;
                       }
                       else if (lastch == '#' && ch == '|') {
                          ++n;
                          ch = -1;
                       }
                       lastch = ch;
                    }
                 }
              }
           break;
  
           case wxT('('):
              if (!inquotes) {
                 stack.Add(pos);
              }
           break;
  
           case wxT(')'):
              if (!inquotes) {
                 if (stack.GetCount() > 0) {
                    int left = stack.Last();
                    stack.RemoveAt(stack.GetCount() - 1);
  
                    mLeftParens[left] = pos;
                    mRightParens[pos] = left;
                 }
              }
           break;
        }
     }
  }
  
  //----------------------------------------------------------------------------
  // NyqRedirector
  //----------------------------------------------------------------------------
  
  NyqRedirector::NyqRedirector(NyqTextCtrl *text)
  :  mText(text)
  {
     mOld = std::cout.rdbuf(this);
  }
  
  NyqRedirector::~NyqRedirector()
  {
     std::cout.flush();
     std::cout.rdbuf(mOld);
     if (s.length() > 0) {
        AppendText();
     }
  }
  
  int NyqRedirector::overflow(int c)
  {
     s += (char)c;
     if (c == '\n') {
        AppendText();
     }
  
     return 0;
  }
  
  void NyqRedirector::AppendText()
  {
     mText->AppendText(wxString(s.c_str(), wxConvISO8859_1));
     s.clear();
  }
  
  //----------------------------------------------------------------------------
  // NyqBench
  //----------------------------------------------------------------------------
  
  enum
  {
     ID_AUTOLOAD = 20000,
  
     ID_AUTOWRAP,
  
     ID_FONT,
     ID_SPLITV,
     ID_SPLITH,
     ID_TOGGLECODE,
     ID_TOGGLEOUTPUT,
     ID_SMALLICONS,
     ID_LARGEICONS,
     ID_MATCH,
     ID_TOP,
     ID_UP,
     ID_PREVIOUS,
     ID_NEXT,
  
     ID_GO,
     ID_STOP,
  
     ID_SCRIPT,
     ID_OUTPUT
  };
  
  BEGIN_EVENT_TABLE(NyqBench, wxFrame)
     EVT_CLOSE(NyqBench::OnClose)
     EVT_MOVE(NyqBench::OnMove)
     EVT_SIZE(NyqBench::OnSize)
  
     EVT_MENU(wxID_NEW, NyqBench::OnNew)
     EVT_MENU(wxID_OPEN, NyqBench::OnOpen)
     EVT_MENU(wxID_SAVE, NyqBench::OnSave)
     EVT_MENU(wxID_SAVEAS, NyqBench::OnSaveAs)
     EVT_MENU(wxID_REVERT_TO_SAVED, NyqBench::OnRevert)
     EVT_MENU(ID_AUTOLOAD, NyqBench::OnAutoLoad)
     EVT_MENU(wxID_CLOSE, NyqBench::OnCloseWindow)
  
     EVT_MENU(wxID_UNDO, NyqBench::OnUndo)
     EVT_MENU(wxID_REDO, NyqBench::OnRedo)
     EVT_MENU(wxID_CUT, NyqBench::OnCut)
     EVT_MENU(wxID_COPY, NyqBench::OnCopy)
     EVT_MENU(wxID_PASTE, NyqBench::OnPaste)
     EVT_MENU(wxID_CLEAR, NyqBench::OnClear)
     EVT_MENU(wxID_SELECTALL, NyqBench::OnSelectAll)
     EVT_MENU(wxID_FIND, NyqBench::OnFind)
     EVT_MENU(ID_MATCH, NyqBench::OnGoMatch)
     EVT_MENU(ID_TOP, NyqBench::OnGoTop)
     EVT_MENU(ID_UP, NyqBench::OnGoUp)
     EVT_MENU(ID_PREVIOUS, NyqBench::OnGoPrev)
     EVT_MENU(ID_NEXT, NyqBench::OnGoNext)
     EVT_MENU(ID_AUTOWRAP, NyqBench::OnAutoWrap)
  
     EVT_MENU(ID_FONT, NyqBench::OnFont)
     EVT_MENU(ID_SPLITV, NyqBench::OnSplitV)
     EVT_MENU(ID_SPLITH, NyqBench::OnSplitH)
     EVT_MENU(ID_TOGGLECODE, NyqBench::OnToggleCode)
     EVT_MENU(ID_TOGGLEOUTPUT, NyqBench::OnToggleOutput)
     EVT_MENU(ID_SMALLICONS, NyqBench::OnSmallIcons)
     EVT_MENU(ID_LARGEICONS, NyqBench::OnLargeIcons)
  
     EVT_MENU(ID_GO, NyqBench::OnGo)
     EVT_MENU(ID_STOP, NyqBench::OnStop)
  
     EVT_MENU(wxID_ABOUT, NyqBench::OnAbout)
  
     EVT_FIND(wxID_ANY, NyqBench::OnFindDialog)
     EVT_FIND_NEXT(wxID_ANY, NyqBench::OnFindDialog)
     EVT_FIND_REPLACE(wxID_ANY, NyqBench::OnFindDialog)
     EVT_FIND_REPLACE_ALL(wxID_ANY, NyqBench::OnFindDialog)
     EVT_FIND_CLOSE(wxID_ANY, NyqBench::OnFindDialog)
  
     EVT_TEXT(ID_SCRIPT, NyqBench::OnTextUpdate)
  
     EVT_UPDATE_UI(wxID_SAVE, NyqBench::OnMenuUpdate)
     EVT_UPDATE_UI(wxID_SAVEAS, NyqBench::OnMenuUpdate)
     EVT_UPDATE_UI(wxID_REVERT_TO_SAVED, NyqBench::OnMenuUpdate)
  
     EVT_UPDATE_UI(wxID_UNDO, NyqBench::OnUndoUpdate)
     EVT_UPDATE_UI(wxID_REDO, NyqBench::OnRedoUpdate)
     EVT_UPDATE_UI(wxID_CUT, NyqBench::OnCutUpdate)
     EVT_UPDATE_UI(wxID_COPY, NyqBench::OnCopyUpdate)
     EVT_UPDATE_UI(wxID_PASTE, NyqBench::OnPasteUpdate)
     EVT_UPDATE_UI(wxID_CLEAR, NyqBench::OnClearUpdate)
  
     EVT_UPDATE_UI(ID_SPLITH, NyqBench::OnViewUpdate)
     EVT_UPDATE_UI(ID_SPLITV, NyqBench::OnViewUpdate)
     EVT_UPDATE_UI(ID_TOGGLECODE, NyqBench::OnViewUpdate)
     EVT_UPDATE_UI(ID_TOGGLEOUTPUT, NyqBench::OnViewUpdate)
  
     EVT_UPDATE_UI(ID_GO, NyqBench::OnRunUpdate)
  
     EVT_UPDATE_UI(ID_SCRIPT, NyqBench::OnScriptUpdate)
     EVT_UPDATE_UI(ID_OUTPUT, NyqBench::OnOutputUpdate)
  END_EVENT_TABLE()
  
  /*static*/ NyqBench *NyqBench::GetBench()
  {
     if (gBench == nullptr)
     {
        gBench = new NyqBench(NULL);
     }
  
     return gBench;
  }
  
  NyqBench::NyqBench(wxWindow * parent)
  :  wxFrame(NULL,
             wxID_ANY,
             wxEmptyString,
             wxDefaultPosition,
             wxDefaultSize,
             wxDEFAULT_FRAME_STYLE |
             wxMINIMIZE_BOX |
             wxMAXIMIZE_BOX |
             wxRESIZE_BORDER)
  {
     mFindDlg = NULL;
     mRunning = false;
     mScriptBox = NULL;
     mOutputBox = NULL;
     mScript = NULL;
     mOutput = NULL;
  
     mPath = gPrefs->Read(wxT("NyqBench/Path"), wxEmptyString);
     mAutoLoad = (gPrefs->Read(wxT("NyqBench/AutoLoad"), 0L) != 0);
     mAutoWrap = (gPrefs->Read(wxT("NyqBench/AutoWrap"), true) != 0);
     mLargeIcons = (gPrefs->Read(wxT("NyqBench/LargeIcons"), 0L) != 0);
     mSplitMode = gPrefs->Read(wxT("NyqBench/SplitMode"), wxSPLIT_VERTICAL);
     mShowCode = (gPrefs->Read(wxT("NyqBench/ShowScript"), true) != 0);
     mShowOutput = (gPrefs->Read(wxT("NyqBench/ShowOutput"), true) != 0);
  
     SetIcon(wxICON(AudacityLogo));
     SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
     ShuttleGui S(this, eIsCreating);
     PopulateOrExchange(S);
     wxMenuBar *bar = new wxMenuBar();
  
     wxMenu *menu = new wxMenu();
     menu->Append(wxID_NEW, wxT("&New\tCtrl+N"));
     menu->Append(wxID_OPEN, wxT("&Open...\tCtrl+O"));
     menu->Append(wxID_SAVE, wxT("&Save...\tCtrl+S"));
     menu->Append(wxID_SAVEAS, wxT("Save &As...\tCtrl+Shift+S"));
     menu->AppendSeparator();
     menu->Append(wxID_REVERT_TO_SAVED, _T("&Revert to Saved"));
     menu->AppendSeparator();
     menu->AppendCheckItem(ID_AUTOLOAD, _T("Auto &Load Last File"))->Check(mAutoLoad);
     menu->AppendSeparator();
     menu->Append(wxID_CLOSE, wxT("&Close Window\tCtrl+W"));
     bar->Append(menu, wxT("&File"));
  
     menu = new wxMenu();
     menu->Append(wxID_UNDO, _("&Undo\tCtrl+Z"));
     menu->Append(wxID_REDO, _("&Redo\tCtrl+Y"));
     menu->AppendSeparator();
     menu->Append(wxID_CUT, _("Cu&t\tCtrl+X"));
     menu->Append(wxID_COPY, _("&Copy\tCtrl+C"));
     menu->Append(wxID_PASTE, _("&Paste\tCtrl+V"));
     menu->Append(wxID_CLEAR, _("Cle&ar\tCtrl+L"));
     menu->AppendSeparator();
     menu->Append(wxID_SELECTALL, _("Select A&ll\tCtrl+A"));
     menu->AppendSeparator();
     menu->Append(wxID_FIND, _("&Find...\tCtrl+F"));
     menu->AppendSeparator();
     wxMenu *sub = new wxMenu();
     sub->Append(ID_MATCH, _("&Matching Paren\tF8"));
     sub->Append(ID_TOP, _("&Top S-expr\tF9"));
     sub->Append(ID_UP, _("&Higher S-expr\tF10"));
     sub->Append(ID_PREVIOUS, _("&Previous S-expr\tF11"));
     sub->Append(ID_NEXT, _("&Next S-expr\tF12"));
     menu->AppendSubMenu(sub, _("&Go to"));
     menu->AppendSeparator();
     menu->AppendCheckItem(ID_AUTOWRAP, _T("Auto &Wrap"))->Check(mAutoWrap);
     bar->Append(menu, wxT("&Edit"));
  
     menu = new wxMenu();
     menu->Append(ID_FONT, _("Select &Font..."));
     menu->AppendSeparator();
     menu->Append(ID_SPLITV, _("Split &Vertically"));
     menu->Append(ID_SPLITH, _("Split &Horizontally"));
     menu->AppendSeparator();
     menu->AppendCheckItem(ID_TOGGLECODE, _("Show S&cript"));
     menu->AppendCheckItem(ID_TOGGLEOUTPUT, _("Show &Output"));
     menu->AppendSeparator();
     sub = new wxMenu();
     sub->AppendRadioItem(ID_LARGEICONS, _("&Large Icons"));
     sub->AppendRadioItem(ID_SMALLICONS, _("&Small Icons"));
     menu->AppendSubMenu(sub, _("Toolbar"));
     bar->Append(menu, wxT("&View"));
  
     menu = new wxMenu();
     menu->Append(ID_GO, _("&Go\tF5"));
     menu->Append(ID_STOP, _("&Stop\tF6"));
     bar->Append(menu, wxT("&Run"));
  
  #if defined(__WXMAC__)
     menu->Append(wxID_ABOUT, _("&About"));
  #else
     menu = new wxMenu();
     menu->Append(wxID_ABOUT, _("&About"));
     bar->Append(menu, wxT("Help"));
  #endif
  
     SetMenuBar(bar);
  
     RecreateToolbar(mLargeIcons);
  
     wxRect r;
     r.SetX(gPrefs->Read(wxT("NyqBench/Window/X"), -1));
     r.SetY(gPrefs->Read(wxT("NyqBench/Window/Y"), -1));
     r.SetWidth(gPrefs->Read(wxT("NyqBench/Window/Width"), -1));
     r.SetHeight(gPrefs->Read(wxT("NyqBench/Window/Height"), -1));
     if (r == wxRect(-1, -1, -1, -1)) {
        Center();
     }
     else {
        SetSize(r);
     }
  
     bool maximized = false;
     gPrefs->Read(wxT("NyqBench/Window/Maximized"), maximized);
     if (maximized) {
        Maximize();
     }
  
     long sashpos;
     sashpos = gPrefs->Read(wxT("NyqBench/SplitX"), 0l);
     if (sashpos > 0) {
        mSplitter->SetSashPosition(sashpos);
     }
  
     wxString dflt = wxSystemSettings::GetFont(wxSYS_SYSTEM_FONT).GetNativeFontInfoDesc();
     wxString desc;
     wxTextAttr attr;
  
     desc = gPrefs->Read(wxT("NyqBench/ScriptFont"), dflt);
     mScriptFont.SetNativeFontInfo(desc);
  #if defined(__WXMSW__)
     // Force SYSTEM encoding to prevent conversion to Unicode in wxTextCtrl::DoWriteText().
     // I don't know if this is something I'm doing wrong, but I'll have to look at this
     // later if I get bored.
     mScriptFont.SetEncoding(wxFONTENCODING_SYSTEM);
  #endif
     attr.SetFont(mScriptFont);
     mScript->SetDefaultStyle(attr);
  
     desc = gPrefs->Read(wxT("NyqBench/OutputFont"), dflt);
     mOutputFont.SetNativeFontInfo(desc);
  #if defined(__WXMSW__)
     // Force SYSTEM encoding to prevent conversion to Unicode in wxTextCtrl::DoWriteText().
     // I don't know if this is something I'm doing wrong, but I'll have to look at this
     // later if I get bored.
     mOutputFont.SetEncoding(wxFONTENCODING_SYSTEM);
  #endif
     attr.SetFont(mOutputFont);
     mOutput->SetDefaultStyle(attr);
  
     if (mAutoLoad && !mPath.GetFullPath().IsEmpty()) {
        LoadFile();
     }
  
     SetWindowTitle();
  }
  
  NyqBench::~NyqBench()
  {
  }
  
  void NyqBench::SavePrefs()
  {
     gPrefs->Write(wxT("NyqBench/Window/Maximized"), IsMaximized());
     if (!IsMaximized()) {
        wxRect r = GetRect();
  
  #if !defined(__WXMAC__)
        if (IsIconized()) {
           r = mLastSize;
        }
  #endif
  
        gPrefs->Write(wxT("NyqBench/Window/X"), r.GetX());
        gPrefs->Write(wxT("NyqBench/Window/Y"), r.GetY());
        gPrefs->Write(wxT("NyqBench/Window/Width"), r.GetWidth());
        gPrefs->Write(wxT("NyqBench/Window/Height"), r.GetHeight());
     }
  
     gPrefs->Write(wxT("NyqBench/AutoLoad"), mAutoLoad);
     gPrefs->Write(wxT("NyqBench/AutoWrap"), mAutoWrap);
     gPrefs->Write(wxT("NyqBench/ScriptFont"), mScriptFont.GetNativeFontInfoDesc());
     gPrefs->Write(wxT("NyqBench/OutputFont"), mOutputFont.GetNativeFontInfoDesc());
     gPrefs->Write(wxT("NyqBench/LargeIcons"), mLargeIcons);
     gPrefs->Write(wxT("NyqBench/SplitX"), mSplitter->IsSplit() ? mSplitter->GetSashPosition() : 0);
     gPrefs->Write(wxT("NyqBench/SplitMode"), mSplitter->IsSplit() ? mSplitter->GetSplitMode() : 0);
     gPrefs->Write(wxT("NyqBench/ShowCode"), mScript->IsShown());
     gPrefs->Write(wxT("NyqBench/ShowOutput"), mOutput->IsShown());
  }
  
  void NyqBench::PopulateOrExchange(ShuttleGui & S)
  {
     S.StartHorizontalLay(wxEXPAND, true);
     {
        wxPanel *scriptp;
        wxPanel *outputp;
        wxStaticBoxSizer *scripts;
        wxStaticBoxSizer *outputs;
        wxBoxSizer *bs;
  
        mSplitter = new wxSplitterWindow(this,
                                         wxID_ANY,
                                         wxDefaultPosition,
                                         wxDefaultSize,
                                         wxSP_LIVE_UPDATE |
                                         wxSP_3DSASH |
                                         wxSP_NOBORDER);
        
        scriptp = new wxPanel(mSplitter,
                              wxID_ANY,
                              wxDefaultPosition,
                              wxDefaultSize,
                              wxNO_FULL_REPAINT_ON_RESIZE |
                              wxCLIP_CHILDREN);
        bs = new wxBoxSizer(wxVERTICAL);
        scriptp->SetSizer(bs);
  
        mScriptBox = new wxStaticBox(scriptp,
                                     wxID_ANY,
                                     _("Script"));
  
        scripts = new wxStaticBoxSizer(mScriptBox,
                                       wxVERTICAL);
        bs->Add(scripts, true, wxEXPAND);
  
        mScript = new NyqTextCtrl(scriptp,
                                  ID_SCRIPT,
                                  wxEmptyString,
                                  wxDefaultPosition,
                                  wxDefaultSize,
                                  wxTE_RICH2 | wxTE_RICH |
                                  (mAutoWrap ? wxTE_BESTWRAP : wxTE_DONTWRAP) |
                                  wxTE_NOHIDESEL |
                                  wxTE_MULTILINE);
        scripts->Add(mScript, true, wxEXPAND);
  
        outputp = new wxPanel(mSplitter,
                              wxID_ANY,
                              wxDefaultPosition,
                              wxDefaultSize,
                              wxNO_FULL_REPAINT_ON_RESIZE |
                              wxCLIP_CHILDREN);
        bs = new wxBoxSizer(wxVERTICAL);
        outputp->SetSizer(bs);
  
        mOutputBox = new wxStaticBox(outputp,
                                     wxID_ANY,
                                     _("Output"));
        outputs = new wxStaticBoxSizer(mOutputBox,
                                       wxVERTICAL);
        bs->Add(outputs, true, wxEXPAND);
  
        mOutput = new NyqTextCtrl(outputp,
                                  ID_OUTPUT,
                                  wxEmptyString,
                                  wxDefaultPosition,
                                  wxDefaultSize,
                                  wxTE_READONLY |
  #if !defined(__WXMAC__)
  // I could not get the bloody horizontal scroll bar to appear on 
  // wxMac, so we can't use wxTE_DONTWRAP as you can't easily scroll
  // left and right.
                                  wxTE_DONTWRAP |
  #endif
                                  wxTE_NOHIDESEL |
                                  wxTE_MULTILINE);
        outputs->Add(mOutput, true, wxEXPAND);
  
        switch (mSplitMode)
        {
           case wxSPLIT_VERTICAL:
              mSplitter->SplitVertically(scriptp, outputp, 300);
           break;
  
           case wxSPLIT_HORIZONTAL:
              mSplitter->SplitHorizontally(scriptp, outputp, 300);
           break;
  
           default:
              mSplitter->Initialize((mShowCode ? scriptp : outputp));
           break;
        }
  
        mSplitter->SetMinimumPaneSize(50);
  
        S.AddSpace(5, 1);
        S.Prop(true);
        S.Position(wxEXPAND).AddWindow(mSplitter);
        S.AddSpace(5, 1);
  
        mSplitter->SetMinSize(wxSize(600, 400));
     }
     S.EndHorizontalLay();
  
     S.AddSpace(1, 5);
  
     return;
  }
  
  void NyqBench::OnClose(wxCloseEvent & e)
  {
     if (!Validate()) {
        e.Veto();
     }
     else {
        Show(false);
     }
  }
  
  void NyqBench::OnMove(wxMoveEvent & e)
  {
     e.Skip();
     if (!IsIconized() && !IsMaximized()) {
        mLastSize.SetPosition(e.GetPosition());
     }
  }
  
  void NyqBench::OnSize(wxSizeEvent & e)
  {
     e.Skip();
     if (!IsIconized() && !IsMaximized()) {
        mLastSize.SetSize(e.GetSize());
     }
  }
  
  void NyqBench::OnCloseWindow(wxCommandEvent & e)
  {
     Close();
  }
  
  void NyqBench::OnNew(wxCommandEvent & e)
  {
     if (!Validate()) {
        return;
     }
  
     mPath.SetFullName(wxEmptyString);
  
     while (mScript->CanUndo()) {
        mScript->Undo();
     }
  
     mScript->Clear();
     mScript->DiscardEdits();
  
     SetWindowTitle();
  }
  
  void NyqBench::OnOpen(wxCommandEvent & e)
  {
     if (mScript->IsModified() && !Validate()) {
        return;
     }
  
     wxFileDialog dlog(this,
                       _("Load Nyquist script"),
                       mPath.GetPath(),
                       wxEmptyString,
                       _("Nyquist scripts (*.ny)|*.ny|Lisp scripts (*.lsp)|*.lsp|All files|*"),
                       wxFD_OPEN | wxRESIZE_BORDER);
   
     if (dlog.ShowModal() != wxID_OK) {
        return;
     }
  
     mPath = dlog.GetPath();
     gPrefs->Write(wxT("NyqBench/Path"), mPath.GetFullPath());
  
     LoadFile();
  
     SetWindowTitle();
  }
  
  void NyqBench::OnSave(wxCommandEvent & e)
  {
     if (mScript->GetLastPosition() == 0) {
        return;
     }
   
     if (mPath.GetFullPath().IsEmpty()) {
        OnSaveAs(e);
        return;
     }
  
     if (!mScript->SaveFile(mPath.GetFullPath()))
     {
        AudacityMessageBox(XO("Script was not saved."),
                     XO("Warning"),
                     wxICON_EXCLAMATION,
                     this);
        return;
     }
  }
  
  void NyqBench::OnSaveAs(wxCommandEvent & e)
  {
     if (mScript->GetLastPosition() == 0) {
        return;
     }
   
     wxFileDialog dlog(this,
                       _("Save Nyquist script"),
                       mPath.GetFullPath(),
                       wxEmptyString,
                       _("Nyquist scripts (*.ny)|*.ny|Lisp scripts (*.lsp)|*.lsp|All files|*"),
                       wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER);
   
     if (dlog.ShowModal() != wxID_OK) {
        return;
     }
  
     mPath = dlog.GetPath();
     gPrefs->Write(wxT("NyqBench/Path"), mPath.GetFullPath());
  
     if (!mScript->SaveFile(mPath.GetFullPath()))
     {
        AudacityMessageBox(XO("Script was not saved."),
                     XO("Warning"),
                     wxICON_EXCLAMATION,
                     this);
        return;
     }
  
     SetWindowTitle();
  }
  
  void NyqBench::OnAutoLoad(wxCommandEvent & e)
  {
     mAutoLoad = e.IsChecked();
  }
  
  void NyqBench::OnRevert(wxCommandEvent & e)
  {
     if (mPath.GetFullPath().IsEmpty()) {
        return;
     }
  
     if (!Validate()) {
        return;
     }
  
     LoadFile();
  }
  
  void NyqBench::OnUndo(wxCommandEvent & e)
  {
     (FindFocus() == mScript ? mScript : mOutput)->Undo();
  }
  
  void NyqBench::OnRedo(wxCommandEvent & e)
  {
     (FindFocus() == mScript ? mScript : mOutput)->Redo();
  }
  
  void NyqBench::OnCut(wxCommandEvent & e)
  {
     (FindFocus() == mScript ? mScript : mOutput)->Cut();
  }
  
  void NyqBench::OnCopy(wxCommandEvent & e)
  {
     (FindFocus() == mScript ? mScript : mOutput)->Copy();
  }
  
  void NyqBench::OnPaste(wxCommandEvent & e)
  {
     (FindFocus() == mScript ? mScript : mOutput)->Paste();
  }
  
  void NyqBench::OnClear(wxCommandEvent & e)
  {
     (FindFocus() == mScript ? mScript : mOutput)->Clear();
  }
  
  void NyqBench::OnSelectAll(wxCommandEvent & e)
  {
     (FindFocus() == mScript ? mScript : mOutput)->SetSelection(-1, -1);
  }
  
  void NyqBench::OnFind(wxCommandEvent & e)
  {
     if (mFindDlg ) {
         delete mFindDlg;
         mFindDlg = NULL;
     }
     else {
        NyqTextCtrl *w = (NyqTextCtrl *) FindFocus();
        if (w == mScript || w == mOutput) {
           mFindText = w;
  
           int flags = 0;
  
           flags |= (gPrefs->Read(wxT("NyqBench/Find/Down"), 0L) ? wxFR_DOWN : 0);
           flags |= (gPrefs->Read(wxT("NyqBench/Find/Word"), 0L) ? wxFR_WHOLEWORD : 0);
           flags |= (gPrefs->Read(wxT("NyqBench/Find/Case"), 0L) ? wxFR_MATCHCASE : 0);
  
           mFindData.SetFlags(flags);
  
           mFindDlg = new wxFindReplaceDialog(this,
                                              &mFindData,
                                              _("Find dialog"),
                                              wxFR_NOWHOLEWORD);
           mFindDlg->Show(true);
        }
     }
  }
  
  void NyqBench::OnGoMatch(wxCommandEvent & e)
  {
     mScript->GoMatch();
  }
  
  void NyqBench::OnGoTop(wxCommandEvent & e)
  {
     mScript->GoTop();
  }
  
  void NyqBench::OnGoUp(wxCommandEvent & e)
  {
     mScript->GoUp();
  }
  
  void NyqBench::OnGoPrev(wxCommandEvent & e)
  {
     mScript->GoPrev();
  }
  
  void NyqBench::OnGoNext(wxCommandEvent & e)
  {
     mScript->GoNext();
  }
  
  void NyqBench::OnAutoWrap(wxCommandEvent & e)
  {
     mAutoWrap = e.IsChecked();
  
     wxWindow *parent = mScript->GetParent();
     wxString text = mScript->GetValue();
     bool focused = wxWindow::FindFocus() == mScript;
     long pos = mScript->GetInsertionPoint();
     long from;
     long to;
     mScript->GetSelection(&from, &to);
  
     wxSizer *s = mScript->GetContainingSizer();
     s->Detach(mScript);
     delete mScript;
  
     mScript = new NyqTextCtrl(parent,
                               ID_SCRIPT,
                               wxEmptyString,
                               wxDefaultPosition,
                               wxDefaultSize,
                               (mAutoWrap ? wxTE_BESTWRAP : wxTE_DONTWRAP) |
                               wxTE_NOHIDESEL | wxTE_RICH2 |
                               wxTE_MULTILINE);
     s->Add(mScript, 1, wxEXPAND);
     s->Layout();
  
     mScript->ChangeValue(text);
     mScript->SetInsertionPoint(pos);
     mScript->SetSelection(from, to);
  
     if (focused) {
        mScript->SetFocus();
     }
  }
  
  void NyqBench::OnFont(wxCommandEvent & e)
  {
     wxWindow *w = FindFocus();
     wxFontData data;
     wxFontDialog dlg(this, data);
  
     if (w != mScript &&  w != mOutput) {
        return;
     }
  
     data.SetInitialFont(w == mScript ? mScriptFont : mOutputFont);
  
     if (dlg.ShowModal() == wxID_OK) {
        wxFontData retData = dlg.GetFontData();
        wxFont font = retData.GetChosenFont();
        wxTextAttr attr;
        attr.SetFont(font);
  
        if (w == mScript) {
           mScriptFont = font;
        }
        else {
           mOutputFont = font;
        }
  
        ((wxTextCtrl *)w)->SetDefaultStyle(attr);
        ((wxTextCtrl *)w)->SetStyle(0, ((wxTextCtrl *)w)->GetLastPosition(), attr);
        w->Refresh();
     }
  }
  
  void NyqBench::OnSplitV(wxCommandEvent & e)
  {
     if (mSplitter->IsSplit()) {
        mSplitter->Unsplit();
     }
  
     mSplitter->SplitVertically(mScript->GetParent(), mOutput->GetParent());
  }
  
  void NyqBench::OnSplitH(wxCommandEvent & e)
  {
     if (mSplitter->IsSplit()) {
        mSplitter->Unsplit();
     }
  
     mSplitter->SplitHorizontally(mScript->GetParent(), mOutput->GetParent());
  }
  
  void NyqBench::OnToggleCode(wxCommandEvent & e)
  {
     if (e.IsChecked()) {
        if (mSplitter->IsSplit()) {
           // Should never happen
           return;
        }
  
        if (mSplitMode == wxSPLIT_VERTICAL) {
           mSplitter->SplitVertically(mScript->GetParent(), mOutput->GetParent());
        }
        else {
           mSplitter->SplitHorizontally(mScript->GetParent(), mOutput->GetParent());
        }
     }
     else {
        if (!mSplitter->IsSplit()) {
           // Should never happen
           return;
        }
  
        mSplitMode = mSplitter->GetSplitMode();
        mSplitter->Unsplit(mScript->GetParent());
     }
  }
  
  void NyqBench::OnToggleOutput(wxCommandEvent & e)
  {
     if (e.IsChecked()) {
        if (mSplitter->IsSplit()) {
           // Should never happen
           return;
        }
  
        if (mSplitMode == wxSPLIT_VERTICAL) {
           mSplitter->SplitVertically(mScript->GetParent(), mOutput->GetParent());
        }
        else {
           mSplitter->SplitHorizontally(mScript->GetParent(), mOutput->GetParent());
        }
     }
     else {
        if (!mSplitter->IsSplit()) {
           // Should never happen
           return;
        }
  
        mSplitMode = mSplitter->GetSplitMode();
        mSplitter->Unsplit(mOutput->GetParent());
     }
  }
  
  void NyqBench::OnSmallIcons(wxCommandEvent & e)
  {
     RecreateToolbar(false);
  }
  
  void NyqBench::OnLargeIcons(wxCommandEvent & e)
  {
     RecreateToolbar(true);
  }
  
  void NyqBench::OnGo(wxCommandEvent & e)
  {
     // No need to delete...EffectManager will do it
     mEffect = new NyquistEffect(wxT("Nyquist Effect Workbench"));
     const PluginID & ID = EffectManager::Get().RegisterEffect(mEffect);
  
     mEffect->SetCommand(mScript->GetValue());
     mEffect->RedirectOutput();
  
     AudacityProject *p = GetActiveProject();
     wxASSERT(p != NULL);
  
     if (p) {
        wxWindowDisabler disable(this);
        NyqRedirector redir((NyqTextCtrl *)mOutput);
  
        mRunning = true;
        UpdateWindowUI();
  
        EffectUI::DoEffect(ID, CommandContext(*p), 0);
  
        mRunning = false;
        UpdateWindowUI();
     }
  
     Raise();
  
     EffectManager::Get().UnregisterEffect(ID);
  }
  
  void NyqBench::OnStop(wxCommandEvent & e)
  {
     mRunning = false;
     mEffect->Stop();
  }
  
  void NyqBench::OnAbout(wxCommandEvent & e)
  {
     wxAboutDialogInfo i;
  
     i.AddArtist(_("Harvey Lubin (logo)"));
     i.AddArtist(_("Tango Icon Gallery (toolbar icons)"));
     i.AddDeveloper(_("Leland Lucius"));
     i.SetCopyright(_("(C) 2009 by Leland Lucius"));
     i.SetDescription(_("External Audacity module which provides a simple IDE for writing effects."));
     i.SetName(_("Nyquist Effect Workbench"));
     i.SetVersion(__TDATE__);
  
     wxAboutBox(i);
  }
  
  void NyqBench::OnFindDialog(wxFindDialogEvent & e)
  {
     wxEventType type = e.GetEventType();
  
     if (type == wxEVT_COMMAND_FIND_CLOSE) {
        wxFindReplaceDialog *dlg = e.GetDialog();
  
        dlg->Destroy();
  
        int flags = mFindData.GetFlags();
  
        gPrefs->Write(wxT("NyqBench/Find/Down"), (flags & wxFR_DOWN) != 0);
        gPrefs->Write(wxT("NyqBench/Find/Word"), (flags & wxFR_WHOLEWORD) != 0);
        gPrefs->Write(wxT("NyqBench/Find/Case"), (flags & wxFR_MATCHCASE) != 0);
  
        mFindDlg = NULL;
        mFindText = NULL;
  
        return;
     }
  
     wxString text = mFindText->GetValue();
  
  #if defined(__WXMSW__)
     // We cheat on Windows.  We know that the Windows text control
     // uses CRLF for line endings and if we don't account for that,
     // the selection positions will be off.
     //
     // Not sure why I thought I needed this, but it appears not to
     // be.  Leaving just in case.
     //
     // text.Replace(wxT("\n"), wxT("\r\n"));
  #endif
  
     size_t startpos = mFindText->GetInsertionPoint();
     size_t len = mFindText->GetLastPosition();
     size_t pos;
  
     wxString find = e.GetFindString();
     bool down = (e.GetFlags() & wxFR_DOWN) != 0;
     bool mixed = (e.GetFlags() & wxFR_MATCHCASE) != 0;
  
     if (!mixed) {
        text.MakeUpper();
        find.MakeUpper();
     }
  
     if (down) {
        pos = text.find(find, startpos);
        if (type == wxEVT_COMMAND_FIND_NEXT && pos == startpos && pos < len) {
           pos = text.find(find, startpos + 1);
        }
     }
     else {
        pos = text.rfind(find, startpos);
        if (type == wxEVT_COMMAND_FIND_NEXT && pos == startpos && pos > 0) {
           pos = text.rfind(find, startpos - 1);
        }
     }
  
     if (pos == wxString::npos) {
        AudacityMessageBox(XO("No matches found"),
                     XO("Nyquist Effect Workbench"),
                     wxOK | wxCENTER,
                     e.GetDialog());
  
        return;
     }
  
     mFindText->SetInsertionPoint((long)pos);
  
  #if defined(__WXGTK__)
     // GTK's selection and intertion pointer interact where the insertion
     // pointer winds up after the second parameter, so we reverse them to
     // force the pointer at the beginning of the selection.  Doing so
     // allows reverse find to work properly.
     mFindText->SetSelection((long)(pos + find.Length()), (long)pos);
  #else
     mFindText->SetSelection((long)pos, (long)(pos + find.Length()));
  #endif
  
  #if defined(__WXMAC__)
     // Doing this coaxes the text control to update the selection.  Without
     // it the selection doesn't appear to change if the found string is within
     // the currently displayed text, i.e., no reposition is needed.
     mFindText->Show(false);
     mFindText->Show(true);
  #endif
  }
  
  void NyqBench::OnTextUpdate(wxCommandEvent & e)
  {
     // This really shouldn't be necessary, but Paste()ing doesn't mark the
     // control as dirty...at least on the Mac.
     ((NyqTextCtrl *) e.GetEventObject())->MarkDirty();
  }
  
  void NyqBench::OnMenuUpdate(wxUpdateUIEvent & e)
  {
     if (e.GetId() != wxID_REVERT_TO_SAVED) {
        e.Enable((mScript->GetLastPosition() > 0) || mScript->IsModified());
     }
     else {
        e.Enable(mScript->IsModified());
     }
  }
  
  void NyqBench::OnUndoUpdate(wxUpdateUIEvent & e)
  {
     e.Enable((FindFocus() == mScript ? mScript : mOutput)->CanUndo());
  }
  
  void NyqBench::OnRedoUpdate(wxUpdateUIEvent & e)
  {
     e.Enable((FindFocus() == mScript ? mScript : mOutput)->CanRedo());
  }
  
  void NyqBench::OnCutUpdate(wxUpdateUIEvent & e)
  {
     e.Enable((FindFocus() == mScript ? mScript : mOutput)->CanCut());
  }
  
  void NyqBench::OnCopyUpdate(wxUpdateUIEvent & e)
  {
     e.Enable((FindFocus() == mScript ? mScript : mOutput)->CanCopy());
  }
  
  void NyqBench::OnPasteUpdate(wxUpdateUIEvent & e)
  {
     e.Enable((FindFocus() == mScript ? mScript : mOutput)->CanPaste());
  }
  
  void NyqBench::OnClearUpdate(wxUpdateUIEvent & e)
  {
     e.Enable(FindFocus() == mOutput ? true : false);
  }
  
  void NyqBench::OnViewUpdate(wxUpdateUIEvent & e)
  {
     wxMenuBar *bar = GetMenuBar();
     bar->Enable(ID_SPLITV, !mSplitter->IsSplit() || mSplitter->GetSplitMode() != wxSPLIT_VERTICAL);
     bar->Enable(ID_SPLITH, !mSplitter->IsSplit() || mSplitter->GetSplitMode() != wxSPLIT_HORIZONTAL);
     bar->Check(ID_TOGGLECODE, mScript->GetParent()->IsShown());
     bar->Check(ID_TOGGLEOUTPUT, mOutput->GetParent()->IsShown());
     bar->Check(ID_LARGEICONS, mLargeIcons);
     bar->Check(ID_SMALLICONS, !mLargeIcons);
  }
  
  void NyqBench::OnRunUpdate(wxUpdateUIEvent & e)
  {
     AudacityProject *p = GetActiveProject();
     wxToolBar *tbar = GetToolBar();
     wxMenuBar *mbar = GetMenuBar();
  
     auto gAudioIO = AudioIOBase::Get();
     if (p && gAudioIO->IsBusy()) {
        mbar->Enable(ID_GO, false);
        mbar->Enable(ID_STOP, false);
  
        tbar->EnableTool(ID_GO, false);
        tbar->EnableTool(ID_STOP, false);
     }
     else {
        mbar->Enable(ID_GO, (mScript->GetLastPosition() > 0) && !mRunning);
        mbar->Enable(ID_STOP, (mScript->GetLastPosition() > 0) && mRunning);
  
        tbar->EnableTool(ID_GO, (mScript->GetLastPosition() > 0) && !mRunning);
        tbar->EnableTool(ID_STOP, (mScript->GetLastPosition() > 0) && mRunning);
     }
  }
  
  void NyqBench::OnScriptUpdate(wxUpdateUIEvent & e)
  {
     if (mScriptBox && mScript && FindFocus() == mScript) {
        wxString label = mScriptBox->GetLabel();
        if (label == _("Script")) {
           label += wxT("*");
           mScriptBox->SetLabel(label);
           mOutputBox->SetLabel(_("Output"));
        }
     }
  }
  
  void NyqBench::OnOutputUpdate(wxUpdateUIEvent & e)
  {
     if (mOutputBox && mOutput && FindFocus() == mOutput) {
        wxString label = mOutputBox->GetLabel();
        if (label == _("Output")) {
           label += wxT("*");
           mOutputBox->SetLabel(label);
           mScriptBox->SetLabel(_("Script"));
        }
     }
  }
  
  bool NyqBench::Validate()
  {
     if (mScript->GetLastPosition() > 0 && mScript->IsModified()) {
        int ans;
        ans = AudacityMessageBox(XO("Code has been modified. Are you sure?"),
                           XO("Warning"),
                           wxYES_NO | wxICON_QUESTION,
                           this);
        if (ans == wxNO) {
           return false;
        }
     }
  
     return true;
  }
  
  void NyqBench::SetWindowTitle()
  {
     wxString name = _("Untitled");
  
     if (!mPath.GetFullPath().IsEmpty()) {
        name = mPath.GetFullName();
     }
  
     SetTitle(_("Nyquist Effect Workbench - ") + name);
  }
  
  void NyqBench::RecreateToolbar(bool large)
  {
     mLargeIcons = large;
  
     wxToolBar *tb = GetToolBar();
     if (tb) {
        delete tb;
     }
     tb = CreateToolBar();
  
     wxSize sz;
  
     if (!mLargeIcons) {
        tb->SetToolBitmapSize(wxSize(16, 16));
        mPics[0] = wxBitmap(document_new_small);
        mPics[1] = wxBitmap(document_open_small);
        mPics[2] = wxBitmap(document_save_as_small);
        mPics[3] = wxBitmap(document_save_small);
        mPics[4] = wxBitmap(edit_copy_small);
        mPics[5] = wxBitmap(edit_cut_small);
        mPics[6] = wxBitmap(edit_paste_small);
        mPics[7] = wxBitmap(edit_clear_small);
        mPics[8] = wxBitmap(edit_delete_small);
        mPics[9] = wxBitmap(edit_select_all_small);
        mPics[10] = wxBitmap(edit_undo_small);
        mPics[11] = wxBitmap(edit_redo_small);
        mPics[12] = wxBitmap(edit_find_small);
        mPics[13] = wxBitmap(system_search_small);
        mPics[14] = wxBitmap(go_top_small);
        mPics[15] = wxBitmap(go_up_small);
        mPics[16] = wxBitmap(go_previous_small);
        mPics[17] = wxBitmap(go_next_small);
        mPics[18] = wxBitmap(media_playback_start_small);
        mPics[19] = wxBitmap(media_playback_stop_small);
     }
     else {
        tb->SetToolBitmapSize(wxSize(32, 32));
        mPics[0] = wxBitmap(document_new_large);
        mPics[1] = wxBitmap(document_open_large);
        mPics[2] = wxBitmap(document_save_as_large);
        mPics[3] = wxBitmap(document_save_large);
        mPics[4] = wxBitmap(edit_copy_large);
        mPics[5] = wxBitmap(edit_cut_large);
        mPics[6] = wxBitmap(edit_paste_large);
        mPics[7] = wxBitmap(edit_clear_large);
        mPics[8] = wxBitmap(edit_delete_large);
        mPics[9] = wxBitmap(edit_select_all_large);
        mPics[10] = wxBitmap(edit_undo_large);
        mPics[11] = wxBitmap(edit_redo_large);
        mPics[12] = wxBitmap(edit_find_large);
        mPics[13] = wxBitmap(system_search_large);
        mPics[14] = wxBitmap(go_top_large);
        mPics[15] = wxBitmap(go_up_large);
        mPics[16] = wxBitmap(go_previous_large);
        mPics[17] = wxBitmap(go_next_large);
        mPics[18] = wxBitmap(media_playback_start_large);
        mPics[19] = wxBitmap(media_playback_stop_large);
     }
  
     tb->SetMargins(2, 2);
  
     tb->AddTool(wxID_NEW, _("New"), mPics[0], _("New script"));
     tb->AddTool(wxID_OPEN, _("Open"), mPics[1], _("Open script"));
     tb->AddTool(wxID_SAVE, _("Save"), mPics[2], _("Save script"));
     tb->AddTool(wxID_SAVEAS, _("Save As"), mPics[3], _("Save script as..."));
     tb->AddSeparator();
     tb->AddTool(wxID_COPY, _("Copy"), mPics[4], _("Copy to clipboard"));
     tb->AddTool(wxID_CUT, _("Cut"), mPics[5], _("Cut to clipboard"));
     tb->AddTool(wxID_PASTE, _("Paste"), mPics[6], _("Paste from clipboard"));
     tb->AddTool(wxID_CLEAR, _("Clear"), mPics[7], _("Clear selection"));
     tb->AddTool(wxID_SELECTALL, _("Select All"), mPics[9], _("Select all text"));
     tb->AddSeparator();
     tb->AddTool(wxID_UNDO, _("Undo"), mPics[10], _("Undo last change"));
     tb->AddTool(wxID_REDO, _("Redo"), mPics[11], _("Redo previous change"));
     tb->AddSeparator();
     tb->AddTool(wxID_FIND, _("Find"), mPics[12], _("Find text"));
     tb->AddSeparator();
     tb->AddTool(ID_MATCH, _("Match"), mPics[13], _("Go to matching paren"));
     tb->AddTool(ID_TOP, _("Top"), mPics[14], _("Go to top S-expr"));
     tb->AddTool(ID_UP, _("Up"), mPics[15], _("Go to higher S-expr"));
     tb->AddTool(ID_PREVIOUS, _("Previous"), mPics[16], _("Go to previous S-expr"));
     tb->AddTool(ID_NEXT, _("Next"), mPics[17], _("Go to next S-expr"));
     tb->AddSeparator();
     tb->AddTool(ID_GO, _("Start"), mPics[18], _("Start script"));
     tb->AddTool(ID_STOP, _("Stop"), mPics[19], _("Stop script"));
     
     tb->Realize();
  
     Layout();
     Fit();
     SetMinSize(GetSize());
  }
  
  void NyqBench::LoadFile()
  {
     wxString path = mPath.GetFullPath();
  
     if (path.IsEmpty()) {
        return;
     }
  
     wxFFile f(path);
     if (f.IsOpened()) {
        wxString t;
        if (f.ReadAll(&t)) {
  //#if defined(__WXGTK__) || defined(__WXMAC__)
           t.Replace(wxT("\r\n"), wxT("\n"));
           t.Replace(wxT("\r"), wxT("\n"));
  //#elif defined(__WXMSW__)
  //         t.Replace("\r\n", "\n");
  //#endif
           mScript->SetValue(t);
           mScript->DiscardEdits();
        }
     }
  
  //   mScript->LoadFile(mPath.GetFullPath());
  }
  
  //----------------------------------------------------------------------------
  // Connects Audacity menu item to an action in this dll.
  // Only one action implemented so far.
  //----------------------------------------------------------------------------
  void NyqBench::ShowNyqBench(const CommandContext &)
  {
     Show();
  }
  