1 // This file is part of Golly.
2 // See docs/License.html for the copyright notice.
3 
4 #include "wx/wxprec.h"      // for compilers that support precompilation
5 #ifndef WX_PRECOMP
6     #include "wx/wx.h"      // for all others include the necessary headers
7 #endif
8 
9 #if wxUSE_TOOLTIPS
10     #include "wx/tooltip.h" // for wxToolTip
11 #endif
12 #include "wx/dnd.h"         // for wxFileDropTarget
13 #include "wx/filename.h"    // for wxFileName
14 #include "wx/dir.h"         // for wxDir
15 #include "wx/clipbrd.h"     // for wxTheClipboard
16 
17 #include "bigint.h"
18 #include "lifealgo.h"
19 #include "qlifealgo.h"
20 #include "hlifealgo.h"
21 
22 #include "wxgolly.h"       // for wxGetApp, statusptr, viewptr, bigview
23 #include "wxutils.h"       // for Warning, Fatal, etc
24 #include "wxprefs.h"       // for gollydir, datadir, SavePrefs, etc
25 #include "wxinfo.h"        // for ShowInfo, GetInfoFrame
26 #include "wxhelp.h"        // for ShowHelp, GetHelpFrame
27 #include "wxstatus.h"      // for statusptr->...
28 #include "wxview.h"        // for viewptr->...
29 #include "wxrender.h"      // for DestroyDrawingData, etc
30 #include "wxedit.h"        // for CreateEditBar, EditBarHeight, etc
31 #include "wxscript.h"      // for inscript, PassKeyToScript
32 #include "wxalgos.h"       // for algo_type, algomenu, algomenupop
33 #include "wxlayer.h"       // for AddLayer, MAX_LAYERS, currlayer
34 #include "wxoverlay.h"     // for curroverlay
35 #include "wxundo.h"        // for currlayer->undoredo->...
36 #include "wxtimeline.h"    // for CreateTimelineBar, TimelineExists, etc
37 #include "wxmain.h"
38 
39 #ifdef __WXMAC__
40     #include <Carbon/Carbon.h>    // for GetCurrentProcess, etc
41 #endif
42 
43 #ifdef USING_VISUAL_LEAK_DETECTOR
44     #include <vld.h>
45 #endif
46 
47 // -----------------------------------------------------------------------------
48 
49 #ifdef __WXMSW__
50     static bool set_focus = false;              // OnIdle needs to call SetFocus?
51     static wxString editpath = wxEmptyString;   // OnIdle calls EditFile if this isn't empty
52 #endif
53 
54 static bool call_close = false;     // OnIdle needs to call Close?
55 static bool edit_file = false;      // edit the clicked file?
56 
57 // -----------------------------------------------------------------------------
58 
59 // ids for bitmap buttons in tool bar
60 enum {
61     START_TOOL = 0,
62     STOP_TOOL,
63     RESET_TOOL,
64     ALGO_TOOL,
65     AUTOFIT_TOOL,
66     HYPER_TOOL,
67     NEW_TOOL,
68     OPEN_TOOL,
69     SAVE_TOOL,
70     FILES_TOOL,
71     INFO_TOOL,
72     HELP_TOOL,
73     NUM_BUTTONS    // must be last
74 };
75 
76 // bitmaps for tool bar buttons
77 #include "bitmaps/play.xpm"
78 #include "bitmaps/stop.xpm"
79 #include "bitmaps/reset.xpm"
80 #include "bitmaps/algo.xpm"
81 #include "bitmaps/autofit.xpm"
82 #include "bitmaps/hyper.xpm"
83 #include "bitmaps/new.xpm"
84 #include "bitmaps/open.xpm"
85 #include "bitmaps/save.xpm"
86 #include "bitmaps/files.xpm"
87 #include "bitmaps/info.xpm"
88 #include "bitmaps/help.xpm"
89 // bitmaps for down state of toggle buttons
90 #include "bitmaps/autofit_down.xpm"
91 #include "bitmaps/hyper_down.xpm"
92 #include "bitmaps/files_down.xpm"
93 
94 // -----------------------------------------------------------------------------
95 
96 // Define our own vertical tool bar to avoid bugs and limitations in wxToolBar:
97 
98 // derive from wxPanel so we get current theme's background color on Windows
99 class ToolBar : public wxPanel
100 {
101 public:
102     ToolBar(wxWindow* parent, wxCoord xorg, wxCoord yorg, int wd, int ht);
~ToolBar()103     ~ToolBar() {}
104 
105     // add a bitmap button to tool bar
106     void AddButton(int id, const wxString& tip);
107 
108     // add a vertical gap between buttons
109     void AddSeparator();
110 
111     // enable/disable button
112     void EnableButton(int id, bool enable);
113 
114     // set state of start/stop button
115     void SetStartStopButton();
116 
117     // set state of a toggle button
118     void SelectButton(int id, bool select);
119 
120     // detect press and release of a bitmap button
121     void OnButtonDown(wxMouseEvent& event);
122     void OnButtonUp(wxMouseEvent& event);
123     void OnKillFocus(wxFocusEvent& event);
124 
125 private:
126     // any class wishing to process wxWidgets events must use this macro
127     DECLARE_EVENT_TABLE()
128 
129     // event handlers
130     void OnPaint(wxPaintEvent& event);
131     void OnMouseDown(wxMouseEvent& event);
132     void OnButton(wxCommandEvent& event);
133 
134     // bitmaps for normal or down state
135     wxBitmap normtool[NUM_BUTTONS];
136     wxBitmap downtool[NUM_BUTTONS];
137 
138 #ifdef __WXMSW__
139     // on Windows we need bitmaps for disabled buttons
140     wxBitmap disnormtool[NUM_BUTTONS];
141     wxBitmap disdowntool[NUM_BUTTONS];
142 #endif
143 
144     // remember state of toggle buttons to avoid unnecessary drawing;
145     // 0 = not yet initialized, 1 = selected, -1 = not selected
146     int buttstate[NUM_BUTTONS];
147 
148     // positioning data used by AddButton and AddSeparator
149     int ypos, xpos, smallgap, biggap;
150 };
151 
152 BEGIN_EVENT_TABLE(ToolBar, wxPanel)
153 EVT_PAINT            (           ToolBar::OnPaint)
154 EVT_LEFT_DOWN        (           ToolBar::OnMouseDown)
155 EVT_BUTTON           (wxID_ANY,  ToolBar::OnButton)
156 END_EVENT_TABLE()
157 
158 ToolBar* toolbarptr = NULL;      // global pointer to tool bar
159 const int TOOLBARWD = 32;        // width of (vertical) tool bar
160 
161 // tool bar buttons (must be global to use Connect/Disconnect on Windows)
162 wxBitmapButton* tbbutt[NUM_BUTTONS];
163 
164 // width and height of bitmap buttons
165 #if defined(__WXOSX_COCOA__) && wxCHECK_VERSION(3,0,0)
166     // note that BUTTON_WD will have to be at least 26 to avoid clipping bitmaps
167     // if we decide to use wxBORDER_SUNKEN rather than wxBORDER_SIMPLE
168     // (and TOOLBARWD will probably need to be increased to 48)
169     const int BUTTON_WD = 24;
170     const int BUTTON_HT = 24;
171 #elif defined(__WXOSX_COCOA__) || defined(__WXGTK__)
172     const int BUTTON_WD = 28;
173     const int BUTTON_HT = 28;
174 #else
175     const int BUTTON_WD = 24;
176     const int BUTTON_HT = 24;
177 #endif
178 
179 // -----------------------------------------------------------------------------
180 
ToolBar(wxWindow * parent,wxCoord xorg,wxCoord yorg,int wd,int ht)181 ToolBar::ToolBar(wxWindow* parent, wxCoord xorg, wxCoord yorg, int wd, int ht)
182 : wxPanel(parent, wxID_ANY, wxPoint(xorg,yorg), wxSize(wd,ht),
183           wxNO_FULL_REPAINT_ON_RESIZE)
184 {
185 #ifdef __WXGTK__
186     // avoid erasing background on GTK+
187     SetBackgroundStyle(wxBG_STYLE_CUSTOM);
188 #endif
189 
190     // init bitmaps for normal state
191     normtool[START_TOOL] =     XPM_BITMAP(play);
192     normtool[STOP_TOOL] =      XPM_BITMAP(stop);
193     normtool[RESET_TOOL] =     XPM_BITMAP(reset);
194     normtool[ALGO_TOOL] =      XPM_BITMAP(algo);
195     normtool[AUTOFIT_TOOL] =   XPM_BITMAP(autofit);
196     normtool[HYPER_TOOL] =     XPM_BITMAP(hyper);
197     normtool[NEW_TOOL] =       XPM_BITMAP(new);
198     normtool[OPEN_TOOL] =      XPM_BITMAP(open);
199     normtool[SAVE_TOOL] =      XPM_BITMAP(save);
200     normtool[FILES_TOOL] =     XPM_BITMAP(files);
201     normtool[INFO_TOOL] =      XPM_BITMAP(info);
202     normtool[HELP_TOOL] =      XPM_BITMAP(help);
203 
204     // toggle buttons also have a down state
205     downtool[AUTOFIT_TOOL] =   XPM_BITMAP(autofit_down);
206     downtool[HYPER_TOOL] =     XPM_BITMAP(hyper_down);
207     downtool[FILES_TOOL] =     XPM_BITMAP(files_down);
208 
209 #ifdef __WXMSW__
210     for (int i = 0; i < NUM_BUTTONS; i++) {
211         CreatePaleBitmap(normtool[i], disnormtool[i]);
212     }
213     CreatePaleBitmap(downtool[AUTOFIT_TOOL],  disdowntool[AUTOFIT_TOOL]);
214     CreatePaleBitmap(downtool[HYPER_TOOL],    disdowntool[HYPER_TOOL]);
215     CreatePaleBitmap(downtool[FILES_TOOL],    disdowntool[FILES_TOOL]);
216 #endif
217 
218     for (int i = 0; i < NUM_BUTTONS; i++) {
219         buttstate[i] = 0;
220     }
221 
222     // init position variables used by AddButton and AddSeparator
223 #ifdef __WXGTK__
224     // buttons are a different size in wxGTK
225     xpos = 2;
226     ypos = 2;
227     smallgap = 6;
228 #else
229     xpos = (32 - BUTTON_WD) / 2;
230     ypos = (32 - BUTTON_HT) / 2;
231     smallgap = 4;
232 #endif
233     biggap = 16;
234 }
235 
236 // -----------------------------------------------------------------------------
237 
OnPaint(wxPaintEvent & WXUNUSED (event))238 void ToolBar::OnPaint(wxPaintEvent& WXUNUSED(event))
239 {
240     wxPaintDC dc(this);
241 
242     int wd, ht;
243     GetClientSize(&wd, &ht);
244     if (wd < 1 || ht < 1 || !showtool) return;
245 
246     wxRect r = wxRect(0, 0, wd, ht);
247 #ifdef __WXMSW__
248     dc.Clear();
249     // draw gray line at top edge
250     dc.SetPen(*wxGREY_PEN);
251     dc.DrawLine(0, 0, r.width, 0);
252     dc.SetPen(wxNullPen);
253 #else
254     // draw gray line at right edge
255 #ifdef __WXMAC__
256     wxBrush brush(wxColor(202,202,202));
257     FillRect(dc, r, brush);
258     wxPen linepen(wxColor(140,140,140));
259     dc.SetPen(linepen);
260 #else
261     dc.SetPen(*wxLIGHT_GREY_PEN);
262 #endif
263     dc.DrawLine(r.GetRight(), 0, r.GetRight(), r.height);
264     dc.SetPen(wxNullPen);
265 #endif
266 }
267 
268 // -----------------------------------------------------------------------------
269 
OnMouseDown(wxMouseEvent & WXUNUSED (event))270 void ToolBar::OnMouseDown(wxMouseEvent& WXUNUSED(event))
271 {
272     // this is NOT called if user clicks a tool bar button;
273     // on Windows we need to reset keyboard focus to viewport window
274     viewptr->SetFocus();
275 }
276 
277 // -----------------------------------------------------------------------------
278 
OnButton(wxCommandEvent & event)279 void ToolBar::OnButton(wxCommandEvent& event)
280 {
281     int id = event.GetId();
282 
283     int cmdid;
284     switch (id) {
285         case START_TOOL:     cmdid = ID_START; break;
286         case RESET_TOOL:     cmdid = ID_RESET; break;
287         case ALGO_TOOL:      return;                    // handled in OnButtonDown
288         case AUTOFIT_TOOL:   cmdid = ID_AUTO; break;
289         case HYPER_TOOL:     cmdid = ID_HYPER; break;
290         case NEW_TOOL:       cmdid = wxID_NEW; break;
291         case OPEN_TOOL:      cmdid = wxID_OPEN; break;
292         case SAVE_TOOL:      cmdid = wxID_SAVE; break;
293         case FILES_TOOL:     cmdid = ID_SHOW_FILES; break;
294         case INFO_TOOL:      cmdid = ID_INFO; break;
295         case HELP_TOOL:      cmdid = ID_HELP_BUTT; break;
296         default:             Warning(_("Unexpected button id!")); return;
297     }
298 
299     // call MainFrame::OnMenu after OnButton finishes;
300     // this avoids start/stop button problem in GTK app
301     wxCommandEvent cmdevt(wxEVT_COMMAND_MENU_SELECTED, cmdid);
302     wxPostEvent(mainptr->GetEventHandler(), cmdevt);
303 
304     // avoid weird bug on Mac where all buttons can be disabled after hitting
305     // RESET_TOOL button *and* the "All controls" option is ticked in
306     // System Prefs > Keyboard & Mouse > Keyboard Shortcuts
307     // (might also fix similar problem on Windows)
308     viewptr->SetFocus();
309 }
310 
311 // -----------------------------------------------------------------------------
312 
OnKillFocus(wxFocusEvent & event)313 void ToolBar::OnKillFocus(wxFocusEvent& event)
314 {
315     int id = event.GetId();
316     tbbutt[id]->SetFocus();   // don't let button lose focus
317 }
318 
319 // -----------------------------------------------------------------------------
320 
OnButtonDown(wxMouseEvent & event)321 void ToolBar::OnButtonDown(wxMouseEvent& event)
322 {
323     // a tool bar button has been pressed
324     int id = event.GetId();
325 
326 #ifdef __WXMSW__
327     // connect a handler that keeps focus with the pressed button
328     if (id != ALGO_TOOL)
329         tbbutt[id]->Connect(id, wxEVT_KILL_FOCUS,
330                             wxFocusEventHandler(ToolBar::OnKillFocus));
331 #endif
332 
333 #ifdef __WXMAC__
334     // close any open tool tip window (probably wxMac bug)
335     wxToolTip::RemoveToolTips();
336 #endif
337 
338     // we want pop-up menu to appear as soon as ALGO_TOOL button is pressed
339     if (id == ALGO_TOOL) {
340         // we use algomenupop here rather than algomenu to avoid assert messages in wx 2.9+
341 #ifdef __WXMSW__
342         tbbutt[id]->PopupMenu(algomenupop, 0, 25);
343         // fix prob on Win (almost -- button-up doesn't always close menu)
344         viewptr->SetFocus();
345         return;
346 #else
347         tbbutt[id]->PopupMenu(algomenupop, 0, 30);
348 #endif
349 
350 #ifdef __WXOSX_COCOA__
351         viewptr->SetFocus();
352         // don't call event.Skip() otherwise algo button will remain selected
353         return;
354 #endif
355     }
356 
357     event.Skip();
358 }
359 
360 // -----------------------------------------------------------------------------
361 
OnButtonUp(wxMouseEvent & event)362 void ToolBar::OnButtonUp(wxMouseEvent& event)
363 {
364     // a tool bar button has been released (only called in wxMSW)
365     int id = event.GetId();
366 
367     wxPoint pt = tbbutt[id]->ScreenToClient( wxGetMousePosition() );
368 
369     int wd, ht;
370     tbbutt[id]->GetClientSize(&wd, &ht);
371     wxRect r(0, 0, wd, ht);
372 
373     // diconnect kill-focus handler
374     if (id != ALGO_TOOL)
375         tbbutt[id]->Disconnect(id, wxEVT_KILL_FOCUS,
376                                wxFocusEventHandler(ToolBar::OnKillFocus));
377     viewptr->SetFocus();
378 
379     if ( r.Contains(pt) ) {
380         // call OnButton
381         wxCommandEvent buttevt(wxEVT_COMMAND_BUTTON_CLICKED, id);
382         buttevt.SetEventObject(tbbutt[id]);
383         tbbutt[id]->GetEventHandler()->ProcessEvent(buttevt);
384     }
385 }
386 
387 // -----------------------------------------------------------------------------
388 
AddButton(int id,const wxString & tip)389 void ToolBar::AddButton(int id, const wxString& tip)
390 {
391     tbbutt[id] = new wxBitmapButton(this, id, normtool[id], wxPoint(xpos,ypos),
392 #if defined(__WXOSX_COCOA__) && wxCHECK_VERSION(3,0,0)
393                                     wxSize(BUTTON_WD, BUTTON_HT), wxBORDER_SIMPLE
394 #else
395                                     wxSize(BUTTON_WD, BUTTON_HT)
396 #endif
397                                     );
398     if (tbbutt[id] == NULL) {
399         Fatal(_("Failed to create tool bar button!"));
400     } else {
401         ypos += BUTTON_HT + smallgap;
402         tbbutt[id]->SetToolTip(tip);
403 #ifdef __WXMSW__
404         // fix problem with tool bar buttons when generating/inscript
405         // due to focus being changed to viewptr
406         tbbutt[id]->Connect(id, wxEVT_LEFT_DOWN, wxMouseEventHandler(ToolBar::OnButtonDown));
407         tbbutt[id]->Connect(id, wxEVT_LEFT_UP, wxMouseEventHandler(ToolBar::OnButtonUp));
408 #else
409         // allow pop-up menu to appear as soon as ALGO_TOOL button is pressed
410         tbbutt[id]->Connect(id, wxEVT_LEFT_DOWN, wxMouseEventHandler(ToolBar::OnButtonDown));
411 #endif
412     }
413 }
414 
415 // -----------------------------------------------------------------------------
416 
AddSeparator()417 void ToolBar::AddSeparator()
418 {
419     ypos += biggap - smallgap;
420 }
421 
422 // -----------------------------------------------------------------------------
423 
EnableButton(int id,bool enable)424 void ToolBar::EnableButton(int id, bool enable)
425 {
426     if (enable == tbbutt[id]->IsEnabled()) return;
427 
428 #ifdef __WXMSW__
429     if (id == START_TOOL && (inscript || mainptr->generating)) {
430         tbbutt[id]->SetBitmapDisabled(disnormtool[STOP_TOOL]);
431 
432     } else if (id == AUTOFIT_TOOL && currlayer->autofit) {
433         tbbutt[id]->SetBitmapDisabled(disdowntool[id]);
434 
435     } else if (id == HYPER_TOOL && currlayer->hyperspeed) {
436         tbbutt[id]->SetBitmapDisabled(disdowntool[id]);
437 
438     } else if (id == FILES_TOOL && showfiles) {
439         tbbutt[id]->SetBitmapDisabled(disdowntool[id]);
440 
441     } else {
442         tbbutt[id]->SetBitmapDisabled(disnormtool[id]);
443     }
444 #endif
445 
446     tbbutt[id]->Enable(enable);
447 }
448 
449 // -----------------------------------------------------------------------------
450 
SetStartStopButton()451 void ToolBar::SetStartStopButton()
452 {
453     if (inscript || mainptr->generating) {
454         // show stop bitmap
455         if (buttstate[START_TOOL] == 1) return;
456         buttstate[START_TOOL] = 1;
457         tbbutt[START_TOOL]->SetBitmapLabel(normtool[STOP_TOOL]);
458         if (inscript)
459             tbbutt[START_TOOL]->SetToolTip(_("Stop script"));
460         else
461             tbbutt[START_TOOL]->SetToolTip(_("Stop generating"));
462     } else {
463         // show start bitmap
464         if (buttstate[START_TOOL] == -1) return;
465         buttstate[START_TOOL] = -1;
466         tbbutt[START_TOOL]->SetBitmapLabel(normtool[START_TOOL]);
467         tbbutt[START_TOOL]->SetToolTip(_("Start generating"));
468     }
469 
470     tbbutt[START_TOOL]->Refresh(false);
471 }
472 
473 // -----------------------------------------------------------------------------
474 
SelectButton(int id,bool select)475 void ToolBar::SelectButton(int id, bool select)
476 {
477     if (select) {
478         if (buttstate[id] == 1) return;
479         buttstate[id] = 1;
480         tbbutt[id]->SetBitmapLabel(downtool[id]);
481     } else {
482         if (buttstate[id] == -1) return;
483         buttstate[id] = -1;
484         tbbutt[id]->SetBitmapLabel(normtool[id]);
485     }
486 
487     tbbutt[id]->Refresh(false);
488 }
489 
490 // -----------------------------------------------------------------------------
491 
CreateToolbar()492 void MainFrame::CreateToolbar()
493 {
494     int wd, ht;
495     GetClientSize(&wd, &ht);
496 
497     toolbarptr = new ToolBar(this, 0, 0, TOOLBARWD, ht);
498 
499     // add buttons to tool bar
500     toolbarptr->AddButton(START_TOOL,      _("Start generating"));
501     toolbarptr->AddButton(RESET_TOOL,      _("Reset"));
502     toolbarptr->AddSeparator();
503     toolbarptr->AddButton(ALGO_TOOL,       _("Set algorithm"));
504     toolbarptr->AddButton(AUTOFIT_TOOL,    _("Auto fit"));
505     toolbarptr->AddButton(HYPER_TOOL,      _("Hyperspeed"));
506     toolbarptr->AddSeparator();
507     toolbarptr->AddButton(NEW_TOOL,        _("New pattern"));
508     toolbarptr->AddButton(OPEN_TOOL,       _("Open pattern"));
509     toolbarptr->AddButton(SAVE_TOOL,       _("Save pattern"));
510     toolbarptr->AddSeparator();
511     toolbarptr->AddButton(FILES_TOOL,      _("Show/hide files"));
512     toolbarptr->AddSeparator();
513     toolbarptr->AddButton(INFO_TOOL,       _("Show pattern information"));
514     toolbarptr->AddButton(HELP_TOOL,       _("Show help window"));
515 
516     toolbarptr->Show(showtool);
517 }
518 
519 // -----------------------------------------------------------------------------
520 
UpdateToolBar()521 void MainFrame::UpdateToolBar()
522 {
523     // update tool bar buttons according to the current state
524     if (toolbarptr && showtool) {
525         bool active = !viewptr->waitingforclick;
526         bool timeline = TimelineExists();
527 
528         // set state of start/stop button
529         toolbarptr->SetStartStopButton();
530 
531         // set state of toggle buttons
532         toolbarptr->SelectButton(AUTOFIT_TOOL,    currlayer->autofit);
533         toolbarptr->SelectButton(HYPER_TOOL,      currlayer->hyperspeed);
534         toolbarptr->SelectButton(FILES_TOOL,      showfiles);
535 
536         toolbarptr->EnableButton(START_TOOL,      active && !timeline);
537         toolbarptr->EnableButton(RESET_TOOL,      active && !timeline && !inscript &&
538                                                   (generating || currlayer->algo->getGeneration() > currlayer->startgen));
539         toolbarptr->EnableButton(ALGO_TOOL,       active && !timeline && !inscript);
540         toolbarptr->EnableButton(AUTOFIT_TOOL,    active);
541         toolbarptr->EnableButton(HYPER_TOOL,      active && !timeline);
542         toolbarptr->EnableButton(NEW_TOOL,        active && !inscript);
543         toolbarptr->EnableButton(OPEN_TOOL,       active && !inscript);
544         toolbarptr->EnableButton(SAVE_TOOL,       active && !inscript);
545         toolbarptr->EnableButton(FILES_TOOL,      active);
546         toolbarptr->EnableButton(INFO_TOOL,       active && !currlayer->currfile.IsEmpty());
547         toolbarptr->EnableButton(HELP_TOOL,       active);
548     }
549 }
550 
551 // -----------------------------------------------------------------------------
552 
ClipboardHasText()553 bool MainFrame::ClipboardHasText()
554 {
555     bool hastext = false;
556 #ifdef __WXGTK__
557     // avoid re-entrancy bug in wxGTK 2.9.x
558     if (wxTheClipboard->IsOpened()) return false;
559 #endif
560     if (wxTheClipboard->Open()) {
561         hastext = wxTheClipboard->IsSupported(wxDF_TEXT);
562         if (!hastext) {
563             // we'll try to convert bitmap data to text pattern
564             hastext = wxTheClipboard->IsSupported(wxDF_BITMAP);
565         }
566         wxTheClipboard->Close();
567     }
568     return hastext;
569 }
570 
571 // -----------------------------------------------------------------------------
572 
EnableAllMenus(bool enable)573 void MainFrame::EnableAllMenus(bool enable)
574 {
575     wxMenuBar* mbar = GetMenuBar();
576     if (mbar) {
577         int count = (int)(mbar->GetMenuCount());
578         int i;
579         for (i = 0; i < count; i++) {
580             mbar->EnableTop(i, enable);
581         }
582 #ifdef __WXOSX_COCOA__
583         // enable/disable items in app menu
584         mbar->Enable(wxID_ABOUT, enable);
585         mbar->Enable(wxID_PREFERENCES, enable);
586         mbar->Enable(wxID_EXIT, enable);
587 #endif
588     }
589 }
590 
591 // -----------------------------------------------------------------------------
592 
UpdateMenuItems()593 void MainFrame::UpdateMenuItems()
594 {
595     // update menu bar items according to the given state
596     wxMenuBar* mbar = GetMenuBar();
597     if (mbar) {
598         // we need to disable most menu items if main window is not in front so user can
599         // hit return key to close help/info window rather than start/stop generating
600         bool active = infront && !viewptr->waitingforclick;
601 
602         bool selexists = viewptr->SelectionExists();
603         bool timeline = TimelineExists();
604         bool textinclip = ClipboardHasText();
605 
606         mbar->Enable(wxID_NEW,           active && !inscript);
607         mbar->Enable(wxID_OPEN,          active && !inscript);
608         mbar->Enable(ID_OPEN_CLIP,       active && !inscript && textinclip);
609         mbar->Enable(ID_OPEN_RECENT,     active && !inscript && numpatterns > 0);
610         mbar->Enable(wxID_SAVE,          active && !inscript);
611         mbar->Enable(ID_SAVE_XRLE,       active);
612         mbar->Enable(ID_RUN_SCRIPT,      active && !timeline && !inscript);
613         mbar->Enable(ID_RUN_CLIP,        active && !timeline && !inscript && textinclip);
614         mbar->Enable(ID_RUN_RECENT,      active && !timeline && !inscript && numscripts > 0);
615         mbar->Enable(ID_SHOW_FILES,      active);
616         mbar->Enable(ID_FILE_DIR,        active);
617         // safer not to allow prefs dialog while script is running???
618         // mbar->Enable(wxID_PREFERENCES,   !inscript);
619 
620         bool can_undo = active && !timeline && currlayer->undoredo->CanUndo();
621         bool can_redo = active && !timeline && currlayer->undoredo->CanRedo();
622 
623         mbar->Enable(ID_UNDO,            can_undo);
624         mbar->Enable(ID_REDO,            can_redo);
625         mbar->Enable(ID_NO_UNDO,         active && !inscript);
626         mbar->Enable(ID_CUT,             active && !timeline && !inscript && selexists);
627         mbar->Enable(ID_COPY,            active && !inscript && selexists);
628         mbar->Enable(ID_CLEAR,           active && !timeline && !inscript && selexists);
629         mbar->Enable(ID_OUTSIDE,         active && !timeline && !inscript && selexists);
630         mbar->Enable(ID_PASTE,           active && !timeline && !inscript && textinclip);
631         mbar->Enable(ID_PASTE_SEL,       active && !timeline && !inscript && textinclip && selexists);
632         mbar->Enable(ID_PLOCATION,       active);
633         mbar->Enable(ID_PMODE,           active);
634         mbar->Enable(ID_SELECTALL,       active && !inscript);
635         mbar->Enable(ID_REMOVE,          active && !inscript && selexists);
636         mbar->Enable(ID_SHRINK,          active && !inscript && selexists);
637         mbar->Enable(ID_RANDOM,          active && !timeline && !inscript && selexists);
638         mbar->Enable(ID_FLIPTB,          active && !timeline && !inscript && selexists);
639         mbar->Enable(ID_FLIPLR,          active && !timeline && !inscript && selexists);
640         mbar->Enable(ID_ROTATEC,         active && !timeline && !inscript && selexists);
641         mbar->Enable(ID_ROTATEA,         active && !timeline && !inscript && selexists);
642         mbar->Enable(ID_CMODE,           active);
643 
644         if (inscript) {
645             // WARNING: next line is no longer needed on wxMac and actually caused a bad memory leak!
646             // mbar->SetLabel(ID_START, _("x"));
647             // escape key can be used to abort a running script
648             mbar->SetLabel(ID_START, _("Stop Script\tEscape"));
649         } else if (generating) {
650             mbar->SetLabel(ID_START, _("Stop Generating") + GetAccelerator(DO_STARTSTOP));
651         } else if (timeline && !currlayer->algo->isrecording()) {
652             if (TimelineIsPlaying())
653                 mbar->SetLabel(ID_START, _("Stop Playing Timeline") + GetAccelerator(DO_STARTSTOP));
654             else
655                 mbar->SetLabel(ID_START, _("Start Playing Timeline") + GetAccelerator(DO_STARTSTOP));
656         } else {
657             mbar->SetLabel(ID_START, _("Start Generating") + GetAccelerator(DO_STARTSTOP));
658         }
659 
660         if (currlayer->algo->isrecording()) {
661             mbar->SetLabel(ID_RECORD, _("Stop Recording") + GetAccelerator(DO_RECORD));
662         } else {
663             mbar->SetLabel(ID_RECORD, _("Start Recording") + GetAccelerator(DO_RECORD));
664         }
665 
666         mbar->Enable(ID_START,        active && !currlayer->algo->isrecording());
667 #ifdef __WXMAC__
668         // for some unknown reason we need to disable these menu items when generating
669         // otherwise auto-repeating space/tab key doesn't work all the time
670         mbar->Enable(ID_NEXT,         active && !timeline && !inscript && !generating);
671         mbar->Enable(ID_STEP,         active && !timeline && !inscript && !generating);
672 #else
673         mbar->Enable(ID_NEXT,         active && !timeline && !inscript);
674         mbar->Enable(ID_STEP,         active && !timeline && !inscript);
675 #endif
676         mbar->Enable(ID_RESET,        active && !timeline && !inscript &&
677                                       (generating || currlayer->algo->getGeneration() > currlayer->startgen));
678         mbar->Enable(ID_SETGEN,       active && !timeline && !inscript);
679         mbar->Enable(ID_FASTER,       active && !inscript && !(timeline && currlayer->algo->isrecording()));
680         mbar->Enable(ID_SLOWER,       active && !inscript && !(timeline && currlayer->algo->isrecording()));
681         mbar->Enable(ID_SETBASE,      active && !timeline && !inscript);
682         mbar->Enable(ID_AUTO,         active);
683         mbar->Enable(ID_HYPER,        active && !timeline);
684         mbar->Enable(ID_HINFO,        active);
685         mbar->Enable(ID_SHOW_POP,     active);
686         mbar->Enable(ID_RECORD,       active && !inscript && currlayer->algo->hyperCapable());
687         mbar->Enable(ID_DELTIME,      active && !inscript && timeline && !currlayer->algo->isrecording());
688         mbar->Enable(ID_CONVERT,      active && !timeline && !inscript);
689         mbar->Enable(ID_SETALGO,      active && !timeline && !inscript);
690         mbar->Enable(ID_SETRULE,      active && !timeline && !inscript);
691 
692         mbar->Enable(ID_FULL,         active);
693         mbar->Enable(ID_FIT,          active);
694         mbar->Enable(ID_FIT_SEL,      active && selexists);
695         mbar->Enable(ID_MIDDLE,       active);
696         mbar->Enable(ID_RESTORE00,    active && (currlayer->originx != bigint::zero ||
697                                                  currlayer->originy != bigint::zero));
698         mbar->Enable(wxID_ZOOM_IN,    active /* && viewptr->GetMag() < MAX_MAG */);
699                                              // don't do this test because Win users won't hear beep
700         mbar->Enable(wxID_ZOOM_OUT,   active);
701         mbar->Enable(ID_SET_SCALE,    active);
702         mbar->Enable(ID_TOOL_BAR,     active);
703         mbar->Enable(ID_LAYER_BAR,    active);
704         mbar->Enable(ID_EDIT_BAR,     active);
705         mbar->Enable(ID_ALL_STATES,   active);
706         mbar->Enable(ID_STATUS_BAR,   active);
707         mbar->Enable(ID_EXACT,        active);
708         mbar->Enable(ID_GRID,         active);
709         mbar->Enable(ID_ICONS,        active);
710         mbar->Enable(ID_INVERT,       active);
711         mbar->Enable(ID_SMARTSCALE,   active);
712         mbar->Enable(ID_TIMELINE,     active);
713         mbar->Enable(ID_SCROLL,       active);
714         mbar->Enable(ID_INFO,         !currlayer->currfile.IsEmpty());
715 
716         mbar->Enable(ID_SAVE_OVERLAY, active && showoverlay && curroverlay->GetOverlayData());
717         mbar->Enable(ID_SHOW_OVERLAY, active);
718         mbar->Enable(ID_DEL_OVERLAY,  active && !inscript && curroverlay->GetOverlayData());
719         mbar->Enable(ID_ADD_LAYER,    active && !inscript && numlayers < MAX_LAYERS);
720         mbar->Enable(ID_CLONE,        active && !inscript && numlayers < MAX_LAYERS);
721         mbar->Enable(ID_DUPLICATE,    active && !inscript && numlayers < MAX_LAYERS);
722         mbar->Enable(ID_DEL_LAYER,    active && !inscript && numlayers > 1);
723         mbar->Enable(ID_DEL_OTHERS,   active && !inscript && numlayers > 1);
724         mbar->Enable(ID_MOVE_LAYER,   active && !inscript && numlayers > 1);
725         mbar->Enable(ID_NAME_LAYER,   active && !inscript);
726         mbar->Enable(ID_SET_COLORS,   active && !inscript);
727         mbar->Enable(ID_SYNC_VIEW,    active);
728         mbar->Enable(ID_SYNC_CURS,    active);
729         mbar->Enable(ID_STACK,        active);
730         mbar->Enable(ID_TILE,         active);
731         for (int i = 0; i < numlayers; i++)
732             mbar->Enable(ID_LAYER0 + i, active && CanSwitchLayer(i));
733 
734         // tick/untick menu items created using AppendCheckItem
735         mbar->Check(ID_SAVE_XRLE,     savexrle);
736         mbar->Check(ID_SHOW_FILES,    showfiles);
737         mbar->Check(ID_NO_UNDO,       !allowundo);
738         mbar->Check(ID_AUTO,          currlayer->autofit);
739         mbar->Check(ID_HYPER,         currlayer->hyperspeed);
740         mbar->Check(ID_HINFO,         currlayer->showhashinfo);
741         mbar->Check(ID_SHOW_POP,      showpopulation);
742         mbar->Check(ID_TOOL_BAR,      showtool);
743         mbar->Check(ID_LAYER_BAR,     showlayer);
744         mbar->Check(ID_EDIT_BAR,      showedit);
745         mbar->Check(ID_ALL_STATES,    showallstates);
746         mbar->Check(ID_STATUS_BAR,    showstatus);
747         mbar->Check(ID_EXACT,         showexact);
748         mbar->Check(ID_GRID,          showgridlines);
749         mbar->Check(ID_ICONS,         showicons);
750         mbar->Check(ID_INVERT,        swapcolors);
751         mbar->Check(ID_SMARTSCALE,    smartscale);
752         mbar->Check(ID_TIMELINE,      showtimeline);
753         mbar->Check(ID_SCROLL,        showscrollbars);
754         mbar->Check(ID_PL_TL,         plocation == TopLeft);
755         mbar->Check(ID_PL_TR,         plocation == TopRight);
756         mbar->Check(ID_PL_BR,         plocation == BottomRight);
757         mbar->Check(ID_PL_BL,         plocation == BottomLeft);
758         mbar->Check(ID_PL_MID,        plocation == Middle);
759         mbar->Check(ID_PM_AND,        pmode == And);
760         mbar->Check(ID_PM_COPY,       pmode == Copy);
761         mbar->Check(ID_PM_OR,         pmode == Or);
762         mbar->Check(ID_PM_XOR,        pmode == Xor);
763         mbar->Check(ID_DRAW,          currlayer->curs == curs_pencil);
764         mbar->Check(ID_PICK,          currlayer->curs == curs_pick);
765         mbar->Check(ID_SELECT,        currlayer->curs == curs_cross);
766         mbar->Check(ID_MOVE,          currlayer->curs == curs_hand);
767         mbar->Check(ID_ZOOMIN,        currlayer->curs == curs_zoomin);
768         mbar->Check(ID_ZOOMOUT,       currlayer->curs == curs_zoomout);
769         mbar->Check(ID_SCALE_1,       viewptr->GetMag() == 0);
770         mbar->Check(ID_SCALE_2,       viewptr->GetMag() == 1);
771         mbar->Check(ID_SCALE_4,       viewptr->GetMag() == 2);
772         mbar->Check(ID_SCALE_8,       viewptr->GetMag() == 3);
773         mbar->Check(ID_SCALE_16,      viewptr->GetMag() == 4);
774         mbar->Check(ID_SCALE_32,      viewptr->GetMag() == 5);
775         mbar->Check(ID_SYNC_VIEW,     syncviews);
776         mbar->Check(ID_SYNC_CURS,     synccursors);
777         mbar->Check(ID_STACK,         stacklayers);
778         mbar->Check(ID_TILE,          tilelayers);
779         mbar->Check(ID_SHOW_OVERLAY,  showoverlay);
780         for (int i = 0; i < NumAlgos(); i++) {
781             mbar->Check(ID_ALGO0 + i, currlayer->algtype == i);
782             // keep algomenupop in sync with algomenu
783             algomenupop->Check(ID_ALGO0 + i, currlayer->algtype == i);
784         }
785         for (int i = 0; i < numlayers; i++) {
786             mbar->Check(ID_LAYER0 + i, currindex == i);
787         }
788     }
789 }
790 
791 // -----------------------------------------------------------------------------
792 
UpdateUserInterface()793 void MainFrame::UpdateUserInterface()
794 {
795     UpdateToolBar();
796     UpdateLayerBar();
797     UpdateEditBar();
798     UpdateTimelineBar();
799     UpdateMenuItems();
800     viewptr->CheckCursor(infront);
801     statusptr->CheckMouseLocation(infront);
802 
803 #ifdef __WXMSW__
804     // ensure viewport window has keyboard focus if main window is active
805     if (infront) viewptr->SetFocus();
806 #endif
807 }
808 
809 // -----------------------------------------------------------------------------
810 
811 // update everything in main window, and menu bar and cursor
UpdateEverything()812 void MainFrame::UpdateEverything()
813 {
814     if (IsIconized()) {
815         // main window has been minimized, so only update menu bar items
816         UpdateMenuItems();
817         return;
818     }
819 
820     // update all tool bars, menus and cursor
821     UpdateUserInterface();
822 
823     if (inscript) {
824         // make sure scroll bars are accurate while running script
825         bigview->UpdateScrollBars();
826         return;
827     }
828 
829     int wd, ht;
830     GetClientSize(&wd, &ht);      // includes status bar and viewport
831 
832     if (wd > 0 && ht > statusptr->statusht) {
833         bigview->Refresh(false);
834         bigview->UpdateScrollBars();
835     }
836 
837     if (wd > 0 && ht > 0 && showstatus) {
838         statusptr->Refresh(false);
839     }
840 }
841 
842 // -----------------------------------------------------------------------------
843 
844 // only update viewport and status bar
UpdatePatternAndStatus(bool update_now)845 void MainFrame::UpdatePatternAndStatus(bool update_now)
846 {
847     if (inscript || currlayer->undoredo->doingscriptchanges) return;
848 
849     if (!IsIconized()) {
850         bigview->Refresh(false);
851         if (update_now) bigview->Update();
852         if (showstatus) {
853             statusptr->CheckMouseLocation(infront);
854             statusptr->Refresh(false);
855             if (update_now) statusptr->Update();
856         }
857     }
858 }
859 
860 // -----------------------------------------------------------------------------
861 
862 // only update status bar
UpdateStatus()863 void MainFrame::UpdateStatus()
864 {
865     if (inscript || currlayer->undoredo->doingscriptchanges) return;
866 
867     if (!IsIconized()) {
868         if (showstatus) {
869             statusptr->CheckMouseLocation(infront);
870             statusptr->Refresh(false);
871         }
872     }
873 }
874 
875 // -----------------------------------------------------------------------------
876 
SimplifyTree(wxString & indir,wxTreeCtrl * treectrl,wxTreeItemId root)877 void MainFrame::SimplifyTree(wxString& indir, wxTreeCtrl* treectrl, wxTreeItemId root)
878 {
879     // delete old tree (except root)
880     treectrl->DeleteChildren(root);
881 
882     // remove any terminating separator
883     wxString dir = indir;
884     if (dir.Last() == wxFILE_SEP_PATH) dir.Truncate(dir.Length()-1);
885 
886     // append dir as only child
887     wxDirItemData* diritem = new wxDirItemData(dir, dir, true);
888     wxTreeItemId id;
889     id = treectrl->AppendItem(root, dir.AfterLast(wxFILE_SEP_PATH), 0, 0, diritem);
890     if ( diritem->HasFiles() || diritem->HasSubDirs() ) {
891         treectrl->SetItemHasChildren(id);
892         treectrl->Expand(id);
893         #ifndef __WXMSW__
894             // can cause crash on Windows
895             treectrl->ScrollTo(root);
896         #endif
897     }
898 
899     // select top folder so hitting left arrow can collapse it and won't cause an assert
900     wxTreeItemIdValue cookie;
901     id = treectrl->GetFirstChild(root, cookie);
902     if (id.IsOk()) treectrl->SelectItem(id);
903 }
904 
905 // -----------------------------------------------------------------------------
906 
907 // Define a window for right pane of split window:
908 
909 class RightWindow : public wxWindow
910 {
911 public:
RightWindow(wxWindow * parent)912     RightWindow(wxWindow* parent)
913     : wxWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
914                 wxNO_BORDER |
915 #ifdef __WXMSW__
916                 // need this to avoid layer/edit/timeline bar buttons flashing on Windows
917                 wxNO_FULL_REPAINT_ON_RESIZE
918 #else
919                 // better for Mac and Linux
920                 wxFULL_REPAINT_ON_RESIZE
921 #endif
922                )
923     {
924 #ifdef __WXGTK__
925         // avoid erasing background on GTK+
926         SetBackgroundStyle(wxBG_STYLE_CUSTOM);
927 #endif
928     }
~RightWindow()929     ~RightWindow() {}
930 
931     // event handlers
932     void OnSize(wxSizeEvent& event);
933     void OnEraseBackground(wxEraseEvent& event);
934 
935     DECLARE_EVENT_TABLE()
936 };
937 
BEGIN_EVENT_TABLE(RightWindow,wxWindow)938 BEGIN_EVENT_TABLE(RightWindow, wxWindow)
939 EVT_ERASE_BACKGROUND (RightWindow::OnEraseBackground)
940 EVT_SIZE             (RightWindow::OnSize)
941 END_EVENT_TABLE()
942 
943 // -----------------------------------------------------------------------------
944 
945 void RightWindow::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
946 {
947     // do nothing because layer/edit/timeline bars and viewport cover all of right pane
948 }
949 
950 // -----------------------------------------------------------------------------
951 
952 static bool ok_to_resize = true;
953 
OnSize(wxSizeEvent & event)954 void RightWindow::OnSize(wxSizeEvent& event)
955 {
956     // we need this to update right pane when dragging sash, or when toggling left pane
957     if (mainptr && ok_to_resize) {
958         mainptr->ResizeBigView();
959     }
960     event.Skip();
961 }
962 
963 // -----------------------------------------------------------------------------
964 
965 RightWindow* rightpane;
966 
RightPane()967 wxWindow* MainFrame::RightPane()
968 {
969     return rightpane;
970 }
971 
972 // -----------------------------------------------------------------------------
973 
ResizeSplitWindow(int wd,int ht)974 void MainFrame::ResizeSplitWindow(int wd, int ht)
975 {
976     int x = showtool ? TOOLBARWD : 0;
977     int y = statusptr->statusht;
978     int w = showtool ? wd - TOOLBARWD : wd;
979     int h = ht > statusptr->statusht ? ht - statusptr->statusht : 0;
980 
981     if (w < 0) w = 0;
982     if (h < 0) h = 0;
983 
984     // following will call RightWindow::OnSize so avoid ResizeBigView being called twice
985     ok_to_resize = false;
986     splitwin->SetSize(x, y, w, h);
987     ok_to_resize = true;
988 
989     ResizeBigView();
990 }
991 
992 // -----------------------------------------------------------------------------
993 
ResizeBigView()994 void MainFrame::ResizeBigView()
995 {
996     int wd, ht;
997     rightpane->GetClientSize(&wd, &ht);
998 
999     if (wd > 0 && ht > 0) {
1000         // resize layer/edit/timeline bars and main viewport window
1001         int y = 0;
1002         if (showlayer) {
1003             ResizeLayerBar(wd);
1004             y += LayerBarHeight();
1005             ht -= LayerBarHeight();
1006         }
1007         if (showedit) {
1008             ResizeEditBar(wd);
1009             y += EditBarHeight();
1010             ht -= EditBarHeight();
1011         }
1012         if (showtimeline) {
1013             ht -= TimelineBarHeight();
1014             // timeline bar goes underneath viewport
1015             ResizeTimelineBar(y + ht, wd);
1016         }
1017 
1018         if (!fullscreen && showscrollbars) {
1019             // make room for hbar and vbar
1020             wd -= 15;
1021             ht -= 15;
1022             if (wd < 0) wd = 0;
1023             if (ht < 0) ht = 0;
1024             // resize hbar and vbar
1025 			#ifdef __WXMSW__
1026 			    // extend scroll bar to right edge to avoid seeing junk
1027 				// in bottom right corner (need to figure out how to create a gripper!!!)
1028                 hbar->SetSize(0, y + ht, wd+15, 15);
1029 			#else
1030 		        hbar->SetSize(0, y + ht, wd, 15);
1031 			#endif
1032             vbar->SetSize(wd, y, 15, ht);
1033         }
1034 
1035         if (wd < 0) wd = 0;
1036         if (ht < 0) ht = 0;
1037         bigview->SetSize(0, y, wd, ht);
1038     }
1039 }
1040 
1041 // -----------------------------------------------------------------------------
1042 
ResizeStatusBar(int wd,int ht)1043 void MainFrame::ResizeStatusBar(int wd, int ht)
1044 {
1045     wxUnusedVar(ht);
1046     // assume showstatus is true
1047     statusptr->statusht = showexact ? STATUS_EXHT : STATUS_HT;
1048     if (showtool) wd -= TOOLBARWD;
1049     if (wd < 0) wd = 0;
1050     statusptr->SetSize(showtool ? TOOLBARWD : 0, 0, wd, statusptr->statusht);
1051 }
1052 
1053 // -----------------------------------------------------------------------------
1054 
ToggleStatusBar()1055 void MainFrame::ToggleStatusBar()
1056 {
1057     showstatus = !showstatus;
1058     int wd, ht;
1059     GetClientSize(&wd, &ht);
1060     if (wd < 0) wd = 0;
1061     if (ht < 0) ht = 0;
1062     if (showstatus) {
1063         ResizeStatusBar(wd, ht);
1064     } else {
1065         statusptr->statusht = 0;
1066         statusptr->SetSize(0, 0, 0, 0);
1067     }
1068     ResizeSplitWindow(wd, ht);
1069     UpdateEverything();
1070 }
1071 
1072 // -----------------------------------------------------------------------------
1073 
ToggleExactNumbers()1074 void MainFrame::ToggleExactNumbers()
1075 {
1076     showexact = !showexact;
1077     int wd, ht;
1078     GetClientSize(&wd, &ht);
1079     if (wd < 0) wd = 0;
1080     if (ht < 0) ht = 0;
1081     if (showstatus) {
1082         ResizeStatusBar(wd, ht);
1083         ResizeSplitWindow(wd, ht);
1084         UpdateEverything();
1085     } else if (showexact) {
1086         // show the status bar using new size
1087         ToggleStatusBar();
1088     } else {
1089         UpdateMenuItems();
1090     }
1091 }
1092 
1093 // -----------------------------------------------------------------------------
1094 
ToggleToolBar()1095 void MainFrame::ToggleToolBar()
1096 {
1097     showtool = !showtool;
1098     int wd, ht;
1099     GetClientSize(&wd, &ht);
1100     if (wd < 0) wd = 0;
1101     if (ht < 0) ht = 0;
1102     if (showstatus) {
1103         ResizeStatusBar(wd, ht);
1104     }
1105     if (showtool) {
1106         // resize tool bar in case window was made larger while tool bar hidden
1107         toolbarptr->SetSize(0, 0, TOOLBARWD, ht);
1108     }
1109     ResizeSplitWindow(wd, ht);
1110     toolbarptr->Show(showtool);
1111 }
1112 
1113 // -----------------------------------------------------------------------------
1114 
ToggleScrollBars()1115 void MainFrame::ToggleScrollBars()
1116 {
1117     showscrollbars = !showscrollbars;
1118     if (showscrollbars) {
1119         hbar->Show(true);
1120         vbar->Show(true);
1121     } else {
1122         // hide scroll bars
1123         hbar->Show(false);
1124         vbar->Show(false);
1125     }
1126     // adjust size of viewport
1127     int wd, ht;
1128     GetClientSize(&wd, &ht);
1129     ResizeSplitWindow(wd, ht);
1130     UpdateEverything();
1131 }
1132 
1133 // -----------------------------------------------------------------------------
1134 
ToggleFullScreen()1135 void MainFrame::ToggleFullScreen()
1136 {
1137     static bool restorestatusbar;    // restore status bar at end of full screen mode?
1138     static bool restorelayerbar;     // restore layer bar?
1139     static bool restoreeditbar;      // restore edit bar?
1140     static bool restoretimelinebar;  // restore timeline bar?
1141     static bool restoretoolbar;      // restore tool bar?
1142     static bool restorefiledir;      // restore file directory?
1143 
1144     if (!fullscreen) {
1145         // save current location and size for use in SavePrefs
1146         wxRect r = GetRect();
1147         mainx = r.x;
1148         mainy = r.y;
1149         mainwd = r.width;
1150         mainht = r.height;
1151     }
1152 
1153     fullscreen = !fullscreen;
1154     ShowFullScreen(fullscreen, wxFULLSCREEN_NOMENUBAR | wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION);
1155 
1156     if (fullscreen) {
1157         if (showscrollbars) {
1158             // hide scroll bars
1159             hbar->Show(false);
1160             vbar->Show(false);
1161         }
1162 
1163         // hide status bar if necessary
1164         restorestatusbar = showstatus;
1165         if (restorestatusbar) {
1166             showstatus = false;
1167             statusptr->statusht = 0;
1168             statusptr->SetSize(0, 0, 0, 0);
1169         }
1170 
1171         // hide layer bar if necessary
1172         restorelayerbar = showlayer;
1173         if (restorelayerbar) {
1174             ToggleLayerBar();
1175         }
1176 
1177         // hide edit bar if necessary
1178         restoreeditbar = showedit;
1179         if (restoreeditbar) {
1180             ToggleEditBar();
1181         }
1182 
1183         // hide timeline bar if necessary
1184         restoretimelinebar = showtimeline;
1185         if (restoretimelinebar) {
1186             ToggleTimelineBar();
1187         }
1188 
1189         // hide tool bar if necessary
1190         restoretoolbar = showtool;
1191         if (restoretoolbar) {
1192             ToggleToolBar();
1193         }
1194 
1195         // hide file directory if necessary
1196         restorefiledir = showfiles;
1197         if (restorefiledir) {
1198             dirwinwd = splitwin->GetSashPosition();
1199             splitwin->Unsplit(filectrl);
1200             showfiles = false;
1201         }
1202 
1203     } else {
1204         // first show tool bar if necessary
1205         if (restoretoolbar && !showtool) {
1206             ToggleToolBar();
1207             if (showstatus) {
1208                 // reduce width of status bar below
1209                 restorestatusbar = true;
1210             }
1211         }
1212 
1213         // show status bar if necessary;
1214         // note that even if it's visible we may have to resize width
1215         if (restorestatusbar) {
1216             showstatus = true;
1217             int wd, ht;
1218             GetClientSize(&wd, &ht);
1219             ResizeStatusBar(wd, ht);
1220         }
1221 
1222         // show layer bar if necessary
1223         if (restorelayerbar && !showlayer) ToggleLayerBar();
1224 
1225         // show edit bar if necessary
1226         if (restoreeditbar && !showedit) ToggleEditBar();
1227 
1228         // show timeline bar if necessary
1229         if (restoretimelinebar && !showtimeline) ToggleTimelineBar();
1230 
1231         // restore file directory if necessary
1232         if ( restorefiledir && !splitwin->IsSplit() ) {
1233             splitwin->SplitVertically(filectrl, RightPane(), dirwinwd);
1234             showfiles = true;
1235         }
1236 
1237         if (showscrollbars) {
1238             // restore scroll bars
1239             hbar->Show(true);
1240             vbar->Show(true);
1241         }
1242     }
1243 
1244     // adjust size of viewport (and file directory if visible)
1245     int wd, ht;
1246     GetClientSize(&wd, &ht);
1247     ResizeSplitWindow(wd, ht);
1248     UpdateEverything();
1249 }
1250 
1251 // -----------------------------------------------------------------------------
1252 
ToggleOverlay()1253 void MainFrame::ToggleOverlay()
1254 {
1255     showoverlay = !showoverlay;
1256     UpdateEverything();
1257 }
1258 
1259 // -----------------------------------------------------------------------------
1260 
DeleteOverlay()1261 void MainFrame::DeleteOverlay()
1262 {
1263     curroverlay->DeleteOverlay();
1264     UpdateEverything();
1265 }
1266 
1267 // -----------------------------------------------------------------------------
1268 
ToggleAllowUndo()1269 void MainFrame::ToggleAllowUndo()
1270 {
1271     if (generating) {
1272         command_pending = true;
1273         cmdevent.SetId(ID_NO_UNDO);
1274         Stop();
1275         return;
1276     }
1277 
1278     allowundo = !allowundo;
1279     if (allowundo) {
1280         if (currlayer->algo->getGeneration() > currlayer->startgen) {
1281             // undo list is empty but user can Reset, so add a generating change
1282             // to undo list so user can Undo or Reset (and then Redo if they wish)
1283             currlayer->undoredo->AddGenChange();
1284         }
1285     } else {
1286         currlayer->undoredo->ClearUndoRedo();
1287         // don't clear undo/redo history for other layers here; only do it
1288         // if allowundo is false when user switches to another layer
1289     }
1290 }
1291 
1292 // -----------------------------------------------------------------------------
1293 
ShowPatternInfo()1294 void MainFrame::ShowPatternInfo()
1295 {
1296     if (viewptr->waitingforclick || currlayer->currfile.IsEmpty()) return;
1297     ShowInfo(currlayer->currfile);
1298 }
1299 
1300 // -----------------------------------------------------------------------------
1301 
1302 // event table and handlers for main window:
1303 
BEGIN_EVENT_TABLE(MainFrame,wxFrame)1304 BEGIN_EVENT_TABLE(MainFrame, wxFrame)
1305 EVT_MENU                (wxID_ANY,      MainFrame::OnMenu)
1306 EVT_SET_FOCUS           (               MainFrame::OnSetFocus)
1307 EVT_ACTIVATE            (               MainFrame::OnActivate)
1308 EVT_IDLE                (               MainFrame::OnIdle)
1309 EVT_SIZE                (               MainFrame::OnSize)
1310 EVT_TREE_SEL_CHANGED    (wxID_TREECTRL, MainFrame::OnDirTreeSelection)
1311 EVT_SPLITTER_DCLICK     (wxID_ANY,      MainFrame::OnSashDblClick)
1312 EVT_TIMER               (ID_GENTIMER,   MainFrame::OnGenTimer)
1313 EVT_TIMER               (ID_OPENTIMER,  MainFrame::OnOpenTimer)
1314 EVT_CLOSE               (               MainFrame::OnClose)
1315 EVT_COMMAND_SCROLL      (wxID_ANY,      MainFrame::OnScroll)
1316 END_EVENT_TABLE()
1317 
1318 // -----------------------------------------------------------------------------
1319 
1320 void MainFrame::OnMenu(wxCommandEvent& event)
1321 {
1322     showbanner = false;
1323     if (keepmessage) {
1324         // don't clear message created by script while generating a pattern
1325         keepmessage = false;
1326     } else {
1327         statusptr->ClearMessage();
1328     }
1329 
1330     int id = event.GetId();
1331     switch (id) {
1332         // File menu
1333         case wxID_NEW:          NewPattern(); break;
1334         case wxID_OPEN:         OpenPattern(); break;
1335         case ID_OPEN_CLIP:      OpenClipboard(); break;
1336         case wxID_SAVE:         SavePattern(); break;
1337         case ID_SAVE_XRLE:      savexrle = !savexrle; break;
1338         case ID_RUN_SCRIPT:     OpenScript(); break;
1339         case ID_RUN_CLIP:       RunClipboard(); break;
1340         case ID_SHOW_FILES:     ToggleShowFiles(); break;
1341         case ID_FILE_DIR:       ChangeFileDir(); break;
1342         case wxID_PREFERENCES:  ShowPrefsDialog(); break;
1343         case wxID_EXIT:         QuitApp(); break;
1344 
1345         // Edit menu
1346         case ID_UNDO:           currlayer->undoredo->UndoChange(); break;
1347         case ID_REDO:           currlayer->undoredo->RedoChange(); break;
1348         case ID_NO_UNDO:        ToggleAllowUndo(); break;
1349         case ID_CUT:            viewptr->CutSelection(); break;
1350         case ID_COPY:           viewptr->CopySelection(); break;
1351         case ID_CLEAR:          viewptr->ClearSelection(); break;
1352         case ID_OUTSIDE:        viewptr->ClearOutsideSelection(); break;
1353         case ID_PASTE:          viewptr->PasteClipboard(false); break;
1354         case ID_PASTE_SEL:      viewptr->PasteClipboard(true); break;
1355         case ID_PL_TL:          SetPasteLocation("TopLeft"); break;
1356         case ID_PL_TR:          SetPasteLocation("TopRight"); break;
1357         case ID_PL_BR:          SetPasteLocation("BottomRight"); break;
1358         case ID_PL_BL:          SetPasteLocation("BottomLeft"); break;
1359         case ID_PL_MID:         SetPasteLocation("Middle"); break;
1360         case ID_PM_AND:         SetPasteMode("And"); break;
1361         case ID_PM_COPY:        SetPasteMode("Copy"); break;
1362         case ID_PM_OR:          SetPasteMode("Or"); break;
1363         case ID_PM_XOR:         SetPasteMode("Xor"); break;
1364         case ID_SELECTALL:      viewptr->SelectAll(); break;
1365         case ID_REMOVE:         viewptr->RemoveSelection(); break;
1366         case ID_SHRINK:         viewptr->ShrinkSelection(false); break;
1367         case ID_SHRINKFIT:      viewptr->ShrinkSelection(true); break;
1368         case ID_RANDOM:         viewptr->RandomFill(); break;
1369         case ID_FLIPTB:         viewptr->FlipSelection(true); break;
1370         case ID_FLIPLR:         viewptr->FlipSelection(false); break;
1371         case ID_ROTATEC:        viewptr->RotateSelection(true); break;
1372         case ID_ROTATEA:        viewptr->RotateSelection(false); break;
1373         case ID_DRAW:           viewptr->SetCursorMode(curs_pencil); break;
1374         case ID_PICK:           viewptr->SetCursorMode(curs_pick); break;
1375         case ID_SELECT:         viewptr->SetCursorMode(curs_cross); break;
1376         case ID_MOVE:           viewptr->SetCursorMode(curs_hand); break;
1377         case ID_ZOOMIN:         viewptr->SetCursorMode(curs_zoomin); break;
1378         case ID_ZOOMOUT:        viewptr->SetCursorMode(curs_zoomout); break;
1379 
1380         // Control menu
1381         case ID_START:          StartOrStop(); break;
1382         case ID_NEXT:           NextGeneration(false); break;
1383         case ID_STEP:           NextGeneration(true); break;
1384         case ID_RESET:          ResetPattern(); break;
1385         case ID_SETGEN:         SetGeneration(); break;
1386         case ID_FASTER:         GoFaster(); break;
1387         case ID_SLOWER:         GoSlower(); break;
1388         case ID_SETBASE:        SetBaseStep(); break;
1389         case ID_AUTO:           ToggleAutoFit(); break;
1390         case ID_HYPER:          ToggleHyperspeed(); break;
1391         case ID_HINFO:          ToggleHashInfo(); break;
1392         case ID_SHOW_POP:       ToggleShowPopulation(); break;
1393         case ID_RECORD:         StartStopRecording(); break;
1394         case ID_DELTIME:        DeleteTimeline(); break;
1395         case ID_CONVERT:        ConvertOldRules(); break;
1396         case ID_SETRULE:        ShowRuleDialog(); break;
1397 
1398         // View menu
1399         case ID_FULL:           ToggleFullScreen(); break;
1400         case ID_FIT:            viewptr->FitPattern(); break;
1401         case ID_FIT_SEL:        viewptr->FitSelection(); break;
1402         case ID_MIDDLE:         viewptr->ViewOrigin(); break;
1403         case ID_RESTORE00:      viewptr->RestoreOrigin(); break;
1404         case wxID_ZOOM_IN:      viewptr->ZoomIn(); break;
1405         case wxID_ZOOM_OUT:     viewptr->ZoomOut(); break;
1406         case ID_SCALE_1:        viewptr->SetPixelsPerCell(1); break;
1407         case ID_SCALE_2:        viewptr->SetPixelsPerCell(2); break;
1408         case ID_SCALE_4:        viewptr->SetPixelsPerCell(4); break;
1409         case ID_SCALE_8:        viewptr->SetPixelsPerCell(8); break;
1410         case ID_SCALE_16:       viewptr->SetPixelsPerCell(16); break;
1411         case ID_SCALE_32:       viewptr->SetPixelsPerCell(32); break;
1412         case ID_TOOL_BAR:       ToggleToolBar(); break;
1413         case ID_LAYER_BAR:      ToggleLayerBar(); break;
1414         case ID_EDIT_BAR:       ToggleEditBar(); break;
1415         case ID_ALL_STATES:     ToggleAllStates(); break;
1416         case ID_STATUS_BAR:     ToggleStatusBar(); break;
1417         case ID_EXACT:          ToggleExactNumbers(); break;
1418         case ID_GRID:           viewptr->ToggleGridLines(); break;
1419         case ID_ICONS:          viewptr->ToggleCellIcons(); break;
1420         case ID_INVERT:         viewptr->ToggleCellColors(); break;
1421         case ID_SMARTSCALE:     viewptr->ToggleSmarterScaling(); break;
1422         case ID_TIMELINE:       ToggleTimelineBar(); break;
1423         case ID_SCROLL:         ToggleScrollBars(); break;
1424         case ID_INFO:           ShowPatternInfo(); break;
1425 
1426         // Layer menu
1427         case ID_SAVE_OVERLAY:   SaveOverlay(); break;
1428         case ID_SHOW_OVERLAY:   ToggleOverlay(); break;
1429         case ID_DEL_OVERLAY:    DeleteOverlay(); break;
1430         case ID_ADD_LAYER:      AddLayer(); break;
1431         case ID_CLONE:          CloneLayer(); break;
1432         case ID_DUPLICATE:      DuplicateLayer(); break;
1433         case ID_DEL_LAYER:      DeleteLayer(); break;
1434         case ID_DEL_OTHERS:     DeleteOtherLayers(); break;
1435         case ID_MOVE_LAYER:     MoveLayerDialog(); break;
1436         case ID_NAME_LAYER:     NameLayerDialog(); break;
1437         case ID_SET_COLORS:     SetLayerColors(); break;
1438         case ID_SYNC_VIEW:      ToggleSyncViews(); break;
1439         case ID_SYNC_CURS:      ToggleSyncCursors(); break;
1440         case ID_STACK:          ToggleStackLayers(); break;
1441         case ID_TILE:           ToggleTileLayers(); break;
1442 
1443         // Help menu
1444         case ID_HELP_INDEX:     ShowHelp(_("Help/index.html")); break;
1445         case ID_HELP_INTRO:     ShowHelp(_("Help/intro.html")); break;
1446         case ID_HELP_TIPS:      ShowHelp(_("Help/tips.html")); break;
1447         case ID_HELP_ALGOS:     ShowHelp(_("Help/algos.html")); break;
1448         case ID_HELP_LEXICON:   ShowHelp(_("Help/Lexicon/lex.htm")); break;
1449         case ID_HELP_ARCHIVES:  ShowHelp(_("Help/archives.html")); break;
1450         case ID_HELP_LUA:       ShowHelp(_("Help/lua.html")); break;
1451         case ID_HELP_OVERLAY:   ShowHelp(_("Help/overlay.html")); break;
1452         case ID_HELP_PYTHON:    ShowHelp(_("Help/python.html")); break;
1453         case ID_HELP_KEYBOARD:  ShowHelp(SHOW_KEYBOARD_SHORTCUTS); break;
1454         case ID_HELP_MOUSE:     ShowHelp(_("Help/mouse.html")); break;
1455         case ID_HELP_FILE:      ShowHelp(_("Help/file.html")); break;
1456         case ID_HELP_EDIT:      ShowHelp(_("Help/edit.html")); break;
1457         case ID_HELP_CONTROL:   ShowHelp(_("Help/control.html")); break;
1458         case ID_HELP_VIEW:      ShowHelp(_("Help/view.html")); break;
1459         case ID_HELP_LAYER:     ShowHelp(_("Help/layer.html")); break;
1460         case ID_HELP_HELP:      ShowHelp(_("Help/help.html")); break;
1461         case ID_HELP_REFS:      ShowHelp(_("Help/refs.html")); break;
1462         case ID_HELP_FORMATS:   ShowHelp(_("Help/formats.html")); break;
1463         case ID_HELP_BOUNDED:   ShowHelp(_("Help/bounded.html")); break;
1464         case ID_HELP_PROBLEMS:  ShowHelp(_("Help/problems.html")); break;
1465         case ID_HELP_CHANGES:   ShowHelp(_("Help/changes.html")); break;
1466         case ID_HELP_CREDITS:   ShowHelp(_("Help/credits.html")); break;
1467         case ID_HELP_BUTT:      ShowHelp(wxEmptyString); break;
1468         case wxID_ABOUT:        ShowAboutBox(); break;
1469 
1470         // Open/Run Recent submenus
1471         case ID_CLEAR_MISSING_PATTERNS:  ClearMissingPatterns(); break;
1472         case ID_CLEAR_ALL_PATTERNS:      ClearAllPatterns(); break;
1473         case ID_CLEAR_MISSING_SCRIPTS:   ClearMissingScripts(); break;
1474         case ID_CLEAR_ALL_SCRIPTS:       ClearAllScripts(); break;
1475 
1476         default:
1477             if ( id > ID_OPEN_RECENT && id <= ID_OPEN_RECENT + numpatterns ) {
1478                 OpenRecentPattern(id);
1479 
1480             } else if ( id > ID_RUN_RECENT && id <= ID_RUN_RECENT + numscripts ) {
1481                 OpenRecentScript(id);
1482 
1483             } else if ( id >= ID_ALGO0 && id <= ID_ALGOMAX ) {
1484                 int newtype = id - ID_ALGO0;
1485                 ChangeAlgorithm(newtype);
1486 
1487             } else if ( id >= ID_LAYER0 && id <= ID_LAYERMAX ) {
1488                 SetLayer(id - ID_LAYER0);
1489 
1490             } else {
1491                 // wxOSX app needs this to handle app menu items like "Hide Golly"
1492                 event.Skip();
1493             }
1494     }
1495 
1496     UpdateUserInterface();
1497 
1498     if (inscript) {
1499         // update viewport, status bar, scroll bars, window title
1500         inscript = false;
1501         UpdatePatternAndStatus();
1502         bigview->UpdateScrollBars();
1503         SetWindowTitle(wxEmptyString);
1504         inscript = true;
1505     }
1506 }
1507 
1508 // -----------------------------------------------------------------------------
1509 
OnSetFocus(wxFocusEvent & WXUNUSED (event))1510 void MainFrame::OnSetFocus(wxFocusEvent& WXUNUSED(event))
1511 {
1512     // this is never called in Mac app, presumably because it doesn't
1513     // make any sense for a wxFrame to get the keyboard focus
1514 
1515 #ifdef __WXMSW__
1516     // fix wxMSW problem: don't let main window get focus after being minimized
1517     if (viewptr) viewptr->SetFocus();
1518 #endif
1519 }
1520 
1521 // -----------------------------------------------------------------------------
1522 
OnActivate(wxActivateEvent & event)1523 void MainFrame::OnActivate(wxActivateEvent& event)
1524 {
1525     // IsActive() is not always reliable so we set infront flag
1526     infront = event.GetActive();
1527 
1528     if (viewptr->waitingforclick && !infront) {
1529         // cancel paste if main window is no longer in front
1530         viewptr->AbortPaste();
1531     }
1532 
1533     if (infront) {
1534         // we must only call UpdateMenuItems when main window is being activated
1535         // (otherwise menu problems will occur on Ubuntu when using Unity)
1536         UpdateUserInterface();
1537         viewptr->SetFocus();    // play safe
1538     }
1539 
1540     event.Skip();
1541 }
1542 
1543 // -----------------------------------------------------------------------------
1544 
OnSize(wxSizeEvent & event)1545 void MainFrame::OnSize(wxSizeEvent& event)
1546 {
1547 #ifdef __WXMSW__
1548     // save current location and size for use in SavePrefs if app
1549     // is closed when window is minimized
1550     wxRect r = GetRect();
1551     mainx = r.x;
1552     mainy = r.y;
1553     mainwd = r.width;
1554     mainht = r.height;
1555 #endif
1556 
1557     int wd, ht;
1558     GetClientSize(&wd, &ht);
1559     if (wd > 0 && ht > 0) {
1560         // toolbarptr/statusptr/viewptr might be NULL if OnSize is called from MainFrame ctor
1561         if (toolbarptr && showtool) {
1562             // adjust size of tool bar
1563             toolbarptr->SetSize(0, 0, TOOLBARWD, ht);
1564         }
1565         if (statusptr && showstatus) {
1566             // adjust size of status bar
1567             ResizeStatusBar(wd, ht);
1568         }
1569         if (viewptr && statusptr && ht > statusptr->statusht) {
1570             // adjust size of viewport (and file directory if visible)
1571             ResizeSplitWindow(wd, ht);
1572         }
1573     }
1574 
1575 #ifdef __WXGTK__
1576     // need to do default processing for menu bar and tool bar
1577     event.Skip();
1578 #else
1579     wxUnusedVar(event);
1580 #endif
1581 }
1582 
1583 // -----------------------------------------------------------------------------
1584 
1585 // avoid recursive call of OpenFile
1586 static bool inopen = false;
1587 
OnOpenTimer(wxTimerEvent & WXUNUSED (event))1588 void MainFrame::OnOpenTimer(wxTimerEvent& WXUNUSED(event))
1589 {
1590     if (inopen) {
1591         if (inscript && pass_file_events && pendingfiles.GetCount() > 0) {
1592             // OpenFile has called RunScript so pass a pending file
1593             // to the script after updating pendingfiles
1594             wxString filepath = pendingfiles[0];
1595             if (pendingfiles.GetCount() == 1) {
1596                 pendingfiles.Clear();
1597             } else {
1598                 pendingfiles.RemoveAt(0);
1599             }
1600             PassFileToScript(filepath);
1601         }
1602         return;
1603     }
1604     if (pendingfiles.GetCount() > 0) {
1605         size_t count = pendingfiles.GetCount();
1606         if (count >= 2 && pendingfiles[count-2] == pendingfiles[count-1]) {
1607             // avoid opening same file twice (can happen in wxMSW and wxMac)
1608             pendingfiles.RemoveAt(count-1);
1609         }
1610         wxString filepath = pendingfiles[0];
1611         // update pendingfiles before calling OpenFile
1612         if (pendingfiles.GetCount() == 1) {
1613             pendingfiles.Clear();
1614         } else {
1615             pendingfiles.RemoveAt(0);
1616         }
1617         // note that OnIdle will be called from OpenFile if it calls RunScript
1618         inopen = true;
1619         OpenFile(filepath);
1620         inopen = false;
1621     }
1622 }
1623 
1624 // -----------------------------------------------------------------------------
1625 
OnIdle(wxIdleEvent & event)1626 void MainFrame::OnIdle(wxIdleEvent& event)
1627 {
1628 #ifdef __WXMSW__
1629     if (set_focus) {
1630         set_focus = false;
1631         // calling SetFocus once doesn't stuff up layer bar buttons
1632         if (infront && viewptr) viewptr->SetFocus();
1633     }
1634 
1635     if (!editpath.IsEmpty()) {
1636         EditFile(editpath);
1637         editpath.Clear();
1638     }
1639 #else
1640     // ensure viewport window has keyboard focus if main window is active;
1641     // note that we can't do this on Windows because it stuffs up clicks
1642     // in layer bar buttons
1643     if (infront && viewptr) viewptr->SetFocus();
1644 #endif
1645 
1646     if (call_close) {
1647         call_close = false;
1648         Close(false);        // false allows OnClose handler to veto close
1649     } else {
1650         // process any pending script/pattern files
1651         if (pendingfiles.GetCount() > 0) {
1652             // there are problems with calling OpenFile in OnIdle, at least on Mac OS
1653             // (if it runs an interactive script then scroll bars don't immediately
1654             // update the viewport, and in some cases modal dialogs can't be closed),
1655             // so we call OpenFile from OnOpenTimer after a short delay
1656             opentimer->Start(10, wxTIMER_ONE_SHOT);
1657         }
1658     }
1659 
1660     event.Skip();
1661 }
1662 
1663 // -----------------------------------------------------------------------------
1664 
EditFile(const wxString & filepath)1665 void MainFrame::EditFile(const wxString& filepath)
1666 {
1667     // prompt user if text editor hasn't been set yet
1668     if (texteditor.IsEmpty()) {
1669         ChooseTextEditor(this, texteditor);
1670         if (texteditor.IsEmpty()) return;
1671     }
1672 
1673     // execute a command to open given file in user's preferred text editor
1674     wxString cmd = wxString::Format(wxT("\"%s\" \"%s\""), texteditor.c_str(), filepath.c_str());
1675     long result = wxExecute(cmd, wxEXEC_ASYNC);
1676 
1677 #if defined(__WXMSW__)
1678     // on Windows, wxExecute returns 0 if cmd fails
1679     if (result == 0)
1680 #elif defined(__WXMAC__)
1681     // on Mac, wxExecute returns -1 if cmd succeeds (bug, or wx docs are wrong)
1682     if (result != -1)
1683 #elif defined(__WXGTK__)
1684     // on Linux, wxExecute always returns a +ve number (pid?) if cmd fails OR succeeds (sheesh!)
1685     // but if it fails an error message appears in shell window
1686     if (result <= 0)
1687 #endif
1688     {
1689         wxString msg = _("Failed to open file in your preferred text editor.\n");
1690         msg += _("Try choosing a different editor in Preferences > File.");
1691         Warning(msg);
1692     }
1693 }
1694 
1695 // -----------------------------------------------------------------------------
1696 
1697 #if defined(__WXMAC__) && wxCHECK_VERSION(2,9,0)
1698     // wxMOD_CONTROL has been changed to mean Command key down (sheesh!)
1699     #define wxMOD_CONTROL wxMOD_RAW_CONTROL
1700     #define ControlDown RawControlDown
1701 #endif
1702 
OnTreeClick(wxMouseEvent & event)1703 void MainFrame::OnTreeClick(wxMouseEvent& event)
1704 {
1705     // set global flag for testing in OnDirTreeSelection
1706     edit_file = event.ControlDown() || event.RightDown();
1707 
1708     wxGenericDirCtrl* dirctrl = NULL;
1709     if (showfiles) dirctrl = mainptr->filectrl;
1710     if (dirctrl) {
1711         wxTreeCtrl* treectrl = dirctrl->GetTreeCtrl();
1712         if (treectrl) {
1713             // determine if an item was clicked
1714             wxPoint pt = event.GetPosition();
1715             int flags;
1716             wxTreeItemId id = treectrl->HitTest(pt, flags);
1717             if (!id.IsOk()) {
1718                 // click wasn't in any item
1719                 event.Skip();
1720                 return;
1721             }
1722 
1723             if (treectrl->ItemHasChildren(id)) {
1724                 // click was in a folder item
1725                 event.Skip();
1726                 return;
1727             }
1728 
1729             // check for click in an already selected item
1730             if (id == treectrl->GetSelection()) {
1731                 // force a selection change so OnDirTreeSelection gets called
1732                 treectrl->Unselect();
1733             }
1734 
1735             treectrl->SelectItem(id);
1736             // OnDirTreeSelection will be called -- don't call event.Skip()
1737         }
1738     }
1739 }
1740 
1741 // -----------------------------------------------------------------------------
1742 
OnDirTreeSelection(wxTreeEvent & event)1743 void MainFrame::OnDirTreeSelection(wxTreeEvent& event)
1744 {
1745     if (viewptr == NULL) return;   // ignore 1st call from MainFrame::MainFrame
1746 
1747     wxTreeItemId id = event.GetItem();
1748     if ( !id.IsOk() ) return;
1749 
1750     wxGenericDirCtrl* dirctrl = NULL;
1751     if (showfiles) dirctrl = filectrl;
1752     if (dirctrl == NULL) return;
1753 
1754     wxString filepath = dirctrl->GetFilePath();
1755 
1756     if ( filepath.IsEmpty() ) {
1757         // user clicked on a folder name
1758 
1759     } else if (edit_file) {
1760         // open file in text editor
1761 #ifdef __WXMSW__
1762         // call EditFile in later OnIdle to avoid right-click problem
1763         editpath = filepath;
1764 #else
1765         EditFile(filepath);
1766 #endif
1767 
1768     } else {
1769         // user clicked on a file name
1770 #ifdef __WXMAC__
1771         if ( !wxFileName::FileExists(filepath) ) {
1772             // avoid wxMac bug in wxGenericDirCtrl::GetFilePath; ie. file name
1773             // can contain "/" rather than ":" (but directory path is okay)
1774             wxFileName fullpath(filepath);
1775             wxString dir = fullpath.GetPath();
1776             wxString name = fullpath.GetFullName();
1777             wxString newpath = dir + wxT(":") + name;
1778             if ( wxFileName::FileExists(newpath) ) filepath = newpath;
1779         }
1780 #endif
1781         if ( inscript ) {
1782             if (pass_file_events) {
1783                 PassFileToScript(filepath);
1784             }
1785         } else {
1786             if (generating) {
1787                 command_pending = true;
1788                 if ( IsScriptFile(filepath) ) {
1789                     AddRecentScript(filepath);
1790                     cmdevent.SetId(ID_RUN_RECENT + 1);
1791                 } else {
1792                     AddRecentPattern(filepath);
1793                     cmdevent.SetId(ID_OPEN_RECENT + 1);
1794                 }
1795                 Stop();
1796             } else {
1797                 // load pattern or run script;
1798                 // call OpenFile in later OnIdle -- this prevents the main window
1799                 // moving in front of the help window if a script calls help(...)
1800                 pendingfiles.Add(filepath);
1801             }
1802         }
1803     }
1804 
1805 #ifdef __WXMSW__
1806     // call SetFocus in next OnIdle
1807     set_focus = true;
1808 #else
1809     viewptr->SetFocus();
1810 #endif
1811 }
1812 
1813 // -----------------------------------------------------------------------------
1814 
OnSashDblClick(wxSplitterEvent & WXUNUSED (event))1815 void MainFrame::OnSashDblClick(wxSplitterEvent& WXUNUSED(event))
1816 {
1817     // splitwin's sash was double-clicked
1818     ToggleShowFiles();
1819     UpdateMenuItems();
1820     UpdateToolBar();
1821 }
1822 
1823 // -----------------------------------------------------------------------------
1824 
OnScroll(wxScrollEvent & event)1825 void MainFrame::OnScroll(wxScrollEvent& event)
1826 {
1827     WXTYPE type = event.GetEventType();
1828     WXTYPE newtype = type;
1829 
1830     // build equivalent wxScrollWinEvent and post it to bigview so that
1831     // PatternView::OnScroll gets called and hbar/vbar are updated
1832 
1833     if (type == wxEVT_SCROLL_LINEUP)        newtype = wxEVT_SCROLLWIN_LINEUP;
1834     if (type == wxEVT_SCROLL_LINEDOWN)      newtype = wxEVT_SCROLLWIN_LINEDOWN;
1835     if (type == wxEVT_SCROLL_PAGEUP)        newtype = wxEVT_SCROLLWIN_PAGEUP;
1836     if (type == wxEVT_SCROLL_PAGEDOWN)      newtype = wxEVT_SCROLLWIN_PAGEDOWN;
1837     if (type == wxEVT_SCROLL_THUMBTRACK)    newtype = wxEVT_SCROLLWIN_THUMBTRACK;
1838     if (type == wxEVT_SCROLL_THUMBRELEASE)  newtype = wxEVT_SCROLLWIN_THUMBRELEASE;
1839 
1840     wxScrollWinEvent newevt(newtype, event.GetPosition(), event.GetOrientation());
1841     wxPostEvent(bigview->GetEventHandler(), newevt);
1842 }
1843 
1844 // -----------------------------------------------------------------------------
1845 
SaveCurrentLayer()1846 bool MainFrame::SaveCurrentLayer()
1847 {
1848     if (currlayer->algo->isEmpty()) return true;    // no need to save empty universe
1849     wxString query;
1850     if (numlayers > 1) {
1851         // make it clear which layer we're asking about
1852         query.Printf(_("Save the changes to layer %d: \"%s\"?"),
1853                      currindex, currlayer->currname.c_str());
1854     } else {
1855         query.Printf(_("Save the changes to \"%s\"?"), currlayer->currname.c_str());
1856     }
1857     int answer = SaveChanges(query, _("If you don't save, your changes will be lost."));
1858     switch (answer) {
1859         case 2: {
1860             bool result = SavePattern();        // true only if pattern was saved
1861             if (inscript && !result) {
1862                 PassKeyToScript(WXK_ESCAPE);    // abort script
1863             }
1864             return result;
1865         }
1866         case 1: {
1867             // don't save changes (but continue)
1868             return true;
1869         }
1870         default: {
1871             // answer == 0 (ie. user selected Cancel)
1872             if (inscript) {
1873                 PassKeyToScript(WXK_ESCAPE);    // abort script
1874             }
1875             return false;
1876         }
1877     }
1878 }
1879 
1880 // -----------------------------------------------------------------------------
1881 
OnClose(wxCloseEvent & event)1882 void MainFrame::OnClose(wxCloseEvent& event)
1883 {
1884     if (event.CanVeto()) {
1885         if (inscript || generating) Stop();
1886 
1887         // if insideYield is true then we might have been called from
1888         // StepPattern in OnGenTimer, so we need to call OnClose again via
1889         // OnIdle until insideYield is false and OnGenTimer has finished
1890         if (insideYield) {
1891             call_close = true;
1892             event.Veto();
1893             return;
1894         }
1895 
1896         // we can cancel the close event if necessary
1897         if (viewptr->waitingforclick) {
1898             event.Veto();
1899             return;
1900         }
1901 
1902         if (askonquit) {
1903             // keep track of which unique clones have been seen;
1904             // we add 1 below to allow for cloneseen[0] (always false)
1905             const int maxseen = MAX_LAYERS/2 + 1;
1906             bool cloneseen[maxseen];
1907             for (int i = 0; i < maxseen; i++) cloneseen[i] = false;
1908 
1909             // for each dirty layer, ask user if they want to save changes
1910             int oldindex = currindex;
1911             for (int i = 0; i < numlayers; i++) {
1912                 // only ask once for each unique clone (cloneid == 0 for non-clone)
1913                 int cid = GetLayer(i)->cloneid;
1914                 if (!cloneseen[cid]) {
1915                     if (cid > 0) cloneseen[cid] = true;
1916                     if (GetLayer(i)->dirty) {
1917                         if (i != currindex) SetLayer(i);
1918                         if (!SaveCurrentLayer()) {
1919                             // user cancelled "save changes" dialog so restore layer
1920                             SetLayer(oldindex);
1921                             UpdateUserInterface();
1922                             event.Veto();
1923                             return;
1924                         }
1925                     }
1926                 }
1927             }
1928         }
1929     }
1930 
1931     if (GetHelpFrame()) GetHelpFrame()->Close(true);
1932     if (GetInfoFrame()) GetInfoFrame()->Close(true);
1933 
1934     if (splitwin->IsSplit()) dirwinwd = splitwin->GetSashPosition();
1935 
1936     // if script is running or pattern is generating then CanVeto was false
1937     // (probably due to user logging out or shutting down system)
1938     // and we need to call exit below
1939     bool callexit = inscript || generating;
1940 
1941     // abort any running script and tidy up; also restores current directory
1942     // to location of Golly app so prefs file will be saved in correct place
1943     FinishScripting();
1944 
1945     // save main window location and other user preferences
1946     SavePrefs();
1947 
1948     // delete any temporary files
1949     if (wxFileExists(luafile)) wxRemoveFile(luafile);
1950     if (wxFileExists(perlfile)) wxRemoveFile(perlfile);
1951     if (wxFileExists(pythonfile)) wxRemoveFile(pythonfile);
1952     for (int i = 0; i < numlayers; i++) {
1953         Layer* layer = GetLayer(i);
1954         if (wxFileExists(layer->tempstart)) wxRemoveFile(layer->tempstart);
1955         // clear all undo/redo history for this layer
1956         layer->undoredo->ClearUndoRedo();
1957     }
1958 
1959     if (wxFileName::DirExists(tempdir)) {
1960         // delete all files in tempdir (we assume it has no subdirs)
1961         wxDir* dir = new wxDir(tempdir);
1962         wxArrayString files;
1963         wxString filename;
1964         bool more = dir->GetFirst(&filename);
1965         while (more) {
1966             files.Add(tempdir + filename);
1967             more = dir->GetNext(&filename);
1968         }
1969         // delete wxDir object now otherwise Rmdir below will fail on Windows
1970         delete dir;
1971         for (size_t n = 0; n < files.GetCount(); n++) {
1972             wxRemoveFile(files[n]);
1973         }
1974         // delete the (hopefully) empty tempdir
1975         if (!wxFileName::Rmdir(tempdir)) {
1976             Warning(_("Could not delete temporary directory:\n") + tempdir);
1977         }
1978     }
1979 
1980     // allow clipboard data to persist after app exits
1981     // (needed on Windows, not needed on Mac, doesn't work on Linux -- sheesh!)
1982     if (wxTheClipboard->Open()) {
1983         wxTheClipboard->Flush();
1984         wxTheClipboard->Close();
1985     }
1986 
1987     // avoid possible error message or seg fault
1988     if (callexit) exit(0);
1989 
1990     Destroy();
1991 
1992 #ifdef __WXGTK__
1993     // avoid seg fault on Linux (only happens if ctrl-Q is used to quit)
1994     exit(0);
1995 #endif
1996 
1997     // deallocate things (usually no need) to help find any real memory leaks
1998     if (debuglevel == 666) {
1999         wxGetApp().Yield(); // (because Destroy() doesn't act immediately)
2000         if (numlayers > 1) DeleteOtherLayers();
2001         delete currlayer;
2002         delete stopwatch;
2003         DeleteAlgorithms();
2004         FreeCursors();
2005         FreeDefaultColors();
2006     }
2007 }
2008 
2009 // -----------------------------------------------------------------------------
2010 
QuitApp()2011 void MainFrame::QuitApp()
2012 {
2013     Close(false);   // false allows OnClose handler to veto close
2014 }
2015 
2016 // -----------------------------------------------------------------------------
2017 
2018 #if wxUSE_DRAG_AND_DROP
2019 
2020 // derive a simple class for handling dropped files
2021 class DnDFile : public wxFileDropTarget
2022 {
2023 public:
DnDFile()2024     DnDFile() {}
2025     virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames);
2026 };
2027 
OnDropFiles(wxCoord,wxCoord,const wxArrayString & filenames)2028 bool DnDFile::OnDropFiles(wxCoord, wxCoord, const wxArrayString& filenames)
2029 {
2030     // bring app to front
2031 #ifdef __WXMAC__
2032     ProcessSerialNumber process;
2033     if ( GetCurrentProcess(&process) == noErr )
2034         SetFrontProcess(&process);
2035 #endif
2036 #ifdef __WXMSW__
2037     SetForegroundWindow( (HWND)mainptr->GetHandle() );
2038 #endif
2039     mainptr->Raise();
2040 
2041     size_t numfiles = filenames.GetCount();
2042     for ( size_t n = 0; n < numfiles; n++ ) {
2043         // safer to call OpenFile via OnIdle
2044         mainptr->pendingfiles.Add(filenames[n]);
2045     }
2046 
2047     return true;
2048 }
2049 
NewDropTarget()2050 wxDropTarget* MainFrame::NewDropTarget()
2051 {
2052     return new DnDFile();
2053 }
2054 
2055 #endif // wxUSE_DRAG_AND_DROP
2056 
2057 // -----------------------------------------------------------------------------
2058 
SetRandomFillPercentage()2059 void MainFrame::SetRandomFillPercentage()
2060 {
2061     // update Random Fill menu item to show randomfill value
2062     wxMenuBar* mbar = GetMenuBar();
2063     if (mbar) {
2064         wxString randlabel;
2065         randlabel.Printf(_("Random Fill (%d%c)"), randomfill, '%');
2066         randlabel += GetAccelerator(DO_RANDFILL);
2067         mbar->SetLabel(ID_RANDOM, randlabel);
2068     }
2069 }
2070 
2071 // -----------------------------------------------------------------------------
2072 
UpdateLayerItem(int index)2073 void MainFrame::UpdateLayerItem(int index)
2074 {
2075     // update name in given layer's menu item
2076     Layer* layer = GetLayer(index);
2077     wxMenuBar* mbar = GetMenuBar();
2078     if (mbar) {
2079         wxString label = wxEmptyString;
2080 
2081         // we no longer show index in front of name
2082         // label.Printf(_("%d: "), index);
2083 
2084         // display asterisk if pattern has been modified
2085         if (layer->dirty) label += wxT("*");
2086 
2087         int cid = layer->cloneid;
2088         while (cid > 0) {
2089             // display one or more "=" chars to indicate this is a cloned layer
2090             label += wxT("=");
2091             cid--;
2092         }
2093 
2094         if (layer->currname.IsEmpty()) {
2095             // this should never happen, but play safe
2096             label += wxT("UNKNOWN");
2097         } else {
2098             label += layer->currname;
2099         }
2100 
2101         // duplicate any ampersands so they appear
2102         label.Replace(wxT("&"), wxT("&&"));
2103 
2104         mbar->SetLabel(ID_LAYER0 + index, label);
2105 
2106         // also update name in corresponding layer button
2107         UpdateLayerButton(index, label);
2108     }
2109 }
2110 
2111 // -----------------------------------------------------------------------------
2112 
AppendLayerItem()2113 void MainFrame::AppendLayerItem()
2114 {
2115     wxMenuBar* mbar = GetMenuBar();
2116     if (mbar) {
2117         wxMenu* layermenu = mbar->GetMenu( mbar->FindMenu(_("Layer")) );
2118         if (layermenu) {
2119             // no point setting the item name here because UpdateLayerItem
2120             // will be called very soon
2121             layermenu->AppendCheckItem(ID_LAYER0 + numlayers - 1, _("foo"));
2122         } else {
2123             Warning(_("Could not find Layer menu!"));
2124         }
2125     }
2126 }
2127 
2128 // -----------------------------------------------------------------------------
2129 
RemoveLayerItem()2130 void MainFrame::RemoveLayerItem()
2131 {
2132     wxMenuBar* mbar = GetMenuBar();
2133     if (mbar) {
2134         wxMenu* layermenu = mbar->GetMenu( mbar->FindMenu(_("Layer")) );
2135         if (layermenu) {
2136             layermenu->Delete(ID_LAYER0 + numlayers);
2137         } else {
2138             Warning(_("Could not find Layer menu!"));
2139         }
2140     }
2141 }
2142 
2143 // -----------------------------------------------------------------------------
2144 
CreateMenus()2145 void MainFrame::CreateMenus()
2146 {
2147     wxMenu* fileMenu = new wxMenu();
2148     wxMenu* editMenu = new wxMenu();
2149     wxMenu* controlMenu = new wxMenu();
2150     wxMenu* viewMenu = new wxMenu();
2151     wxMenu* layerMenu = new wxMenu();
2152     wxMenu* helpMenu = new wxMenu();
2153 
2154     // create submenus
2155     wxMenu* plocSubMenu = new wxMenu();
2156     wxMenu* pmodeSubMenu = new wxMenu();
2157     wxMenu* cmodeSubMenu = new wxMenu();
2158     wxMenu* scaleSubMenu = new wxMenu();
2159 
2160     plocSubMenu->AppendCheckItem(ID_PL_TL,       _("Top Left"));
2161     plocSubMenu->AppendCheckItem(ID_PL_TR,       _("Top Right"));
2162     plocSubMenu->AppendCheckItem(ID_PL_BR,       _("Bottom Right"));
2163     plocSubMenu->AppendCheckItem(ID_PL_BL,       _("Bottom Left"));
2164     plocSubMenu->AppendCheckItem(ID_PL_MID,      _("Middle"));
2165 
2166     pmodeSubMenu->AppendCheckItem(ID_PM_AND,     _("And"));
2167     pmodeSubMenu->AppendCheckItem(ID_PM_COPY,    _("Copy"));
2168     pmodeSubMenu->AppendCheckItem(ID_PM_OR,      _("Or"));
2169     pmodeSubMenu->AppendCheckItem(ID_PM_XOR,     _("Xor"));
2170 
2171     cmodeSubMenu->AppendCheckItem(ID_DRAW,       _("Draw") + GetAccelerator(DO_CURSDRAW));
2172     cmodeSubMenu->AppendCheckItem(ID_PICK,       _("Pick") + GetAccelerator(DO_CURSPICK));
2173     cmodeSubMenu->AppendCheckItem(ID_SELECT,     _("Select") + GetAccelerator(DO_CURSSEL));
2174     cmodeSubMenu->AppendCheckItem(ID_MOVE,       _("Move") + GetAccelerator(DO_CURSMOVE));
2175     cmodeSubMenu->AppendCheckItem(ID_ZOOMIN,     _("Zoom In") + GetAccelerator(DO_CURSIN));
2176     cmodeSubMenu->AppendCheckItem(ID_ZOOMOUT,    _("Zoom Out") + GetAccelerator(DO_CURSOUT));
2177 
2178     scaleSubMenu->AppendCheckItem(ID_SCALE_1,    _("1:1") + GetAccelerator(DO_SCALE1));
2179     scaleSubMenu->AppendCheckItem(ID_SCALE_2,    _("1:2") + GetAccelerator(DO_SCALE2));
2180     scaleSubMenu->AppendCheckItem(ID_SCALE_4,    _("1:4") + GetAccelerator(DO_SCALE4));
2181     scaleSubMenu->AppendCheckItem(ID_SCALE_8,    _("1:8") + GetAccelerator(DO_SCALE8));
2182     scaleSubMenu->AppendCheckItem(ID_SCALE_16,   _("1:16") + GetAccelerator(DO_SCALE16));
2183     scaleSubMenu->AppendCheckItem(ID_SCALE_32,   _("1:32") + GetAccelerator(DO_SCALE32));
2184 
2185     fileMenu->Append(wxID_NEW,                   _("New Pattern") + GetAccelerator(DO_NEWPATT));
2186     fileMenu->AppendSeparator();
2187     fileMenu->Append(wxID_OPEN,                  _("Open Pattern...") + GetAccelerator(DO_OPENPATT));
2188     fileMenu->Append(ID_OPEN_CLIP,               _("Open Clipboard") + GetAccelerator(DO_OPENCLIP));
2189     fileMenu->Append(ID_OPEN_RECENT,             _("Open Recent"), patternSubMenu);
2190     fileMenu->AppendSeparator();
2191     fileMenu->Append(wxID_SAVE,                  _("Save Pattern...") + GetAccelerator(DO_SAVE));
2192     fileMenu->AppendCheckItem(ID_SAVE_XRLE,      _("Save Extended RLE") + GetAccelerator(DO_SAVEXRLE));
2193     fileMenu->AppendSeparator();
2194     fileMenu->Append(ID_RUN_SCRIPT,              _("Run Script...") + GetAccelerator(DO_RUNSCRIPT));
2195     fileMenu->Append(ID_RUN_CLIP,                _("Run Clipboard") + GetAccelerator(DO_RUNCLIP));
2196     fileMenu->Append(ID_RUN_RECENT,              _("Run Recent"), scriptSubMenu);
2197     fileMenu->AppendSeparator();
2198     fileMenu->AppendCheckItem(ID_SHOW_FILES,     _("Show Files") + GetAccelerator(DO_SHOWFILES));
2199     fileMenu->Append(ID_FILE_DIR,                _("Set File Folder...") + GetAccelerator(DO_FILEDIR));
2200 #if !defined(__WXOSX_COCOA__)
2201     fileMenu->AppendSeparator();
2202 #endif
2203     // on the Mac the wxID_PREFERENCES item is moved to the app menu
2204     fileMenu->Append(wxID_PREFERENCES,           _("Preferences...") + GetAccelerator(DO_PREFS));
2205 #if !defined(__WXOSX_COCOA__)
2206     fileMenu->AppendSeparator();
2207 #endif
2208     // on the Mac the wxID_EXIT item is moved to the app menu and the app name is appended to "Quit "
2209     fileMenu->Append(wxID_EXIT,                  _("Quit") + GetAccelerator(DO_QUIT));
2210 
2211     editMenu->Append(ID_UNDO,                    _("Undo") + GetAccelerator(DO_UNDO));
2212     editMenu->Append(ID_REDO,                    _("Redo") + GetAccelerator(DO_REDO));
2213     editMenu->AppendCheckItem(ID_NO_UNDO,        _("Disable Undo/Redo") + GetAccelerator(DO_DISABLE));
2214     editMenu->AppendSeparator();
2215     editMenu->Append(ID_CUT,                     _("Cut") + GetAccelerator(DO_CUT));
2216     editMenu->Append(ID_COPY,                    _("Copy") + GetAccelerator(DO_COPY));
2217     editMenu->Append(ID_CLEAR,                   _("Clear") + GetAccelerator(DO_CLEAR));
2218     editMenu->Append(ID_OUTSIDE,                 _("Clear Outside") + GetAccelerator(DO_CLEAROUT));
2219     editMenu->AppendSeparator();
2220     editMenu->Append(ID_PASTE,                   _("Paste") + GetAccelerator(DO_PASTE));
2221     editMenu->Append(ID_PMODE,                   _("Paste Mode"), pmodeSubMenu);
2222     editMenu->Append(ID_PLOCATION,               _("Paste Location"), plocSubMenu);
2223     editMenu->Append(ID_PASTE_SEL,               _("Paste to Selection") + GetAccelerator(DO_PASTESEL));
2224     editMenu->AppendSeparator();
2225     editMenu->Append(ID_SELECTALL,               _("Select All") + GetAccelerator(DO_SELALL));
2226     editMenu->Append(ID_REMOVE,                  _("Remove Selection") + GetAccelerator(DO_REMOVESEL));
2227     editMenu->Append(ID_SHRINK,                  _("Shrink Selection") + GetAccelerator(DO_SHRINK));
2228     // full label will be set later by SetRandomFillPercentage
2229     editMenu->Append(ID_RANDOM,                  _("Random Fill") + GetAccelerator(DO_RANDFILL));
2230     editMenu->Append(ID_FLIPTB,                  _("Flip Top-Bottom") + GetAccelerator(DO_FLIPTB));
2231     editMenu->Append(ID_FLIPLR,                  _("Flip Left-Right") + GetAccelerator(DO_FLIPLR));
2232     editMenu->Append(ID_ROTATEC,                 _("Rotate Clockwise") + GetAccelerator(DO_ROTATECW));
2233     editMenu->Append(ID_ROTATEA,                 _("Rotate Anticlockwise") + GetAccelerator(DO_ROTATEACW));
2234     editMenu->AppendSeparator();
2235     editMenu->Append(ID_CMODE,                   _("Cursor Mode"), cmodeSubMenu);
2236 
2237     controlMenu->Append(ID_START,                _("Start Generating") + GetAccelerator(DO_STARTSTOP));
2238     controlMenu->Append(ID_NEXT,                 _("Next Generation") + GetAccelerator(DO_NEXTGEN));
2239     controlMenu->Append(ID_STEP,                 _("Next Step") + GetAccelerator(DO_NEXTSTEP));
2240     controlMenu->AppendSeparator();
2241     controlMenu->Append(ID_RESET,                _("Reset") + GetAccelerator(DO_RESET));
2242     controlMenu->Append(ID_SETGEN,               _("Set Generation...") + GetAccelerator(DO_SETGEN));
2243     controlMenu->AppendSeparator();
2244     controlMenu->Append(ID_FASTER,               _("Faster") + GetAccelerator(DO_FASTER));
2245     controlMenu->Append(ID_SLOWER,               _("Slower") + GetAccelerator(DO_SLOWER));
2246     controlMenu->Append(ID_SETBASE,              _("Set Base Step...") + GetAccelerator(DO_SETBASE));
2247     controlMenu->AppendSeparator();
2248     controlMenu->AppendCheckItem(ID_AUTO,        _("Auto Fit") + GetAccelerator(DO_AUTOFIT));
2249     controlMenu->AppendCheckItem(ID_HYPER,       _("Hyperspeed") + GetAccelerator(DO_HYPER));
2250     controlMenu->AppendCheckItem(ID_HINFO,       _("Show Hash Info") + GetAccelerator(DO_HASHINFO));
2251     controlMenu->AppendCheckItem(ID_SHOW_POP,    _("Show Population") + GetAccelerator(DO_SHOWPOP));
2252     controlMenu->AppendSeparator();
2253     controlMenu->Append(ID_RECORD,               _("Start Recording") + GetAccelerator(DO_RECORD));
2254     controlMenu->Append(ID_DELTIME,              _("Delete Timeline") + GetAccelerator(DO_DELTIME));
2255     controlMenu->AppendSeparator();
2256     controlMenu->Append(ID_CONVERT,              _("Convert Old Rules"));   // rarely used, so no accelerator
2257     controlMenu->AppendSeparator();
2258     controlMenu->Append(ID_SETALGO,              _("Set Algorithm"), algomenu);
2259     controlMenu->Append(ID_SETRULE,              _("Set Rule...") + GetAccelerator(DO_SETRULE));
2260 
2261     viewMenu->Append(ID_FULL,                    _("Full Screen") + GetAccelerator(DO_FULLSCREEN));
2262     viewMenu->AppendSeparator();
2263     viewMenu->Append(ID_FIT,                     _("Fit Pattern") + GetAccelerator(DO_FIT));
2264     viewMenu->Append(ID_FIT_SEL,                 _("Fit Selection") + GetAccelerator(DO_FITSEL));
2265     viewMenu->Append(ID_MIDDLE,                  _("Middle") + GetAccelerator(DO_MIDDLE));
2266     viewMenu->Append(ID_RESTORE00,               _("Restore Origin") + GetAccelerator(DO_RESTORE00));
2267     viewMenu->AppendSeparator();
2268     viewMenu->Append(wxID_ZOOM_IN,               _("Zoom In") + GetAccelerator(DO_ZOOMIN));
2269     viewMenu->Append(wxID_ZOOM_OUT,              _("Zoom Out") + GetAccelerator(DO_ZOOMOUT));
2270     viewMenu->Append(ID_SET_SCALE,               _("Set Scale"), scaleSubMenu);
2271     viewMenu->AppendSeparator();
2272     viewMenu->AppendCheckItem(ID_TOOL_BAR,       _("Show Tool Bar") + GetAccelerator(DO_SHOWTOOL));
2273     viewMenu->AppendCheckItem(ID_LAYER_BAR,      _("Show Layer Bar") + GetAccelerator(DO_SHOWLAYER));
2274     viewMenu->AppendCheckItem(ID_EDIT_BAR,       _("Show Edit Bar") + GetAccelerator(DO_SHOWEDIT));
2275     viewMenu->AppendCheckItem(ID_ALL_STATES,     _("Show All States") + GetAccelerator(DO_SHOWSTATES));
2276     viewMenu->AppendCheckItem(ID_STATUS_BAR,     _("Show Status Bar") + GetAccelerator(DO_SHOWSTATUS));
2277     viewMenu->AppendCheckItem(ID_EXACT,          _("Show Exact Numbers") + GetAccelerator(DO_SHOWEXACT));
2278     viewMenu->AppendCheckItem(ID_GRID,           _("Show Grid Lines") + GetAccelerator(DO_SHOWGRID));
2279     viewMenu->AppendCheckItem(ID_ICONS,          _("Show Cell Icons") + GetAccelerator(DO_SHOWICONS));
2280     viewMenu->AppendCheckItem(ID_INVERT,         _("Invert Colors") + GetAccelerator(DO_INVERT));
2281     viewMenu->AppendCheckItem(ID_SMARTSCALE,     _("Smarter Scaling") + GetAccelerator(DO_SMARTSCALE));
2282     viewMenu->AppendCheckItem(ID_TIMELINE,       _("Show Timeline") + GetAccelerator(DO_SHOWTIME));
2283     viewMenu->AppendCheckItem(ID_SCROLL,         _("Show Scroll Bars") + GetAccelerator(DO_SHOWSCROLL));
2284     viewMenu->AppendSeparator();
2285     viewMenu->Append(ID_INFO,                    _("Pattern Info") + GetAccelerator(DO_INFO));
2286 
2287     layerMenu->Append(ID_SAVE_OVERLAY,           _("Save Overlay...") + GetAccelerator(DO_SAVEOVERLAY));
2288     layerMenu->AppendCheckItem(ID_SHOW_OVERLAY,  _("Show Overlay") + GetAccelerator(DO_SHOWOVERLAY));
2289     layerMenu->Append(ID_DEL_OVERLAY,            _("Delete Overlay") + GetAccelerator(DO_DELOVERLAY));
2290     layerMenu->AppendSeparator();
2291     layerMenu->Append(ID_ADD_LAYER,              _("Add Layer") + GetAccelerator(DO_ADD));
2292     layerMenu->Append(ID_CLONE,                  _("Clone Layer") + GetAccelerator(DO_CLONE));
2293     layerMenu->Append(ID_DUPLICATE,              _("Duplicate Layer") + GetAccelerator(DO_DUPLICATE));
2294     layerMenu->AppendSeparator();
2295     layerMenu->Append(ID_DEL_LAYER,              _("Delete Layer") + GetAccelerator(DO_DELETE));
2296     layerMenu->Append(ID_DEL_OTHERS,             _("Delete Other Layers") + GetAccelerator(DO_DELOTHERS));
2297     layerMenu->AppendSeparator();
2298     layerMenu->Append(ID_MOVE_LAYER,             _("Move Layer...") + GetAccelerator(DO_MOVELAYER));
2299     layerMenu->Append(ID_NAME_LAYER,             _("Name Layer...") + GetAccelerator(DO_NAMELAYER));
2300     layerMenu->Append(ID_SET_COLORS,             _("Set Layer Colors...") + GetAccelerator(DO_SETCOLORS));
2301     layerMenu->AppendSeparator();
2302     layerMenu->AppendCheckItem(ID_SYNC_VIEW,     _("Synchronize Views") + GetAccelerator(DO_SYNCVIEWS));
2303     layerMenu->AppendCheckItem(ID_SYNC_CURS,     _("Synchronize Cursors") + GetAccelerator(DO_SYNCCURS));
2304     layerMenu->AppendSeparator();
2305     layerMenu->AppendCheckItem(ID_STACK,         _("Stack Layers") + GetAccelerator(DO_STACK));
2306     layerMenu->AppendCheckItem(ID_TILE,          _("Tile Layers") + GetAccelerator(DO_TILE));
2307     layerMenu->AppendSeparator();
2308     layerMenu->AppendCheckItem(ID_LAYER0,        _("0"));
2309     // UpdateLayerItem will soon change the above item name
2310 
2311     helpMenu->Append(ID_HELP_INDEX,              _("Contents"));
2312     helpMenu->Append(ID_HELP_INTRO,              _("Introduction"));
2313     helpMenu->Append(ID_HELP_TIPS,               _("Hints and Tips"));
2314     helpMenu->Append(ID_HELP_ALGOS,              _("Algorithms"));
2315     helpMenu->Append(ID_HELP_LEXICON,            _("Life Lexicon"));
2316     helpMenu->Append(ID_HELP_ARCHIVES,           _("Online Archives"));
2317     helpMenu->AppendSeparator();
2318     helpMenu->Append(ID_HELP_LUA,                _("Lua Scripting"));
2319     helpMenu->Append(ID_HELP_OVERLAY,            _("Overlay"));
2320     helpMenu->Append(ID_HELP_PYTHON,             _("Python Scripting"));
2321     helpMenu->AppendSeparator();
2322     helpMenu->Append(ID_HELP_KEYBOARD,           _("Keyboard Shortcuts"));
2323     helpMenu->Append(ID_HELP_MOUSE,              _("Mouse Shortcuts"));
2324     helpMenu->AppendSeparator();
2325     helpMenu->Append(ID_HELP_FILE,               _("File Menu"));
2326     helpMenu->Append(ID_HELP_EDIT,               _("Edit Menu"));
2327     helpMenu->Append(ID_HELP_CONTROL,            _("Control Menu"));
2328     helpMenu->Append(ID_HELP_VIEW,               _("View Menu"));
2329     helpMenu->Append(ID_HELP_LAYER,              _("Layer Menu"));
2330     helpMenu->Append(ID_HELP_HELP,               _("Help Menu"));
2331     helpMenu->AppendSeparator();
2332     helpMenu->Append(ID_HELP_REFS,               _("References"));
2333     helpMenu->Append(ID_HELP_FORMATS,            _("File Formats"));
2334     helpMenu->Append(ID_HELP_BOUNDED,            _("Bounded Grids"));
2335     helpMenu->Append(ID_HELP_PROBLEMS,           _("Known Problems"));
2336     helpMenu->Append(ID_HELP_CHANGES,            _("Changes"));
2337     helpMenu->Append(ID_HELP_CREDITS,            _("Credits"));
2338 #ifndef __WXMAC__
2339     helpMenu->AppendSeparator();
2340 #endif
2341     // on the Mac the wxID_ABOUT item gets moved to the app menu
2342     helpMenu->Append(wxID_ABOUT,                 _("About Golly") + GetAccelerator(DO_ABOUT));
2343 
2344     // create the menu bar and append menus;
2345     // avoid using "&" in menu names because it prevents using keyboard shortcuts
2346     // like Alt+L on Linux
2347     wxMenuBar* menuBar = new wxMenuBar();
2348     menuBar->Append(fileMenu,     _("File"));
2349     menuBar->Append(editMenu,     _("Edit"));
2350     menuBar->Append(controlMenu,  _("Control"));
2351     menuBar->Append(viewMenu,     _("View"));
2352     menuBar->Append(layerMenu,    _("Layer"));
2353 #ifdef __WXMAC__
2354     // wxMac bug: need the "&" otherwise we get an extra Help menu
2355     menuBar->Append(helpMenu,  _("&Help"));
2356 #else
2357     menuBar->Append(helpMenu,  _("Help"));
2358 #endif
2359 
2360 #ifdef __WXMAC__
2361     // prevent Window menu being added automatically by wxMac 2.6.1+
2362     menuBar->SetAutoWindowMenu(false);
2363 #endif
2364 
2365     // attach menu bar to the frame
2366     SetMenuBar(menuBar);
2367 }
2368 
2369 // -----------------------------------------------------------------------------
2370 
UpdateMenuAccelerators()2371 void MainFrame::UpdateMenuAccelerators()
2372 {
2373     // keyboard shortcuts have changed, so update all menu item accelerators
2374     wxMenuBar* mbar = GetMenuBar();
2375     if (mbar) {
2376         // wxMac bug: these app menu items aren't updated (but user isn't likely
2377         // to change them so don't bother trying to fix the bug)
2378         SetAccelerator(mbar, wxID_ABOUT,         DO_ABOUT);
2379         SetAccelerator(mbar, wxID_PREFERENCES,   DO_PREFS);
2380         SetAccelerator(mbar, wxID_EXIT,          DO_QUIT);
2381 
2382         SetAccelerator(mbar, ID_DRAW,            DO_CURSDRAW);
2383         SetAccelerator(mbar, ID_PICK,            DO_CURSPICK);
2384         SetAccelerator(mbar, ID_SELECT,          DO_CURSSEL);
2385         SetAccelerator(mbar, ID_MOVE,            DO_CURSMOVE);
2386         SetAccelerator(mbar, ID_ZOOMIN,          DO_CURSIN);
2387         SetAccelerator(mbar, ID_ZOOMOUT,         DO_CURSOUT);
2388 
2389         SetAccelerator(mbar, ID_SCALE_1,         DO_SCALE1);
2390         SetAccelerator(mbar, ID_SCALE_2,         DO_SCALE2);
2391         SetAccelerator(mbar, ID_SCALE_4,         DO_SCALE4);
2392         SetAccelerator(mbar, ID_SCALE_8,         DO_SCALE8);
2393         SetAccelerator(mbar, ID_SCALE_16,        DO_SCALE16);
2394         SetAccelerator(mbar, ID_SCALE_32,        DO_SCALE32);
2395 
2396         SetAccelerator(mbar, wxID_NEW,           DO_NEWPATT);
2397         SetAccelerator(mbar, wxID_OPEN,          DO_OPENPATT);
2398         SetAccelerator(mbar, ID_OPEN_CLIP,       DO_OPENCLIP);
2399         SetAccelerator(mbar, wxID_SAVE,          DO_SAVE);
2400         SetAccelerator(mbar, ID_SAVE_XRLE,       DO_SAVEXRLE);
2401         SetAccelerator(mbar, ID_RUN_SCRIPT,      DO_RUNSCRIPT);
2402         SetAccelerator(mbar, ID_RUN_CLIP,        DO_RUNCLIP);
2403         SetAccelerator(mbar, ID_SHOW_FILES,      DO_SHOWFILES);
2404         SetAccelerator(mbar, ID_FILE_DIR,        DO_FILEDIR);
2405 
2406         SetAccelerator(mbar, ID_UNDO,            DO_UNDO);
2407         SetAccelerator(mbar, ID_REDO,            DO_REDO);
2408         SetAccelerator(mbar, ID_NO_UNDO,         DO_DISABLE);
2409         SetAccelerator(mbar, ID_CUT,             DO_CUT);
2410         SetAccelerator(mbar, ID_COPY,            DO_COPY);
2411         SetAccelerator(mbar, ID_CLEAR,           DO_CLEAR);
2412         SetAccelerator(mbar, ID_OUTSIDE,         DO_CLEAROUT);
2413         SetAccelerator(mbar, ID_PASTE,           DO_PASTE);
2414         SetAccelerator(mbar, ID_PASTE_SEL,       DO_PASTESEL);
2415         SetAccelerator(mbar, ID_SELECTALL,       DO_SELALL);
2416         SetAccelerator(mbar, ID_REMOVE,          DO_REMOVESEL);
2417         SetAccelerator(mbar, ID_SHRINK,          DO_SHRINK);
2418         SetAccelerator(mbar, ID_RANDOM,          DO_RANDFILL);
2419         SetAccelerator(mbar, ID_FLIPTB,          DO_FLIPTB);
2420         SetAccelerator(mbar, ID_FLIPLR,          DO_FLIPLR);
2421         SetAccelerator(mbar, ID_ROTATEC,         DO_ROTATECW);
2422         SetAccelerator(mbar, ID_ROTATEA,         DO_ROTATEACW);
2423 
2424         SetAccelerator(mbar, ID_START,           DO_STARTSTOP);
2425         SetAccelerator(mbar, ID_NEXT,            DO_NEXTGEN);
2426         SetAccelerator(mbar, ID_STEP,            DO_NEXTSTEP);
2427         SetAccelerator(mbar, ID_RESET,           DO_RESET);
2428         SetAccelerator(mbar, ID_SETGEN,          DO_SETGEN);
2429         SetAccelerator(mbar, ID_FASTER,          DO_FASTER);
2430         SetAccelerator(mbar, ID_SLOWER,          DO_SLOWER);
2431         SetAccelerator(mbar, ID_SETBASE,         DO_SETBASE);
2432         SetAccelerator(mbar, ID_AUTO,            DO_AUTOFIT);
2433         SetAccelerator(mbar, ID_HYPER,           DO_HYPER);
2434         SetAccelerator(mbar, ID_HINFO,           DO_HASHINFO);
2435         SetAccelerator(mbar, ID_SHOW_POP,        DO_SHOWPOP);
2436         SetAccelerator(mbar, ID_RECORD,          DO_RECORD);
2437         SetAccelerator(mbar, ID_DELTIME,         DO_DELTIME);
2438         SetAccelerator(mbar, ID_SETRULE,         DO_SETRULE);
2439 
2440         SetAccelerator(mbar, ID_FULL,            DO_FULLSCREEN);
2441         SetAccelerator(mbar, ID_FIT,             DO_FIT);
2442         SetAccelerator(mbar, ID_FIT_SEL,         DO_FITSEL);
2443         SetAccelerator(mbar, ID_MIDDLE,          DO_MIDDLE);
2444         SetAccelerator(mbar, ID_RESTORE00,       DO_RESTORE00);
2445         SetAccelerator(mbar, wxID_ZOOM_IN,       DO_ZOOMIN);
2446         SetAccelerator(mbar, wxID_ZOOM_OUT,      DO_ZOOMOUT);
2447         SetAccelerator(mbar, ID_TOOL_BAR,        DO_SHOWTOOL);
2448         SetAccelerator(mbar, ID_LAYER_BAR,       DO_SHOWLAYER);
2449         SetAccelerator(mbar, ID_EDIT_BAR,        DO_SHOWEDIT);
2450         SetAccelerator(mbar, ID_ALL_STATES,      DO_SHOWSTATES);
2451         SetAccelerator(mbar, ID_STATUS_BAR,      DO_SHOWSTATUS);
2452         SetAccelerator(mbar, ID_EXACT,           DO_SHOWEXACT);
2453         SetAccelerator(mbar, ID_GRID,            DO_SHOWGRID);
2454         SetAccelerator(mbar, ID_ICONS,           DO_SHOWICONS);
2455         SetAccelerator(mbar, ID_INVERT,          DO_INVERT);
2456         SetAccelerator(mbar, ID_SMARTSCALE,      DO_SMARTSCALE);
2457         SetAccelerator(mbar, ID_TIMELINE,        DO_SHOWTIME);
2458         SetAccelerator(mbar, ID_SCROLL,          DO_SHOWSCROLL);
2459         SetAccelerator(mbar, ID_INFO,            DO_INFO);
2460 
2461         SetAccelerator(mbar, ID_SAVE_OVERLAY,    DO_SAVEOVERLAY);
2462         SetAccelerator(mbar, ID_SHOW_OVERLAY,    DO_SHOWOVERLAY);
2463         SetAccelerator(mbar, ID_DEL_OVERLAY,     DO_DELOVERLAY);
2464         SetAccelerator(mbar, ID_ADD_LAYER,       DO_ADD);
2465         SetAccelerator(mbar, ID_CLONE,           DO_CLONE);
2466         SetAccelerator(mbar, ID_DUPLICATE,       DO_DUPLICATE);
2467         SetAccelerator(mbar, ID_DEL_LAYER,       DO_DELETE);
2468         SetAccelerator(mbar, ID_DEL_OTHERS,      DO_DELOTHERS);
2469         SetAccelerator(mbar, ID_MOVE_LAYER,      DO_MOVELAYER);
2470         SetAccelerator(mbar, ID_NAME_LAYER,      DO_NAMELAYER);
2471         SetAccelerator(mbar, ID_SET_COLORS,      DO_SETCOLORS);
2472         SetAccelerator(mbar, ID_SYNC_VIEW,       DO_SYNCVIEWS);
2473         SetAccelerator(mbar, ID_SYNC_CURS,       DO_SYNCCURS);
2474         SetAccelerator(mbar, ID_STACK,           DO_STACK);
2475         SetAccelerator(mbar, ID_TILE,            DO_TILE);
2476     }
2477 }
2478 
2479 // -----------------------------------------------------------------------------
2480 
CreateDirControl()2481 void MainFrame::CreateDirControl()
2482 {
2483     filectrl = new wxGenericDirCtrl(splitwin, wxID_ANY, wxEmptyString,
2484                                     wxDefaultPosition, wxDefaultSize,
2485 #ifdef __WXMSW__
2486                                     // speed up a bit
2487                                     wxDIRCTRL_DIR_ONLY | wxNO_BORDER,
2488 #else
2489                                     wxNO_BORDER,
2490 #endif
2491                                     wxEmptyString   // see all file types
2492                                     );
2493 
2494 #ifdef __WXMSW__
2495     // now remove wxDIRCTRL_DIR_ONLY so we see files
2496     filectrl->SetWindowStyle(wxNO_BORDER);
2497 #endif
2498 
2499 #if defined(__WXGTK__)
2500     // make sure background is white when using KDE's GTK theme
2501 #if wxCHECK_VERSION(2,9,0)
2502     filectrl->GetTreeCtrl()->SetBackgroundStyle(wxBG_STYLE_ERASE);
2503 #else
2504     filectrl->GetTreeCtrl()->SetBackgroundStyle(wxBG_STYLE_COLOUR);
2505 #endif
2506     filectrl->GetTreeCtrl()->SetBackgroundColour(*wxWHITE);
2507     // reduce indent a bit
2508     filectrl->GetTreeCtrl()->SetIndent(8);
2509 #elif defined(__WXMAC__)
2510     // reduce indent a bit more
2511     filectrl->GetTreeCtrl()->SetIndent(6);
2512 #else
2513     // reduce indent a lot on Windows
2514     filectrl->GetTreeCtrl()->SetIndent(4);
2515 #endif
2516 
2517 #ifdef __WXMAC__
2518     // reduce font size (to get this to reduce line height we had to
2519     // make a few changes to wxMac/src/generic/treectlg.cpp)
2520     wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
2521     font.SetPointSize(12);
2522     filectrl->GetTreeCtrl()->SetFont(font);
2523 #endif
2524 
2525     if ( wxFileName::DirExists(filedir) ) {
2526         // only show filedir and its contents
2527         SimplifyTree(filedir, filectrl->GetTreeCtrl(), filectrl->GetRootId());
2528     }
2529 
2530     // install event handler to detect clicking on a file
2531     filectrl->GetTreeCtrl()->Connect(wxID_ANY, wxEVT_LEFT_DOWN, wxMouseEventHandler(MainFrame::OnTreeClick));
2532     filectrl->GetTreeCtrl()->Connect(wxID_ANY, wxEVT_RIGHT_DOWN, wxMouseEventHandler(MainFrame::OnTreeClick));
2533     filectrl->GetTreeCtrl()->Connect(wxID_ANY, wxEVT_LEFT_DCLICK, wxMouseEventHandler(MainFrame::OnTreeClick));
2534 }
2535 
2536 // -----------------------------------------------------------------------------
2537 
2538 // create the main window
MainFrame()2539 MainFrame::MainFrame()
2540 : wxFrame(NULL, wxID_ANY, wxEmptyString, wxPoint(mainx,mainy), wxSize(mainwd,mainht))
2541 {
2542     wxGetApp().SetFrameIcon(this);
2543 
2544     pendingfiles.Clear();      // no pending script/pattern files
2545     command_pending = false;   // no pending command
2546     draw_pending = false;      // no pending draw
2547     keepmessage = false;       // clear status message
2548     generating = false;        // not generating pattern
2549     fullscreen = false;        // not in full screen mode
2550     showbanner = true;         // avoid first file clearing banner message
2551 
2552     // initialize paths to some temporary files (in datadir so no need to be hidden);
2553     // they must be absolute paths in case they are used from a script command when the
2554     // current directory has been changed to the location of the script file
2555     clipfile = datadir + wxT("golly_clipboard");
2556     luafile = datadir + wxT("golly_clip.lua");
2557     perlfile = datadir + wxT("golly_clip.pl");
2558     pythonfile = datadir + wxT("golly_clip.py");
2559 
2560     // create timer for generating patterns (see OnGenTimer in wxcontrol.cpp)
2561     gentimer = new wxTimer(this, ID_GENTIMER);
2562 
2563     // create timer for calling OpenFile via OnIdle
2564     opentimer = new wxTimer(this, ID_OPENTIMER);
2565 
2566     // create a scriptable graphics layer
2567     curroverlay = new Overlay();
2568 
2569     CreateMenus();
2570     CreateToolbar();
2571 
2572     // if tool bar is visible then adjust position of other child windows
2573     int toolwd = showtool ? TOOLBARWD : 0;
2574 
2575     int wd, ht;
2576     GetClientSize(&wd, &ht);
2577     // wd or ht might be < 1 on Windows
2578     if (wd < 1) wd = 1;
2579     if (ht < 1) ht = 1;
2580 
2581     // wxStatusBar can only appear at bottom of frame so we use our own
2582     // status bar class which creates a child window at top of frame
2583     // but to the right of the tool bar
2584     int statht = showexact ? STATUS_EXHT : STATUS_HT;
2585     if (!showstatus) statht = 0;
2586     statusptr = new StatusBar(this, toolwd, 0, wd - toolwd, statht);
2587 
2588     // create a split window with file directory in left pane
2589     // and layer/edit/timeline bars and pattern viewport in right pane
2590     splitwin = new wxSplitterWindow(this, wxID_ANY,
2591                                     wxPoint(toolwd, statht),
2592                                     wxSize(wd - toolwd, ht - statht),
2593 #ifdef __WXMSW__
2594                                     wxSP_BORDER |
2595 #endif
2596                                     wxSP_3DSASH | wxSP_NO_XP_THEME | wxSP_LIVE_UPDATE);
2597 
2598     // create filectrl in left pane
2599     CreateDirControl();
2600 
2601     // create a window for right pane which contains layer/edit/timeline bars
2602     // and pattern viewport
2603     rightpane = new RightWindow(splitwin);
2604 
2605     // create layer bar and initial layer
2606     CreateLayerBar(rightpane);
2607     AddLayer();
2608 
2609     // create edit bar
2610     CreateEditBar(rightpane);
2611 
2612     // create timeline bar
2613     CreateTimelineBar(rightpane);
2614 
2615     // enable/disable tool tips after creating bars with buttons
2616 #if wxUSE_TOOLTIPS
2617     wxToolTip::Enable(showtips);
2618     wxToolTip::SetDelay(1500);          // 1.5 secs
2619 #endif
2620 
2621     CreateTranslucentControls();        // must be done BEFORE creating viewport
2622 
2623     // create viewport at minimum size
2624     int y = 0;
2625     if (showlayer) y += LayerBarHeight();
2626     if (showedit) y += EditBarHeight();
2627     viewptr = new PatternView(rightpane, 0, y, 40, 40,
2628                               wxNO_BORDER |
2629                               wxWANTS_CHARS |              // receive all keyboard events
2630                               wxFULL_REPAINT_ON_RESIZE);
2631 
2632     // this is the main viewport window (tile windows have a tileindex >= 0)
2633     viewptr->tileindex = -1;
2634     bigview = viewptr;
2635 
2636     // create the scroll bars
2637     hbar = new wxScrollBar(rightpane, wxID_ANY, wxPoint(0,0), wxSize(-1, 15), wxSB_HORIZONTAL);
2638     vbar = new wxScrollBar(rightpane, wxID_ANY, wxPoint(0,0), wxSize(15, -1), wxSB_VERTICAL);
2639     hbar->SetMinSize(wxDefaultSize);
2640     vbar->SetMinSize(wxDefaultSize);
2641 
2642     if (!showscrollbars) {
2643         // hide scroll bars
2644         hbar->Show(false);
2645         vbar->Show(false);
2646     }
2647 
2648 #if wxUSE_DRAG_AND_DROP
2649     // let users drop files onto viewport
2650     viewptr->SetDropTarget(new DnDFile());
2651 #endif
2652 
2653     // these seemingly redundant steps are needed to avoid problems on Windows
2654     splitwin->SplitVertically(filectrl, rightpane, dirwinwd);
2655     splitwin->SetSashPosition(dirwinwd);
2656     splitwin->SetMinimumPaneSize(MIN_DIRWD);
2657     splitwin->Unsplit(filectrl);
2658     splitwin->UpdateSize();
2659 
2660     if (showfiles) splitwin->SplitVertically(filectrl, rightpane, dirwinwd);
2661 }
2662 
2663 // -----------------------------------------------------------------------------
2664 
~MainFrame()2665 MainFrame::~MainFrame()
2666 {
2667     delete hbar;
2668     delete vbar;
2669     delete curroverlay;
2670     delete gentimer;
2671     delete opentimer;
2672     DestroyDrawingData();
2673 }
2674