source: git/src/aven.cc @ 8a7804fb

Last change on this file since 8a7804fb was 0b99107, checked in by Olly Betts <olly@…>, 12 days ago

Eliminate old FSF addresses

Update GPL/LGPL licence files and boilerplate to direct people who
didn't receive the licence text to the FSF website, as the current
versions of the FSF licence texts now do, rather than giving a postal
address.

  • Property mode set to 100644
File size: 13.8 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.
[1698844]7//  Copyright (C) 2002-2025 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
[0b99107]20//  along with this program; if not, see
21//  <https://www.gnu.org/licenses/>.
[5809313]22//
[0060ba5]23
[b462168]24#include <config.h>
25
[9990aab]26#define MSG_SETUP_PROJ_SEARCH_PATH 1
27
[5809313]28#include "aven.h"
[78924eb]29#include "log.h"
[caa5fda]30#include "gla.h"
[5809313]31#include "mainfrm.h"
[706b033]32
[d07944e]33#include "cmdline.h"
[93c3f97]34#include "message.h"
[c49e27f]35#include "useful.h"
[0060ba5]36
[7af6fff]37#include <algorithm>
38
[0060ba5]39#include <assert.h>
[c49e27f]40#include <stdio.h>
[0060ba5]41
[e0ffc2c]42#include <wx/confbase.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).
[ca18348]46#include <wx/display.h>
47#endif
[573f4e9]48
[faf83bee]49#ifdef __WXMSW__
50#include <windows.h>
51#endif
52
[879e9c3]53static const struct option long_opts[] = {
54    /* const char *name; int has_arg (0 no_argument, 1 required_*, 2 optional_*); int *flag; int val; */
55    {"survey", required_argument, 0, 's'},
56    {"print", no_argument, 0, 'p'},
57    {"help", no_argument, 0, HLP_HELP},
58    {"version", no_argument, 0, HLP_VERSION},
59    {0, 0, 0, 0}
60};
61
62#define short_opts "s:p"
63
64static struct help_msg help[] = {
[45af761]65    /*                          <-- */
[736f7df]66    /* TRANSLATORS: --help output for --survey option.
67     *
68     * "this" has been added to English translation */
[5a2d346]69    {HLP_ENCODELONG(0),       /*only load the sub-survey with this prefix*/199, 0, 0},
[736f7df]70    /* TRANSLATORS: --help output for aven --print option */
[5a2d346]71    {HLP_ENCODELONG(1),       /*print and exit (requires a 3d file)*/119, 0, 0},
72    {0, 0, 0, 0}
[879e9c3]73};
74
[5627cbb]75#ifdef __WXMSW__
[5809313]76IMPLEMENT_APP(Aven)
[5627cbb]77#else
78IMPLEMENT_APP_NO_MAIN(Aven)
79IMPLEMENT_WX_THEME_SUPPORT
80#endif
[0060ba5]81
[a3f83057]82Aven::Aven()
[0060ba5]83{
[1f81f3d]84    wxFont::SetDefaultEncoding(wxFONTENCODING_UTF8);
[0060ba5]85}
86
[2c1d2f4]87Aven::~Aven()
88{
[81f1266]89    delete m_pageSetupData;
[2c1d2f4]90}
91
[5627cbb]92static int getopt_first_response = 0;
93
[5440c295]94static char ** utf8_argv;
95
[5627cbb]96#ifdef __WXMSW__
97bool Aven::Initialize(int& my_argc, wxChar **my_argv)
98{
[faf83bee]99    const wxChar * cmd_line = GetCommandLineW();
100
[b208d0b]101    // Horrible bodge to handle therion's assumptions about the "Process"
102    // file association.
[faf83bee]103    if (cmd_line) {
104        // None of these are valid aven command line options, so this is not
105        // going to be triggered accidentally.
[ccaf632]106        const wxChar * p = wxStrstr(cmd_line,
107                                    wxT("aven.exe\" --quiet --log --output="));
[faf83bee]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        }
[b208d0b]116    }
117
[faf83bee]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){
[1698844]150            // We can't use osstrdup() before msg_init() but this is
151            // platform-specific code so we can assume strdup() is
152            // available.
[8562abc]153            utf8_argv[i] = strdup(wxString(new_argv[i]).utf8_str());
[faf83bee]154        }
155        utf8_argv[utf8_argc] = NULL;
156
157        if (!failed) LocalFree(new_argv);
[5627cbb]158    }
159
[5440c295]160    msg_init(utf8_argv);
[293eec8]161    /* Want --version and decent --help output, which cmdline does for us.
[f1cbf74]162     * wxCmdLine is much less good.
163     */
[736f7df]164    /* TRANSLATORS: Here "survey" is a "cave map" rather than list of questions
165     * - it should be translated to the terminology that cavers using the
166     * language would use.
[b49ac56]167     *
[736f7df]168     * Part of aven --help */
[d8dbdff]169    cmdline_set_syntax_message(/*[SURVEY_FILE]*/269, 0, NULL);
[faf83bee]170    cmdline_init(utf8_argc, utf8_argv, short_opts, long_opts, NULL, help, 0, 1);
[5627cbb]171    getopt_first_response = cmdline_getopt();
[faf83bee]172
[3786977]173    // We're done writing to the terminal so switch to UTF-8 messages.
174    select_charset(CHARSET_UTF8);
175
[faf83bee]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{
[96c239c]183#ifdef __WXGTK3__
[47f55ed]184# if !(wxUSE_GLCANVAS_EGL-0)
185    // The GLX-based wxGLCanvas doesn't work under Wayland, and the code
[736ec93]186    // segfaults: https://github.com/wxWidgets/wxWidgets/issues/17702
[47f55ed]187    //
[fd0e32a]188    // Therefore we force X11 unless we're using the EGL-based wxGLCanvas.
[47f55ed]189    //
[96c239c]190    // Setting GDK_BACKEND=x11 is the recommended workaround, and it seems to
[b0908ea]191    // work to set it here.  GTK2 doesn't support Wayland, so doesn't need
192    // this.
[96c239c]193    setenv("GDK_BACKEND", "x11", 1);
[47f55ed]194# endif
[96c239c]195#endif
[705adee9]196
[27b8b59]197#ifdef __WXMAC__
[879e9c3]198    // MacOS passes a magic -psn_XXXX command line argument in argv[1] which
199    // wx ignores for us, but in wxApp::Initialize() which hasn't been
200    // called yet.  So we need to remove it ourselves.
[5627cbb]201    if (argc > 1 && strncmp(argv[1], "-psn_", 5) == 0) {
202        --argc;
203        memmove(argv + 1, argv + 2, argc * sizeof(char *));
[879e9c3]204    }
205#endif
206    // Call msg_init() and start processing the command line first so that
207    // we can respond to --help and --version even without an X display.
[5627cbb]208    msg_init(argv);
[293eec8]209    /* Want --version and decent --help output, which cmdline does for us.
[5627cbb]210     * wxCmdLine is much less good.
211     */
[d8dbdff]212    cmdline_set_syntax_message(/*[SURVEY_FILE]*/269, 0, NULL);
[5627cbb]213    cmdline_init(argc, argv, short_opts, long_opts, NULL, help, 0, 1);
[879e9c3]214    getopt_first_response = cmdline_getopt();
[5627cbb]215
[3786977]216    // We're done writing to the terminal so switch to UTF-8 messages.
217    select_charset(CHARSET_UTF8);
[5440c295]218    utf8_argv = argv;
[5627cbb]219
[f891a0c]220#if wxUSE_UNICODE
[5627cbb]221    wxWCharBuffer buf(wxConvFileName->cMB2WX(argv[0]));
222    wxChar * wargv[2];
223    if (buf) {
224        wargv[0] = wxStrdup(buf);
225    } else {
226        // Eep - couldn't convert the executable's name to wide characters!
227        wargv[0] = wxStrdup(APP_NAME);
228    }
229    wargv[1] = NULL;
230    int wargc = 1;
231    return wxEntry(wargc, wargv);
[495cd3e]232#else
[a212391]233    char *dummy_argv[2] = { argv[0], NULL };
234    int dummy_argc = 1;
235    return wxEntry(dummy_argc, dummy_argv);
[495cd3e]236#endif
[879e9c3]237}
[5627cbb]238#endif
[879e9c3]239
240bool Aven::OnInit()
241{
[4013d9c]242    wxLog::SetActiveTarget(new MyLogWindow());
[421b7d2]243
[d607cea]244    {
[b1876d3]245        // Suppress message box warnings about messages not found.
[71ea9e1]246        wxLogNull logNo;
247        wxLocale *loc = new wxLocale();
[8a05a7a]248        loc->AddCatalogLookupPathPrefix(wmsg_cfgpth());
[b1876d3]249        wxString msg_lang_str(msg_lang, wxConvUTF8);
250        const char *lang = msg_lang2 ? msg_lang2 : msg_lang;
251        wxString lang_str(lang, wxConvUTF8);
[f1d5d26]252        loc->Init(msg_lang_str, lang_str, msg_lang_str);
[71ea9e1]253        // The existence of the wxLocale object is enough - no need to keep a
254        // pointer to it!
[003d953]255    }
[93c3f97]256
[668b930]257    const char* opt_survey = NULL;
[3ee64fb]258    bool print_and_exit = false;
[421b7d2]259
[0580c6a]260    while (true) {
[879e9c3]261        int opt;
262        if (getopt_first_response) {
263            opt = getopt_first_response;
264            getopt_first_response = 0;
265        } else {
266            opt = cmdline_getopt();
267        }
[d07944e]268        if (opt == EOF) break;
269        if (opt == 's') {
[668b930]270            if (opt_survey != NULL) {
271                // FIXME: Not a helpful error, but this is temporary until
272                // we actually hook up support for specifying multiple
273                // --survey options properly here.
274                cmdline_syntax();
275                exit(1);
276            }
277            opt_survey = optarg;
[d07944e]278        }
[3ee64fb]279        if (opt == 'p') {
280            print_and_exit = true;
281        }
282    }
283
[5440c295]284    if (print_and_exit && !utf8_argv[optind]) {
[3ee64fb]285        cmdline_syntax(); // FIXME : not a helpful error...
286        exit(1);
[d07944e]287    }
[068b4f2]288
[5627cbb]289    wxString fnm;
[5440c295]290    if (utf8_argv[optind]) {
291        fnm = wxString(utf8_argv[optind], wxConvUTF8);
292        if (fnm.empty() && *(utf8_argv[optind])) {
[c49e27f]293            ReportError(wxT("File argument's filename has bad encoding"));
[5627cbb]294            return false;
295        }
296    }
297
[caa5fda]298    if (!GLACanvas::check_visual()) {
299        wxString m;
300        /* TRANSLATORS: %s will be replaced with "Aven" currently (and
301         * perhaps by "Survex" or other things in future). */
302        m.Printf(wmsg(/*This version of %s requires OpenGL to work, but it isn’t available.*/405), APP_NAME);
303        wxMessageBox(m, APP_NAME, wxOK | wxCENTRE | wxICON_EXCLAMATION);
304        exit(1);
[5892e08]305    }
306
[573f4e9]307    wxImage::AddHandler(new wxPNGHandler);
[08d2074]308
[4909e6bc]309    // Obtain the screen geometry.
310#if wxUSE_DISPLAY
311    wxRect geom = wxDisplay().GetGeometry();
312#else
313    wxRect geom;
314    wxClientDisplayRect(&geom.x, &geom.y, &geom.width, &geom.height);
315#endif
316
[3db0798]317    wxPoint pos(wxDefaultPosition);
[ca18348]318    int width, height;
[5627cbb]319    wxConfigBase::Get()->Read(wxT("width"), &width, 0);
320    if (width > 0) wxConfigBase::Get()->Read(wxT("height"), &height, 0);
[30621f5]321    // We used to persist full screen mode (-1 was maximized,
322    // -2 full screen), but people would get stuck in full
323    // screen mode, unsure how to exit.
324    bool maximized = (width <= -1);
[3db0798]325    if (width <= 0 || height <= 0) {
326        pos.x = geom.x;
327        pos.y = geom.y;
328        width = geom.width;
329        height = geom.height;
[486ae6a]330
[3db0798]331        // Calculate a reasonable size for our window.
332        pos.x += width / 8;
333        pos.y += height / 8;
334        width = width * 3 / 4;
335        height = height * 3 / 4;
[4909e6bc]336    } else {
337        // Impose a minimum size for sanity, and make sure the window fits on
338        // the display (in case the current display is smaller than the one
339        // in use when the window size was saved).  (480x320) is about the
340        // smallest usable size for aven's window.
341        const int min_width = min(geom.width, 480);
342        const int min_height = min(geom.height, 320);
343        if (width < min_width || height < min_height) {
344            if (width < min_width) {
345                width = min_width;
346            }
347            if (height < min_height) {
348                height = min_height;
349            }
350            pos.x = geom.x + (geom.width - width) / 4;
351            pos.y = geom.y + (geom.height - height) / 4;
352        }
[3db0798]353    }
[486ae6a]354
355    // Create the main window.
[3db0798]356    m_Frame = new MainFrm(APP_NAME, pos, wxSize(width, height));
357
[30621f5]358    // Select maximised if that's the saved state.
359    if (maximized) {
[3db0798]360        m_Frame->Maximize();
361    }
[068b4f2]362
[5440c295]363    if (utf8_argv[optind]) {
[668b930]364        if (!opt_survey) opt_survey = "";
365        m_Frame->OpenFile(fnm, wxString(opt_survey, wxConvUTF8));
[d07944e]366    }
[068b4f2]367
[3ee64fb]368    if (print_and_exit) {
[4ed8154]369        m_Frame->PrintAndExit();
[3ee64fb]370        return true;
371    }
372
[5809313]373    m_Frame->Show(true);
[526775d]374#ifdef _WIN32
375    m_Frame->SetFocus();
376#endif
[0060ba5]377    return true;
378}
379
[e0ffc2c]380wxPageSetupDialogData *
381Aven::GetPageSetupDialogData()
382{
[2c1d2f4]383    if (!m_pageSetupData) m_pageSetupData = new wxPageSetupDialogData;
[e0ffc2c]384#ifdef __WXGTK__
385    // Fetch paper margins stored on disk.
386    int left, right, top, bottom;
387    wxConfigBase * cfg = wxConfigBase::Get();
388    // These default margins were chosen by looking at all the .ppd files
389    // on my machine.
[5627cbb]390    cfg->Read(wxT("paper_margin_left"), &left, 7);
391    cfg->Read(wxT("paper_margin_right"), &right, 7);
392    cfg->Read(wxT("paper_margin_top"), &top, 14);
393    cfg->Read(wxT("paper_margin_bottom"), &bottom, 14);
[2c1d2f4]394    m_pageSetupData->SetMarginTopLeft(wxPoint(left, top));
395    m_pageSetupData->SetMarginBottomRight(wxPoint(right, bottom));
[e0ffc2c]396#endif
[2c1d2f4]397    return m_pageSetupData;
[e0ffc2c]398}
399
400void
401Aven::SetPageSetupDialogData(const wxPageSetupDialogData & psdd)
402{
[2c1d2f4]403    if (!m_pageSetupData) m_pageSetupData = new wxPageSetupDialogData;
404    *m_pageSetupData = psdd;
[e0ffc2c]405#ifdef __WXGTK__
406    wxPoint topleft = psdd.GetMarginTopLeft();
407    wxPoint bottomright = psdd.GetMarginBottomRight();
408
409    // Store user specified paper margins on disk/in registry.
410    wxConfigBase * cfg = wxConfigBase::Get();
[5627cbb]411    cfg->Write(wxT("paper_margin_left"), topleft.x);
412    cfg->Write(wxT("paper_margin_right"), bottomright.x);
413    cfg->Write(wxT("paper_margin_top"), topleft.y);
414    cfg->Write(wxT("paper_margin_bottom"), bottomright.y);
[e0ffc2c]415    cfg->Flush();
416#endif
417}
418
[ddc0994]419#ifdef __WXMAC__
420void
421Aven::MacOpenFiles(const wxArrayString & filenames)
422{
423    if (filenames.size() != 1) {
424        ReportError(wxT("Aven can only load one file at a time"));
425        return;
426    }
427    m_Frame->OpenFile(filenames[0], wxString());
428}
429
430void
431Aven::MacPrintFiles(const wxArrayString & filenames)
432{
433    if (filenames.size() != 1) {
434        ReportError(wxT("Aven can only print one file at a time"));
435        return;
436    }
437    m_Frame->OpenFile(filenames[0], wxString());
438    m_Frame->PrintAndExit();
439}
440#endif
441
[5809313]442void Aven::ReportError(const wxString& msg)
[0060ba5]443{
[c8b5fa5]444    if (!m_Frame) {
445        wxMessageBox(msg, APP_NAME, wxOK | wxICON_ERROR);
446        return;
447    }
[7015222]448    wxMessageDialog dlg(m_Frame, msg, APP_NAME, wxOK | wxICON_ERROR);
449    dlg.ShowModal();
[0060ba5]450}
[59fda0a]451
[8a05a7a]452const wxString &
453wmsg_cfgpth()
454{
455    static wxString path;
456    if (path.empty())
457        path = wxString(msg_cfgpth(), wxConvUTF8);
458    return path;
459}
460
[a9a32f2]461// called to report errors by message.c
462extern "C" void
463aven_v_report(int severity, const char *fnm, int line, int en, va_list ap)
464{
[c49e27f]465    wxString m;
466    if (fnm) {
467        m = wxString(fnm, wxConvUTF8);
468        if (line) m += wxString::Format(wxT(":%d"), line);
469        m += wxT(": ");
470    }
471
[37d6b84]472    if (severity == DIAG_WARN) {
[7962c9d]473        m += wmsg(/*warning*/106);
[c49e27f]474        m += wxT(": ");
475    }
476
477    char buf[1024];
[dba56bb]478#ifdef HAVE__VSPRINTF_P
479    // Microsoft's vsnprintf() doesn't support positional argument specifiers,
480    // so we need to use the Microsoft-specific _vsprintf_p() which (despite
481    // its name) takes a buffer size like vsnprintf() does.
482    _vsprintf_p(buf, sizeof(buf), msg(en), ap);
483#else
[9b5f251]484    vsnprintf(buf, sizeof(buf), msg(en), ap);
[dba56bb]485#endif
[c49e27f]486    m += wxString(buf, wxConvUTF8);
487    if (wxTheApp == NULL) {
488        // We haven't initialised the Aven app object yet.
489        if (!wxInitialize()) {
490            fputs(buf, stderr);
491            PUTC('\n', stderr);
492            exit(1);
493        }
494        wxMessageBox(m, APP_NAME, wxOK | wxICON_ERROR);
495        wxUninitialize();
496    } else {
497        wxGetApp().ReportError(m);
498    }
[a9a32f2]499}
Note: See TracBrowser for help on using the repository browser.