source: git/src/aven.cc

walls-data
Last change on this file was 4c83f84, checked in by Olly Betts <olly@…>, 4 days ago

Don't check HAVE_CONFIG_H in most cases

This check is only useful for img.c, which is intended to be usable
outside of Survex (and had fallbacks for functions which may not be
available which will get used if built in a non-autotools project).
For all the other source files it's just useless boilerplate.

  • Property mode set to 100644
File size: 13.5 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,2018 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#include <config.h>
25
26#define MSG_SETUP_PROJ_SEARCH_PATH 1
27
28#include "aven.h"
29#include "log.h"
30#include "gla.h"
31#include "mainfrm.h"
32
33#include "cmdline.h"
34#include "message.h"
35#include "useful.h"
36
37#include <assert.h>
38#include <stdio.h>
39
40#include <wx/confbase.h>
41#include <wx/image.h>
42#if wxUSE_DISPLAY
43// wxDisplay was added in wx 2.5; but it may not be built for mingw (because
44// the header seems to be missing).
45#include <wx/display.h>
46#endif
47
48#ifdef __WXMSW__
49#include <windows.h>
50#endif
51
52static const struct option long_opts[] = {
53    /* const char *name; int has_arg (0 no_argument, 1 required_*, 2 optional_*); int *flag; int val; */
54    {"survey", required_argument, 0, 's'},
55    {"print", no_argument, 0, 'p'},
56    {"help", no_argument, 0, HLP_HELP},
57    {"version", no_argument, 0, HLP_VERSION},
58    {0, 0, 0, 0}
59};
60
61#define short_opts "s:p"
62
63static struct help_msg help[] = {
64    /*                          <-- */
65    /* TRANSLATORS: --help output for --survey option.
66     *
67     * "this" has been added to English translation */
68    {HLP_ENCODELONG(0),       /*only load the sub-survey with this prefix*/199, 0},
69    /* TRANSLATORS: --help output for aven --print option */
70    {HLP_ENCODELONG(1),       /*print and exit (requires a 3d file)*/119, 0},
71    {0, 0, 0}
72};
73
74#ifdef __WXMSW__
75IMPLEMENT_APP(Aven)
76#else
77IMPLEMENT_APP_NO_MAIN(Aven)
78IMPLEMENT_WX_THEME_SUPPORT
79#endif
80
81Aven::Aven() :
82    m_Frame(NULL), m_pageSetupData(NULL)
83{
84    wxFont::SetDefaultEncoding(wxFONTENCODING_UTF8);
85}
86
87Aven::~Aven()
88{
89    delete m_pageSetupData;
90}
91
92static int getopt_first_response = 0;
93
94static char ** utf8_argv;
95
96#ifdef __WXMSW__
97bool Aven::Initialize(int& my_argc, wxChar **my_argv)
98{
99    const wxChar * cmd_line = GetCommandLineW();
100
101    // Horrible bodge to handle therion's assumptions about the "Process"
102    // file association.
103    if (cmd_line) {
104        // None of these are valid aven command line options, so this is not
105        // going to be triggered accidentally.
106        const wxChar * p = wxStrstr(cmd_line,
107                                    wxT("aven.exe\" --quiet --log --output="));
108        if (p) {
109            // Just change the command name in the command line string - that
110            // way the quoting should match what the C runtime expects.
111            wxString cmd(cmd_line, p - cmd_line);
112            cmd += "cavern";
113            cmd += p + 4;
114            exit(wxExecute(cmd, wxEXEC_SYNC));
115        }
116    }
117
118    int utf8_argc;
119    {
120        // wxWidgets doesn't split up the command line in the standard way, so
121        // redo it ourselves using the standard API function.
122        //
123        // Warning: The returned array from this has no terminating NULL
124        // element.
125        wxChar ** new_argv = NULL;
126        if (cmd_line)
127            new_argv = CommandLineToArgvW(cmd_line, &utf8_argc);
128        bool failed = (new_argv == NULL);
129        if (failed) {
130            wxChar * p;
131            FormatMessage(
132                    FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
133                    NULL,
134                    GetLastError(),
135                    0,
136                    (LPWSTR)&p,
137                    4096,
138                    NULL);
139            wxString m = "CommandLineToArgvW failed: ";
140            m += p;
141            wxMessageBox(m, APP_NAME, wxOK | wxCENTRE | wxICON_EXCLAMATION);
142            LocalFree(p);
143            utf8_argc = my_argc;
144            new_argv = my_argv;
145        }
146
147        // Convert wide characters to UTF-8.
148        utf8_argv = new char * [utf8_argc + 1];
149        for (int i = 0; i < utf8_argc; ++i){
150            utf8_argv[i] = strdup(wxString(new_argv[i]).utf8_str());
151        }
152        utf8_argv[utf8_argc] = NULL;
153
154        if (!failed) LocalFree(new_argv);
155    }
156
157    msg_init(utf8_argv);
158    select_charset(CHARSET_UTF8);
159    /* Want --version and decent --help output, which cmdline does for us.
160     * wxCmdLine is much less good.
161     */
162    /* TRANSLATORS: Here "survey" is a "cave map" rather than list of questions
163     * - it should be translated to the terminology that cavers using the
164     * language would use.
165     *
166     * Part of aven --help */
167    cmdline_set_syntax_message(/*[SURVEY_FILE]*/269, 0, NULL);
168    cmdline_init(utf8_argc, utf8_argv, short_opts, long_opts, NULL, help, 0, 1);
169    getopt_first_response = cmdline_getopt();
170
171    // The argc and argv arguments don't actually get used here.
172    int dummy_argc = 0;
173    return wxApp::Initialize(dummy_argc, NULL);
174}
175#else
176int main(int argc, char **argv)
177{
178#ifdef __WXGTK3__
179# if !(wxUSE_GLCANVAS_EGL-0)
180    // The GLX-based wxGLCanvas doesn't work under Wayland, and the code
181    // segfaults: https://trac.wxwidgets.org/ticket/17702
182    //
183    // Therefore we force X11 unless we're using the EGL-based wxGLCanvas
184    // (which was added in wxWidgets 3.1.5 and hasn't been backported to
185    // 3.0.x).
186    //
187    // Setting GDK_BACKEND=x11 is the recommended workaround, and it seems to
188    // work to set it here.  GTK2 doesn't support Wayland, so doesn't need
189    // this.
190    setenv("GDK_BACKEND", "x11", 1);
191# endif
192#endif
193
194#ifdef __WXMAC__
195    // MacOS passes a magic -psn_XXXX command line argument in argv[1] which
196    // wx ignores for us, but in wxApp::Initialize() which hasn't been
197    // called yet.  So we need to remove it ourselves.
198    if (argc > 1 && strncmp(argv[1], "-psn_", 5) == 0) {
199        --argc;
200        memmove(argv + 1, argv + 2, argc * sizeof(char *));
201    }
202#endif
203    // Call msg_init() and start processing the command line first so that
204    // we can respond to --help and --version even without an X display.
205    msg_init(argv);
206    select_charset(CHARSET_UTF8);
207    /* Want --version and decent --help output, which cmdline does for us.
208     * wxCmdLine is much less good.
209     */
210    cmdline_set_syntax_message(/*[SURVEY_FILE]*/269, 0, NULL);
211    cmdline_init(argc, argv, short_opts, long_opts, NULL, help, 0, 1);
212    getopt_first_response = cmdline_getopt();
213
214    utf8_argv = argv;
215
216#if wxUSE_UNICODE
217    wxWCharBuffer buf(wxConvFileName->cMB2WX(argv[0]));
218    wxChar * wargv[2];
219    if (buf) {
220        wargv[0] = wxStrdup(buf);
221    } else {
222        // Eep - couldn't convert the executable's name to wide characters!
223        wargv[0] = wxStrdup(APP_NAME);
224    }
225    wargv[1] = NULL;
226    int wargc = 1;
227    return wxEntry(wargc, wargv);
228#else
229    char *dummy_argv[2] = { argv[0], NULL };
230    int dummy_argc = 1;
231    return wxEntry(dummy_argc, dummy_argv);
232#endif
233}
234#endif
235
236bool Aven::OnInit()
237{
238    wxLog::SetActiveTarget(new MyLogWindow());
239
240    {
241        // Suppress message box warnings about messages not found.
242        wxLogNull logNo;
243        wxLocale *loc = new wxLocale();
244        loc->AddCatalogLookupPathPrefix(wmsg_cfgpth());
245        wxString msg_lang_str(msg_lang, wxConvUTF8);
246        const char *lang = msg_lang2 ? msg_lang2 : msg_lang;
247        wxString lang_str(lang, wxConvUTF8);
248        loc->Init(msg_lang_str, lang_str, msg_lang_str);
249        // The existence of the wxLocale object is enough - no need to keep a
250        // pointer to it!
251    }
252
253    const char* opt_survey = NULL;
254    bool print_and_exit = false;
255
256    while (true) {
257        int opt;
258        if (getopt_first_response) {
259            opt = getopt_first_response;
260            getopt_first_response = 0;
261        } else {
262            opt = cmdline_getopt();
263        }
264        if (opt == EOF) break;
265        if (opt == 's') {
266            if (opt_survey != NULL) {
267                // FIXME: Not a helpful error, but this is temporary until
268                // we actually hook up support for specifying multiple
269                // --survey options properly here.
270                cmdline_syntax();
271                exit(1);
272            }
273            opt_survey = optarg;
274        }
275        if (opt == 'p') {
276            print_and_exit = true;
277        }
278    }
279
280    if (print_and_exit && !utf8_argv[optind]) {
281        cmdline_syntax(); // FIXME : not a helpful error...
282        exit(1);
283    }
284
285    wxString fnm;
286    if (utf8_argv[optind]) {
287        fnm = wxString(utf8_argv[optind], wxConvUTF8);
288        if (fnm.empty() && *(utf8_argv[optind])) {
289            ReportError(wxT("File argument's filename has bad encoding"));
290            return false;
291        }
292    }
293
294    if (!GLACanvas::check_visual()) {
295        wxString m;
296        /* TRANSLATORS: %s will be replaced with "Aven" currently (and
297         * perhaps by "Survex" or other things in future). */
298        m.Printf(wmsg(/*This version of %s requires OpenGL to work, but it isn’t available.*/405), APP_NAME);
299        wxMessageBox(m, APP_NAME, wxOK | wxCENTRE | wxICON_EXCLAMATION);
300        exit(1);
301    }
302
303    wxImage::AddHandler(new wxPNGHandler);
304
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
313    wxPoint pos(wxDefaultPosition);
314    int width, height;
315    wxConfigBase::Get()->Read(wxT("width"), &width, 0);
316    if (width > 0) wxConfigBase::Get()->Read(wxT("height"), &height, 0);
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);
321    if (width <= 0 || height <= 0) {
322        pos.x = geom.x;
323        pos.y = geom.y;
324        width = geom.width;
325        height = geom.height;
326
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;
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        }
349    }
350
351    // Create the main window.
352    m_Frame = new MainFrm(APP_NAME, pos, wxSize(width, height));
353
354    // Select maximised if that's the saved state.
355    if (maximized) {
356        m_Frame->Maximize();
357    }
358
359    if (utf8_argv[optind]) {
360        if (!opt_survey) opt_survey = "";
361        m_Frame->OpenFile(fnm, wxString(opt_survey, wxConvUTF8));
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    wxMessageDialog dlg(m_Frame, msg, APP_NAME, wxOK | wxICON_ERROR);
445    dlg.ShowModal();
446}
447
448const wxString &
449wmsg_cfgpth()
450{
451    static wxString path;
452    if (path.empty())
453        path = wxString(msg_cfgpth(), wxConvUTF8);
454    return path;
455}
456
457// called to report errors by message.c
458extern "C" void
459aven_v_report(int severity, const char *fnm, int line, int en, va_list ap)
460{
461    wxString m;
462    if (fnm) {
463        m = wxString(fnm, wxConvUTF8);
464        if (line) m += wxString::Format(wxT(":%d"), line);
465        m += wxT(": ");
466    }
467
468    if (severity == DIAG_WARN) {
469        m += wmsg(/*warning*/4);
470        m += wxT(": ");
471    }
472
473    char buf[1024];
474    vsnprintf(buf, sizeof(buf), msg(en), ap);
475    m += wxString(buf, wxConvUTF8);
476    if (wxTheApp == NULL) {
477        // We haven't initialised the Aven app object yet.
478        if (!wxInitialize()) {
479            fputs(buf, stderr);
480            PUTC('\n', stderr);
481            exit(1);
482        }
483        wxMessageBox(m, APP_NAME, wxOK | wxICON_ERROR);
484        wxUninitialize();
485    } else {
486        wxGetApp().ReportError(m);
487    }
488}
Note: See TracBrowser for help on using the repository browser.