1 // -*- mode: c++; c-file-style: "linux"; c-basic-offset: 2; indent-tabs-mode: nil -*-
2 //
3 // Copyright (C) 2004-2015 Andrej Vodopivec <andrej.vodopivec@gmail.com>
4 // (C) 2015-2018 Gunter Königsmann <wxMaxima@physikbuch.de>
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 //
21 // SPDX-License-Identifier: GPL-2.0+
22
23 /*!\file
24 The file that defines the starting point of the program
25 */
26
27 #include <wx/wx.h>
28 #include <wx/tipdlg.h>
29 #include <wx/config.h>
30 #include <wx/intl.h>
31 #include <wx/fs_zip.h>
32 #include <wx/image.h>
33 #include <wx/utils.h>
34 #include <wx/cmdline.h>
35 #include <wx/fileconf.h>
36 #include <wx/sysopt.h>
37 #include "Dirstructure.h"
38 #include <iostream>
39
40 #include "../examples/examples.h"
41 #include "wxMaxima.h"
42 #include "Version.h"
43
44 // On wxGTK2 we support printing only if wxWidgets is compiled with gnome_print.
45 // We have to force gnome_print support to be linked in static builds of wxMaxima.
46
47 #if defined wxUSE_LIBGNOMEPRINT
48 #if wxUSE_LIBGNOMEPRINT
49 #include "wx/html/forcelnk.h"
50 FORCE_LINK(gnome_print)
51 #endif
52 #endif
53
54
55 IMPLEMENT_APP_NO_MAIN(MyApp);
56 IMPLEMENT_WX_THEME_SUPPORT;
57
58 #ifndef __WXMSW__
main(int argc,char * argv[])59 int main(int argc, char *argv[])
60 {
61 wxEntryStart( argc, argv );
62 wxTheApp->CallOnInit();
63 #pragma omp parallel
64 #pragma omp master
65 wxTheApp->OnRun();
66 return 0;
67 }
68 #else
WinMain(HINSTANCE hI,HINSTANCE hPrevI,LPSTR lpCmdLine,int nCmdShow)69 int WINAPI WinMain( HINSTANCE hI, HINSTANCE hPrevI, LPSTR lpCmdLine, int nCmdShow )
70 {
71 wxEntryStart(hI, hPrevI, lpCmdLine, nCmdShow);
72 wxTheApp->CallOnInit();
73 #pragma omp parallel
74 #pragma omp master
75 wxTheApp->OnRun();
76 return 0;
77 }
78 #endif
79
80 std::list<wxMaxima *> MyApp::m_topLevelWindows;
81
82
OnInit()83 bool MyApp::OnInit()
84 {
85 // On the Mac if any of these commands outputs text to stderr maxima fails to
86 // connect to wxMaxima. We therefore delay all output to the log until there
87 // is a window that can display it on the GUI instead.
88 wxLogBuffer noStdErr;
89 {
90
91 Connect(wxID_NEW, wxEVT_MENU, wxCommandEventHandler(MyApp::OnFileMenu), NULL, this);
92 Connect(wxMaximaFrame::menu_help_tutorials_start, wxMaximaFrame::menu_help_tutorials_end,
93 wxEVT_MENU, wxCommandEventHandler(MyApp::OnFileMenu), NULL, this);
94
95 // If supported: Generate symbolic backtraces on crashes.
96 #if wxUSE_ON_FATAL_EXCEPTION
97 wxHandleFatalExceptions(true);
98 #endif
99 int major;
100 int minor;
101 wxGetOsVersion(&major, &minor);
102
103 // Directdraw should be faster, but crashes on closing on Win7.
104 if((major > 6) || ((major == 6) && (minor >1)))
105 wxSystemOptions::SetOption("msw.display.directdraw","1");
106 else
107 wxSystemOptions::SetOption("msw.display.directdraw","0");
108 // No spell checking in our dialog's input portions on the mac.
109 wxSystemOptions::SetOption("mac.textcontrol-use-spell-checker","0");
110
111 // Migrate an eventual old config file to the location XDG wants it to be.
112 #ifndef __WXMSW__
113 #if wxCHECK_VERSION(3, 1, 1)
114 wxStandardPaths::Get().SetFileLayout(wxStandardPaths::FileLayout_Classic);
115 wxString configFileOld = wxStandardPaths::Get().GetUserConfigDir() + wxT("/") +
116 wxStandardPaths::Get().MakeConfigFileName(
117 wxString(wxT("wxMaxima")),
118 wxStandardPaths::ConfigFileConv_Dot);
119 wxStandardPaths::Get().SetFileLayout(wxStandardPaths::FileLayout_XDG);
120 wxString configFileXDG = wxStandardPaths::Get().GetUserConfigDir() + wxT("/") +
121 wxStandardPaths::Get().MakeConfigFileName(
122 wxString(wxT("wxMaxima")),
123 wxStandardPaths::ConfigFileConv_Ext);
124
125 if(!wxFileExists(configFileXDG))
126 {
127 wxFileName xdgDir(configFileXDG);
128 wxString dirName(xdgDir.GetPath());
129 if(!wxDirExists(dirName))
130 wxMkDir(dirName,0x700);
131 if(wxFileExists(configFileOld))
132 wxCopyFile(configFileOld,configFileXDG);
133 }
134 #endif
135 #endif
136 wxConfig::Set(new wxConfig(wxT("wxMaxima"), wxEmptyString, m_configFileName));
137
138 m_locale.AddCatalogLookupPathPrefix(m_dirstruct.LocaleDir());
139 m_locale.AddCatalogLookupPathPrefix(m_dirstruct.LocaleDir() + wxT("/wxwin"));
140 m_locale.AddCatalogLookupPathPrefix(wxT("/usr/share/locale"));
141 m_locale.AddCatalogLookupPathPrefix(wxT("/usr/local/share/locale"));
142 wxConfigBase *config = wxConfig::Get();
143 long lang = wxLocale::GetSystemLanguage();
144 config->Read(wxT("language"), &lang);
145 if(lang == wxLANGUAGE_UNKNOWN)
146 lang = wxLANGUAGE_DEFAULT;
147 m_locale.Init(lang);
148
149 // Do we reckong we improve something if we set maxima's language, as well?
150 if((wxLocale::IsAvailable(lang)) && (lang != wxLANGUAGE_DEFAULT))
151 {
152 // Set maxima's language, as well.
153 wxString localeName = m_locale.GetCanonicalName();
154 if(lang != wxLocale::GetSystemLanguage())
155 {
156 if(m_locale.GetSystemEncoding() == wxFONTENCODING_UTF16)
157 localeName+=wxT(".UTF-16");
158 else
159 {
160 if(m_locale.GetSystemEncoding() == wxFONTENCODING_UTF32)
161 localeName+=wxT(".UTF-32");
162 else
163 {
164 localeName+=wxT(".UTF-8");
165 }
166 }
167 wxSetEnv(wxT("LANG"), localeName);
168 }
169 }
170 m_locale.AddCatalog(wxT("wxMaxima"));
171 m_locale.AddCatalog(wxT("wxMaxima-wxstd"));
172 }
173
174 bool exitAfterEval = false;
175 bool evalOnStartup = false;
176 wxCmdLineParser cmdLineParser(argc, argv);
177
178 static const wxCmdLineEntryDesc cmdLineDesc[] =
179 {
180 {wxCMD_LINE_SWITCH, "v", "version", "Output the version info", wxCMD_LINE_VAL_NONE , 0},
181 /* Usually wxCMD_LINE_OPTION_HELP is used with the following option, but that displays a message
182 * using a own window and we want the message on the command line. If a user enters a command
183 * line option, he expects probably a answer just on the command line... */
184 {wxCMD_LINE_SWITCH, "h", "help", "show this help message", wxCMD_LINE_VAL_NONE, 0},
185 {wxCMD_LINE_OPTION, "o", "open", "open a file", wxCMD_LINE_VAL_STRING , 0},
186 {wxCMD_LINE_SWITCH, "e", "eval",
187 "evaluate the file after opening it.", wxCMD_LINE_VAL_NONE , 0},
188 {wxCMD_LINE_SWITCH, "b", "batch",
189 "run the file and exit afterwards. Halts on questions and stops on errors.", wxCMD_LINE_VAL_NONE, 0},
190 {wxCMD_LINE_SWITCH, "", "logtostdout",
191 "Log all \"debug messages\" sidebar messages to stderr, too.", wxCMD_LINE_VAL_NONE, 0},
192 {wxCMD_LINE_SWITCH, "", "pipe",
193 "Pipe messages from Maxima to stdout.", wxCMD_LINE_VAL_NONE, 0},
194 {wxCMD_LINE_SWITCH, "", "exit-on-error",
195 "Close the program on any Maxima error.", wxCMD_LINE_VAL_NONE, 0},
196 {wxCMD_LINE_OPTION, "f", "ini", "allows to specify a file to store the configuration in", wxCMD_LINE_VAL_STRING , 0},
197 {wxCMD_LINE_OPTION, "u", "use-version",
198 "Use Maxima version <str>.", wxCMD_LINE_VAL_STRING, 0},
199 {wxCMD_LINE_OPTION, "l", "lisp",
200 "Use a Maxima compiled with lisp compiler <str>.", wxCMD_LINE_VAL_STRING, 0},
201 {wxCMD_LINE_OPTION, "X", "extra-args",
202 "Allows to specify extra Maxima arguments", wxCMD_LINE_VAL_STRING, 0},
203 { wxCMD_LINE_OPTION, "m", "maxima", "allows to specify the location of the Maxima binary", wxCMD_LINE_VAL_STRING , 0},
204 {wxCMD_LINE_PARAM, NULL, NULL, "input file", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL | wxCMD_LINE_PARAM_MULTIPLE},
205 {wxCMD_LINE_NONE, "", "", "", wxCMD_LINE_VAL_NONE, 0}
206 };
207
208 cmdLineParser.SetDesc(cmdLineDesc);
209 int cmdLineError = cmdLineParser.Parse();
210
211 if (cmdLineParser.Found(wxT("h")))
212 {
213 std::cout << "A feature-rich graphical user interface for the computer algebra system Maxima\n";
214 std::cout << cmdLineParser.GetUsageString();
215 exit(0);
216 }
217
218 if(cmdLineError != 0)
219 exit(-1);
220
221 wxString ini, file;
222 // Attention: The config file is changed by wxMaximaFrame::wxMaximaFrame::ReReadConfig
223 if (cmdLineParser.Found(wxT("f"),&ini))
224 {
225 wxFileName configFile(ini);
226 configFile.MakeAbsolute();
227 Configuration::m_configfileLocation_override = configFile.GetFullPath();
228 wxConfig::Set(new wxConfig(wxT("wxMaxima"), wxEmptyString,
229 Configuration::m_configfileLocation_override));
230 }
231 else
232 wxConfig::Set(new wxConfig(wxT("wxMaxima")));
233
234 if (cmdLineParser.Found(wxT("logtostdout")))
235 ErrorRedirector::LogToStdErr();
236
237 if (cmdLineParser.Found(wxT("pipe")))
238 wxMaxima::PipeToStdout();
239
240 if (cmdLineParser.Found(wxT("exit-on-error")))
241 wxMaxima::ExitOnError();
242
243 wxString extraMaximaArgs;
244 wxString arg;
245 if (cmdLineParser.Found(wxT("l"), &arg))
246 extraMaximaArgs += " -l " + arg;
247
248 if (cmdLineParser.Found(wxT("X"), &arg))
249 extraMaximaArgs += " -X " + arg;
250
251 if (cmdLineParser.Found(wxT("u"), &arg))
252 extraMaximaArgs += " -u " + arg;
253
254 wxMaxima::ExtraMaximaArgs(extraMaximaArgs);
255
256 wxImage::AddHandler(new wxPNGHandler);
257 wxImage::AddHandler(new wxXPMHandler);
258 wxImage::AddHandler(new wxJPEGHandler);
259 wxImage::AddHandler(new wxGIFHandler);
260
261 wxFileSystem::AddHandler(new wxZipFSHandler);
262
263 #ifdef __WXMSW__
264 wxString oldWorkingDir = wxGetCwd();
265 if (!wxGetEnv(wxT("BUILD_DIR"), NULL))
266 {
267 wxString dir = wxPathOnly(wxStandardPaths::Get().GetExecutablePath());
268 if(dir != wxEmptyString)
269 wxSetWorkingDirectory(wxPathOnly(wxStandardPaths::Get().GetExecutablePath()));
270 }
271 /* Add private jsMath fonts, if they exist */
272 #if wxCHECK_VERSION(3, 1, 1)
273 wxString fontPrefix = m_dirstruct.FontDir() + wxT("/");
274 if (wxFileExists(fontPrefix + wxT(CMEX10) + wxT(".ttf"))) wxFont::AddPrivateFont(fontPrefix + wxT(CMEX10) + wxT(".ttf"));
275 if (wxFileExists(fontPrefix + wxT(CMSY10) + wxT(".ttf"))) wxFont::AddPrivateFont(fontPrefix + wxT(CMSY10) + wxT(".ttf"));
276 if (wxFileExists(fontPrefix + wxT(CMR10) + wxT(".ttf"))) wxFont::AddPrivateFont(fontPrefix + wxT(CMR10) + wxT(".ttf"));
277 if (wxFileExists(fontPrefix + wxT(CMMI10) + wxT(".ttf"))) wxFont::AddPrivateFont(fontPrefix + wxT(CMMI10) + wxT(".ttf"));
278 if (wxFileExists(fontPrefix + wxT(CMTI10) + wxT(".ttf"))) wxFont::AddPrivateFont(fontPrefix + wxT(CMTI10) + wxT(".ttf"));
279
280 /* Add private Libertine fonts, if they exist */
281 if (wxFileExists(fontPrefix + wxT(LIBERTINE1)))
282 wxFont::AddPrivateFont(fontPrefix + wxT(LIBERTINE1));
283 if (wxFileExists(fontPrefix + wxT(LIBERTINE2))) wxFont::AddPrivateFont(fontPrefix + wxT(LIBERTINE2));
284 if (wxFileExists(fontPrefix + wxT(LIBERTINE3))) wxFont::AddPrivateFont(fontPrefix + wxT(LIBERTINE3));
285 if (wxFileExists(fontPrefix + wxT(LIBERTINE4))) wxFont::AddPrivateFont(fontPrefix + wxT(LIBERTINE4));
286 if (wxFileExists(fontPrefix + wxT(LIBERTINE5))) wxFont::AddPrivateFont(fontPrefix + wxT(LIBERTINE5));
287 if (wxFileExists(fontPrefix + wxT(LIBERTINE6))) wxFont::AddPrivateFont(fontPrefix + wxT(LIBERTINE6));
288 if (wxFileExists(fontPrefix + wxT(LIBERTINE7))) wxFont::AddPrivateFont(fontPrefix + wxT(LIBERTINE7));
289 if (wxFileExists(fontPrefix + wxT(LIBERTINE8))) wxFont::AddPrivateFont(fontPrefix + wxT(LIBERTINE8));
290 if (wxFileExists(fontPrefix + wxT(LIBERTINE9))) wxFont::AddPrivateFont(fontPrefix + wxT(LIBERTINE9));
291 #endif
292 wxSetWorkingDirectory(oldWorkingDir);
293
294 #endif
295
296 #if defined __WXOSX__
297 wxString path;
298 wxGetEnv(wxT("PATH"), &path);
299 wxSetEnv(wxT("PATH"), path << wxT(":/usr/local/bin"));
300
301 wxApp::SetExitOnFrameDelete(false);
302 wxMenuBar::SetAutoWindowMenu(true);
303 wxMenuBar *menuBar = new wxMenuBar;
304 // Enables the window list on MacOs.
305 menuBar->SetAutoWindowMenu(true);
306 wxMenu *fileMenu = new wxMenu;
307 fileMenu->Append(wxID_NEW, _("&New\tCtrl+N"));
308 fileMenu->Append(wxID_OPEN, _("&Open\tCtrl+O"));
309 menuBar->Append(fileMenu, _("File"));
310 wxMenuBar::MacSetCommonMenuBar(menuBar);
311 Connect(wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MyApp::OnFileMenu));
312 #endif
313
314 if (cmdLineParser.Found(wxT("v")))
315 {
316 std::cout << "wxMaxima ";
317 std::cout << GITVERSION;
318 #if defined(WXMAXIMA_GIT_VERSION)
319 std::cout << " (Git version: " << WXMAXIMA_GIT_VERSION << ")";
320 #endif
321 std::cout << "\n";
322 exit(0);
323 }
324
325 if (cmdLineParser.Found(wxT("b")))
326 {
327 evalOnStartup = true;
328 exitAfterEval = true;
329 }
330
331 if (cmdLineParser.Found(wxT("e")))
332 evalOnStartup = true;
333
334 bool windowOpened = false;
335
336 if (cmdLineParser.Found(wxT("o"), &file))
337 {
338 wxFileName FileName = file;
339 FileName.MakeAbsolute();
340 wxString CanonicalFilename = FileName.GetFullPath();
341 NewWindow(wxString(CanonicalFilename), evalOnStartup, exitAfterEval);
342 windowOpened = true;
343 }
344
345 if(cmdLineParser.GetParamCount() > 0)
346 {
347 for (unsigned int i=0; i < cmdLineParser.GetParamCount(); i++)
348 {
349 wxFileName FileName = cmdLineParser.GetParam(i);
350 FileName.MakeAbsolute();
351
352 wxString CanonicalFilename = FileName.GetFullPath();
353 NewWindow(CanonicalFilename, evalOnStartup, exitAfterEval);
354 }
355 windowOpened = true;
356 }
357
358 if(!windowOpened)
359 NewWindow();
360
361 // Now we can finally send our debug output to a window without making
362 // std::cerr confusing the mac.
363 wxString logMessagesSoFar = noStdErr.GetBuffer();
364 if(!logMessagesSoFar.IsEmpty())
365 wxLogMessage("Log messages during early startup: " + logMessagesSoFar);
366 return true;
367 }
368
OnExit()369 int MyApp::OnExit()
370 {
371 return 0;
372 }
373
OnRun()374 int MyApp::OnRun()
375 {
376 wxApp::OnRun();
377 return 0;
378 }
379
NewWindow(wxString file,bool evalOnStartup,bool exitAfterEval,unsigned char * wxmData,int wxmLen)380 void MyApp::NewWindow(wxString file, bool evalOnStartup, bool exitAfterEval, unsigned char *wxmData, int wxmLen)
381 {
382 int numberOfWindows = m_topLevelWindows.size();
383
384 wxString title = _("wxMaxima");
385 if (file.Length() > 0)
386 title = file;
387
388 if (numberOfWindows > 1)
389 title = wxString::Format(_("wxMaxima %d"), numberOfWindows);
390
391 wxMaxima *frame = new wxMaxima(NULL, -1, &m_locale, title, file);
392 if (wxmData)
393 {
394 // Unzip the .wxm file
395 wxMemoryInputStream istream(wxmData, wxmLen);
396 wxZlibInputStream zstream(istream);
397 wxTextInputStream textIn(zstream, "\t", wxConvAuto(wxFONTENCODING_UTF8));
398 wxString initialContents;
399 wxString line;
400 wxString block;
401 while(!zstream.Eof())
402 {
403 line = textIn.ReadLine();
404 if((line.StartsWith("/*")) || (line.EndsWith("*/")))
405 {
406 initialContents += _(block);
407 initialContents += line + "\n";
408 block = wxEmptyString;
409 }
410 else
411 block += line + "\n";
412 }
413 initialContents += _(block);
414 frame->SetWXMdata(initialContents);
415 }
416
417 frame->ExitAfterEval(exitAfterEval);
418 frame->EvalOnStartup(evalOnStartup);
419 m_topLevelWindows.push_back(frame);
420
421 SetTopWindow(frame);
422 frame->Show(true);
423 frame->ShowTip(false);
424 }
425
OnFileMenu(wxCommandEvent & ev)426 void MyApp::OnFileMenu(wxCommandEvent &ev)
427 {
428 switch (ev.GetId())
429 {
430 case wxMaxima::menu_help_numberformats:
431 NewWindow(wxEmptyString, false, false,
432 numberFormats_wxm_gz, numberFormats_wxm_gz_len);
433 break;
434 case wxMaxima::menu_help_3d:
435 NewWindow(wxEmptyString, false, false,
436 displaying3DCurves_wxm_gz, displaying3DCurves_wxm_gz_len);
437 break;
438
439 case wxMaxima::menu_help_varnames:
440 NewWindow(wxEmptyString, false, false,
441 variableNames_wxm_gz, variableNames_wxm_gz_len);
442 break;
443
444 case wxMaxima::menu_help_listaccess:
445 NewWindow(wxEmptyString, false, false,
446 fastListAccess_wxm_gz, fastListAccess_wxm_gz_len);
447 break;
448
449 case wxMaxima::menu_help_fittingData:
450 NewWindow(wxEmptyString, false, false,
451 fittingEquations_wxm_gz, fittingEquations_wxm_gz_len);
452 break;
453 case wxMaxima::menu_help_solving:
454 NewWindow(wxEmptyString, false, false,
455 solvingEquations_wxm_gz, solvingEquations_wxm_gz_len);
456 break;
457 case wxMaxima::menu_help_diffequations:
458 NewWindow(wxEmptyString, false, false,
459 diffEquations_wxm_gz, diffEquations_wxm_gz_len);
460 break;
461 case wxMaxima::menu_help_tolerances:
462 NewWindow(wxEmptyString, false, false,
463 toleranceCalculations_wxm_gz, toleranceCalculations_wxm_gz_len);
464 break;
465 case wxID_NEW:
466 {
467 // Mac computers insist that all instances of a new application need to share
468 // the same process. On all other OSes we create a separate process for each
469 // window: This way if one instance of wxMaxima crashes all the other instances
470 // are still alive.
471 #if defined __WXOSX__
472 NewWindow();
473 #else
474 // NewWindow();
475 wxString args;
476 if(Configuration::m_configfileLocation_override != wxEmptyString)
477 args += " -f \"" + Configuration::m_configfileLocation_override + "\"";
478 if(Configuration::m_maximaLocation_override != wxEmptyString)
479 args += " -m \"" + Configuration::m_maximaLocation_override + "\"";
480
481 wxExecute(wxT("\"") + wxStandardPaths::Get().GetExecutablePath() + wxT("\"") + args);
482 #endif
483 break;
484 }
485 case wxID_EXIT:
486 {
487 std::list<wxMaxima *>::const_iterator it=m_topLevelWindows.begin();
488 while(it != m_topLevelWindows.end())
489 {
490 if (*it != NULL)
491 {
492 wxCloseEvent *event = new wxCloseEvent(wxEVT_CLOSE_WINDOW);
493 event->SetCanVeto(true);
494 event->SetLoggingOff(false);
495 (*it)->GetEventHandler()->QueueEvent(event);
496 }
497 ++it;
498 }
499 }
500 break;
501 }
502 }
503
MacNewFile()504 void MyApp::MacNewFile()
505 {
506 wxWindow *frame = GetTopWindow();
507 if (frame == NULL)
508 NewWindow();
509 }
510
MacOpenFile(const wxString & file)511 void MyApp::MacOpenFile(const wxString &file)
512 {
513 NewWindow(file);
514 }
515