source: git/src/aven.cc @ 1deb6b5

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

Avoid buffering the line containing the error

Just write it out as we read it to avoid having to truncate it if
it doesn't fit in the fixed sized buffer, or have to worry about
dynamically sizing a buffer.

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