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 #include "wx/wxhtml.h" // for wxHtmlWindow
10 #include "wx/file.h" // for wxFile
11 #include "wx/protocol/http.h" // for wxHTTP
12 #include "wx/wfstream.h" // for wxFileOutputStream, wxFileInputStream
13 #include "wx/zipstrm.h" // for wxZipInputStream
14
15 #include "lifealgo.h" // for lifealgo class
16 #include "ruleloaderalgo.h" // for noTABLEorTREE
17
18 #include "wxgolly.h" // for wxGetApp, mainptr, viewptr
19 #include "wxmain.h" // for mainptr->...
20 #include "wxview.h" // for viewptr->...
21 #include "wxutils.h" // for Warning, BeginProgress, etc
22 #include "wxprefs.h" // for GetShortcutTable, helpfontsize, gollydir, etc
23 #include "wxscript.h" // for inscript, pass_file_events, etc
24 #include "wxlayer.h" // for numlayers, GetLayer, etc
25 #include "wxalgos.h" // for QLIFE_ALGO
26 #include "wxhelp.h"
27
28 // -----------------------------------------------------------------------------
29
30 // define a modeless help window:
31
32 class HelpFrame : public wxFrame
33 {
34 public:
35 HelpFrame();
36
SetStatus(const wxString & text)37 void SetStatus(const wxString& text) {
38 status->SetLabel(text);
39 }
40
41 bool infront; // help window is active?
42
43 private:
44 // ids for buttons in help window (see also wxID_CLOSE)
45 enum {
46 ID_BACK_BUTT = wxID_HIGHEST + 1,
47 ID_FORWARD_BUTT,
48 ID_CONTENTS_BUTT
49 };
50
51 // event handlers
52 void OnActivate(wxActivateEvent& event);
53 void OnBackButton(wxCommandEvent& event);
54 void OnForwardButton(wxCommandEvent& event);
55 void OnContentsButton(wxCommandEvent& event);
56 void OnCloseButton(wxCommandEvent& event);
57 void OnClose(wxCloseEvent& event);
58
59 wxStaticText* status; // status line at bottom of help window
60
61 // any class wishing to process wxWidgets events must use this macro
62 DECLARE_EVENT_TABLE()
63 };
64
65 BEGIN_EVENT_TABLE(HelpFrame, wxFrame)
66 EVT_ACTIVATE ( HelpFrame::OnActivate)
67 EVT_BUTTON (ID_BACK_BUTT, HelpFrame::OnBackButton)
68 EVT_BUTTON (ID_FORWARD_BUTT, HelpFrame::OnForwardButton)
69 EVT_BUTTON (ID_CONTENTS_BUTT, HelpFrame::OnContentsButton)
70 EVT_BUTTON (wxID_CLOSE, HelpFrame::OnCloseButton)
71 EVT_CLOSE ( HelpFrame::OnClose)
72 END_EVENT_TABLE()
73
74 // -----------------------------------------------------------------------------
75
76 // define a child window for displaying html info:
77
78 class HtmlView : public wxHtmlWindow
79 {
80 public:
HtmlView(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style)81 HtmlView(wxWindow* parent, wxWindowID id, const wxPoint& pos,
82 const wxSize& size, long style)
83 : wxHtmlWindow(parent, id, pos, size, style) {
84 editlink = false;
85 linkrect = wxRect(0,0,0,0);
86 }
87
88 virtual void OnLinkClicked(const wxHtmlLinkInfo& link);
89 virtual void OnCellMouseHover(wxHtmlCell* cell, wxCoord x, wxCoord y);
90
91 void ClearStatus(); // clear help window's status line
92
93 void SetFontSizes(int size);
94 void ChangeFontSizes(int size);
95
96 void CheckAndLoad(const wxString& filepath);
97
StartTimer()98 void StartTimer() {
99 htmltimer = new wxTimer(this, wxID_ANY);
100 // call OnTimer 10 times per sec
101 htmltimer->Start(100, wxTIMER_CONTINUOUS);
102 }
103
StopTimer()104 void StopTimer() {
105 htmltimer->Stop();
106 delete htmltimer;
107 }
108
109 bool editlink; // open clicked file in editor?
110 bool canreload; // can OnSize call CheckAndLoad?
111
112 private:
113 #ifdef __WXMSW__
114 // see HtmlView::OnKeyUp for why we do this
115 void OnKeyUp(wxKeyEvent& event);
116 #else
117 void OnKeyDown(wxKeyEvent& event);
118 #endif
119
120 void OnChar(wxKeyEvent& event);
121 void OnSize(wxSizeEvent& event);
122 void OnMouseMotion(wxMouseEvent& event);
123 void OnMouseLeave(wxMouseEvent& event);
124 void OnMouseDown(wxMouseEvent& event);
125 void OnTimer(wxTimerEvent& event);
126
127 wxTimer* htmltimer;
128 wxRect linkrect; // rect for cell containing link
129
130 // any class wishing to process wxWidgets events must use this macro
131 DECLARE_EVENT_TABLE()
132 };
133
134 BEGIN_EVENT_TABLE(HtmlView, wxHtmlWindow)
135 #ifdef __WXMSW__
136 // see HtmlView::OnKeyUp for why we do this
137 EVT_KEY_UP (HtmlView::OnKeyUp)
138 #else
139 EVT_KEY_DOWN (HtmlView::OnKeyDown)
140 #endif
141 EVT_CHAR (HtmlView::OnChar)
142 EVT_SIZE (HtmlView::OnSize)
143 EVT_MOTION (HtmlView::OnMouseMotion)
144 EVT_ENTER_WINDOW (HtmlView::OnMouseMotion)
145 EVT_LEAVE_WINDOW (HtmlView::OnMouseLeave)
146 EVT_LEFT_DOWN (HtmlView::OnMouseDown)
147 EVT_RIGHT_DOWN (HtmlView::OnMouseDown)
148 EVT_TIMER (wxID_ANY, HtmlView::OnTimer)
149 END_EVENT_TABLE()
150
151 // -----------------------------------------------------------------------------
152
153 HelpFrame* helpptr = NULL; // help window
154 HtmlView* htmlwin = NULL; // html child window
155
156 wxButton* backbutt; // back button
157 wxButton* forwbutt; // forwards button
158 wxButton* contbutt; // Contents button
159
160 long whenactive; // when help window became active (elapsed millisecs)
161
162 const wxString helphome = _("Help/index.html"); // contents page
163 wxString currhelp = helphome; // current help file
164 const wxString lexicon_name = _("lexicon"); // name of lexicon layer
165
166 int lexlayer; // index of existing lexicon layer (-ve if not present)
167 wxString lexpattern; // lexicon pattern data
168
169 // prefix of most recent full URL in a html get ("get:http://.../foo.html")
170 // to allow later relative gets ("get:foo.rle")
171 wxString urlprefix = wxEmptyString;
172 const wxString HTML_PREFIX = _("GET---"); // prepended to html filename
173
174 // -----------------------------------------------------------------------------
175
GetHelpFrame()176 wxFrame* GetHelpFrame() {
177 return helpptr;
178 }
179
180 // -----------------------------------------------------------------------------
181
182 // create the help window
HelpFrame()183 HelpFrame::HelpFrame()
184 : wxFrame(NULL, wxID_ANY, _(""), wxPoint(helpx,helpy), wxSize(helpwd,helpht))
185 {
186 wxGetApp().SetFrameIcon(this);
187
188 #ifdef __WXMSW__
189 // use current theme's background colour
190 SetBackgroundColour(wxNullColour);
191 #endif
192
193 htmlwin = new HtmlView(this, wxID_ANY,
194 // specify small size to avoid clipping scroll bar on resize
195 wxDefaultPosition, wxSize(30,30),
196 wxHW_DEFAULT_STYLE | wxSUNKEN_BORDER);
197 htmlwin->StartTimer();
198 htmlwin->SetBorders(4);
199 htmlwin->SetFontSizes(helpfontsize);
200
201 wxBoxSizer* vbox = new wxBoxSizer(wxVERTICAL);
202 wxBoxSizer* hbox = new wxBoxSizer(wxHORIZONTAL);
203
204 backbutt = new wxButton(this, ID_BACK_BUTT, _("<"),
205 wxDefaultPosition, wxSize(40,wxDefaultCoord));
206 hbox->Add(backbutt, 0, wxALL | wxALIGN_LEFT, 10);
207
208 forwbutt = new wxButton(this, ID_FORWARD_BUTT, _(">"),
209 wxDefaultPosition, wxSize(40,wxDefaultCoord));
210 hbox->Add(forwbutt, 0, wxTOP | wxBOTTOM | wxALIGN_LEFT, 10);
211
212 contbutt = new wxButton(this, ID_CONTENTS_BUTT, _("Contents"));
213 hbox->Add(contbutt, 0, wxALL | wxALIGN_LEFT, 10);
214
215 hbox->AddStretchSpacer(1);
216
217 wxButton* closebutt = new wxButton(this, wxID_CLOSE, _("Close"));
218 closebutt->SetDefault();
219 hbox->Add(closebutt, 0, wxALL, 10);
220
221 vbox->Add(hbox, 0, wxALL | wxEXPAND | wxALIGN_TOP, 0);
222
223 vbox->Add(htmlwin, 1, wxLEFT | wxRIGHT | wxEXPAND | wxALIGN_TOP, 10);
224
225 status = new wxStaticText(this, wxID_STATIC, wxEmptyString);
226 #ifdef __WXMAC__
227 status->SetWindowVariant(wxWINDOW_VARIANT_SMALL);
228 #endif
229 wxBoxSizer* statbox = new wxBoxSizer(wxHORIZONTAL);
230 statbox->Add(status);
231 vbox->AddSpacer(2);
232 vbox->Add(statbox, 0, wxLEFT | wxALIGN_LEFT, 10);
233 vbox->AddSpacer(4);
234
235 SetMinSize(wxSize(minhelpwd, minhelpht));
236 SetSizer(vbox);
237
238 // expand sizer now to avoid seeing small htmlwin and buttons in top left corner
239 vbox->SetDimension(0, 0, helpwd, helpht);
240 }
241
242 // -----------------------------------------------------------------------------
243
UpdateHelpButtons()244 void UpdateHelpButtons()
245 {
246 backbutt->Enable( htmlwin->HistoryCanBack() );
247 forwbutt->Enable( htmlwin->HistoryCanForward() );
248 // check for title used in Help/index.html
249 contbutt->Enable( htmlwin->GetOpenedPageTitle() != _("Golly Help: Contents") );
250
251 wxString location = htmlwin->GetOpenedPage();
252 if ( !location.IsEmpty() ) {
253
254 if (location.StartsWith(wxT("file:"))) {
255 // this happens in wx 3.1.0, so convert location from URL to file path
256 wxFileName fname = wxFileSystem::URLToFileName(location);
257 location = fname.GetFullPath();
258 #ifdef __WXMSW__
259 location.Replace(wxT("\\"), wxT("/"));
260 #endif
261 }
262
263 // avoid bug in wx 3.1.0
264 location.Replace(wxT("%20"), wxT(" "));
265 location.Replace(wxT("%23"), wxT("#"));
266
267 // set currhelp so user can close help window and then open same page
268 currhelp = location;
269
270 // if filename starts with HTML_PREFIX then set urlprefix to corresponding
271 // url so any later relative "get:foo.rle" links will work
272 wxString filename = location.AfterLast('/');
273 if (filename.StartsWith(HTML_PREFIX)) {
274 // replace HTML_PREFIX with "http://" and convert spaces to '/'
275 // (ie. reverse what we did in GetURL)
276 urlprefix = filename;
277 urlprefix.Replace(HTML_PREFIX, wxT("http://"), false); // do once
278 urlprefix.Replace(wxT(" "), wxT("/"));
279 urlprefix = urlprefix.BeforeLast('/');
280 urlprefix += wxT("/"); // must end in slash
281 }
282 }
283
284 htmlwin->ClearStatus();
285 htmlwin->SetFocus(); // for keyboard shortcuts
286 }
287
288 // -----------------------------------------------------------------------------
289
ShowHelp(const wxString & filepath)290 void ShowHelp(const wxString& filepath)
291 {
292 // display given html file in help window
293 if (helpptr) {
294 // help window exists so bring it to front and display given file
295 if (!filepath.IsEmpty()) {
296 htmlwin->CheckAndLoad(filepath);
297 UpdateHelpButtons();
298 }
299 helpptr->Raise();
300
301 } else {
302 helpptr = new HelpFrame();
303 if (helpptr == NULL) {
304 Warning(_("Could not create help window!"));
305 return;
306 }
307
308 // assume our .html files contain a <title> tag
309 htmlwin->SetRelatedFrame(helpptr, _("%s"));
310
311 if (!filepath.IsEmpty()) {
312 htmlwin->CheckAndLoad(filepath);
313 } else {
314 htmlwin->CheckAndLoad(currhelp);
315 }
316
317 // prevent HtmlView::OnSize calling CheckAndLoad twice
318 htmlwin->canreload = false;
319
320 helpptr->Show(true);
321 UpdateHelpButtons(); // must be after Show to avoid scroll bar appearing on Mac
322
323 // allow HtmlView::OnSize to call CheckAndLoad if window is resized
324 htmlwin->canreload = true;
325 }
326 whenactive = 0;
327 }
328
329 // -----------------------------------------------------------------------------
330
OnActivate(wxActivateEvent & event)331 void HelpFrame::OnActivate(wxActivateEvent& event)
332 {
333 // IsActive() is not always reliable so we set infront flag
334 infront = event.GetActive();
335 if (infront) {
336 // help window is being activated
337 whenactive = stopwatch->Time();
338
339 // ensure correct menu items, esp after help window
340 // is clicked while app is in background
341 mainptr->UpdateMenuItems();
342 }
343 event.Skip();
344 }
345
346 // -----------------------------------------------------------------------------
347
OnBackButton(wxCommandEvent & WXUNUSED (event))348 void HelpFrame::OnBackButton(wxCommandEvent& WXUNUSED(event))
349 {
350 if ( htmlwin->HistoryBack() ) {
351 UpdateHelpButtons();
352 } else {
353 Beep();
354 }
355 }
356
357 // -----------------------------------------------------------------------------
358
OnForwardButton(wxCommandEvent & WXUNUSED (event))359 void HelpFrame::OnForwardButton(wxCommandEvent& WXUNUSED(event))
360 {
361 if ( htmlwin->HistoryForward() ) {
362 UpdateHelpButtons();
363 } else {
364 Beep();
365 }
366 }
367
368 // -----------------------------------------------------------------------------
369
OnContentsButton(wxCommandEvent & WXUNUSED (event))370 void HelpFrame::OnContentsButton(wxCommandEvent& WXUNUSED(event))
371 {
372 ShowHelp(helphome);
373 }
374
375 // -----------------------------------------------------------------------------
376
OnCloseButton(wxCommandEvent & WXUNUSED (event))377 void HelpFrame::OnCloseButton(wxCommandEvent& WXUNUSED(event))
378 {
379 Close(true);
380 }
381
382 // -----------------------------------------------------------------------------
383
OnClose(wxCloseEvent & WXUNUSED (event))384 void HelpFrame::OnClose(wxCloseEvent& WXUNUSED(event))
385 {
386 #ifdef __WXMSW__
387 if (!IsIconized()) {
388 #endif
389 // save current location and size for later use in SavePrefs
390 wxRect r = GetRect();
391 helpx = r.x;
392 helpy = r.y;
393 helpwd = r.width;
394 helpht = r.height;
395 #ifdef __WXMSW__
396 }
397 #endif
398
399 // stop htmltimer immediately (if we do it in ~HtmlView dtor then timer
400 // only stops when app becomes idle)
401 htmlwin->StopTimer();
402
403 Destroy(); // also deletes all child windows (buttons, etc)
404 helpptr = NULL;
405 }
406
407 // -----------------------------------------------------------------------------
408
LoadRule(const wxString & rulestring,bool fromfile)409 void LoadRule(const wxString& rulestring, bool fromfile)
410 {
411 wxString oldrule = wxString(currlayer->algo->getrule(),wxConvLocal);
412 int oldmaxstate = currlayer->algo->NumCellStates() - 1;
413 const char* err;
414
415 // selection might change if grid becomes smaller,
416 // so save current selection for RememberRuleChange/RememberAlgoChange
417 viewptr->SaveCurrentSelection();
418
419 mainptr->Raise();
420
421 if (mainptr->generating) {
422 Warning(_("Cannot change rule while generating a pattern."));
423 return;
424 } else if (inscript) {
425 Warning(_("Cannot change rule while a script is running."));
426 return;
427 }
428
429 if (fromfile) {
430 // load given rule from a .rule file
431
432 // InitAlgorithms ensures the RuleLoader algo is the last algo
433 int rule_loader_algo = NumAlgos() - 1;
434
435 if (currlayer->algtype == rule_loader_algo) {
436 // RuleLoader is current algo so no need to switch
437 err = currlayer->algo->setrule( rulestring.mb_str(wxConvLocal) );
438 } else {
439 // switch to RuleLoader algo
440 lifealgo* tempalgo = CreateNewUniverse(rule_loader_algo);
441 err = tempalgo->setrule( rulestring.mb_str(wxConvLocal) );
442 delete tempalgo;
443 if (!err) {
444 // change the current algorithm and switch to the new rule
445 mainptr->ChangeAlgorithm(rule_loader_algo, rulestring);
446 if (rule_loader_algo != currlayer->algtype) {
447 RestoreRule(oldrule);
448 Warning(_("Algorithm could not be changed (pattern is too big to convert)."));
449 } else {
450 mainptr->SetWindowTitle(wxEmptyString);
451 mainptr->UpdateEverything();
452 }
453 return;
454 }
455 }
456
457 if (err) {
458 // RuleLoader algo found some sort of error
459 if (strcmp(err, noTABLEorTREE) == 0) {
460 // .rule file has no TABLE or TREE section but it might be used
461 // to override a built-in rule, so try each algo
462 wxString temprule = rulestring;
463 temprule.Replace(wxT("_"), wxT("/")); // eg. convert B3_S23 to B3/S23
464 for (int i = 0; i < NumAlgos(); i++) {
465 lifealgo* tempalgo = CreateNewUniverse(i);
466 err = tempalgo->setrule( temprule.mb_str(wxConvLocal) );
467 delete tempalgo;
468 if (!err) {
469 // change the current algorithm and switch to the new rule
470 mainptr->ChangeAlgorithm(i, temprule);
471 if (i != currlayer->algtype) {
472 RestoreRule(oldrule);
473 Warning(_("Algorithm could not be changed (pattern is too big to convert)."));
474 } else {
475 mainptr->SetWindowTitle(wxEmptyString);
476 mainptr->UpdateEverything();
477 }
478 return;
479 }
480 }
481 }
482 RestoreRule(oldrule);
483 wxString msg = _("The rule file is not valid:\n") + rulestring;
484 msg += _("\n\nThe error message:\n") + wxString(err,wxConvLocal);
485 Warning(msg);
486 return;
487 }
488
489 } else {
490 // fromfile is false, so switch to rule given in a "rule:" link
491
492 err = currlayer->algo->setrule( rulestring.mb_str(wxConvLocal) );
493 if (err) {
494 // try to find another algorithm that supports the given rule
495 for (int i = 0; i < NumAlgos(); i++) {
496 if (i != currlayer->algtype) {
497 lifealgo* tempalgo = CreateNewUniverse(i);
498 err = tempalgo->setrule( rulestring.mb_str(wxConvLocal) );
499 delete tempalgo;
500 if (!err) {
501 // change the current algorithm and switch to the new rule
502 mainptr->ChangeAlgorithm(i, rulestring);
503 if (i != currlayer->algtype) {
504 RestoreRule(oldrule);
505 Warning(_("Algorithm could not be changed (pattern is too big to convert)."));
506 } else {
507 mainptr->SetWindowTitle(wxEmptyString);
508 mainptr->UpdateEverything();
509 }
510 return;
511 }
512 }
513 }
514 RestoreRule(oldrule);
515 Warning(_("Given rule is not valid in any algorithm:\n") + rulestring);
516 return;
517 }
518 }
519
520 wxString newrule = wxString(currlayer->algo->getrule(),wxConvLocal);
521 int newmaxstate = currlayer->algo->NumCellStates() - 1;
522 if (oldrule != newrule || oldmaxstate != newmaxstate) {
523 // show new rule in main window's title but don't change name
524 mainptr->SetWindowTitle(wxEmptyString);
525
526 // if pattern exists and is at starting gen then ensure savestart is true
527 // so that SaveStartingPattern will save pattern to suitable file
528 // (and thus undo/reset will work correctly)
529 if (currlayer->algo->getGeneration() == currlayer->startgen && !currlayer->algo->isEmpty()) {
530 currlayer->savestart = true;
531 }
532
533 // if grid is bounded then remove any live cells outside grid edges
534 if (currlayer->algo->gridwd > 0 || currlayer->algo->gridht > 0) {
535 mainptr->ClearOutsideGrid();
536 }
537
538 // new rule might have changed the number of cell states;
539 // if there are fewer states then pattern might change
540 if (newmaxstate < oldmaxstate && !currlayer->algo->isEmpty()) {
541 mainptr->ReduceCellStates(newmaxstate);
542 }
543
544 if (allowundo && !currlayer->stayclean) {
545 currlayer->undoredo->RememberRuleChange(oldrule);
546 }
547 }
548
549 // update colors and/or icons for the new rule
550 UpdateLayerColors();
551
552 // pattern might have changed or colors/icons might have changed
553 mainptr->UpdateEverything();
554 }
555
556 // -----------------------------------------------------------------------------
557
DownloadFile(const wxString & url,const wxString & filepath)558 bool DownloadFile(const wxString& url, const wxString& filepath)
559 {
560 bool result = false;
561
562 wxHTTP http;
563 http.SetTimeout(5); // secs
564 http.SetHeader(wxT("Accept") , wxT("*/*")); // any file type
565 http.SetHeader(wxT("User-Agent"), wxT("Golly"));
566
567 // Connect() wants a server address (eg. "www.foo.com"), not a full URL
568 wxString temp = url.AfterFirst('/');
569 while (temp[0] == '/') temp = temp.Mid(1);
570 size_t slashpos = temp.Find('/');
571 wxString server = temp.Left(slashpos);
572 if (http.Connect(server, 80)) {
573 // GetInputStream() wants everything after the server address
574 wxString respath = temp.Right(temp.length() - slashpos);
575 wxInputStream* instream = http.GetInputStream(respath);
576 if (instream) {
577
578 wxFileOutputStream outstream(filepath);
579 if (outstream.Ok()) {
580 // read and write in chunks so we can show a progress dialog
581 const int BUFFER_SIZE = 4000; // seems ok (on Mac at least)
582 char buf[BUFFER_SIZE];
583 size_t incount = 0;
584 size_t outcount = 0;
585 size_t lastread, lastwrite;
586 double filesize = (double) instream->GetSize();
587 if (filesize <= 0.0) filesize = -1.0; // show indeterminate progress
588
589 BeginProgress(_("Downloading file"));
590 while (true) {
591 instream->Read(buf, BUFFER_SIZE);
592 lastread = instream->LastRead();
593 if (lastread == 0) break;
594 outstream.Write(buf, lastread);
595 lastwrite = outstream.LastWrite();
596 incount += lastread;
597 outcount += lastwrite;
598 if (incount != outcount) {
599 Warning(_("Error occurred while writing file:\n") + filepath);
600 break;
601 }
602 char msg[128];
603 sprintf(msg, "File size: %.2f MB", double(incount) / 1048576.0);
604 if (AbortProgress((double)incount / filesize, wxString(msg,wxConvLocal))) {
605 // force false result
606 outcount = 0;
607 break;
608 }
609 }
610 EndProgress();
611
612 result = (incount == outcount);
613 if (!result) {
614 // delete incomplete filepath
615 if (wxFileExists(filepath)) wxRemoveFile(filepath);
616 }
617 } else {
618 Warning(_("Could not open output stream for file:\n") + filepath);
619 }
620 delete instream;
621
622 } else {
623 int err = http.GetError();
624 if (err == wxPROTO_NOFILE) {
625 Warning(_("Remote file does not exist:\n") + url);
626 } else {
627 // we get wxPROTO_NETERR (generic network error) with some naughty servers
628 // that use LF rather than CRLF to terminate HTTP header messages
629 // eg: http://fano.ics.uci.edu/ca/rules/b0135s014/g1.lif
630 // (wxProtocol::ReadLine needs to be made more tolerant)
631 Warning(wxString::Format(_("Could not download file (error %d):\n"),err) + url);
632 }
633 }
634 } else {
635 Warning(_("Could not connect to server:\n") + server);
636 }
637
638 http.Close();
639 return result;
640 }
641
642 // -----------------------------------------------------------------------------
643
GetURL(const wxString & url)644 void GetURL(const wxString& url)
645 {
646 wxString fullurl;
647 if (url.StartsWith(wxT("http:"))) {
648 fullurl = url;
649 } else {
650 // relative get, so prepend prefix set earlier in UpdateHelpButtons
651 fullurl = urlprefix + url;
652 }
653
654 wxString filename = fullurl.AfterLast('/');
655
656 // remove ugly stuff at start of file names downloaded from ConwayLife.com
657 if (filename.StartsWith(wxT("download.php?f=")) ||
658 filename.StartsWith(wxT("pattern.asp?p=")) ||
659 filename.StartsWith(wxT("script.asp?s="))) {
660 filename = filename.AfterFirst('=');
661 }
662
663 // create full path for downloaded file based on given url;
664 // first remove initial "http://"
665 wxString filepath = fullurl.AfterFirst('/');
666 while (filepath[0] == '/') filepath = filepath.Mid(1);
667 if (IsHTMLFile(filename)) {
668 // create special name for html file so UpdateHelpButtons can detect it
669 // and set urlprefix
670 filepath.Replace(wxT("/"), wxT(" ")); // assume url has no spaces
671 filepath = HTML_PREFIX + filepath;
672 } else {
673 // no need for url info in file name
674 filepath = filename;
675 }
676 #ifdef __WXMSW__
677 // replace chars that can appear in URLs but are not allowed in filenames on Windows
678 filepath.Replace(wxT("*"), wxT("_"));
679 filepath.Replace(wxT("?"), wxT("_"));
680 #endif
681
682 if (IsRuleFile(filename)) {
683 // create file in user's rules directory (rulesdir might be read-only)
684 filepath = userrules + filename;
685 } else if (IsHTMLFile(filename)) {
686 // nicer to store html files in temporary directory
687 filepath = tempdir + filepath;
688 } else {
689 // all other files are stored in user's download directory
690 filepath = downloaddir + filepath;
691 }
692
693 // try to download the file
694 if ( !DownloadFile(fullurl, filepath) ) return;
695
696 if (htmlwin->editlink) {
697 if (IsRuleFile(filename) && filename.Lower().EndsWith(wxT(".icons"))) {
698 // let user see b&w image in .icons file
699 mainptr->Raise();
700 mainptr->OpenFile(filepath);
701 } else {
702 mainptr->EditFile(filepath);
703 }
704 return;
705 }
706
707 if (IsHTMLFile(filename)) {
708 // display html file in help window
709 htmlwin->LoadPage(filepath);
710
711 } else if (IsRuleFile(filename)) {
712 // load corresponding .rule file
713 LoadRule(filename.BeforeLast('.'));
714
715 } else if (IsTextFile(filename)) {
716 // open text file in user's text editor
717 mainptr->EditFile(filepath);
718
719 } else if (IsZipFile(filename)) {
720 // open zip file (don't raise main window here)
721 mainptr->OpenFile(filepath);
722
723 } else if (IsScriptFile(filename)) {
724 // run script depending on safety check; if it is allowed to run
725 // then we remember script in the Run Recent submenu
726 mainptr->CheckBeforeRunning(filepath, true, wxEmptyString);
727
728 } else {
729 // assume pattern file, so try to load it
730 mainptr->Raise();
731 mainptr->OpenFile(filepath);
732 }
733
734 if (helpptr && helpptr->infront) UpdateHelpButtons();
735 }
736
737 // -----------------------------------------------------------------------------
738
UnzipFile(const wxString & zippath,const wxString & entry)739 void UnzipFile(const wxString& zippath, const wxString& entry)
740 {
741 wxString filename = entry.AfterLast(wxFILE_SEP_PATH);
742 wxString tempfile = tempdir + filename;
743
744 if ( IsRuleFile(filename) ) {
745 // rule-related file should have already been extracted and installed
746 // into userrules, so check that file exists and load rule
747 wxString rulefile = userrules + filename;
748 if (wxFileExists(rulefile)) {
749 if (htmlwin->editlink) {
750 if (filename.Lower().EndsWith(wxT(".icons"))) {
751 // let user see b&w image in .icons file
752 mainptr->Raise();
753 mainptr->OpenFile(rulefile);
754 } else {
755 mainptr->EditFile(rulefile);
756 }
757 } else {
758 // load corresponding .rule file
759 LoadRule(filename.BeforeLast('.'));
760 }
761 } else {
762 Warning(_("Rule-related file was not installed:\n") + rulefile);
763 }
764
765 } else if ( mainptr->ExtractZipEntry(zippath, entry, tempfile) ) {
766 if (htmlwin->editlink) {
767 mainptr->EditFile(tempfile);
768
769 } else if ( IsHTMLFile(filename) ) {
770 // display html file
771 htmlwin->LoadPage(tempfile);
772 if (helpptr && helpptr->infront) UpdateHelpButtons();
773
774 } else if ( IsTextFile(filename) ) {
775 // open text file in user's text editor
776 mainptr->EditFile(tempfile);
777
778 } else if ( IsScriptFile(filename) ) {
779 // run script depending on safety check; note that because the script is
780 // included in a zip file we don't remember it in the Run Recent submenu
781 mainptr->CheckBeforeRunning(tempfile, false, zippath);
782
783 } else {
784 // open pattern but don't remember in Open Recent menu
785 mainptr->Raise();
786 mainptr->OpenFile(tempfile, false);
787 }
788 }
789 }
790
791 // -----------------------------------------------------------------------------
792
ClickLexiconPattern(const wxHtmlCell * htmlcell)793 void ClickLexiconPattern(const wxHtmlCell* htmlcell)
794 {
795 if (htmlcell) {
796 wxHtmlContainerCell* parent = htmlcell->GetParent();
797 if (parent) {
798 parent = parent->GetParent();
799 if (parent) {
800 wxHtmlCell* container = parent->GetFirstChild();
801
802 // extract pattern data and store in lexpattern
803 lexpattern.Clear();
804 while (container) {
805 wxHtmlCell* cell = container->GetFirstChild();
806 while (cell) {
807 wxString celltext = cell->ConvertToText(NULL);
808 if (celltext.IsEmpty()) {
809 // probably a formatting cell
810 } else {
811 lexpattern += celltext;
812 // append eol char(s)
813 #ifdef __WXMSW__
814 // use DOS line ending (CR+LF) on Windows
815 lexpattern += '\r';
816 lexpattern += '\n';
817 #else
818 // use LF on Linux or Mac
819 lexpattern += '\n';
820 #endif
821 }
822 cell = cell->GetNext();
823 }
824 container = container->GetNext();
825 }
826
827 if (!lexpattern.IsEmpty()) {
828 mainptr->Raise();
829 // look for existing lexicon layer
830 lexlayer = -1;
831 for (int i = 0; i < numlayers; i++) {
832 if (GetLayer(i)->currname == lexicon_name) {
833 lexlayer = i;
834 break;
835 }
836 }
837 if (lexlayer < 0 && numlayers == MAX_LAYERS) {
838 Warning(_("Cannot create new layer for lexicon pattern."));
839 return;
840 }
841
842 if (mainptr->generating) {
843 mainptr->command_pending = true;
844 mainptr->cmdevent.SetId(ID_LOAD_LEXICON);
845 mainptr->Stop();
846 return;
847 }
848
849 LoadLexiconPattern();
850 }
851 }
852 }
853 }
854 }
855
856 // -----------------------------------------------------------------------------
857
LoadLexiconPattern()858 void LoadLexiconPattern()
859 {
860 // switch to existing lexicon layer or create a new such layer
861 if (lexlayer >= 0) {
862 SetLayer(lexlayer);
863 } else {
864 AddLayer();
865 mainptr->SetWindowTitle(lexicon_name);
866 }
867
868 // copy lexpattern data to tempstart file so we can handle
869 // all formats supported by readpattern
870 wxFile outfile(currlayer->tempstart, wxFile::write);
871 if ( outfile.IsOpened() ) {
872 outfile.Write(lexpattern);
873 outfile.Close();
874
875 // all Life Lexicon patterns assume we're using Conway's Life so try
876 // switching to B3/S23 or Life; if that fails then switch to QuickLife
877 const char* err = currlayer->algo->setrule("B3/S23");
878 if (err) {
879 // try "Life" in case current algo is RuleLoader and Life.rule exists
880 // (also had to make a similar change to the loadpattern code in readpattern.cpp)
881 err = currlayer->algo->setrule("Life");
882 }
883 if (err) {
884 mainptr->ChangeAlgorithm(QLIFE_ALGO, wxString("B3/S23",wxConvLocal));
885 }
886
887 // load lexicon pattern into current layer
888 mainptr->LoadPattern(currlayer->tempstart, lexicon_name);
889 } else {
890 Warning(_("Could not create tempstart file!"));
891 }
892 }
893
894 // -----------------------------------------------------------------------------
895
OnLinkClicked(const wxHtmlLinkInfo & link)896 void HtmlView::OnLinkClicked(const wxHtmlLinkInfo& link)
897 {
898 #ifdef __WXMAC__
899 if ( stopwatch->Time() - whenactive < 500 ) {
900 // avoid problem on Mac:
901 // ignore click in link if the help window was in the background;
902 // this isn't fail safe because OnLinkClicked is only called AFTER
903 // the mouse button is released (better soln would be to set an
904 // ignoreclick flag in OnMouseDown handler if click occurred very
905 // soon after activate)
906 return;
907 }
908 #endif
909
910 wxString url = link.GetHref();
911 if ( url.StartsWith(wxT("http:")) || url.StartsWith(wxT("mailto:")) ) {
912 // pass http/mailto URL to user's preferred browser/emailer
913 if ( !wxLaunchDefaultBrowser(url) )
914 Warning(_("Could not open URL in browser!"));
915
916 } else if ( url.StartsWith(wxT("get:")) ) {
917 if (mainptr->generating) {
918 Warning(_("Cannot download file while generating a pattern."));
919 } else if (inscript) {
920 Warning(_("Cannot download file while a script is running."));
921 } else if (editlink && IsZipFile(url)) {
922 Warning(_("Opening a zip file in a text editor is not a good idea."));
923 } else {
924 // download clicked file
925 GetURL( url.AfterFirst(':') );
926 }
927
928 } else if ( url.StartsWith(wxT("unzip:")) ) {
929 if (inscript) {
930 Warning(_("Cannot extract zip entry while a script is running."));
931 } else {
932 // extract clicked entry from zip file
933 wxString zippath = url.AfterFirst(':');
934 wxString entry = url.AfterLast(':');
935 zippath = zippath.BeforeLast(':');
936 UnzipFile(zippath, entry);
937 }
938
939 } else if ( url.StartsWith(wxT("edit:")) ) {
940 // open clicked file in user's preferred text editor
941 wxString path = url.AfterFirst(':');
942 #ifdef __WXMSW__
943 path.Replace(wxT("/"), wxT("\\"));
944 #endif
945 wxFileName fname(path);
946 if (!fname.IsAbsolute()) path = gollydir + path;
947 mainptr->EditFile(path);
948
949 } else if ( url.StartsWith(wxT("lexpatt:")) ) {
950 if (inscript) {
951 Warning(_("Cannot load lexicon pattern while a script is running."));
952 } else {
953 // user clicked on pattern in Life Lexicon
954 ClickLexiconPattern( link.GetHtmlCell() );
955 }
956
957 } else if ( url.StartsWith(wxT("prefs:")) ) {
958 // user clicked on link to Preferences dialog
959 mainptr->ShowPrefsDialog( url.AfterFirst(':') );
960
961 } else if ( url.StartsWith(wxT("open:")) ) {
962 wxString path = url.AfterFirst(':');
963 #ifdef __WXMSW__
964 path.Replace(wxT("/"), wxT("\\"));
965 #endif
966 wxFileName fname(path);
967 if (!fname.IsAbsolute()) path = gollydir + path;
968 if (inscript) {
969 if (pass_file_events) {
970 PassFileToScript(path);
971 }
972 } else {
973 // open clicked file
974 if (editlink) {
975 mainptr->EditFile(path);
976 } else {
977 mainptr->Raise();
978 mainptr->OpenFile(path);
979 }
980 }
981
982 } else if ( url.StartsWith(wxT("rule:")) ) {
983 // switch to given rule (false = not necessarily in a .rule file)
984 LoadRule( url.AfterFirst(':'), false );
985
986 } else {
987 // assume it's a link to a local target or another help file
988 CheckAndLoad(url);
989 if (helpptr && helpptr->infront) UpdateHelpButtons();
990 }
991 }
992
993 // -----------------------------------------------------------------------------
994
OnCellMouseHover(wxHtmlCell * cell,wxCoord x,wxCoord y)995 void HtmlView::OnCellMouseHover(wxHtmlCell* cell, wxCoord x, wxCoord y)
996 {
997 if (helpptr && helpptr->infront && cell) {
998 wxHtmlLinkInfo* link = cell->GetLink(x,y);
999 if (link) {
1000 wxString href = link->GetHref();
1001 href.Replace(wxT("&"), wxT("&&"));
1002 helpptr->SetStatus(href);
1003 wxPoint pt = ScreenToClient( wxGetMousePosition() );
1004 linkrect = wxRect(pt.x-x, pt.y-y, cell->GetWidth(), cell->GetHeight());
1005 } else {
1006 ClearStatus();
1007 }
1008 }
1009 }
1010
1011 // -----------------------------------------------------------------------------
1012
OnMouseMotion(wxMouseEvent & event)1013 void HtmlView::OnMouseMotion(wxMouseEvent& event)
1014 {
1015 if (helpptr && helpptr->infront && !linkrect.IsEmpty()) {
1016 int x = event.GetX();
1017 int y = event.GetY();
1018 if (!linkrect.Contains(x,y)) ClearStatus();
1019 }
1020 event.Skip();
1021 }
1022
1023 // -----------------------------------------------------------------------------
1024
OnMouseLeave(wxMouseEvent & event)1025 void HtmlView::OnMouseLeave(wxMouseEvent& event)
1026 {
1027 if (helpptr && helpptr->infront) {
1028 ClearStatus();
1029 }
1030 event.Skip();
1031 }
1032
1033 // -----------------------------------------------------------------------------
1034
ClearStatus()1035 void HtmlView::ClearStatus()
1036 {
1037 if (helpptr) {
1038 helpptr->SetStatus(wxEmptyString);
1039 linkrect = wxRect(0,0,0,0);
1040 }
1041 }
1042
1043 // -----------------------------------------------------------------------------
1044
1045 #if defined(__WXMAC__) && wxCHECK_VERSION(2,9,0)
1046 // wxMOD_CONTROL has been changed to mean Command key down (sheesh!)
1047 #define wxMOD_CONTROL wxMOD_RAW_CONTROL
1048 #define ControlDown RawControlDown
1049 #endif
1050
OnMouseDown(wxMouseEvent & event)1051 void HtmlView::OnMouseDown(wxMouseEvent& event)
1052 {
1053 // set flag so ctrl/right-clicked file can be opened in editor
1054 // (this is consistent with how we handle clicks in the file pane)
1055 editlink = event.ControlDown() || event.RightDown();
1056 event.Skip();
1057 }
1058
1059 // -----------------------------------------------------------------------------
1060
CheckAndLoad(const wxString & filepath)1061 void HtmlView::CheckAndLoad(const wxString& filepath)
1062 {
1063 if (filepath == SHOW_KEYBOARD_SHORTCUTS) {
1064 // build HTML string to display current keyboard shortcuts
1065 wxString contents = GetShortcutTable();
1066
1067 // write contents to file and call LoadPage so that back/forwards buttons work
1068 wxString htmlfile = tempdir + SHOW_KEYBOARD_SHORTCUTS;
1069 wxFile outfile(htmlfile, wxFile::write);
1070 if (outfile.IsOpened()) {
1071 outfile.Write(contents);
1072 outfile.Close();
1073 LoadPage(htmlfile);
1074 } else {
1075 Warning(_("Could not create file:\n") + htmlfile);
1076 // might as well show contents
1077 SetPage(contents);
1078 currhelp = SHOW_KEYBOARD_SHORTCUTS;
1079 }
1080
1081 } else if ( filepath.StartsWith(_("Help/")) ) {
1082 // prepend location of Golly so user can open help while running a script
1083 wxString fullpath = gollydir + filepath;
1084 LoadPage(fullpath);
1085
1086 } else {
1087 // assume full path or local link
1088 #if defined(__WXMSW__) && wxCHECK_VERSION(3,1,0)
1089 wxFileName fname(filepath);
1090 if (fname.IsAbsolute()) {
1091 // call LoadFile rather than LoadPage to avoid bug in wx 3.1.0 on Windows 10
1092 LoadFile(fname);
1093 } else {
1094 LoadPage(filepath);
1095 }
1096 #else
1097 LoadPage(filepath);
1098 #endif
1099 }
1100 }
1101
1102 // -----------------------------------------------------------------------------
1103
1104 #ifdef __WXMSW__
1105 // we have to use OnKeyUp handler on Windows otherwise wxHtmlWindow's OnKeyUp
1106 // gets called which detects ctrl-C and clobbers our clipboard fix
OnKeyUp(wxKeyEvent & event)1107 void HtmlView::OnKeyUp(wxKeyEvent& event)
1108 #else
1109 // we have to use OnKeyDown handler on Mac -- if OnKeyUp handler is used and
1110 // cmd-C is pressed quickly then key code is 400!!!
1111 void HtmlView::OnKeyDown(wxKeyEvent& event)
1112 #endif
1113 {
1114 int key = event.GetKeyCode();
1115 if (event.CmdDown()) {
1116 // let cmd-A select all text
1117 if (key == 'A') {
1118 SelectAll();
1119 return;
1120 }
1121 #ifdef __WXMAC__
1122 // let cmd-W close help window or about box
1123 if (key == 'W') {
1124 GetParent()->Close(true);
1125 return;
1126 }
1127 #endif
1128 }
1129 if ( event.CmdDown() || event.AltDown() ) {
1130 if ( key == 'C' ) {
1131 // copy any selected text to the clipboard
1132 wxString text = SelectionToText();
1133 if ( text.Length() > 0 ) {
1134 if ( helpptr && helpptr->infront &&
1135 GetOpenedPageTitle().StartsWith(wxT("Life Lexicon")) ) {
1136 // avoid wxHTML bug when copying text inside <pre>...</pre>!!!
1137 // if there are at least 2 lines and the 1st line is twice
1138 // the size of the 2nd line then insert \n in middle of 1st line
1139 if ( text.Freq('\n') > 0 ) {
1140 wxString line1 = text.BeforeFirst('\n');
1141 wxString aftern = text.AfterFirst('\n');
1142 wxString line2 = aftern.BeforeFirst('\n');
1143 size_t line1len = line1.Length();
1144 size_t line2len = line2.Length();
1145 if ( line1len == 2 * line2len ) {
1146 wxString left = text.Left(line2len);
1147 wxString right = text.Mid(line2len);
1148 text = left;
1149 text += '\n';
1150 text += right;
1151 }
1152 }
1153 }
1154 mainptr->CopyTextToClipboard(text);
1155 }
1156 } else {
1157 event.Skip();
1158 }
1159 } else {
1160 // this handler is also called from ShowAboutBox
1161 if ( helpptr == NULL || !helpptr->infront ) {
1162 if ( key == WXK_NUMPAD_ENTER || key == WXK_RETURN ) {
1163 // allow enter key or return key to close about box
1164 GetParent()->Close(true);
1165 return;
1166 }
1167 event.Skip();
1168 return;
1169 }
1170 // let escape/return/enter key close help window
1171 if ( key == WXK_ESCAPE || key == WXK_RETURN || key == WXK_NUMPAD_ENTER ) {
1172 helpptr->Close(true);
1173 } else if ( key == WXK_HOME ) {
1174 ShowHelp(helphome);
1175 } else {
1176 event.Skip();
1177 }
1178 }
1179 }
1180
1181 // -----------------------------------------------------------------------------
1182
OnChar(wxKeyEvent & event)1183 void HtmlView::OnChar(wxKeyEvent& event)
1184 {
1185 // this handler is also called from ShowAboutBox
1186 if ( helpptr == NULL || !helpptr->infront ) {
1187 event.Skip();
1188 return;
1189 }
1190 int key = event.GetKeyCode();
1191 if ( key == '+' || key == '=' || key == WXK_ADD ) {
1192 if ( helpfontsize < maxfontsize ) {
1193 helpfontsize++;
1194 ChangeFontSizes(helpfontsize);
1195 }
1196 } else if ( key == '-' || key == WXK_SUBTRACT ) {
1197 if ( helpfontsize > minfontsize ) {
1198 helpfontsize--;
1199 ChangeFontSizes(helpfontsize);
1200 }
1201 } else if ( key == '[' || key == WXK_LEFT ) {
1202 if ( HistoryBack() ) {
1203 UpdateHelpButtons();
1204 }
1205 } else if ( key == ']' || key == WXK_RIGHT ) {
1206 if ( HistoryForward() ) {
1207 UpdateHelpButtons();
1208 }
1209 } else {
1210 event.Skip(); // so up/down arrows and pg up/down keys work
1211 }
1212 }
1213
1214 // -----------------------------------------------------------------------------
1215
1216 // avoid scroll position being reset to top when wxHtmlWindow is resized
OnSize(wxSizeEvent & event)1217 void HtmlView::OnSize(wxSizeEvent& event)
1218 {
1219 int x, y;
1220 GetViewStart(&x, &y); // save current position
1221
1222 wxHtmlWindow::OnSize(event);
1223
1224 wxString location = GetOpenedPage();
1225 if ( !location.IsEmpty() && canreload ) {
1226
1227 if (location.StartsWith(wxT("file:"))) {
1228 // this happens in wx 3.1.0, so convert location from URL to file path
1229 wxFileName fname = wxFileSystem::URLToFileName(location);
1230 location = fname.GetFullPath();
1231 #ifdef __WXMSW__
1232 location.Replace(wxT("\\"), wxT("/"));
1233 #endif
1234 }
1235
1236 // avoid bug in wx 3.1.0
1237 location.Replace(wxT("%20"), wxT(" "));
1238 location.Replace(wxT("%23"), wxT("#"));
1239
1240 CheckAndLoad(location); // reload page
1241 Scroll(x, y); // scroll to old position
1242 }
1243
1244 // prevent wxHtmlWindow::OnSize being called again
1245 event.Skip(false);
1246 }
1247
1248 // -----------------------------------------------------------------------------
1249
OnTimer(wxTimerEvent & WXUNUSED (event))1250 void HtmlView::OnTimer(wxTimerEvent& WXUNUSED(event))
1251 {
1252 if (helpptr && helpptr->infront) {
1253 // send idle event to html window so cursor gets updated
1254 // even while app is busy doing something else (eg. generating)
1255 wxIdleEvent idleevent;
1256 #if wxCHECK_VERSION(2,9,2)
1257 // SendIdleEvents is now in wxWindow
1258 SendIdleEvents(idleevent);
1259 #else
1260 wxGetApp().SendIdleEvents(this, idleevent);
1261 #endif
1262 }
1263 }
1264
1265 // -----------------------------------------------------------------------------
1266
SetFontSizes(int size)1267 void HtmlView::SetFontSizes(int size)
1268 {
1269 // set font sizes for <FONT SIZE=-2> to <FONT SIZE=+4>
1270 int f_sizes[7];
1271 f_sizes[0] = int(size * 0.6);
1272 f_sizes[1] = int(size * 0.8);
1273 f_sizes[2] = size;
1274 f_sizes[3] = int(size * 1.2);
1275 f_sizes[4] = int(size * 1.4);
1276 f_sizes[5] = int(size * 1.6);
1277 f_sizes[6] = int(size * 1.8);
1278 #ifdef __WXOSX_COCOA__
1279 SetFonts(wxT("Lucida Grande"), wxT("Monaco"), f_sizes);
1280 #else
1281 SetFonts(wxEmptyString, wxEmptyString, f_sizes);
1282 #endif
1283 }
1284
1285 // -----------------------------------------------------------------------------
1286
ChangeFontSizes(int size)1287 void HtmlView::ChangeFontSizes(int size)
1288 {
1289 // changing font sizes resets pos to top, so save and restore pos
1290 int x, y;
1291 GetViewStart(&x, &y);
1292 SetFontSizes(size);
1293 if (y > 0) Scroll(-1, y);
1294 }
1295
1296 // -----------------------------------------------------------------------------
1297
ShowAboutBox()1298 void ShowAboutBox()
1299 {
1300 if (viewptr->waitingforclick) return;
1301
1302 wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
1303 wxDialog dlg(mainptr, wxID_ANY, wxString(_("About Golly")));
1304
1305 HtmlView* html = new HtmlView(&dlg, wxID_ANY, wxDefaultPosition,
1306 #if wxCHECK_VERSION(2,9,0)
1307 // work around SetSize bug below!!!
1308 wxSize(400, 320),
1309 #elif defined(__WXGTK__)
1310 wxSize(460, 220),
1311 #else
1312 wxSize(386, 220),
1313 #endif
1314 wxHW_SCROLLBAR_NEVER | wxSUNKEN_BORDER);
1315 html->SetBorders(0);
1316 #ifdef __WXOSX_COCOA__
1317 html->SetFontSizes(helpfontsize);
1318 #endif
1319 html->CheckAndLoad(_("Help/about.html"));
1320
1321 // avoid HtmlView::OnSize calling CheckAndLoad again
1322 html->canreload = false;
1323
1324 // this call seems to be ignored in __WXOSX_COCOA__!!!
1325 html->SetSize(html->GetInternalRepresentation()->GetWidth(),
1326 html->GetInternalRepresentation()->GetHeight());
1327
1328 topsizer->Add(html, 1, wxALL, 10);
1329
1330 wxButton* okbutt = new wxButton(&dlg, wxID_OK, _("OK"));
1331 okbutt->SetDefault();
1332 topsizer->Add(okbutt, 0, wxBOTTOM | wxALIGN_CENTER, 10);
1333
1334 dlg.SetSizer(topsizer);
1335 topsizer->Fit(&dlg);
1336 dlg.Centre();
1337 dlg.ShowModal();
1338 viewptr->ResetMouseDown();
1339 }
1340