source: git/src/aven.cc @ cde8002

RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernloglog-selectstereowalls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since cde8002 was faf83bee, checked in by Olly Betts <olly@…>, 9 years ago

Escape for CreateProcess?(), not cmd.exe

We now use wxProcess::Open() to run cavern, which uses wxExecute() which
uses CreateProcess?().

And use wxExecute() not wxSystem() to start an editor, so that the
quoting needed there is the same as that needed for cavern.

  • Property mode set to 100644
File size: 13.2 KB
RevLine 
[0060ba5]1//
[203d2a7]2//  aven.cc
[0060ba5]3//
[5809313]4//  Main class for Aven.
[0060ba5]5//
[3675a18]6//  Copyright (C) 2001 Mark R. Shinwell.
[faf83bee]7//  Copyright (C) 2002,2003,2004,2005,2006,2011,2013,2014,2015,2016 Olly Betts
[0060ba5]8//
[89231c4]9//  This program is free software; you can redistribute it and/or modify
10//  it under the terms of the GNU General Public License as published by
11//  the Free Software Foundation; either version 2 of the License, or
12//  (at your option) any later version.
[0060ba5]13//
14//  This program is distributed in the hope that it will be useful,
15//  but WITHOUT ANY WARRANTY; without even the implied warranty of
[89231c4]16//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17//  GNU General Public License for more details.
[0060ba5]18//
[89231c4]19//  You should have received a copy of the GNU General Public License
20//  along with this program; if not, write to the Free Software
[ecbc6c18]21//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
[5809313]22//
[0060ba5]23
[b462168]24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
[5809313]28#include "aven.h"
[78924eb]29#include "log.h"
[5809313]30#include "mainfrm.h"
[706b033]31
[d07944e]32#include "cmdline.h"
[93c3f97]33#include "message.h"
[c49e27f]34#include "useful.h"
[0060ba5]35
36#include <assert.h>
[c49e27f]37#include <stdio.h>
[0060ba5]38
[e0ffc2c]39#include <wx/confbase.h>
[573f4e9]40#include <wx/image.h>
[e0ffc2c]41#if wxUSE_DISPLAY
42// wxDisplay was added in wx 2.5; but it may not be built for mingw (because
43// the header seems to be missing).
[ca18348]44#include <wx/display.h>
45#endif
[573f4e9]46
[706ddcf]47#if defined __WXMAC__ || defined __WXMSW__
48#include <proj_api.h>
49#endif
50
[faf83bee]51#ifdef __WXMSW__
52#include <windows.h>
53#endif
54
[c293aa9]55bool double_buffered = false;
56
[879e9c3]57static const struct option long_opts[] = {
58    /* const char *name; int has_arg (0 no_argument, 1 required_*, 2 optional_*); int *flag; int val; */
59    {"survey", required_argument, 0, 's'},
60    {"print", no_argument, 0, 'p'},
61    {"help", no_argument, 0, HLP_HELP},
62    {"version", no_argument, 0, HLP_VERSION},
63    {0, 0, 0, 0}
64};
65
66#define short_opts "s:p"
67
68static struct help_msg help[] = {
[45af761]69    /*                          <-- */
[736f7df]70    /* TRANSLATORS: --help output for --survey option.
71     *
72     * "this" has been added to English translation */
[45af761]73    {HLP_ENCODELONG(0),       /*only load the sub-survey with this prefix*/199, 0},
[736f7df]74    /* TRANSLATORS: --help output for aven --print option */
[45af761]75    {HLP_ENCODELONG(1),       /*print and exit (requires a 3d file)*/119, 0},
76    {0, 0, 0}
[879e9c3]77};
78
[5627cbb]79#ifdef __WXMSW__
[5809313]80IMPLEMENT_APP(Aven)
[5627cbb]81#else
82IMPLEMENT_APP_NO_MAIN(Aven)
83IMPLEMENT_WX_THEME_SUPPORT
84#endif
[0060ba5]85
[5809313]86Aven::Aven() :
[2c1d2f4]87    m_Frame(NULL), m_pageSetupData(NULL)
[0060ba5]88{
[1f81f3d]89    wxFont::SetDefaultEncoding(wxFONTENCODING_UTF8);
[0060ba5]90}
91
[2c1d2f4]92Aven::~Aven()
93{
[81f1266]94    delete m_pageSetupData;
[2c1d2f4]95}
96
[5627cbb]97static int getopt_first_response = 0;
98
[5440c295]99static char ** utf8_argv;
100
[5627cbb]101#ifdef __WXMSW__
102bool Aven::Initialize(int& my_argc, wxChar **my_argv)
103{
[faf83bee]104    const wxChar * cmd_line = GetCommandLineW();
105
[b208d0b]106    // Horrible bodge to handle therion's assumptions about the "Process"
107    // file association.
[faf83bee]108    if (cmd_line) {
109        // None of these are valid aven command line options, so this is not
110        // going to be triggered accidentally.
111        wxChar * p = wxStrStr(cmd_line, "aven.exe\" --quiet --log --output=");
112        if (p) {
113            // Just change the command name in the command line string - that
114            // way the quoting should match what the C runtime expects.
115            wxString cmd(cmd_line, p - cmd_line);
116            cmd += "cavern";
117            cmd += p + 4;
118            exit(wxExecute(cmd, wxEXEC_SYNC));
119        }
[b208d0b]120    }
121
[faf83bee]122    int utf8_argc;
123    {
124        // wxWidgets doesn't split up the command line in the standard way, so
125        // redo it ourselves using the standard API function.
126        //
127        // Warning: The returned array from this has no terminating NULL
128        // element.
129        wxChar ** new_argv = NULL;
130        if (cmd_line)
131            new_argv = CommandLineToArgvW(cmd_line, &utf8_argc);
132        bool failed = (new_argv == NULL);
133        if (failed) {
134            wxChar * p;
135            FormatMessage(
136                    FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
137                    NULL,
138                    GetLastError(),
139                    0,
140                    (LPWSTR)&p,
141                    4096,
142                    NULL);
143            wxString m = "CommandLineToArgvW failed: ";
144            m += p;
145            wxMessageBox(m, APP_NAME, wxOK | wxCENTRE | wxICON_EXCLAMATION);
146            LocalFree(p);
147            utf8_argc = my_argc;
148            new_argv = my_argv;
149        }
150
151        // Convert wide characters to UTF-8.
152        utf8_argv = new char * [utf8_argc + 1];
153        for (int i = 0; i < utf8_argc; ++i){
154            utf8_argv[i] = strdup(wxString(new_argv[i]).mb_str());
155        }
156        utf8_argv[utf8_argc] = NULL;
157
158        if (!failed) LocalFree(new_argv);
[5627cbb]159    }
160
[5440c295]161    msg_init(utf8_argv);
[d18e778]162    pj_set_finder(msg_proj_finder);
[f1cbf74]163    select_charset(CHARSET_UTF8);
[293eec8]164    /* Want --version and decent --help output, which cmdline does for us.
[f1cbf74]165     * wxCmdLine is much less good.
166     */
[736f7df]167    /* TRANSLATORS: Here "survey" is a "cave map" rather than list of questions
168     * - it should be translated to the terminology that cavers using the
169     * language would use.
[b49ac56]170     *
[736f7df]171     * Part of aven --help */
[d8dbdff]172    cmdline_set_syntax_message(/*[SURVEY_FILE]*/269, 0, NULL);
[faf83bee]173    cmdline_init(utf8_argc, utf8_argv, short_opts, long_opts, NULL, help, 0, 1);
[5627cbb]174    getopt_first_response = cmdline_getopt();
[faf83bee]175
176    // The argc and argv arguments don't actually get used here.
177    int dummy_argc = 0;
178    return wxApp::Initialize(dummy_argc, NULL);
[f1cbf74]179}
[5627cbb]180#else
[b49ac56]181int main(int argc, char **argv)
[0060ba5]182{
[27b8b59]183#ifdef __WXMAC__
[879e9c3]184    // MacOS passes a magic -psn_XXXX command line argument in argv[1] which
185    // wx ignores for us, but in wxApp::Initialize() which hasn't been
186    // called yet.  So we need to remove it ourselves.
[5627cbb]187    if (argc > 1 && strncmp(argv[1], "-psn_", 5) == 0) {
188        --argc;
189        memmove(argv + 1, argv + 2, argc * sizeof(char *));
[879e9c3]190    }
191#endif
192    // Call msg_init() and start processing the command line first so that
193    // we can respond to --help and --version even without an X display.
[5627cbb]194    msg_init(argv);
[d18e778]195#ifdef __WXMAC__
196    pj_set_finder(msg_proj_finder);
197#endif
[5627cbb]198    select_charset(CHARSET_UTF8);
[293eec8]199    /* Want --version and decent --help output, which cmdline does for us.
[5627cbb]200     * wxCmdLine is much less good.
201     */
[d8dbdff]202    cmdline_set_syntax_message(/*[SURVEY_FILE]*/269, 0, NULL);
[5627cbb]203    cmdline_init(argc, argv, short_opts, long_opts, NULL, help, 0, 1);
[879e9c3]204    getopt_first_response = cmdline_getopt();
[5627cbb]205
[5440c295]206    utf8_argv = argv;
[5627cbb]207
[f891a0c]208#if wxUSE_UNICODE
[5627cbb]209    wxWCharBuffer buf(wxConvFileName->cMB2WX(argv[0]));
210    wxChar * wargv[2];
211    if (buf) {
212        wargv[0] = wxStrdup(buf);
213    } else {
214        // Eep - couldn't convert the executable's name to wide characters!
215        wargv[0] = wxStrdup(APP_NAME);
216    }
217    wargv[1] = NULL;
218    int wargc = 1;
219    return wxEntry(wargc, wargv);
[495cd3e]220#else
[a212391]221    char *dummy_argv[2] = { argv[0], NULL };
222    int dummy_argc = 1;
223    return wxEntry(dummy_argc, dummy_argv);
[495cd3e]224#endif
[879e9c3]225}
[5627cbb]226#endif
[879e9c3]227
228bool Aven::OnInit()
229{
[4013d9c]230    wxLog::SetActiveTarget(new MyLogWindow());
[421b7d2]231
[d607cea]232    {
[b1876d3]233        // Suppress message box warnings about messages not found.
[71ea9e1]234        wxLogNull logNo;
235        wxLocale *loc = new wxLocale();
[8a05a7a]236        loc->AddCatalogLookupPathPrefix(wmsg_cfgpth());
[b1876d3]237        wxString msg_lang_str(msg_lang, wxConvUTF8);
238        const char *lang = msg_lang2 ? msg_lang2 : msg_lang;
239        wxString lang_str(lang, wxConvUTF8);
[f1d5d26]240#if wxCHECK_VERSION(2,9,0)
241        loc->Init(msg_lang_str, lang_str, msg_lang_str);
242#else
[6d1bc83]243        loc->Init(msg_lang_str, lang_str, msg_lang_str, true, true);
[f1d5d26]244#endif
[71ea9e1]245        // The existence of the wxLocale object is enough - no need to keep a
246        // pointer to it!
[003d953]247    }
[93c3f97]248
[d07944e]249    wxString survey;
[3ee64fb]250    bool print_and_exit = false;
[421b7d2]251
[0580c6a]252    while (true) {
[879e9c3]253        int opt;
254        if (getopt_first_response) {
255            opt = getopt_first_response;
256            getopt_first_response = 0;
257        } else {
258            opt = cmdline_getopt();
259        }
[d07944e]260        if (opt == EOF) break;
261        if (opt == 's') {
[5627cbb]262            survey = wxString(optarg, wxConvUTF8);
[d07944e]263        }
[3ee64fb]264        if (opt == 'p') {
265            print_and_exit = true;
266        }
267    }
268
[5440c295]269    if (print_and_exit && !utf8_argv[optind]) {
[3ee64fb]270        cmdline_syntax(); // FIXME : not a helpful error...
271        exit(1);
[d07944e]272    }
[068b4f2]273
[5627cbb]274    wxString fnm;
[5440c295]275    if (utf8_argv[optind]) {
276        fnm = wxString(utf8_argv[optind], wxConvUTF8);
277        if (fnm.empty() && *(utf8_argv[optind])) {
[c49e27f]278            ReportError(wxT("File argument's filename has bad encoding"));
[5627cbb]279            return false;
280        }
281    }
282
[c293aa9]283    // Use a double-buffered visual if available, as it will give much smoother
284    // animation.
285    double_buffered = true;
[cbd9829]286    const int wx_gl_attribs[] = {
287        WX_GL_DOUBLEBUFFER,
288        WX_GL_RGBA,
289        0
290    };
[c293aa9]291    if (!InitGLVisual(wx_gl_attribs)) {
[cbd9829]292        if (!InitGLVisual(wx_gl_attribs + 1)) {
[c293aa9]293            wxString m;
[736f7df]294            /* TRANSLATORS: %s will be replaced with "Aven" currently (and
295             * perhaps by "Survex" or other things in future). */
[ee7511a]296            m.Printf(wmsg(/*This version of %s requires OpenGL to work, but it isn’t available.*/405), APP_NAME);
[c293aa9]297            wxMessageBox(m, APP_NAME, wxOK | wxCENTRE | wxICON_EXCLAMATION);
298            exit(1);
299        }
300        double_buffered = false;
[5892e08]301    }
302
[573f4e9]303    wxImage::AddHandler(new wxPNGHandler);
[08d2074]304
[4909e6bc]305    // Obtain the screen geometry.
306#if wxUSE_DISPLAY
307    wxRect geom = wxDisplay().GetGeometry();
308#else
309    wxRect geom;
310    wxClientDisplayRect(&geom.x, &geom.y, &geom.width, &geom.height);
311#endif
312
[3db0798]313    wxPoint pos(wxDefaultPosition);
[ca18348]314    int width, height;
[5627cbb]315    wxConfigBase::Get()->Read(wxT("width"), &width, 0);
316    if (width > 0) wxConfigBase::Get()->Read(wxT("height"), &height, 0);
[30621f5]317    // We used to persist full screen mode (-1 was maximized,
318    // -2 full screen), but people would get stuck in full
319    // screen mode, unsure how to exit.
320    bool maximized = (width <= -1);
[3db0798]321    if (width <= 0 || height <= 0) {
322        pos.x = geom.x;
323        pos.y = geom.y;
324        width = geom.width;
325        height = geom.height;
[486ae6a]326
[3db0798]327        // Calculate a reasonable size for our window.
328        pos.x += width / 8;
329        pos.y += height / 8;
330        width = width * 3 / 4;
331        height = height * 3 / 4;
[4909e6bc]332    } else {
333        // Impose a minimum size for sanity, and make sure the window fits on
334        // the display (in case the current display is smaller than the one
335        // in use when the window size was saved).  (480x320) is about the
336        // smallest usable size for aven's window.
337        const int min_width = min(geom.width, 480);
338        const int min_height = min(geom.height, 320);
339        if (width < min_width || height < min_height) {
340            if (width < min_width) {
341                width = min_width;
342            }
343            if (height < min_height) {
344                height = min_height;
345            }
346            pos.x = geom.x + (geom.width - width) / 4;
347            pos.y = geom.y + (geom.height - height) / 4;
348        }
[3db0798]349    }
[486ae6a]350
351    // Create the main window.
[3db0798]352    m_Frame = new MainFrm(APP_NAME, pos, wxSize(width, height));
353
[30621f5]354    // Select maximised if that's the saved state.
355    if (maximized) {
[3db0798]356        m_Frame->Maximize();
357    }
[068b4f2]358
[5440c295]359    if (utf8_argv[optind]) {
[5627cbb]360        m_Frame->OpenFile(fnm, survey);
[d07944e]361    }
[068b4f2]362
[3ee64fb]363    if (print_and_exit) {
[4ed8154]364        m_Frame->PrintAndExit();
[3ee64fb]365        return true;
366    }
367
[5809313]368    m_Frame->Show(true);
[526775d]369#ifdef _WIN32
370    m_Frame->SetFocus();
371#endif
[0060ba5]372    return true;
373}
374
[e0ffc2c]375wxPageSetupDialogData *
376Aven::GetPageSetupDialogData()
377{
[2c1d2f4]378    if (!m_pageSetupData) m_pageSetupData = new wxPageSetupDialogData;
[e0ffc2c]379#ifdef __WXGTK__
380    // Fetch paper margins stored on disk.
381    int left, right, top, bottom;
382    wxConfigBase * cfg = wxConfigBase::Get();
383    // These default margins were chosen by looking at all the .ppd files
384    // on my machine.
[5627cbb]385    cfg->Read(wxT("paper_margin_left"), &left, 7);
386    cfg->Read(wxT("paper_margin_right"), &right, 7);
387    cfg->Read(wxT("paper_margin_top"), &top, 14);
388    cfg->Read(wxT("paper_margin_bottom"), &bottom, 14);
[2c1d2f4]389    m_pageSetupData->SetMarginTopLeft(wxPoint(left, top));
390    m_pageSetupData->SetMarginBottomRight(wxPoint(right, bottom));
[e0ffc2c]391#endif
[2c1d2f4]392    return m_pageSetupData;
[e0ffc2c]393}
394
395void
396Aven::SetPageSetupDialogData(const wxPageSetupDialogData & psdd)
397{
[2c1d2f4]398    if (!m_pageSetupData) m_pageSetupData = new wxPageSetupDialogData;
399    *m_pageSetupData = psdd;
[e0ffc2c]400#ifdef __WXGTK__
401    wxPoint topleft = psdd.GetMarginTopLeft();
402    wxPoint bottomright = psdd.GetMarginBottomRight();
403
404    // Store user specified paper margins on disk/in registry.
405    wxConfigBase * cfg = wxConfigBase::Get();
[5627cbb]406    cfg->Write(wxT("paper_margin_left"), topleft.x);
407    cfg->Write(wxT("paper_margin_right"), bottomright.x);
408    cfg->Write(wxT("paper_margin_top"), topleft.y);
409    cfg->Write(wxT("paper_margin_bottom"), bottomright.y);
[e0ffc2c]410    cfg->Flush();
411#endif
412}
413
[ddc0994]414#ifdef __WXMAC__
415void
416Aven::MacOpenFiles(const wxArrayString & filenames)
417{
418    if (filenames.size() != 1) {
419        ReportError(wxT("Aven can only load one file at a time"));
420        return;
421    }
422    m_Frame->OpenFile(filenames[0], wxString());
423}
424
425void
426Aven::MacPrintFiles(const wxArrayString & filenames)
427{
428    if (filenames.size() != 1) {
429        ReportError(wxT("Aven can only print one file at a time"));
430        return;
431    }
432    m_Frame->OpenFile(filenames[0], wxString());
433    m_Frame->PrintAndExit();
434}
435#endif
436
[5809313]437void Aven::ReportError(const wxString& msg)
[0060ba5]438{
[c8b5fa5]439    if (!m_Frame) {
440        wxMessageBox(msg, APP_NAME, wxOK | wxICON_ERROR);
441        return;
442    }
[1c6b20d7]443    AvenAllowOnTop ontop(m_Frame);
[7015222]444    wxMessageDialog dlg(m_Frame, msg, APP_NAME, wxOK | wxICON_ERROR);
445    dlg.ShowModal();
[0060ba5]446}
[59fda0a]447
[5627cbb]448wxString
[b49ac56]449wmsg(int msg_no)
[5627cbb]450{
[833c6bc]451    return wxString::FromUTF8(msg(msg_no));
[5627cbb]452}
453
[8a05a7a]454const wxString &
455wmsg_cfgpth()
456{
457    static wxString path;
458    if (path.empty())
459        path = wxString(msg_cfgpth(), wxConvUTF8);
460    return path;
461}
462
[a9a32f2]463// called to report errors by message.c
464extern "C" void
465aven_v_report(int severity, const char *fnm, int line, int en, va_list ap)
466{
[c49e27f]467    wxString m;
468    if (fnm) {
469        m = wxString(fnm, wxConvUTF8);
470        if (line) m += wxString::Format(wxT(":%d"), line);
471        m += wxT(": ");
472    }
473
474    if (severity == 0) {
475        m += wmsg(/*warning*/4);
476        m += wxT(": ");
477    }
478
479    char buf[1024];
480    vsnprintf(buf, sizeof(buf), msg(en), ap);
481    m += wxString(buf, wxConvUTF8);
482    if (wxTheApp == NULL) {
483        // We haven't initialised the Aven app object yet.
484        if (!wxInitialize()) {
485            fputs(buf, stderr);
486            PUTC('\n', stderr);
487            exit(1);
488        }
489        wxMessageBox(m, APP_NAME, wxOK | wxICON_ERROR);
490        wxUninitialize();
491    } else {
492        wxGetApp().ReportError(m);
493    }
[a9a32f2]494}
Note: See TracBrowser for help on using the repository browser.