1 /**********************************************************************
2 
3   NyqBench.cpp
4 
5   Leland Lucius
6 
7 **********************************************************************/
8 
9 
10 
11 #include <wx/defs.h>
12 
13 #include <wx/aboutdlg.h>
14 #include <wx/filedlg.h>
15 #include <wx/font.h>
16 #include <wx/fontdlg.h>
17 #include <wx/menu.h>
18 #include <wx/msgdlg.h>
19 #include <wx/settings.h>
20 #include <wx/sizer.h>
21 #include <wx/splitter.h>
22 #include <wx/statbox.h>
23 #include <wx/textctrl.h>
24 #include <wx/toolbar.h>
25 
26 #include "ActiveProject.h"
27 #include "AudioIOBase.h"
28 #include "CommonCommandFlags.h"
29 #include "ModuleConstants.h"
30 #include "Prefs.h"
31 #include "Project.h"
32 #include "ShuttleGui.h"
33 #include "effects/EffectManager.h"
34 #include "effects/EffectUI.h"
35 #include "effects/nyquist/Nyquist.h"
36 #include "../images/AudacityLogo.xpm"
37 #include "../../src/commands/CommandContext.h"
38 #include "../../src/commands/CommandManager.h"
39 #include "widgets/AudacityMessageBox.h"
40 
41 #include "NyqBench.h"
42 
43 #include <iostream>
44 #include <ostream>
45 #include <sstream>
46 
47 //
48 // Images are from the Tango Icon Gallery
49 // http://tango.freedesktop.org/Tango_Icon_Gallery
50 //
51 #include "images/document-new-small.xpm"
52 #include "images/document-open-small.xpm"
53 #include "images/document-save-as-small.xpm"
54 #include "images/document-save-small.xpm"
55 #include "images/edit-clear-small.xpm"
56 #include "images/edit-copy-small.xpm"
57 #include "images/edit-cut-small.xpm"
58 #include "images/edit-delete-small.xpm"
59 #include "images/edit-find-small.xpm"
60 #include "images/edit-paste-small.xpm"
61 #include "images/edit-redo-small.xpm"
62 #include "images/edit-select-all-small.xpm"
63 #include "images/edit-undo-small.xpm"
64 #include "images/go-top-small.xpm"
65 #include "images/go-up-small.xpm"
66 #include "images/go-previous-small.xpm"
67 #include "images/go-next-small.xpm"
68 #include "images/system-search-small.xpm"
69 #include "images/media-playback-start-small.xpm"
70 #include "images/media-playback-stop-small.xpm"
71 
72 #include "images/document-new-large.xpm"
73 #include "images/document-open-large.xpm"
74 #include "images/document-save-as-large.xpm"
75 #include "images/document-save-large.xpm"
76 #include "images/edit-clear-large.xpm"
77 #include "images/edit-copy-large.xpm"
78 #include "images/edit-cut-large.xpm"
79 #include "images/edit-delete-large.xpm"
80 #include "images/edit-find-large.xpm"
81 #include "images/edit-paste-large.xpm"
82 #include "images/edit-redo-large.xpm"
83 #include "images/edit-select-all-large.xpm"
84 #include "images/edit-undo-large.xpm"
85 #include "images/go-top-large.xpm"
86 #include "images/go-up-large.xpm"
87 #include "images/go-previous-large.xpm"
88 #include "images/go-next-large.xpm"
89 #include "images/system-search-large.xpm"
90 #include "images/media-playback-start-large.xpm"
91 #include "images/media-playback-stop-large.xpm"
92 
93 /*
94 //#define ModuleDispatchName "ModuleDispatch"
95 See the example in this file.  It has several cases/options in it.
96 */
97 
98 namespace {
findme(AudacityProject &)99 CommandHandlerObject &findme(AudacityProject&)
100 {
101    return *NyqBench::GetBench();
102 }
103 
RegisterMenuItems()104 void RegisterMenuItems()
105 {
106   // Get here only after the module version check passes
107    using namespace MenuTable;
108    static AttachedItem sAttachment{ wxT("Tools"),
109       ( FinderScope( findme ), Section( wxT("NyquistWorkBench"),
110          Command( wxT("NyqBench"), XXO("&Nyquist Workbench..."),
111             static_cast<CommandFunctorPointer>(&NyqBench::ShowNyqBench),
112             AudioIONotBusyFlag())
113       ) )
114    };
115 }
116 }
117 
118 DEFINE_VERSION_CHECK
119 
120 extern "C"
121 {
122    static NyqBench *gBench = NULL;
123 
124    extern int DLL_API ModuleDispatch(ModuleDispatchTypes type);
125    // ModuleDispatch
126    // is called by Audacity to initialize/terminate the module
127    int ModuleDispatch(ModuleDispatchTypes type){
128       switch (type){
129          case ModuleInitialize:
130             RegisterMenuItems();
131             break;
132          case AppQuiting: {
133             //It is perfectly OK for gBench to be NULL.
134             //Can happen if the menu item was never invoked.
135             //wxASSERT(gBench != NULL);
136             if (gBench) {
137                // be sure to do this while gPrefs still exists:
138                gBench->SavePrefs();
139                gBench->Destroy();
140                gBench = NULL;
141             }
142          }
143          break;
144          default:
145          break;
146       }
147       return 1;
148    }
149 };
150 
151 //----------------------------------------------------------------------------
152 // NyqTextCtrl
153 //----------------------------------------------------------------------------
154 
BEGIN_EVENT_TABLE(NyqTextCtrl,wxTextCtrl)155 BEGIN_EVENT_TABLE(NyqTextCtrl, wxTextCtrl)
156 #if defined(__WXMAC__)
157    EVT_KEY_DOWN(NyqTextCtrl::OnKeyDown)
158 #endif
159    EVT_KEY_UP(NyqTextCtrl::OnKeyUp)
160    EVT_CHAR(NyqTextCtrl::OnChar)
161    EVT_UPDATE_UI(wxID_ANY, NyqTextCtrl::OnUpdate)
162 END_EVENT_TABLE()
163 
164 NyqTextCtrl::NyqTextCtrl(wxWindow *parent,
165                          wxWindowID id,
166                          const wxString &value,
167                          const wxPoint & pos,
168                          const wxSize & size,
169                          int style)
170 :  wxTextCtrl(parent, id, value, pos, size, style)
171 {
172    mLastCaretPos = -1;
173    mLeftParen = -1;
174    mRightParen = -1;
175 
176    mOn.SetTextColour(*wxRED);
177    mOff.SetTextColour(*wxBLACK);
178 }
179 
SetFocusFromKbd()180 void NyqTextCtrl::SetFocusFromKbd()
181 {
182 #if defined(__WXMSW__)
183    // We do this to prevent wxMSW from selecting all text when the
184    // user tabs into the text controls.
185    wxWindowBase::SetFocusFromKbd();
186 #else
187    wxTextCtrl::SetFocusFromKbd();
188 #endif
189 }
190 
MarkDirty()191 void NyqTextCtrl::MarkDirty()
192 {
193    wxTextCtrl::MarkDirty();
194    FindParens();
195 }
196 
OnChar(wxKeyEvent & e)197 void NyqTextCtrl::OnChar(wxKeyEvent & e)
198 {
199 	e.Skip();
200 
201    // Hide any previously highlighted parens
202    if (mLeftParen >= 0) {
203 #if defined(__WXMSW__)
204       Freeze(); // Prevents selection flashing on Windows
205 #endif
206 
207       SetStyle(mLeftParen, mLeftParen + 1, mOff);
208       SetStyle(mLeftParens[mLeftParen], mLeftParens[mLeftParen] + 1, mOff);
209       mLeftParen = -1;
210       mRightParen = -1;
211 
212 #if defined(__WXMSW__)
213       Thaw(); // Prevents selection flashing on Windows
214 #endif
215    }
216 }
217 
218 #if defined(__WXMAC__REMOVED_UNTIL_ITS_PROVEN_THAT_IT_IS_STILL_NEEDED)
219 #include <wx/mac/uma.h>
220 
221 // This is hackage to correct a problem on Leopard where the
222 // caret jumps up two lines when the up arrow is pressed and
223 // the caret is at the beginning of the line.
OnKeyDown(wxKeyEvent & e)224 void NyqTextCtrl::OnKeyDown(wxKeyEvent & e)
225 {
226    e.Skip();
227    if (UMAGetSystemVersion() >= 0x1050) {
228       if (e.GetKeyCode() == WXK_UP && e.GetModifiers() == 0) {
229          long x;
230          long y;
231 
232          PositionToXY(GetInsertionPoint(), &x, &y);
233          if (x == 0 && y > 1) {
234             y--;
235             SetInsertionPoint(XYToPosition(x, y) - 1);
236             e.Skip(false);
237          }
238       }
239    }
240 }
241 #endif
242 
OnKeyUp(wxKeyEvent & e)243 void NyqTextCtrl::OnKeyUp(wxKeyEvent & e)
244 {
245    e.Skip();
246 
247    int pos = GetInsertionPoint();
248    int lpos = wxMax(0, pos - 1);
249 
250    wxString text = GetRange(lpos, pos);
251 
252    if (text[0] == wxT('(')) {
253       wxLongToLongHashMap::const_iterator left = mLeftParens.find(lpos);
254       if (left != mLeftParens.end()) {
255          Colorize(lpos, left->second);
256       }
257    }
258    else if (text[0] == wxT(')')) {
259       wxLongToLongHashMap::const_iterator right = mRightParens.find(lpos);
260       if (right != mRightParens.end()) {
261          Colorize(right->second, lpos);
262       }
263    }
264 }
265 
OnUpdate(wxUpdateUIEvent & e)266 void NyqTextCtrl::OnUpdate(wxUpdateUIEvent & e)
267 {
268    int pos = GetInsertionPoint();
269 
270    if (pos != mLastCaretPos) {
271       int lpos = wxMax(0, pos - 1);
272 
273       wxString text = GetRange(lpos, pos);
274       if (text.Length() > 0) {
275          if (text[0] == wxT('(')) {
276             wxLongToLongHashMap::const_iterator left = mLeftParens.find(lpos);
277             if (left != mLeftParens.end()) {
278                Colorize(lpos, left->second);
279             }
280          }
281          else if (text[0] == wxT(')')) {
282             wxLongToLongHashMap::const_iterator right = mRightParens.find(lpos);
283             if (right != mRightParens.end()) {
284                Colorize(right->second, lpos);
285             }
286          }
287       }
288 
289       mLastCaretPos = pos;
290    }
291 }
292 
GoMatch()293 void NyqTextCtrl::GoMatch()
294 {
295    MoveCursor(mRightParen, mLeftParen);
296 }
297 
GoTop()298 void NyqTextCtrl::GoTop()
299 {
300    wxLongToLongHashMap::const_iterator it;
301    long first = -1;
302    long second = -1;
303 
304    if (mLeftParen != -1) {
305       for (it = mLeftParens.begin(); it != mLeftParens.end(); it++) {
306          if (mLeftParen > it->first && mLeftParen < it->second) {
307             if (first == -1 || it->first < first) {
308                first = it->first;
309                second = it->second;
310             }
311          }
312       }
313    }
314 
315    if (first != -1) {
316       MoveCursor(first, second);
317    }
318 }
319 
GoUp()320 void NyqTextCtrl::GoUp()
321 {
322    wxLongToLongHashMap::const_iterator it;
323    long first = -1;
324    long second = -1;
325 
326    if (mLeftParen != -1) {
327       for (it = mLeftParens.begin(); it != mLeftParens.end(); it++) {
328          if (mLeftParen > it->first && mLeftParen < it->second) {
329             if (first == -1 || it->first > first) {
330                first = it->first;
331                second = it->second;
332             }
333          }
334       }
335    }
336 
337    if (first != -1) {
338       MoveCursor(first, second);
339    }
340 }
341 
GoPrev()342 void NyqTextCtrl::GoPrev()
343 {
344    wxLongToLongHashMap::const_iterator it;
345    long first = -1;
346    long second = -1;
347 
348    if (mLeftParen != -1) {
349       for (it = mLeftParens.begin(); it != mLeftParens.end(); it++) {
350          if (it->first < mLeftParen && it->first >= first) {
351             first = it->first;
352             second = it->second;
353          }
354       }
355    }
356 
357    if (first != -1) {
358       MoveCursor(first, second);
359    }
360 }
361 
GoNext()362 void NyqTextCtrl::GoNext()
363 {
364    wxLongToLongHashMap::const_iterator it;
365    long first = -1;
366    long second = -1;
367 
368    if (mLeftParen != -1) {
369       for (it = mLeftParens.begin(); it != mLeftParens.end(); it++) {
370          if (it->first > mLeftParen && (first == -1 || it->first < first)) {
371             first = it->first;
372             second = it->second;
373          }
374       }
375    }
376 
377    if (first != -1) {
378       MoveCursor(first, second);
379    }
380 }
381 
MoveCursor(long first,long second)382 void NyqTextCtrl::MoveCursor(long first, long second)
383 {
384    int pos = GetInsertionPoint();
385    int lpos = wxMax(0, pos - 1);
386 
387    wxString text = GetRange(lpos, pos);
388 
389    if (text[0] == wxT('(')) {
390       SetInsertionPoint(first + 1);
391       Colorize(first, second);
392    }
393    else if (text[0] == wxT(')')) {
394       SetInsertionPoint(second + 1);
395       Colorize(first, second);
396    }
397 }
398 
Colorize(long left,long right)399 void NyqTextCtrl::Colorize(long left, long right)
400 {
401    // Hide any previously highlighted parens
402    if (mLeftParen >= 0) {
403 #if defined(__WXMSW__)
404       Freeze(); // Prevents selection flashing on Windows
405 #endif
406 
407       SetStyle(mLeftParen, mLeftParen + 1, mOff);
408       SetStyle(mLeftParens[mLeftParen], mLeftParens[mLeftParen] + 1, mOff);
409       mLeftParen = -1;
410       mRightParen = -1;
411 
412 #if defined(__WXMSW__)
413       Thaw(); // Prevents selection flashing on Windows
414 #endif
415    }
416 
417    mLeftParen = left;
418    mRightParen = right;
419 
420    if (mLeftParen != -1) {
421       SetStyle(mLeftParen, mLeftParen + 1, mOn);
422       SetStyle(mRightParen, mRightParen + 1, mOn);
423 
424       SetStyle(mLeftParen + 1, mLeftParen + 1, mOff);
425       SetStyle(mRightParen + 1, mRightParen + 1, mOff);
426    }
427 }
428 
FindParens()429 void NyqTextCtrl::FindParens()
430 {
431    wxString text = GetValue();
432    bool inquotes = false;
433    wxArrayInt stack;
434    long len = (long) text.Length();
435    long pos;
436 
437    mLeftParens.clear();
438    mRightParens.clear();
439 
440    for (pos = 0; pos < len; pos++) {
441       wxChar c = text[pos];
442       switch (c)
443       {
444          case wxT('"'):
445             inquotes = !inquotes;
446          break;
447 
448          case wxT(';'):
449             if (!inquotes) {
450                pos = (long)text.find(wxT('\n'), pos);
451                if (pos == (long)wxString::npos) {
452                   pos = len;
453                }
454             }
455          break;
456 
457          case wxT('#'):
458             if (!inquotes) {
459                long ndx = pos + 1;
460                if (ndx < len && text[(int)ndx] == wxT('|')) {
461                   // Shamelessly stolen from xlread.c/pcomment()
462                   wxChar lastch = -1;
463                   int n = 1;
464 
465                   /* look for the matching delimiter (and handle nesting) */
466                   while (n > 0 && ++pos < len) {
467                      wxChar ch = text[(int)pos];
468                      if (lastch == '|' && ch == '#') {
469                         --n;
470                         ch = -1;
471                      }
472                      else if (lastch == '#' && ch == '|') {
473                         ++n;
474                         ch = -1;
475                      }
476                      lastch = ch;
477                   }
478                }
479             }
480          break;
481 
482          case wxT('('):
483             if (!inquotes) {
484                stack.Add(pos);
485             }
486          break;
487 
488          case wxT(')'):
489             if (!inquotes) {
490                if (stack.GetCount() > 0) {
491                   int left = stack.Last();
492                   stack.RemoveAt(stack.GetCount() - 1);
493 
494                   mLeftParens[left] = pos;
495                   mRightParens[pos] = left;
496                }
497             }
498          break;
499       }
500    }
501 }
502 
503 //----------------------------------------------------------------------------
504 // NyqRedirector
505 //----------------------------------------------------------------------------
506 
NyqRedirector(NyqTextCtrl * text)507 NyqRedirector::NyqRedirector(NyqTextCtrl *text)
508 :  mText(text)
509 {
510    mOld = std::cout.rdbuf(this);
511 }
512 
~NyqRedirector()513 NyqRedirector::~NyqRedirector()
514 {
515    std::cout.flush();
516    std::cout.rdbuf(mOld);
517    if (s.length() > 0) {
518       AppendText();
519    }
520 }
521 
overflow(int c)522 int NyqRedirector::overflow(int c)
523 {
524    s += (char)c;
525    if (c == '\n') {
526       AppendText();
527    }
528 
529    return 0;
530 }
531 
AppendText()532 void NyqRedirector::AppendText()
533 {
534    mText->AppendText(wxString(s.c_str(), wxConvISO8859_1));
535    s.clear();
536 }
537 
538 //----------------------------------------------------------------------------
539 // NyqBench
540 //----------------------------------------------------------------------------
541 
542 enum
543 {
544    ID_AUTOLOAD = 20000,
545 
546    ID_AUTOWRAP,
547 
548    ID_FONT,
549    ID_SPLITV,
550    ID_SPLITH,
551    ID_TOGGLECODE,
552    ID_TOGGLEOUTPUT,
553    ID_SMALLICONS,
554    ID_LARGEICONS,
555    ID_MATCH,
556    ID_TOP,
557    ID_UP,
558    ID_PREVIOUS,
559    ID_NEXT,
560 
561    ID_GO,
562    ID_STOP,
563 
564    ID_SCRIPT,
565    ID_OUTPUT
566 };
567 
BEGIN_EVENT_TABLE(NyqBench,wxFrame)568 BEGIN_EVENT_TABLE(NyqBench, wxFrame)
569    EVT_CLOSE(NyqBench::OnClose)
570    EVT_MOVE(NyqBench::OnMove)
571    EVT_SIZE(NyqBench::OnSize)
572 
573    EVT_MENU(wxID_NEW, NyqBench::OnNew)
574    EVT_MENU(wxID_OPEN, NyqBench::OnOpen)
575    EVT_MENU(wxID_SAVE, NyqBench::OnSave)
576    EVT_MENU(wxID_SAVEAS, NyqBench::OnSaveAs)
577    EVT_MENU(wxID_REVERT_TO_SAVED, NyqBench::OnRevert)
578    EVT_MENU(ID_AUTOLOAD, NyqBench::OnAutoLoad)
579    EVT_MENU(wxID_CLOSE, NyqBench::OnCloseWindow)
580 
581    EVT_MENU(wxID_UNDO, NyqBench::OnUndo)
582    EVT_MENU(wxID_REDO, NyqBench::OnRedo)
583    EVT_MENU(wxID_CUT, NyqBench::OnCut)
584    EVT_MENU(wxID_COPY, NyqBench::OnCopy)
585    EVT_MENU(wxID_PASTE, NyqBench::OnPaste)
586    EVT_MENU(wxID_CLEAR, NyqBench::OnClear)
587    EVT_MENU(wxID_SELECTALL, NyqBench::OnSelectAll)
588    EVT_MENU(wxID_FIND, NyqBench::OnFind)
589    EVT_MENU(ID_MATCH, NyqBench::OnGoMatch)
590    EVT_MENU(ID_TOP, NyqBench::OnGoTop)
591    EVT_MENU(ID_UP, NyqBench::OnGoUp)
592    EVT_MENU(ID_PREVIOUS, NyqBench::OnGoPrev)
593    EVT_MENU(ID_NEXT, NyqBench::OnGoNext)
594    EVT_MENU(ID_AUTOWRAP, NyqBench::OnAutoWrap)
595 
596    EVT_MENU(ID_FONT, NyqBench::OnFont)
597    EVT_MENU(ID_SPLITV, NyqBench::OnSplitV)
598    EVT_MENU(ID_SPLITH, NyqBench::OnSplitH)
599    EVT_MENU(ID_TOGGLECODE, NyqBench::OnToggleCode)
600    EVT_MENU(ID_TOGGLEOUTPUT, NyqBench::OnToggleOutput)
601    EVT_MENU(ID_SMALLICONS, NyqBench::OnSmallIcons)
602    EVT_MENU(ID_LARGEICONS, NyqBench::OnLargeIcons)
603 
604    EVT_MENU(ID_GO, NyqBench::OnGo)
605    EVT_MENU(ID_STOP, NyqBench::OnStop)
606 
607    EVT_MENU(wxID_ABOUT, NyqBench::OnAbout)
608 
609    EVT_FIND(wxID_ANY, NyqBench::OnFindDialog)
610    EVT_FIND_NEXT(wxID_ANY, NyqBench::OnFindDialog)
611    EVT_FIND_REPLACE(wxID_ANY, NyqBench::OnFindDialog)
612    EVT_FIND_REPLACE_ALL(wxID_ANY, NyqBench::OnFindDialog)
613    EVT_FIND_CLOSE(wxID_ANY, NyqBench::OnFindDialog)
614 
615    EVT_TEXT(ID_SCRIPT, NyqBench::OnTextUpdate)
616 
617    EVT_UPDATE_UI(wxID_SAVE, NyqBench::OnMenuUpdate)
618    EVT_UPDATE_UI(wxID_SAVEAS, NyqBench::OnMenuUpdate)
619    EVT_UPDATE_UI(wxID_REVERT_TO_SAVED, NyqBench::OnMenuUpdate)
620 
621    EVT_UPDATE_UI(wxID_UNDO, NyqBench::OnUndoUpdate)
622    EVT_UPDATE_UI(wxID_REDO, NyqBench::OnRedoUpdate)
623    EVT_UPDATE_UI(wxID_CUT, NyqBench::OnCutUpdate)
624    EVT_UPDATE_UI(wxID_COPY, NyqBench::OnCopyUpdate)
625    EVT_UPDATE_UI(wxID_PASTE, NyqBench::OnPasteUpdate)
626    EVT_UPDATE_UI(wxID_CLEAR, NyqBench::OnClearUpdate)
627 
628    EVT_UPDATE_UI(ID_SPLITH, NyqBench::OnViewUpdate)
629    EVT_UPDATE_UI(ID_SPLITV, NyqBench::OnViewUpdate)
630    EVT_UPDATE_UI(ID_TOGGLECODE, NyqBench::OnViewUpdate)
631    EVT_UPDATE_UI(ID_TOGGLEOUTPUT, NyqBench::OnViewUpdate)
632 
633    EVT_UPDATE_UI(ID_GO, NyqBench::OnRunUpdate)
634 
635    EVT_UPDATE_UI(ID_SCRIPT, NyqBench::OnScriptUpdate)
636    EVT_UPDATE_UI(ID_OUTPUT, NyqBench::OnOutputUpdate)
637 END_EVENT_TABLE()
638 
639 /*static*/ NyqBench *NyqBench::GetBench()
640 {
641    if (gBench == nullptr)
642    {
643       gBench = new NyqBench(NULL);
644    }
645 
646    return gBench;
647 }
648 
NyqBench(wxWindow * parent)649 NyqBench::NyqBench(wxWindow * parent)
650 :  wxFrame(NULL,
651            wxID_ANY,
652            wxEmptyString,
653            wxDefaultPosition,
654            wxDefaultSize,
655            wxDEFAULT_FRAME_STYLE |
656            wxMINIMIZE_BOX |
657            wxMAXIMIZE_BOX |
658            wxRESIZE_BORDER)
659 {
660    mFindDlg = NULL;
661    mRunning = false;
662    mScriptBox = NULL;
663    mOutputBox = NULL;
664    mScript = NULL;
665    mOutput = NULL;
666 
667    mPath = gPrefs->Read(wxT("NyqBench/Path"), wxEmptyString);
668    mAutoLoad = (gPrefs->Read(wxT("NyqBench/AutoLoad"), 0L) != 0);
669    mAutoWrap = (gPrefs->Read(wxT("NyqBench/AutoWrap"), true) != 0);
670    mLargeIcons = (gPrefs->Read(wxT("NyqBench/LargeIcons"), 0L) != 0);
671    mSplitMode = gPrefs->Read(wxT("NyqBench/SplitMode"), wxSPLIT_VERTICAL);
672    mShowCode = (gPrefs->Read(wxT("NyqBench/ShowScript"), true) != 0);
673    mShowOutput = (gPrefs->Read(wxT("NyqBench/ShowOutput"), true) != 0);
674 
675    SetIcon(wxICON(AudacityLogo));
676    SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
677    ShuttleGui S(this, eIsCreating);
678    PopulateOrExchange(S);
679    wxMenuBar *bar = new wxMenuBar();
680 
681    wxMenu *menu = new wxMenu();
682    menu->Append(wxID_NEW, wxT("&New\tCtrl+N"));
683    menu->Append(wxID_OPEN, wxT("&Open...\tCtrl+O"));
684    menu->Append(wxID_SAVE, wxT("&Save...\tCtrl+S"));
685    menu->Append(wxID_SAVEAS, wxT("Save &As...\tCtrl+Shift+S"));
686    menu->AppendSeparator();
687    menu->Append(wxID_REVERT_TO_SAVED, _T("&Revert to Saved"));
688    menu->AppendSeparator();
689    menu->AppendCheckItem(ID_AUTOLOAD, _T("Auto &Load Last File"))->Check(mAutoLoad);
690    menu->AppendSeparator();
691    menu->Append(wxID_CLOSE, wxT("&Close Window\tCtrl+W"));
692    bar->Append(menu, wxT("&File"));
693 
694    menu = new wxMenu();
695    menu->Append(wxID_UNDO, _("&Undo\tCtrl+Z"));
696    menu->Append(wxID_REDO, _("&Redo\tCtrl+Y"));
697    menu->AppendSeparator();
698    menu->Append(wxID_CUT, _("Cu&t\tCtrl+X"));
699    menu->Append(wxID_COPY, _("&Copy\tCtrl+C"));
700    menu->Append(wxID_PASTE, _("&Paste\tCtrl+V"));
701    menu->Append(wxID_CLEAR, _("Cle&ar\tCtrl+L"));
702    menu->AppendSeparator();
703    menu->Append(wxID_SELECTALL, _("Select A&ll\tCtrl+A"));
704    menu->AppendSeparator();
705    menu->Append(wxID_FIND, _("&Find...\tCtrl+F"));
706    menu->AppendSeparator();
707    wxMenu *sub = new wxMenu();
708    sub->Append(ID_MATCH, _("&Matching Paren\tF8"));
709    sub->Append(ID_TOP, _("&Top S-expr\tF9"));
710    sub->Append(ID_UP, _("&Higher S-expr\tF10"));
711    sub->Append(ID_PREVIOUS, _("&Previous S-expr\tF11"));
712    sub->Append(ID_NEXT, _("&Next S-expr\tF12"));
713    menu->AppendSubMenu(sub, _("&Go to"));
714    menu->AppendSeparator();
715    menu->AppendCheckItem(ID_AUTOWRAP, _T("Auto &Wrap"))->Check(mAutoWrap);
716    bar->Append(menu, wxT("&Edit"));
717 
718    menu = new wxMenu();
719    menu->Append(ID_FONT, _("Select &Font..."));
720    menu->AppendSeparator();
721    menu->Append(ID_SPLITV, _("Split &Vertically"));
722    menu->Append(ID_SPLITH, _("Split &Horizontally"));
723    menu->AppendSeparator();
724    menu->AppendCheckItem(ID_TOGGLECODE, _("Show S&cript"));
725    menu->AppendCheckItem(ID_TOGGLEOUTPUT, _("Show &Output"));
726    menu->AppendSeparator();
727    sub = new wxMenu();
728    sub->AppendRadioItem(ID_LARGEICONS, _("&Large Icons"));
729    sub->AppendRadioItem(ID_SMALLICONS, _("&Small Icons"));
730    menu->AppendSubMenu(sub, _("Toolbar"));
731    bar->Append(menu, wxT("&View"));
732 
733    menu = new wxMenu();
734    menu->Append(ID_GO, _("&Go\tF5"));
735    menu->Append(ID_STOP, _("&Stop\tF6"));
736    bar->Append(menu, wxT("&Run"));
737 
738 #if defined(__WXMAC__)
739    menu->Append(wxID_ABOUT, _("&About"));
740 #else
741    menu = new wxMenu();
742    menu->Append(wxID_ABOUT, _("&About"));
743    bar->Append(menu, wxT("Help"));
744 #endif
745 
746    SetMenuBar(bar);
747 
748    RecreateToolbar(mLargeIcons);
749 
750    wxRect r;
751    r.SetX(gPrefs->Read(wxT("NyqBench/Window/X"), -1));
752    r.SetY(gPrefs->Read(wxT("NyqBench/Window/Y"), -1));
753    r.SetWidth(gPrefs->Read(wxT("NyqBench/Window/Width"), -1));
754    r.SetHeight(gPrefs->Read(wxT("NyqBench/Window/Height"), -1));
755    if (r == wxRect(-1, -1, -1, -1)) {
756       Center();
757    }
758    else {
759       SetSize(r);
760    }
761 
762    bool maximized = false;
763    gPrefs->Read(wxT("NyqBench/Window/Maximized"), maximized);
764    if (maximized) {
765       Maximize();
766    }
767 
768    long sashpos;
769    sashpos = gPrefs->Read(wxT("NyqBench/SplitX"), 0l);
770    if (sashpos > 0) {
771       mSplitter->SetSashPosition(sashpos);
772    }
773 
774    wxString dflt = wxSystemSettings::GetFont(wxSYS_SYSTEM_FONT).GetNativeFontInfoDesc();
775    wxString desc;
776    wxTextAttr attr;
777 
778    desc = gPrefs->Read(wxT("NyqBench/ScriptFont"), dflt);
779    mScriptFont.SetNativeFontInfo(desc);
780 #if defined(__WXMSW__)
781    // Force SYSTEM encoding to prevent conversion to Unicode in wxTextCtrl::DoWriteText().
782    // I don't know if this is something I'm doing wrong, but I'll have to look at this
783    // later if I get bored.
784    mScriptFont.SetEncoding(wxFONTENCODING_SYSTEM);
785 #endif
786    attr.SetFont(mScriptFont);
787    mScript->SetDefaultStyle(attr);
788 
789    desc = gPrefs->Read(wxT("NyqBench/OutputFont"), dflt);
790    mOutputFont.SetNativeFontInfo(desc);
791 #if defined(__WXMSW__)
792    // Force SYSTEM encoding to prevent conversion to Unicode in wxTextCtrl::DoWriteText().
793    // I don't know if this is something I'm doing wrong, but I'll have to look at this
794    // later if I get bored.
795    mOutputFont.SetEncoding(wxFONTENCODING_SYSTEM);
796 #endif
797    attr.SetFont(mOutputFont);
798    mOutput->SetDefaultStyle(attr);
799 
800    if (mAutoLoad && !mPath.GetFullPath().IsEmpty()) {
801       LoadFile();
802    }
803 
804    SetWindowTitle();
805 }
806 
~NyqBench()807 NyqBench::~NyqBench()
808 {
809 }
810 
SavePrefs()811 void NyqBench::SavePrefs()
812 {
813    gPrefs->Write(wxT("NyqBench/Window/Maximized"), IsMaximized());
814    if (!IsMaximized()) {
815       wxRect r = GetRect();
816 
817 #if !defined(__WXMAC__)
818       if (IsIconized()) {
819          r = mLastSize;
820       }
821 #endif
822 
823       gPrefs->Write(wxT("NyqBench/Window/X"), r.GetX());
824       gPrefs->Write(wxT("NyqBench/Window/Y"), r.GetY());
825       gPrefs->Write(wxT("NyqBench/Window/Width"), r.GetWidth());
826       gPrefs->Write(wxT("NyqBench/Window/Height"), r.GetHeight());
827    }
828 
829    gPrefs->Write(wxT("NyqBench/AutoLoad"), mAutoLoad);
830    gPrefs->Write(wxT("NyqBench/AutoWrap"), mAutoWrap);
831    gPrefs->Write(wxT("NyqBench/ScriptFont"), mScriptFont.GetNativeFontInfoDesc());
832    gPrefs->Write(wxT("NyqBench/OutputFont"), mOutputFont.GetNativeFontInfoDesc());
833    gPrefs->Write(wxT("NyqBench/LargeIcons"), mLargeIcons);
834    gPrefs->Write(wxT("NyqBench/SplitX"), mSplitter->IsSplit() ? mSplitter->GetSashPosition() : 0);
835    gPrefs->Write(wxT("NyqBench/SplitMode"), mSplitter->IsSplit() ? mSplitter->GetSplitMode() : 0);
836    gPrefs->Write(wxT("NyqBench/ShowCode"), mScript->IsShown());
837    gPrefs->Write(wxT("NyqBench/ShowOutput"), mOutput->IsShown());
838 }
839 
PopulateOrExchange(ShuttleGui & S)840 void NyqBench::PopulateOrExchange(ShuttleGui & S)
841 {
842    S.StartHorizontalLay(wxEXPAND, true);
843    {
844       wxPanel *scriptp;
845       wxPanel *outputp;
846       wxStaticBoxSizer *scripts;
847       wxStaticBoxSizer *outputs;
848       wxBoxSizer *bs;
849 
850       mSplitter = new wxSplitterWindow(this,
851                                        wxID_ANY,
852                                        wxDefaultPosition,
853                                        wxDefaultSize,
854                                        wxSP_LIVE_UPDATE |
855                                        wxSP_3DSASH |
856                                        wxSP_NOBORDER);
857 
858       scriptp = new wxPanel(mSplitter,
859                             wxID_ANY,
860                             wxDefaultPosition,
861                             wxDefaultSize,
862                             wxNO_FULL_REPAINT_ON_RESIZE |
863                             wxCLIP_CHILDREN);
864       bs = new wxBoxSizer(wxVERTICAL);
865       scriptp->SetSizer(bs);
866 
867       mScriptBox = new wxStaticBox(scriptp,
868                                    wxID_ANY,
869                                    _("Script"));
870 
871       scripts = new wxStaticBoxSizer(mScriptBox,
872                                      wxVERTICAL);
873       bs->Add(scripts, true, wxEXPAND);
874 
875       mScript = new NyqTextCtrl(scriptp,
876                                 ID_SCRIPT,
877                                 wxEmptyString,
878                                 wxDefaultPosition,
879                                 wxDefaultSize,
880                                 wxTE_RICH2 | wxTE_RICH |
881                                 (mAutoWrap ? wxTE_BESTWRAP : wxTE_DONTWRAP) |
882                                 wxTE_NOHIDESEL |
883                                 wxTE_MULTILINE);
884       scripts->Add(mScript, true, wxEXPAND);
885 
886       outputp = new wxPanel(mSplitter,
887                             wxID_ANY,
888                             wxDefaultPosition,
889                             wxDefaultSize,
890                             wxNO_FULL_REPAINT_ON_RESIZE |
891                             wxCLIP_CHILDREN);
892       bs = new wxBoxSizer(wxVERTICAL);
893       outputp->SetSizer(bs);
894 
895       mOutputBox = new wxStaticBox(outputp,
896                                    wxID_ANY,
897                                    _("Output"));
898       outputs = new wxStaticBoxSizer(mOutputBox,
899                                      wxVERTICAL);
900       bs->Add(outputs, true, wxEXPAND);
901 
902       mOutput = new NyqTextCtrl(outputp,
903                                 ID_OUTPUT,
904                                 wxEmptyString,
905                                 wxDefaultPosition,
906                                 wxDefaultSize,
907                                 wxTE_READONLY |
908 #if !defined(__WXMAC__)
909 // I could not get the bloody horizontal scroll bar to appear on
910 // wxMac, so we can't use wxTE_DONTWRAP as you can't easily scroll
911 // left and right.
912                                 wxTE_DONTWRAP |
913 #endif
914                                 wxTE_NOHIDESEL |
915                                 wxTE_MULTILINE);
916       outputs->Add(mOutput, true, wxEXPAND);
917 
918       switch (mSplitMode)
919       {
920          case wxSPLIT_VERTICAL:
921             mSplitter->SplitVertically(scriptp, outputp, 300);
922          break;
923 
924          case wxSPLIT_HORIZONTAL:
925             mSplitter->SplitHorizontally(scriptp, outputp, 300);
926          break;
927 
928          default:
929             mSplitter->Initialize((mShowCode ? scriptp : outputp));
930          break;
931       }
932 
933       mSplitter->SetMinimumPaneSize(50);
934 
935       S.AddSpace(5, 1);
936       S.Prop(true);
937       S.Position(wxEXPAND).AddWindow(mSplitter);
938       S.AddSpace(5, 1);
939 
940       mSplitter->SetMinSize(wxSize(600, 400));
941    }
942    S.EndHorizontalLay();
943 
944    S.AddSpace(1, 5);
945 
946    return;
947 }
948 
OnClose(wxCloseEvent & e)949 void NyqBench::OnClose(wxCloseEvent & e)
950 {
951    if (!Validate()) {
952       e.Veto();
953    }
954    else {
955       Show(false);
956    }
957 }
958 
OnMove(wxMoveEvent & e)959 void NyqBench::OnMove(wxMoveEvent & e)
960 {
961    e.Skip();
962    if (!IsIconized() && !IsMaximized()) {
963       mLastSize.SetPosition(e.GetPosition());
964    }
965 }
966 
OnSize(wxSizeEvent & e)967 void NyqBench::OnSize(wxSizeEvent & e)
968 {
969    e.Skip();
970    if (!IsIconized() && !IsMaximized()) {
971       mLastSize.SetSize(e.GetSize());
972    }
973 }
974 
OnCloseWindow(wxCommandEvent & e)975 void NyqBench::OnCloseWindow(wxCommandEvent & e)
976 {
977    Close();
978 }
979 
OnNew(wxCommandEvent & e)980 void NyqBench::OnNew(wxCommandEvent & e)
981 {
982    if (!Validate()) {
983       return;
984    }
985 
986    mPath.SetFullName(wxEmptyString);
987 
988    while (mScript->CanUndo()) {
989       mScript->Undo();
990    }
991 
992    mScript->Clear();
993    mScript->DiscardEdits();
994 
995    SetWindowTitle();
996 }
997 
OnOpen(wxCommandEvent & e)998 void NyqBench::OnOpen(wxCommandEvent & e)
999 {
1000    if (mScript->IsModified() && !Validate()) {
1001       return;
1002    }
1003 
1004    wxFileDialog dlog(this,
1005                      _("Load Nyquist script"),
1006                      mPath.GetPath(),
1007                      wxEmptyString,
1008                      _("Nyquist scripts (*.ny)|*.ny|Lisp scripts (*.lsp)|*.lsp|All files|*"),
1009                      wxFD_OPEN | wxRESIZE_BORDER);
1010 
1011    if (dlog.ShowModal() != wxID_OK) {
1012       return;
1013    }
1014 
1015    mPath = dlog.GetPath();
1016    gPrefs->Write(wxT("NyqBench/Path"), mPath.GetFullPath());
1017 
1018    LoadFile();
1019 
1020    SetWindowTitle();
1021 }
1022 
OnSave(wxCommandEvent & e)1023 void NyqBench::OnSave(wxCommandEvent & e)
1024 {
1025    if (mScript->GetLastPosition() == 0) {
1026       return;
1027    }
1028 
1029    if (mPath.GetFullPath().IsEmpty()) {
1030       OnSaveAs(e);
1031       return;
1032    }
1033 
1034    if (!mScript->SaveFile(mPath.GetFullPath()))
1035    {
1036       AudacityMessageBox(XO("Script was not saved."),
1037                    XO("Warning"),
1038                    wxICON_EXCLAMATION,
1039                    this);
1040       return;
1041    }
1042 }
1043 
OnSaveAs(wxCommandEvent & e)1044 void NyqBench::OnSaveAs(wxCommandEvent & e)
1045 {
1046    if (mScript->GetLastPosition() == 0) {
1047       return;
1048    }
1049 
1050    wxFileDialog dlog(this,
1051                      _("Save Nyquist script"),
1052                      mPath.GetFullPath(),
1053                      wxEmptyString,
1054                      _("Nyquist scripts (*.ny)|*.ny|Lisp scripts (*.lsp)|*.lsp|All files|*"),
1055                      wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER);
1056 
1057    if (dlog.ShowModal() != wxID_OK) {
1058       return;
1059    }
1060 
1061    mPath = dlog.GetPath();
1062    gPrefs->Write(wxT("NyqBench/Path"), mPath.GetFullPath());
1063 
1064    if (!mScript->SaveFile(mPath.GetFullPath()))
1065    {
1066       AudacityMessageBox(XO("Script was not saved."),
1067                    XO("Warning"),
1068                    wxICON_EXCLAMATION,
1069                    this);
1070       return;
1071    }
1072 
1073    SetWindowTitle();
1074 }
1075 
OnAutoLoad(wxCommandEvent & e)1076 void NyqBench::OnAutoLoad(wxCommandEvent & e)
1077 {
1078    mAutoLoad = e.IsChecked();
1079 }
1080 
OnRevert(wxCommandEvent & e)1081 void NyqBench::OnRevert(wxCommandEvent & e)
1082 {
1083    if (mPath.GetFullPath().IsEmpty()) {
1084       return;
1085    }
1086 
1087    if (!Validate()) {
1088       return;
1089    }
1090 
1091    LoadFile();
1092 }
1093 
OnUndo(wxCommandEvent & e)1094 void NyqBench::OnUndo(wxCommandEvent & e)
1095 {
1096    (FindFocus() == mScript ? mScript : mOutput)->Undo();
1097 }
1098 
OnRedo(wxCommandEvent & e)1099 void NyqBench::OnRedo(wxCommandEvent & e)
1100 {
1101    (FindFocus() == mScript ? mScript : mOutput)->Redo();
1102 }
1103 
OnCut(wxCommandEvent & e)1104 void NyqBench::OnCut(wxCommandEvent & e)
1105 {
1106    (FindFocus() == mScript ? mScript : mOutput)->Cut();
1107 }
1108 
OnCopy(wxCommandEvent & e)1109 void NyqBench::OnCopy(wxCommandEvent & e)
1110 {
1111    (FindFocus() == mScript ? mScript : mOutput)->Copy();
1112 }
1113 
OnPaste(wxCommandEvent & e)1114 void NyqBench::OnPaste(wxCommandEvent & e)
1115 {
1116    (FindFocus() == mScript ? mScript : mOutput)->Paste();
1117 }
1118 
OnClear(wxCommandEvent & e)1119 void NyqBench::OnClear(wxCommandEvent & e)
1120 {
1121    (FindFocus() == mScript ? mScript : mOutput)->Clear();
1122 }
1123 
OnSelectAll(wxCommandEvent & e)1124 void NyqBench::OnSelectAll(wxCommandEvent & e)
1125 {
1126    (FindFocus() == mScript ? mScript : mOutput)->SetSelection(-1, -1);
1127 }
1128 
OnFind(wxCommandEvent & e)1129 void NyqBench::OnFind(wxCommandEvent & e)
1130 {
1131    if (mFindDlg ) {
1132        delete mFindDlg;
1133        mFindDlg = NULL;
1134    }
1135    else {
1136       NyqTextCtrl *w = (NyqTextCtrl *) FindFocus();
1137       if (w == mScript || w == mOutput) {
1138          mFindText = w;
1139 
1140          int flags = 0;
1141 
1142          flags |= (gPrefs->Read(wxT("NyqBench/Find/Down"), 0L) ? wxFR_DOWN : 0);
1143          flags |= (gPrefs->Read(wxT("NyqBench/Find/Word"), 0L) ? wxFR_WHOLEWORD : 0);
1144          flags |= (gPrefs->Read(wxT("NyqBench/Find/Case"), 0L) ? wxFR_MATCHCASE : 0);
1145 
1146          mFindData.SetFlags(flags);
1147 
1148          mFindDlg = new wxFindReplaceDialog(this,
1149                                             &mFindData,
1150                                             _("Find dialog"),
1151                                             wxFR_NOWHOLEWORD);
1152          mFindDlg->Show(true);
1153       }
1154    }
1155 }
1156 
OnGoMatch(wxCommandEvent & e)1157 void NyqBench::OnGoMatch(wxCommandEvent & e)
1158 {
1159    mScript->GoMatch();
1160 }
1161 
OnGoTop(wxCommandEvent & e)1162 void NyqBench::OnGoTop(wxCommandEvent & e)
1163 {
1164    mScript->GoTop();
1165 }
1166 
OnGoUp(wxCommandEvent & e)1167 void NyqBench::OnGoUp(wxCommandEvent & e)
1168 {
1169    mScript->GoUp();
1170 }
1171 
OnGoPrev(wxCommandEvent & e)1172 void NyqBench::OnGoPrev(wxCommandEvent & e)
1173 {
1174    mScript->GoPrev();
1175 }
1176 
OnGoNext(wxCommandEvent & e)1177 void NyqBench::OnGoNext(wxCommandEvent & e)
1178 {
1179    mScript->GoNext();
1180 }
1181 
OnAutoWrap(wxCommandEvent & e)1182 void NyqBench::OnAutoWrap(wxCommandEvent & e)
1183 {
1184    mAutoWrap = e.IsChecked();
1185 
1186    wxWindow *parent = mScript->GetParent();
1187    wxString text = mScript->GetValue();
1188    bool focused = wxWindow::FindFocus() == mScript;
1189    long pos = mScript->GetInsertionPoint();
1190    long from;
1191    long to;
1192    mScript->GetSelection(&from, &to);
1193 
1194    wxSizer *s = mScript->GetContainingSizer();
1195    s->Detach(mScript);
1196    delete mScript;
1197 
1198    mScript = new NyqTextCtrl(parent,
1199                              ID_SCRIPT,
1200                              wxEmptyString,
1201                              wxDefaultPosition,
1202                              wxDefaultSize,
1203                              (mAutoWrap ? wxTE_BESTWRAP : wxTE_DONTWRAP) |
1204                              wxTE_NOHIDESEL | wxTE_RICH2 |
1205                              wxTE_MULTILINE);
1206    s->Add(mScript, 1, wxEXPAND);
1207    s->Layout();
1208 
1209    mScript->ChangeValue(text);
1210    mScript->SetInsertionPoint(pos);
1211    mScript->SetSelection(from, to);
1212 
1213    if (focused) {
1214       mScript->SetFocus();
1215    }
1216 }
1217 
OnFont(wxCommandEvent & e)1218 void NyqBench::OnFont(wxCommandEvent & e)
1219 {
1220    wxWindow *w = FindFocus();
1221    wxFontData data;
1222    wxFontDialog dlg(this, data);
1223 
1224    if (w != mScript &&  w != mOutput) {
1225       return;
1226    }
1227 
1228    data.SetInitialFont(w == mScript ? mScriptFont : mOutputFont);
1229 
1230    if (dlg.ShowModal() == wxID_OK) {
1231       wxFontData retData = dlg.GetFontData();
1232       wxFont font = retData.GetChosenFont();
1233       wxTextAttr attr;
1234       attr.SetFont(font);
1235 
1236       if (w == mScript) {
1237          mScriptFont = font;
1238       }
1239       else {
1240          mOutputFont = font;
1241       }
1242 
1243       ((wxTextCtrl *)w)->SetDefaultStyle(attr);
1244       ((wxTextCtrl *)w)->SetStyle(0, ((wxTextCtrl *)w)->GetLastPosition(), attr);
1245       w->Refresh();
1246    }
1247 }
1248 
OnSplitV(wxCommandEvent & e)1249 void NyqBench::OnSplitV(wxCommandEvent & e)
1250 {
1251    if (mSplitter->IsSplit()) {
1252       mSplitter->Unsplit();
1253    }
1254 
1255    mSplitter->SplitVertically(mScript->GetParent(), mOutput->GetParent());
1256 }
1257 
OnSplitH(wxCommandEvent & e)1258 void NyqBench::OnSplitH(wxCommandEvent & e)
1259 {
1260    if (mSplitter->IsSplit()) {
1261       mSplitter->Unsplit();
1262    }
1263 
1264    mSplitter->SplitHorizontally(mScript->GetParent(), mOutput->GetParent());
1265 }
1266 
OnToggleCode(wxCommandEvent & e)1267 void NyqBench::OnToggleCode(wxCommandEvent & e)
1268 {
1269    if (e.IsChecked()) {
1270       if (mSplitter->IsSplit()) {
1271          // Should never happen
1272          return;
1273       }
1274 
1275       if (mSplitMode == wxSPLIT_VERTICAL) {
1276          mSplitter->SplitVertically(mScript->GetParent(), mOutput->GetParent());
1277       }
1278       else {
1279          mSplitter->SplitHorizontally(mScript->GetParent(), mOutput->GetParent());
1280       }
1281    }
1282    else {
1283       if (!mSplitter->IsSplit()) {
1284          // Should never happen
1285          return;
1286       }
1287 
1288       mSplitMode = mSplitter->GetSplitMode();
1289       mSplitter->Unsplit(mScript->GetParent());
1290    }
1291 }
1292 
OnToggleOutput(wxCommandEvent & e)1293 void NyqBench::OnToggleOutput(wxCommandEvent & e)
1294 {
1295    if (e.IsChecked()) {
1296       if (mSplitter->IsSplit()) {
1297          // Should never happen
1298          return;
1299       }
1300 
1301       if (mSplitMode == wxSPLIT_VERTICAL) {
1302          mSplitter->SplitVertically(mScript->GetParent(), mOutput->GetParent());
1303       }
1304       else {
1305          mSplitter->SplitHorizontally(mScript->GetParent(), mOutput->GetParent());
1306       }
1307    }
1308    else {
1309       if (!mSplitter->IsSplit()) {
1310          // Should never happen
1311          return;
1312       }
1313 
1314       mSplitMode = mSplitter->GetSplitMode();
1315       mSplitter->Unsplit(mOutput->GetParent());
1316    }
1317 }
1318 
OnSmallIcons(wxCommandEvent & e)1319 void NyqBench::OnSmallIcons(wxCommandEvent & e)
1320 {
1321    RecreateToolbar(false);
1322 }
1323 
OnLargeIcons(wxCommandEvent & e)1324 void NyqBench::OnLargeIcons(wxCommandEvent & e)
1325 {
1326    RecreateToolbar(true);
1327 }
1328 
OnGo(wxCommandEvent & e)1329 void NyqBench::OnGo(wxCommandEvent & e)
1330 {
1331    auto pEffect =
1332       std::make_unique<NyquistEffect>(L"Nyquist Effect Workbench");
1333    mEffect = pEffect.get();
1334    const PluginID & ID =
1335       EffectManager::Get().RegisterEffect(std::move(pEffect));
1336 
1337    mEffect->SetCommand(mScript->GetValue());
1338    mEffect->RedirectOutput();
1339 
1340    auto p = GetActiveProject().lock();
1341    wxASSERT(p);
1342 
1343    if (p) {
1344       wxWindowDisabler disable(this);
1345       NyqRedirector redir((NyqTextCtrl *)mOutput);
1346 
1347       mRunning = true;
1348       UpdateWindowUI();
1349 
1350       EffectUI::DoEffect(ID, CommandContext(*p), 0);
1351 
1352       mRunning = false;
1353       UpdateWindowUI();
1354    }
1355 
1356    Raise();
1357 
1358    EffectManager::Get().UnregisterEffect(ID);
1359 }
1360 
OnStop(wxCommandEvent & e)1361 void NyqBench::OnStop(wxCommandEvent & e)
1362 {
1363    mRunning = false;
1364    mEffect->Stop();
1365 }
1366 
OnAbout(wxCommandEvent & e)1367 void NyqBench::OnAbout(wxCommandEvent & e)
1368 {
1369    wxAboutDialogInfo i;
1370 
1371    i.AddArtist(_("Harvey Lubin (logo)"));
1372    i.AddArtist(_("Tango Icon Gallery (toolbar icons)"));
1373    i.AddDeveloper(_("Leland Lucius"));
1374    i.SetCopyright(_("(C) 2009 by Leland Lucius"));
1375    i.SetDescription(_("External Audacity module which provides a simple IDE for writing effects."));
1376    i.SetName(_("Nyquist Effect Workbench"));
1377    i.SetVersion(__TDATE__);
1378 
1379    wxAboutBox(i);
1380 }
1381 
OnFindDialog(wxFindDialogEvent & e)1382 void NyqBench::OnFindDialog(wxFindDialogEvent & e)
1383 {
1384    wxEventType type = e.GetEventType();
1385 
1386    if (type == wxEVT_COMMAND_FIND_CLOSE) {
1387       wxFindReplaceDialog *dlg = e.GetDialog();
1388 
1389       dlg->Destroy();
1390 
1391       int flags = mFindData.GetFlags();
1392 
1393       gPrefs->Write(wxT("NyqBench/Find/Down"), (flags & wxFR_DOWN) != 0);
1394       gPrefs->Write(wxT("NyqBench/Find/Word"), (flags & wxFR_WHOLEWORD) != 0);
1395       gPrefs->Write(wxT("NyqBench/Find/Case"), (flags & wxFR_MATCHCASE) != 0);
1396 
1397       mFindDlg = NULL;
1398       mFindText = NULL;
1399 
1400       return;
1401    }
1402 
1403    wxString text = mFindText->GetValue();
1404 
1405 #if defined(__WXMSW__)
1406    // We cheat on Windows.  We know that the Windows text control
1407    // uses CRLF for line endings and if we don't account for that,
1408    // the selection positions will be off.
1409    //
1410    // Not sure why I thought I needed this, but it appears not to
1411    // be.  Leaving just in case.
1412    //
1413    // text.Replace(wxT("\n"), wxT("\r\n"));
1414 #endif
1415 
1416    size_t startpos = mFindText->GetInsertionPoint();
1417    size_t len = mFindText->GetLastPosition();
1418    size_t pos;
1419 
1420    wxString find = e.GetFindString();
1421    bool down = (e.GetFlags() & wxFR_DOWN) != 0;
1422    bool mixed = (e.GetFlags() & wxFR_MATCHCASE) != 0;
1423 
1424    if (!mixed) {
1425       text.MakeUpper();
1426       find.MakeUpper();
1427    }
1428 
1429    if (down) {
1430       pos = text.find(find, startpos);
1431       if (type == wxEVT_COMMAND_FIND_NEXT && pos == startpos && pos < len) {
1432          pos = text.find(find, startpos + 1);
1433       }
1434    }
1435    else {
1436       pos = text.rfind(find, startpos);
1437       if (type == wxEVT_COMMAND_FIND_NEXT && pos == startpos && pos > 0) {
1438          pos = text.rfind(find, startpos - 1);
1439       }
1440    }
1441 
1442    if (pos == wxString::npos) {
1443       AudacityMessageBox(XO("No matches found"),
1444                    XO("Nyquist Effect Workbench"),
1445                    wxOK | wxCENTER,
1446                    e.GetDialog());
1447 
1448       return;
1449    }
1450 
1451    mFindText->SetInsertionPoint((long)pos);
1452 
1453 #if defined(__WXGTK__)
1454    // GTK's selection and intertion pointer interact where the insertion
1455    // pointer winds up after the second parameter, so we reverse them to
1456    // force the pointer at the beginning of the selection.  Doing so
1457    // allows reverse find to work properly.
1458    mFindText->SetSelection((long)(pos + find.Length()), (long)pos);
1459 #else
1460    mFindText->SetSelection((long)pos, (long)(pos + find.Length()));
1461 #endif
1462 
1463 #if defined(__WXMAC__)
1464    // Doing this coaxes the text control to update the selection.  Without
1465    // it the selection doesn't appear to change if the found string is within
1466    // the currently displayed text, i.e., no reposition is needed.
1467    mFindText->Show(false);
1468    mFindText->Show(true);
1469 #endif
1470 }
1471 
OnTextUpdate(wxCommandEvent & e)1472 void NyqBench::OnTextUpdate(wxCommandEvent & e)
1473 {
1474    // This really shouldn't be necessary, but Paste()ing doesn't mark the
1475    // control as dirty...at least on the Mac.
1476    ((NyqTextCtrl *) e.GetEventObject())->MarkDirty();
1477 }
1478 
OnMenuUpdate(wxUpdateUIEvent & e)1479 void NyqBench::OnMenuUpdate(wxUpdateUIEvent & e)
1480 {
1481    if (e.GetId() != wxID_REVERT_TO_SAVED) {
1482       e.Enable((mScript->GetLastPosition() > 0) || mScript->IsModified());
1483    }
1484    else {
1485       e.Enable(mScript->IsModified());
1486    }
1487 }
1488 
OnUndoUpdate(wxUpdateUIEvent & e)1489 void NyqBench::OnUndoUpdate(wxUpdateUIEvent & e)
1490 {
1491    e.Enable((FindFocus() == mScript ? mScript : mOutput)->CanUndo());
1492 }
1493 
OnRedoUpdate(wxUpdateUIEvent & e)1494 void NyqBench::OnRedoUpdate(wxUpdateUIEvent & e)
1495 {
1496    e.Enable((FindFocus() == mScript ? mScript : mOutput)->CanRedo());
1497 }
1498 
OnCutUpdate(wxUpdateUIEvent & e)1499 void NyqBench::OnCutUpdate(wxUpdateUIEvent & e)
1500 {
1501    e.Enable((FindFocus() == mScript ? mScript : mOutput)->CanCut());
1502 }
1503 
OnCopyUpdate(wxUpdateUIEvent & e)1504 void NyqBench::OnCopyUpdate(wxUpdateUIEvent & e)
1505 {
1506    e.Enable((FindFocus() == mScript ? mScript : mOutput)->CanCopy());
1507 }
1508 
OnPasteUpdate(wxUpdateUIEvent & e)1509 void NyqBench::OnPasteUpdate(wxUpdateUIEvent & e)
1510 {
1511    e.Enable((FindFocus() == mScript ? mScript : mOutput)->CanPaste());
1512 }
1513 
OnClearUpdate(wxUpdateUIEvent & e)1514 void NyqBench::OnClearUpdate(wxUpdateUIEvent & e)
1515 {
1516    e.Enable(FindFocus() == mOutput ? true : false);
1517 }
1518 
OnViewUpdate(wxUpdateUIEvent & e)1519 void NyqBench::OnViewUpdate(wxUpdateUIEvent & e)
1520 {
1521    wxMenuBar *bar = GetMenuBar();
1522    bar->Enable(ID_SPLITV, !mSplitter->IsSplit() || mSplitter->GetSplitMode() != wxSPLIT_VERTICAL);
1523    bar->Enable(ID_SPLITH, !mSplitter->IsSplit() || mSplitter->GetSplitMode() != wxSPLIT_HORIZONTAL);
1524    bar->Check(ID_TOGGLECODE, mScript->GetParent()->IsShown());
1525    bar->Check(ID_TOGGLEOUTPUT, mOutput->GetParent()->IsShown());
1526    bar->Check(ID_LARGEICONS, mLargeIcons);
1527    bar->Check(ID_SMALLICONS, !mLargeIcons);
1528 }
1529 
OnRunUpdate(wxUpdateUIEvent & e)1530 void NyqBench::OnRunUpdate(wxUpdateUIEvent & e)
1531 {
1532    auto p = GetActiveProject().lock();
1533    wxToolBar *tbar = GetToolBar();
1534    wxMenuBar *mbar = GetMenuBar();
1535 
1536    auto gAudioIO = AudioIOBase::Get();
1537    if (p && gAudioIO->IsBusy()) {
1538       mbar->Enable(ID_GO, false);
1539       mbar->Enable(ID_STOP, false);
1540 
1541       tbar->EnableTool(ID_GO, false);
1542       tbar->EnableTool(ID_STOP, false);
1543    }
1544    else {
1545       mbar->Enable(ID_GO, (mScript->GetLastPosition() > 0) && !mRunning);
1546       mbar->Enable(ID_STOP, (mScript->GetLastPosition() > 0) && mRunning);
1547 
1548       tbar->EnableTool(ID_GO, (mScript->GetLastPosition() > 0) && !mRunning);
1549       tbar->EnableTool(ID_STOP, (mScript->GetLastPosition() > 0) && mRunning);
1550    }
1551 }
1552 
OnScriptUpdate(wxUpdateUIEvent & e)1553 void NyqBench::OnScriptUpdate(wxUpdateUIEvent & e)
1554 {
1555    if (mScriptBox && mScript && FindFocus() == mScript) {
1556       wxString label = mScriptBox->GetLabel();
1557       if (label == _("Script")) {
1558          label += wxT("*");
1559          mScriptBox->SetLabel(label);
1560          mOutputBox->SetLabel(_("Output"));
1561       }
1562    }
1563 }
1564 
OnOutputUpdate(wxUpdateUIEvent & e)1565 void NyqBench::OnOutputUpdate(wxUpdateUIEvent & e)
1566 {
1567    if (mOutputBox && mOutput && FindFocus() == mOutput) {
1568       wxString label = mOutputBox->GetLabel();
1569       if (label == _("Output")) {
1570          label += wxT("*");
1571          mOutputBox->SetLabel(label);
1572          mScriptBox->SetLabel(_("Script"));
1573       }
1574    }
1575 }
1576 
Validate()1577 bool NyqBench::Validate()
1578 {
1579    if (mScript->GetLastPosition() > 0 && mScript->IsModified()) {
1580       int ans;
1581       ans = AudacityMessageBox(XO("Code has been modified. Are you sure?"),
1582                          XO("Warning"),
1583                          wxYES_NO | wxICON_QUESTION,
1584                          this);
1585       if (ans == wxNO) {
1586          return false;
1587       }
1588    }
1589 
1590    return true;
1591 }
1592 
SetWindowTitle()1593 void NyqBench::SetWindowTitle()
1594 {
1595    wxString name = _("Untitled");
1596 
1597    if (!mPath.GetFullPath().IsEmpty()) {
1598       name = mPath.GetFullName();
1599    }
1600 
1601    SetTitle(_("Nyquist Effect Workbench - ") + name);
1602 }
1603 
RecreateToolbar(bool large)1604 void NyqBench::RecreateToolbar(bool large)
1605 {
1606    mLargeIcons = large;
1607 
1608    wxToolBar *tb = GetToolBar();
1609    if (tb) {
1610       delete tb;
1611    }
1612    tb = CreateToolBar();
1613 
1614    wxSize sz;
1615 
1616    if (!mLargeIcons) {
1617       tb->SetToolBitmapSize(wxSize(16, 16));
1618       mPics[0] = wxBitmap(document_new_small);
1619       mPics[1] = wxBitmap(document_open_small);
1620       mPics[2] = wxBitmap(document_save_as_small);
1621       mPics[3] = wxBitmap(document_save_small);
1622       mPics[4] = wxBitmap(edit_copy_small);
1623       mPics[5] = wxBitmap(edit_cut_small);
1624       mPics[6] = wxBitmap(edit_paste_small);
1625       mPics[7] = wxBitmap(edit_clear_small);
1626       mPics[8] = wxBitmap(edit_delete_small);
1627       mPics[9] = wxBitmap(edit_select_all_small);
1628       mPics[10] = wxBitmap(edit_undo_small);
1629       mPics[11] = wxBitmap(edit_redo_small);
1630       mPics[12] = wxBitmap(edit_find_small);
1631       mPics[13] = wxBitmap(system_search_small);
1632       mPics[14] = wxBitmap(go_top_small);
1633       mPics[15] = wxBitmap(go_up_small);
1634       mPics[16] = wxBitmap(go_previous_small);
1635       mPics[17] = wxBitmap(go_next_small);
1636       mPics[18] = wxBitmap(media_playback_start_small);
1637       mPics[19] = wxBitmap(media_playback_stop_small);
1638    }
1639    else {
1640       tb->SetToolBitmapSize(wxSize(32, 32));
1641       mPics[0] = wxBitmap(document_new_large);
1642       mPics[1] = wxBitmap(document_open_large);
1643       mPics[2] = wxBitmap(document_save_as_large);
1644       mPics[3] = wxBitmap(document_save_large);
1645       mPics[4] = wxBitmap(edit_copy_large);
1646       mPics[5] = wxBitmap(edit_cut_large);
1647       mPics[6] = wxBitmap(edit_paste_large);
1648       mPics[7] = wxBitmap(edit_clear_large);
1649       mPics[8] = wxBitmap(edit_delete_large);
1650       mPics[9] = wxBitmap(edit_select_all_large);
1651       mPics[10] = wxBitmap(edit_undo_large);
1652       mPics[11] = wxBitmap(edit_redo_large);
1653       mPics[12] = wxBitmap(edit_find_large);
1654       mPics[13] = wxBitmap(system_search_large);
1655       mPics[14] = wxBitmap(go_top_large);
1656       mPics[15] = wxBitmap(go_up_large);
1657       mPics[16] = wxBitmap(go_previous_large);
1658       mPics[17] = wxBitmap(go_next_large);
1659       mPics[18] = wxBitmap(media_playback_start_large);
1660       mPics[19] = wxBitmap(media_playback_stop_large);
1661    }
1662 
1663    tb->SetMargins(2, 2);
1664 
1665    tb->AddTool(wxID_NEW, _("New"), mPics[0], _("New script"));
1666    tb->AddTool(wxID_OPEN, _("Open"), mPics[1], _("Open script"));
1667    tb->AddTool(wxID_SAVE, _("Save"), mPics[2], _("Save script"));
1668    tb->AddTool(wxID_SAVEAS, _("Save As"), mPics[3], _("Save script as..."));
1669    tb->AddSeparator();
1670    tb->AddTool(wxID_COPY, _("Copy"), mPics[4], _("Copy to clipboard"));
1671    tb->AddTool(wxID_CUT, _("Cut"), mPics[5], _("Cut to clipboard"));
1672    tb->AddTool(wxID_PASTE, _("Paste"), mPics[6], _("Paste from clipboard"));
1673    tb->AddTool(wxID_CLEAR, _("Clear"), mPics[7], _("Clear selection"));
1674    tb->AddTool(wxID_SELECTALL, _("Select All"), mPics[9], _("Select all text"));
1675    tb->AddSeparator();
1676    tb->AddTool(wxID_UNDO, _("Undo"), mPics[10], _("Undo last change"));
1677    tb->AddTool(wxID_REDO, _("Redo"), mPics[11], _("Redo previous change"));
1678    tb->AddSeparator();
1679    tb->AddTool(wxID_FIND, _("Find"), mPics[12], _("Find text"));
1680    tb->AddSeparator();
1681    tb->AddTool(ID_MATCH, _("Match"), mPics[13], _("Go to matching paren"));
1682    tb->AddTool(ID_TOP, _("Top"), mPics[14], _("Go to top S-expr"));
1683    tb->AddTool(ID_UP, _("Up"), mPics[15], _("Go to higher S-expr"));
1684    tb->AddTool(ID_PREVIOUS, _("Previous"), mPics[16], _("Go to previous S-expr"));
1685    tb->AddTool(ID_NEXT, _("Next"), mPics[17], _("Go to next S-expr"));
1686    tb->AddSeparator();
1687    tb->AddTool(ID_GO, _("Start"), mPics[18], _("Start script"));
1688    tb->AddTool(ID_STOP, _("Stop"), mPics[19], _("Stop script"));
1689 
1690    tb->Realize();
1691 
1692    Layout();
1693    Fit();
1694    SetMinSize(GetSize());
1695 }
1696 
LoadFile()1697 void NyqBench::LoadFile()
1698 {
1699    wxString path = mPath.GetFullPath();
1700 
1701    if (path.IsEmpty()) {
1702       return;
1703    }
1704 
1705    wxFFile f(path);
1706    if (f.IsOpened()) {
1707       wxString t;
1708       if (f.ReadAll(&t)) {
1709 //#if defined(__WXGTK__) || defined(__WXMAC__)
1710          t.Replace(wxT("\r\n"), wxT("\n"));
1711          t.Replace(wxT("\r"), wxT("\n"));
1712 //#elif defined(__WXMSW__)
1713 //         t.Replace("\r\n", "\n");
1714 //#endif
1715          mScript->SetValue(t);
1716          mScript->DiscardEdits();
1717       }
1718    }
1719 
1720 //   mScript->LoadFile(mPath.GetFullPath());
1721 }
1722 
1723 //----------------------------------------------------------------------------
1724 // Connects Audacity menu item to an action in this dll.
1725 // Only one action implemented so far.
1726 //----------------------------------------------------------------------------
ShowNyqBench(const CommandContext &)1727 void NyqBench::ShowNyqBench(const CommandContext &)
1728 {
1729    Show();
1730 }
1731