source: git/src/aven.cc @ 81b44f0

RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernloglog-selectstereowalls-datawalls-data-hanging-as-warning
Last change on this file since 81b44f0 was c09cb9c, checked in by Olly Betts <olly@…>, 6 years ago

Inline wmsg() from aven.h

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