source: git/src/mainfrm.cc @ ad83822

RELEASE/1.0RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernlogstereowalls-datawalls-data-hanging-as-warning
Last change on this file since ad83822 was ad83822, checked in by Olly Betts <olly@…>, 19 years ago

Backport "aven icon in about dialog" patch.

git-svn-id: file:///home/survex-svn/survex/trunk@2980 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

  • Property mode set to 100644
File size: 46.9 KB
Line 
1//
2//  mainfrm.cc
3//
4//  Main frame handling for Aven.
5//
6//  Copyright (C) 2000-2002 Mark R. Shinwell
7//  Copyright (C) 2001-2003,2004,2005 Olly Betts
8//  Copyright (C) 2004 Philip Underwood
9//
10//  This program is free software; you can redistribute it and/or modify
11//  it under the terms of the GNU General Public License as published by
12//  the Free Software Foundation; either version 2 of the License, or
13//  (at your option) any later version.
14//
15//  This program is distributed in the hope that it will be useful,
16//  but WITHOUT ANY WARRANTY; without even the implied warranty of
17//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18//  GNU General Public License for more details.
19//
20//  You should have received a copy of the GNU General Public License
21//  along with this program; if not, write to the Free Software
22//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23//
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#include "mainfrm.h"
30#include "aven.h"
31#include "aboutdlg.h"
32
33#include "message.h"
34#include "img.h"
35#include "namecmp.h"
36#include "printwx.h"
37#include "filename.h"
38
39#include <wx/confbase.h>
40#include <wx/regex.h>
41
42#include <float.h>
43#include <functional>
44#include <stack>
45
46using namespace std;
47
48const int NUM_DEPTH_COLOURS = 13; // up to 13
49
50#include "avenpal.h"
51
52class AvenSplitterWindow : public wxSplitterWindow {
53    MainFrm *parent;
54
55    public:
56        AvenSplitterWindow(MainFrm *parent_)
57            : wxSplitterWindow(parent_, -1, wxDefaultPosition, wxDefaultSize,
58                               wxSP_3D | wxSP_LIVE_UPDATE),
59              parent(parent_)
60        {
61        }
62
63        void OnSplitterDClick(wxSplitterEvent &e) {
64#if wxCHECK_VERSION(2,3,0)
65            e.Veto();
66#endif
67#if defined(__UNIX__) && !wxCHECK_VERSION(2,3,5)
68            parent->m_SashPosition = GetSashPosition(); // save width of panel
69            // Calling Unsplit from OnSplitterDClick() doesn't work in debian
70            // wxGtk 2.3.3.2 (which calls itself 2.3.4) - it does work from CVS
71            // prior to the actual 2.3.4 though - FIXME: monitor this
72            // situation...
73            SetSashPosition(0);
74#else
75            parent->ToggleSidePanel();
76#endif
77        }
78
79    private:
80        DECLARE_EVENT_TABLE()
81};
82
83BEGIN_EVENT_TABLE(AvenSplitterWindow, wxSplitterWindow)
84    // The wx docs say "EVT_SPLITTER_DOUBLECLICKED" but the wx headers say
85    // "EVT_SPLITTER_DCLICK" (wx docs corrected to agree with headers in 2.3)
86#ifdef EVT_SPLITTER_DOUBLECLICKED
87    EVT_SPLITTER_DOUBLECLICKED(-1, AvenSplitterWindow::OnSplitterDClick)
88#else
89    EVT_SPLITTER_DCLICK(-1, AvenSplitterWindow::OnSplitterDClick)
90#endif
91END_EVENT_TABLE()
92
93BEGIN_EVENT_TABLE(MainFrm, wxFrame)
94    EVT_BUTTON(button_FIND, MainFrm::OnFind)
95    EVT_BUTTON(button_HIDE, MainFrm::OnHide)
96
97    EVT_MENU(menu_FILE_OPEN, MainFrm::OnOpen)
98    EVT_MENU(menu_FILE_PRINT, MainFrm::OnPrint)
99    EVT_MENU(menu_FILE_PAGE_SETUP, MainFrm::OnPageSetup)
100    EVT_MENU(menu_FILE_EXPORT, MainFrm::OnExport)
101    EVT_MENU(menu_FILE_QUIT, MainFrm::OnQuit)
102    EVT_MENU_RANGE(wxID_FILE1, wxID_FILE9, MainFrm::OnMRUFile)
103
104    EVT_CLOSE(MainFrm::OnClose)
105    EVT_SET_FOCUS(MainFrm::OnSetFocus)
106
107    EVT_MENU(menu_ROTATION_START, MainFrm::OnStartRotation)
108    EVT_MENU(menu_ROTATION_TOGGLE, MainFrm::OnToggleRotation)
109    EVT_MENU(menu_ROTATION_STOP, MainFrm::OnStopRotation)
110    EVT_MENU(menu_ROTATION_SPEED_UP, MainFrm::OnSpeedUp)
111    EVT_MENU(menu_ROTATION_SLOW_DOWN, MainFrm::OnSlowDown)
112    EVT_MENU(menu_ROTATION_REVERSE, MainFrm::OnReverseDirectionOfRotation)
113    EVT_MENU(menu_ROTATION_STEP_CCW, MainFrm::OnStepOnceAnticlockwise)
114    EVT_MENU(menu_ROTATION_STEP_CW, MainFrm::OnStepOnceClockwise)
115    EVT_MENU(menu_ORIENT_MOVE_NORTH, MainFrm::OnMoveNorth)
116    EVT_MENU(menu_ORIENT_MOVE_EAST, MainFrm::OnMoveEast)
117    EVT_MENU(menu_ORIENT_MOVE_SOUTH, MainFrm::OnMoveSouth)
118    EVT_MENU(menu_ORIENT_MOVE_WEST, MainFrm::OnMoveWest)
119    EVT_MENU(menu_ORIENT_SHIFT_LEFT, MainFrm::OnShiftDisplayLeft)
120    EVT_MENU(menu_ORIENT_SHIFT_RIGHT, MainFrm::OnShiftDisplayRight)
121    EVT_MENU(menu_ORIENT_SHIFT_UP, MainFrm::OnShiftDisplayUp)
122    EVT_MENU(menu_ORIENT_SHIFT_DOWN, MainFrm::OnShiftDisplayDown)
123    EVT_MENU(menu_ORIENT_PLAN, MainFrm::OnPlan)
124    EVT_MENU(menu_ORIENT_ELEVATION, MainFrm::OnElevation)
125    EVT_MENU(menu_ORIENT_HIGHER_VP, MainFrm::OnHigherViewpoint)
126    EVT_MENU(menu_ORIENT_LOWER_VP, MainFrm::OnLowerViewpoint)
127    EVT_MENU(menu_ORIENT_ZOOM_IN, MainFrm::OnZoomIn)
128    EVT_MENU(menu_ORIENT_ZOOM_OUT, MainFrm::OnZoomOut)
129    EVT_MENU(menu_ORIENT_DEFAULTS, MainFrm::OnDefaults)
130    EVT_MENU(menu_VIEW_SHOW_LEGS, MainFrm::OnShowSurveyLegs)
131    EVT_MENU(menu_VIEW_SHOW_CROSSES, MainFrm::OnShowCrosses)
132    EVT_MENU(menu_VIEW_SHOW_ENTRANCES, MainFrm::OnShowEntrances)
133    EVT_MENU(menu_VIEW_SHOW_FIXED_PTS, MainFrm::OnShowFixedPts)
134    EVT_MENU(menu_VIEW_SHOW_EXPORTED_PTS, MainFrm::OnShowExportedPts)
135    EVT_MENU(menu_VIEW_SHOW_NAMES, MainFrm::OnShowStationNames)
136    EVT_MENU(menu_VIEW_SHOW_OVERLAPPING_NAMES, MainFrm::OnDisplayOverlappingNames)
137    EVT_MENU(menu_VIEW_SHOW_SURFACE, MainFrm::OnShowSurface)
138    EVT_MENU(menu_VIEW_SURFACE_DEPTH, MainFrm::OnShowSurfaceDepth)
139    EVT_MENU(menu_VIEW_SURFACE_DASHED, MainFrm::OnShowSurfaceDashed)
140    EVT_MENU(menu_VIEW_COMPASS, MainFrm::OnViewCompass)
141    EVT_MENU(menu_VIEW_CLINO, MainFrm::OnViewClino)
142    EVT_MENU(menu_VIEW_GRID, MainFrm::OnViewGrid)
143    EVT_MENU(menu_VIEW_DEPTH_BAR, MainFrm::OnToggleDepthbar)
144    EVT_MENU(menu_VIEW_SCALE_BAR, MainFrm::OnToggleScalebar)
145    EVT_MENU(menu_VIEW_SIDE_PANEL, MainFrm::OnViewSidePanel)
146    EVT_MENU(menu_VIEW_METRIC, MainFrm::OnToggleMetric)
147    EVT_MENU(menu_VIEW_DEGREES, MainFrm::OnToggleDegrees)
148    EVT_MENU(menu_CTL_REVERSE, MainFrm::OnReverseControls)
149    EVT_MENU(menu_CTL_CANCEL_DIST_LINE, MainFrm::OnCancelDistLine)
150    EVT_MENU(menu_HELP_ABOUT, MainFrm::OnAbout)
151
152    EVT_UPDATE_UI(menu_FILE_PRINT, MainFrm::OnPrintUpdate)
153    EVT_UPDATE_UI(menu_FILE_EXPORT, MainFrm::OnExportUpdate)
154    EVT_UPDATE_UI(menu_ROTATION_START, MainFrm::OnStartRotationUpdate)
155    EVT_UPDATE_UI(menu_ROTATION_TOGGLE, MainFrm::OnToggleRotationUpdate)
156    EVT_UPDATE_UI(menu_ROTATION_STOP, MainFrm::OnStopRotationUpdate)
157    EVT_UPDATE_UI(menu_ROTATION_SPEED_UP, MainFrm::OnSpeedUpUpdate)
158    EVT_UPDATE_UI(menu_ROTATION_SLOW_DOWN, MainFrm::OnSlowDownUpdate)
159    EVT_UPDATE_UI(menu_ROTATION_REVERSE, MainFrm::OnReverseDirectionOfRotationUpdate)
160    EVT_UPDATE_UI(menu_ROTATION_STEP_CCW, MainFrm::OnStepOnceAnticlockwiseUpdate)
161    EVT_UPDATE_UI(menu_ROTATION_STEP_CW, MainFrm::OnStepOnceClockwiseUpdate)
162    EVT_UPDATE_UI(menu_ORIENT_MOVE_NORTH, MainFrm::OnMoveNorthUpdate)
163    EVT_UPDATE_UI(menu_ORIENT_MOVE_EAST, MainFrm::OnMoveEastUpdate)
164    EVT_UPDATE_UI(menu_ORIENT_MOVE_SOUTH, MainFrm::OnMoveSouthUpdate)
165    EVT_UPDATE_UI(menu_ORIENT_MOVE_WEST, MainFrm::OnMoveWestUpdate)
166    EVT_UPDATE_UI(menu_ORIENT_SHIFT_LEFT, MainFrm::OnShiftDisplayLeftUpdate)
167    EVT_UPDATE_UI(menu_ORIENT_SHIFT_RIGHT, MainFrm::OnShiftDisplayRightUpdate)
168    EVT_UPDATE_UI(menu_ORIENT_SHIFT_UP, MainFrm::OnShiftDisplayUpUpdate)
169    EVT_UPDATE_UI(menu_ORIENT_SHIFT_DOWN, MainFrm::OnShiftDisplayDownUpdate)
170    EVT_UPDATE_UI(menu_ORIENT_PLAN, MainFrm::OnPlanUpdate)
171    EVT_UPDATE_UI(menu_ORIENT_ELEVATION, MainFrm::OnElevationUpdate)
172    EVT_UPDATE_UI(menu_ORIENT_HIGHER_VP, MainFrm::OnHigherViewpointUpdate)
173    EVT_UPDATE_UI(menu_ORIENT_LOWER_VP, MainFrm::OnLowerViewpointUpdate)
174    EVT_UPDATE_UI(menu_ORIENT_ZOOM_IN, MainFrm::OnZoomInUpdate)
175    EVT_UPDATE_UI(menu_ORIENT_ZOOM_OUT, MainFrm::OnZoomOutUpdate)
176    EVT_UPDATE_UI(menu_ORIENT_DEFAULTS, MainFrm::OnDefaultsUpdate)
177    EVT_UPDATE_UI(menu_VIEW_SHOW_LEGS, MainFrm::OnShowSurveyLegsUpdate)
178    EVT_UPDATE_UI(menu_VIEW_SHOW_CROSSES, MainFrm::OnShowCrossesUpdate)
179    EVT_UPDATE_UI(menu_VIEW_SHOW_ENTRANCES, MainFrm::OnShowEntrancesUpdate)
180    EVT_UPDATE_UI(menu_VIEW_SHOW_FIXED_PTS, MainFrm::OnShowFixedPtsUpdate)
181    EVT_UPDATE_UI(menu_VIEW_SHOW_EXPORTED_PTS, MainFrm::OnShowExportedPtsUpdate)
182    EVT_UPDATE_UI(menu_VIEW_SHOW_NAMES, MainFrm::OnShowStationNamesUpdate)
183    EVT_UPDATE_UI(menu_VIEW_SHOW_SURFACE, MainFrm::OnShowSurfaceUpdate)
184    EVT_UPDATE_UI(menu_VIEW_SURFACE_DEPTH, MainFrm::OnShowSurfaceDepthUpdate)
185    EVT_UPDATE_UI(menu_VIEW_SURFACE_DASHED, MainFrm::OnShowSurfaceDashedUpdate)
186    EVT_UPDATE_UI(menu_VIEW_SHOW_OVERLAPPING_NAMES, MainFrm::OnDisplayOverlappingNamesUpdate)
187    EVT_UPDATE_UI(menu_VIEW_COMPASS, MainFrm::OnViewCompassUpdate)
188    EVT_UPDATE_UI(menu_VIEW_CLINO, MainFrm::OnViewClinoUpdate)
189    EVT_UPDATE_UI(menu_VIEW_DEPTH_BAR, MainFrm::OnToggleDepthbarUpdate)
190    EVT_UPDATE_UI(menu_VIEW_SCALE_BAR, MainFrm::OnToggleScalebarUpdate)
191    EVT_UPDATE_UI(menu_VIEW_GRID, MainFrm::OnViewGridUpdate)
192    EVT_UPDATE_UI(menu_VIEW_INDICATORS, MainFrm::OnIndicatorsUpdate)
193    EVT_UPDATE_UI(menu_VIEW_SIDE_PANEL, MainFrm::OnViewSidePanelUpdate)
194    EVT_UPDATE_UI(menu_CTL_REVERSE, MainFrm::OnReverseControlsUpdate)
195    EVT_UPDATE_UI(menu_CTL_CANCEL_DIST_LINE, MainFrm::OnCancelDistLineUpdate)
196    EVT_UPDATE_UI(menu_VIEW_METRIC, MainFrm::OnToggleMetricUpdate)
197    EVT_UPDATE_UI(menu_VIEW_DEGREES, MainFrm::OnToggleDegreesUpdate)
198END_EVENT_TABLE()
199
200class LabelCmp : public greater<const LabelInfo*> {
201    int separator;
202public:
203    LabelCmp(int separator_) : separator(separator_) {}
204    bool operator()(const LabelInfo* pt1, const LabelInfo* pt2) {
205        return name_cmp(pt1->GetText(), pt2->GetText(), separator) < 0;
206    }
207};
208
209class LabelPlotCmp : public greater<const LabelInfo*> {
210    int separator;
211public:
212    LabelPlotCmp(int separator_) : separator(separator_) {}
213    bool operator()(const LabelInfo* pt1, const LabelInfo* pt2) {
214        int n = pt1->flags - pt2->flags;
215        if (n) return n > 0;
216        wxString l1 = pt1->text.AfterLast(separator);
217        wxString l2 = pt2->text.AfterLast(separator);
218        n = name_cmp(l1, l2, separator);
219        if (n) return n < 0;
220        // Prefer non-2-nodes...
221        // FIXME; implement
222        // if leaf names are the same, prefer shorter labels as we can
223        // display more of them
224        n = pt1->text.length() - pt2->text.length();
225        if (n) return n < 0;
226        // make sure that we don't ever compare different labels as equal
227        return name_cmp(pt1->text, pt2->text, separator) < 0;
228    }
229};
230
231#if wxUSE_DRAG_AND_DROP
232class DnDFile : public wxFileDropTarget {
233    public:
234        DnDFile(MainFrm *parent) : m_Parent(parent) { }
235        virtual bool OnDropFiles(wxCoord, wxCoord,
236                        const wxArrayString &filenames);
237
238    private:
239        MainFrm * m_Parent;
240};
241
242bool
243DnDFile::OnDropFiles(wxCoord, wxCoord, const wxArrayString &filenames)
244{
245    // Load a survey file by drag-and-drop.
246    assert(filenames.GetCount() > 0);
247
248    if (filenames.GetCount() != 1) {
249        wxGetApp().ReportError(msg(/*You may only view one 3d file at a time.*/336));
250        return FALSE;
251    }
252
253    m_Parent->OpenFile(filenames[0]);
254    return TRUE;
255}
256#endif
257
258MainFrm::MainFrm(const wxString& title, const wxPoint& pos, const wxSize& size) :
259    wxFrame(NULL, 101, title, pos, size, wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE),
260    m_Gfx(NULL), m_NumEntrances(0), m_NumFixedPts(0), m_NumExportedPts(0)
261{
262    icon_path = msg_cfgpth();
263    icon_path += wxCONFIG_PATH_SEPARATOR;
264    icon_path += "icons";
265    icon_path += wxCONFIG_PATH_SEPARATOR;
266
267#ifdef _WIN32
268    // The peculiar name is so that the icon is the first in the file
269    // (required by Microsoft Windows for this type of icon)
270    SetIcon(wxIcon("aaaaaAven"));
271#else
272    SetIcon(wxIcon(icon_path + "aven.png", wxBITMAP_TYPE_PNG));
273#endif
274
275    InitialisePensAndBrushes();
276    CreateMenuBar();
277    CreateToolBar();
278    CreateSidePanel();
279
280#ifdef __X__ // wxMotif or wxX11
281    int x;
282    int y;
283    GetSize(&x, &y);
284    // X seems to require a forced resize.
285    SetSize(-1, -1, x, y);
286#endif
287
288#if wxUSE_DRAG_AND_DROP
289    SetDropTarget(new DnDFile(this));
290#endif
291}
292
293MainFrm::~MainFrm()
294{
295    ClearPointLists();
296    delete[] m_Points;
297    delete[] m_Pens;
298    delete[] m_Brushes;
299}
300
301void MainFrm::InitialisePensAndBrushes()
302{
303    m_Points = new list<PointInfo*>[NUM_DEPTH_COLOURS + 1];
304    m_Pens = new wxPen[NUM_DEPTH_COLOURS + 1];
305    m_Brushes = new wxBrush[NUM_DEPTH_COLOURS + 1];
306    for (int pen = 0; pen < NUM_DEPTH_COLOURS + 1; ++pen) {
307        m_Pens[pen].SetColour(REDS[pen], GREENS[pen], BLUES[pen]);
308        m_Brushes[pen].SetColour(REDS[pen], GREENS[pen], BLUES[pen]);
309    }
310}
311
312void MainFrm::CreateMenuBar()
313{
314    // Create the menus and the menu bar.
315
316    wxMenu* filemenu = new wxMenu;
317    filemenu->Append(menu_FILE_OPEN, GetTabMsg(/*@Open...##Ctrl+O*/220));
318    filemenu->AppendSeparator();
319    filemenu->Append(menu_FILE_PRINT, GetTabMsg(/*@Print...##Ctrl+P*/380));
320    filemenu->Append(menu_FILE_PAGE_SETUP, GetTabMsg(/*P@age Setup...*/381));
321    filemenu->AppendSeparator();
322    filemenu->Append(menu_FILE_EXPORT, "Export as..."); // FIXME TRANSLATE
323    filemenu->AppendSeparator();
324    filemenu->Append(menu_FILE_QUIT, GetTabMsg(/*@Quit##Ctrl+Q*/221));
325
326    m_history.UseMenu(filemenu);
327    m_history.Load(*wxConfigBase::Get());
328
329    wxMenu* rotmenu = new wxMenu;
330    rotmenu->Append(menu_ROTATION_START, GetTabMsg(/*@Start Rotation##Return*/230));
331    rotmenu->Append(menu_ROTATION_STOP, GetTabMsg(/*S@top Rotation##Space*/231));
332    rotmenu->AppendSeparator();
333    rotmenu->Append(menu_ROTATION_SPEED_UP, GetTabMsg(/*Speed @Up*/232));
334    rotmenu->Append(menu_ROTATION_SLOW_DOWN, GetTabMsg(/*Slow @Down*/233));
335    rotmenu->AppendSeparator();
336    rotmenu->Append(menu_ROTATION_REVERSE, GetTabMsg(/*@Reverse Direction*/234));
337    rotmenu->AppendSeparator();
338    rotmenu->Append(menu_ROTATION_STEP_CCW, GetTabMsg(/*Step Once @Anticlockwise*/235));
339    rotmenu->Append(menu_ROTATION_STEP_CW, GetTabMsg(/*Step Once @Clockwise*/236));
340
341    wxMenu* orientmenu = new wxMenu;
342    orientmenu->Append(menu_ORIENT_MOVE_NORTH, GetTabMsg(/*View @North*/240));
343    orientmenu->Append(menu_ORIENT_MOVE_EAST, GetTabMsg(/*View @East*/241));
344    orientmenu->Append(menu_ORIENT_MOVE_SOUTH, GetTabMsg(/*View @South*/242));
345    orientmenu->Append(menu_ORIENT_MOVE_WEST, GetTabMsg(/*View @West*/243));
346    orientmenu->AppendSeparator();
347    orientmenu->Append(menu_ORIENT_SHIFT_LEFT, GetTabMsg(/*Shift Survey @Left*/244));
348    orientmenu->Append(menu_ORIENT_SHIFT_RIGHT, GetTabMsg(/*Shift Survey @Right*/245));
349    orientmenu->Append(menu_ORIENT_SHIFT_UP, GetTabMsg(/*Shift Survey @Up*/246));
350    orientmenu->Append(menu_ORIENT_SHIFT_DOWN, GetTabMsg(/*Shift Survey @Down*/247));
351    orientmenu->AppendSeparator();
352    orientmenu->Append(menu_ORIENT_PLAN, GetTabMsg(/*@Plan View*/248));
353    orientmenu->Append(menu_ORIENT_ELEVATION, GetTabMsg(/*Ele@vation*/249));
354    orientmenu->AppendSeparator();
355    orientmenu->Append(menu_ORIENT_HIGHER_VP, GetTabMsg(/*@Higher Viewpoint*/250));
356    orientmenu->Append(menu_ORIENT_LOWER_VP, GetTabMsg(/*L@ower Viewpoint*/251));
357    orientmenu->AppendSeparator();
358    orientmenu->Append(menu_ORIENT_ZOOM_IN, GetTabMsg(/*@Zoom In##]*/252));
359    orientmenu->Append(menu_ORIENT_ZOOM_OUT, GetTabMsg(/*Zoo@m Out##[*/253));
360    orientmenu->AppendSeparator();
361    orientmenu->Append(menu_ORIENT_DEFAULTS, GetTabMsg(/*Restore De@fault Settings*/254));
362
363    wxMenu* viewmenu = new wxMenu;
364    viewmenu->Append(menu_VIEW_SHOW_NAMES, GetTabMsg(/*Station @Names##Ctrl+N*/270), "", true);
365    viewmenu->Append(menu_VIEW_SHOW_CROSSES, GetTabMsg(/*@Crosses##Ctrl+X*/271), "", true);
366    viewmenu->Append(menu_VIEW_GRID, GetTabMsg(/*@Grid##Ctrl+G*/297), "", true);
367    viewmenu->AppendSeparator();
368    viewmenu->Append(menu_VIEW_SHOW_LEGS, GetTabMsg(/*@Underground Survey Legs##Ctrl+L*/272), "", true);
369    viewmenu->Append(menu_VIEW_SHOW_SURFACE, GetTabMsg(/*@Surface Survey Legs##Ctrl+F*/291), "", true);
370    viewmenu->AppendSeparator();
371    viewmenu->Append(menu_VIEW_SURFACE_DEPTH, GetTabMsg(/*@Altitude Colouring on Surface Surveys*/292), "", true);
372    viewmenu->Append(menu_VIEW_SURFACE_DASHED, GetTabMsg(/*@Dashed Surface Surveys*/293), "", true);
373    viewmenu->AppendSeparator();
374    viewmenu->Append(menu_VIEW_SHOW_OVERLAPPING_NAMES, GetTabMsg(/*@Overlapping Names*/273), "", true);
375    viewmenu->AppendSeparator();
376    viewmenu->Append(menu_VIEW_SHOW_ENTRANCES, GetTabMsg(/*Highlight @Entrances*/294), "", true);
377    viewmenu->Append(menu_VIEW_SHOW_FIXED_PTS, GetTabMsg(/*Highlight @Fixed Points*/295), "", true);
378    viewmenu->Append(menu_VIEW_SHOW_EXPORTED_PTS, GetTabMsg(/*Highlight E@xported Points*/296), "", true);
379
380    wxMenu* ctlmenu = new wxMenu;
381    ctlmenu->Append(menu_CTL_REVERSE, GetTabMsg(/*@Reverse Sense##Ctrl+R*/280), "", true);
382    ctlmenu->AppendSeparator();
383    ctlmenu->Append(menu_CTL_CANCEL_DIST_LINE, GetTabMsg(/*@Cancel Measuring Line##Escape*/281));
384    ctlmenu->AppendSeparator();
385    wxMenu* indmenu = new wxMenu;
386    indmenu->Append(menu_VIEW_COMPASS, GetTabMsg(/*@Compass*/274), "", true);
387    indmenu->Append(menu_VIEW_CLINO, GetTabMsg(/*C@linometer*/275), "", true);
388    indmenu->Append(menu_VIEW_DEPTH_BAR, GetTabMsg(/*@Depth Bar*/276), "", true);
389    indmenu->Append(menu_VIEW_SCALE_BAR, GetTabMsg(/*@Scale Bar*/277), "", true);
390    ctlmenu->Append(menu_VIEW_INDICATORS, GetTabMsg(/*@Indicators*/299), indmenu);
391    ctlmenu->Append(menu_VIEW_SIDE_PANEL, GetTabMsg(/*@Side Panel*/337), "", true);
392    ctlmenu->AppendSeparator();
393    ctlmenu->Append(menu_VIEW_METRIC, GetTabMsg(/*@Metric*/342), "", true);
394    ctlmenu->Append(menu_VIEW_DEGREES, GetTabMsg(/*@Degrees*/343), "", true);
395
396    wxMenu* helpmenu = new wxMenu;
397    helpmenu->Append(menu_HELP_ABOUT, GetTabMsg(/*@About...*/290));
398
399    wxMenuBar* menubar = new wxMenuBar(wxMB_DOCKABLE);
400    menubar->Append(filemenu, GetTabMsg(/*@File*/210));
401    menubar->Append(rotmenu, GetTabMsg(/*@Rotation*/211));
402    menubar->Append(orientmenu, GetTabMsg(/*@Orientation*/212));
403    menubar->Append(viewmenu, GetTabMsg(/*@View*/213));
404    menubar->Append(ctlmenu, GetTabMsg(/*@Controls*/214));
405    menubar->Append(helpmenu, GetTabMsg(/*@Help*/215));
406    SetMenuBar(menubar);
407}
408
409// ICON must be a literal string.
410#define TOOLBAR_BITMAP(ICON) wxBitmap(icon_path + ICON".png", wxBITMAP_TYPE_PNG)
411
412void MainFrm::CreateToolBar()
413{
414    // Create the toolbar.
415
416    wxToolBar* toolbar = wxFrame::CreateToolBar();
417
418#ifdef __WXGTK12__
419    toolbar->SetMargins(5, 5);
420#endif
421
422    toolbar->AddTool(menu_FILE_OPEN, TOOLBAR_BITMAP("open"), "Open a 3D file for viewing");
423    toolbar->AddSeparator();
424    toolbar->AddTool(menu_ROTATION_TOGGLE, TOOLBAR_BITMAP("rotation"),
425                     wxNullBitmap, true, -1, -1, NULL, "Toggle rotation");
426    toolbar->AddSeparator();
427    toolbar->AddTool(menu_ORIENT_PLAN, TOOLBAR_BITMAP("plan"), "Switch to plan view");
428    toolbar->AddTool(menu_ORIENT_ELEVATION, TOOLBAR_BITMAP("elevation"), "Switch to elevation view");
429    toolbar->AddSeparator();
430    toolbar->AddTool(menu_ORIENT_DEFAULTS, TOOLBAR_BITMAP("defaults"), "Restore default view");
431    toolbar->AddSeparator();
432    toolbar->AddTool(menu_VIEW_SHOW_NAMES, TOOLBAR_BITMAP("names"), wxNullBitmap, true,
433                     -1, -1, NULL, "Show station names");
434    toolbar->AddTool(menu_VIEW_SHOW_CROSSES, TOOLBAR_BITMAP("crosses"), wxNullBitmap, true,
435                     -1, -1, NULL, "Show crosses on stations");
436    toolbar->AddTool(menu_VIEW_SHOW_ENTRANCES, TOOLBAR_BITMAP("entrances"), wxNullBitmap, true,
437                     -1, -1, NULL, "Highlight entrances");
438    toolbar->AddTool(menu_VIEW_SHOW_FIXED_PTS, TOOLBAR_BITMAP("fixed-pts"), wxNullBitmap, true,
439                     -1, -1, NULL, "Highlight fixed points");
440    toolbar->AddTool(menu_VIEW_SHOW_EXPORTED_PTS, TOOLBAR_BITMAP("exported-pts"), wxNullBitmap, true,
441                     -1, -1, NULL, "Highlight exported stations");
442    toolbar->AddSeparator();
443    toolbar->AddTool(menu_VIEW_SHOW_LEGS, TOOLBAR_BITMAP("ug-legs"), wxNullBitmap, true,
444                     -1, -1, NULL, "Show underground surveys");
445    toolbar->AddTool(menu_VIEW_SHOW_SURFACE, TOOLBAR_BITMAP("surface-legs"), wxNullBitmap, true,
446                     -1, -1, NULL, "Show surface surveys");
447    toolbar->AddSeparator();
448
449    toolbar->Realize();
450}
451
452void MainFrm::CreateSidePanel()
453{
454    m_Splitter = new AvenSplitterWindow(this);
455
456    m_Panel = new wxPanel(m_Splitter);
457    m_Tree = new AvenTreeCtrl(this, m_Panel);
458    wxPanel *find_panel = new wxPanel(m_Panel);
459    m_Panel->Show(false);
460
461    m_FindBox = new wxTextCtrl(find_panel, -1, "");
462    wxButton *find_button, *hide_button;
463    find_button = new wxButton(find_panel, button_FIND, msg(/*Find*/332));
464    find_button->SetDefault();
465    find_panel->SetDefaultItem(find_button);
466    hide_button = new wxButton(find_panel, button_HIDE, msg(/*Hide*/333));
467    m_RegexpCheckBox = new wxCheckBox(find_panel, -1,
468                                      msg(/*Regular expression*/334));
469    m_Coords = new wxStaticText(find_panel, -1, "");
470    m_StnCoords = new wxStaticText(find_panel, -1, "");
471    //  m_MousePtr = new wxStaticText(find_panel, -1, "Mouse coordinates");
472    m_StnName = new wxStaticText(find_panel, -1, "");
473    m_StnAlt = new wxStaticText(find_panel, -1, "");
474    m_Dist1 = new wxStaticText(find_panel, -1, "");
475    m_Dist2 = new wxStaticText(find_panel, -1, "");
476    m_Dist3 = new wxStaticText(find_panel, -1, "");
477    m_Found = new wxStaticText(find_panel, -1, "");
478
479    wxBoxSizer *find_button_sizer = new wxBoxSizer(wxHORIZONTAL);
480    find_button_sizer->Add(m_FindBox, 1, wxALL, 2);
481#ifdef _WIN32
482    find_button_sizer->Add(find_button, 0, wxALL, 2);
483#else
484    // GTK+ (and probably Motif) default buttons have a thick external
485    // border we need to allow for
486    find_button_sizer->Add(find_button, 0, wxALL, 4);
487#endif
488
489    wxBoxSizer *hide_button_sizer = new wxBoxSizer(wxHORIZONTAL);
490    hide_button_sizer->Add(m_Found, 1, wxALL, 2);
491#ifdef _WIN32
492    hide_button_sizer->Add(hide_button, 0, wxALL, 2);
493#else
494    hide_button_sizer->Add(hide_button, 0, wxALL, 4);
495#endif
496
497    wxBoxSizer *find_sizer = new wxBoxSizer(wxVERTICAL);
498    find_sizer->Add(find_button_sizer, 0, wxALL | wxEXPAND, 2);
499    find_sizer->Add(hide_button_sizer, 0, wxALL | wxEXPAND, 2);
500    find_sizer->Add(m_RegexpCheckBox, 0, wxALL | wxEXPAND, 2);
501    find_sizer->Add(10, 5, 0, wxALL | wxEXPAND, 2);
502    //   find_sizer->Add(m_MousePtr, 0, wxALL | wxEXPAND, 2);
503    find_sizer->Add(m_Coords, 0, wxALL | wxEXPAND, 2);
504    find_sizer->Add(10, 5, 0, wxALL | wxEXPAND, 2);
505    find_sizer->Add(m_StnName, 0, wxALL | wxEXPAND, 2);
506    find_sizer->Add(m_StnCoords, 0, wxALL | wxEXPAND, 2);
507    find_sizer->Add(m_StnAlt, 0, wxALL | wxEXPAND, 2);
508    find_sizer->Add(10, 5, 0, wxALL | wxEXPAND, 2);
509    find_sizer->Add(m_Dist1, 0, wxALL | wxEXPAND, 2);
510    find_sizer->Add(m_Dist2, 0, wxALL | wxEXPAND, 2);
511    find_sizer->Add(m_Dist3, 0, wxALL | wxEXPAND, 2);
512
513    find_panel->SetAutoLayout(true);
514    find_panel->SetSizer(find_sizer);
515    find_sizer->Fit(find_panel);
516    find_sizer->SetSizeHints(find_panel);
517
518    wxBoxSizer *panel_sizer = new wxBoxSizer(wxVERTICAL);
519    panel_sizer->Add(m_Tree, 1, wxALL | wxEXPAND, 2);
520    panel_sizer->Add(find_panel, 0, wxALL | wxEXPAND, 2);
521    m_Panel->SetAutoLayout(true);
522    m_Panel->SetSizer(panel_sizer);
523//    panel_sizer->Fit(m_Panel);
524//    panel_sizer->SetSizeHints(m_Panel);
525
526    m_Gfx = new GfxCore(this, m_Splitter);
527
528    m_Splitter->Initialize(m_Gfx);
529}
530
531void MainFrm::ClearPointLists()
532{
533    // Free memory occupied by the contents of the point and label lists.
534
535    for (int band = 0; band < NUM_DEPTH_COLOURS + 1; band++) {
536        list<PointInfo*>::iterator pos = m_Points[band].begin();
537        list<PointInfo*>::iterator end = m_Points[band].end();
538        while (pos != end) {
539            PointInfo* point = *pos++;
540            delete point;
541        }
542        m_Points[band].clear();
543    }
544
545    list<LabelInfo*>::iterator pos = m_Labels.begin();
546    while (pos != m_Labels.end()) {
547        LabelInfo* label = *pos++;
548        delete label;
549    }
550    m_Labels.clear();
551}
552
553bool MainFrm::LoadData(const wxString& file, wxString prefix)
554{
555    // Load survey data from file, centre the dataset around the origin,
556    // chop legs such that no legs cross depth colour boundaries and prepare
557    // the data for drawing.
558
559    // Load the survey data.
560#if 0
561    wxStopWatch timer;
562    timer.Start();
563#endif
564
565    img* survey = img_open_survey(file, prefix.c_str());
566    if (!survey) {
567        wxString m = wxString::Format(msg(img_error()), file.c_str());
568        wxGetApp().ReportError(m);
569        return false;
570    }
571
572    m_File = survey->filename_opened;
573
574    m_Tree->DeleteAllItems();
575
576    m_TreeRoot = m_Tree->AddRoot(wxFileNameFromPath(file));
577    m_Tree->SetEnabled();
578
579    // Create a list of all the leg vertices, counting them and finding the
580    // extent of the survey at the same time.
581
582    m_NumLegs = 0;
583    m_NumPoints = 0;
584    m_NumExtraLegs = 0;
585    m_NumCrosses = 0;
586    m_NumFixedPts = 0;
587    m_NumExportedPts = 0;
588    m_NumEntrances = 0;
589
590    // Delete any existing list entries.
591    ClearPointLists();
592
593    Double xmin = DBL_MAX;
594    Double xmax = -DBL_MAX;
595    Double ymin = DBL_MAX;
596    Double ymax = -DBL_MAX;
597    m_ZMin = DBL_MAX;
598    Double zmax = -DBL_MAX;
599
600    list<PointInfo*> points;
601
602    int result;
603    do {
604        img_point pt;
605        result = img_read_item(survey, &pt);
606        switch (result) {
607            case img_MOVE:
608            case img_LINE:
609            {
610                m_NumPoints++;
611
612                // Update survey extents.
613                if (pt.x < xmin) xmin = pt.x;
614                if (pt.x > xmax) xmax = pt.x;
615                if (pt.y < ymin) ymin = pt.y;
616                if (pt.y > ymax) ymax = pt.y;
617                if (pt.z < m_ZMin) m_ZMin = pt.z;
618                if (pt.z > zmax) zmax = pt.z;
619
620                PointInfo* info = new PointInfo;
621                info->x = pt.x;
622                info->y = pt.y;
623                info->z = pt.z;
624
625                if (result == img_LINE) {
626                    // Set flags to say this is a line rather than a move
627                    m_NumLegs++;
628                    info->isLine = true;
629                    info->isSurface = (survey->flags & img_FLAG_SURFACE);
630                } else {
631                    info->isLine = false;
632                }
633
634                // Add this point to the list.
635                points.push_back(info);
636                break;
637            }
638
639            case img_LABEL: {
640                LabelInfo* label = new LabelInfo;
641                label->text = survey->label;
642                label->x = pt.x;
643                label->y = pt.y;
644                label->z = pt.z;
645                if (survey->flags & img_SFLAG_ENTRANCE) {
646                    survey->flags ^= (img_SFLAG_ENTRANCE | LFLAG_ENTRANCE);
647                }
648                label->flags = survey->flags;
649                if (label->IsEntrance()) {
650                    m_NumEntrances++;
651                }
652                if (label->IsFixedPt()) {
653                    m_NumFixedPts++;
654                }
655                if (label->IsExportedPt()) {
656                    m_NumExportedPts++;
657                }
658                m_Labels.push_back(label);
659                m_NumCrosses++;
660
661                break;
662            }
663
664            case img_BAD: {
665                m_Labels.clear();
666
667                // FIXME: Do we need to reset all these? - Olly
668                m_NumLegs = 0;
669                m_NumPoints = 0;
670                m_NumExtraLegs = 0;
671                m_NumCrosses = 0;
672                m_NumFixedPts = 0;
673                m_NumExportedPts = 0;
674                m_NumEntrances = 0;
675
676                m_ZMin = DBL_MAX;
677
678                img_close(survey);
679
680                wxString m = wxString::Format(msg(img_error()), file.c_str());
681                wxGetApp().ReportError(m);
682
683                return false;
684            }
685
686            default:
687                break;
688        }
689    } while (result != img_STOP);
690
691    separator = survey->separator;
692    m_Title = survey->title;
693    m_DateStamp = survey->datestamp;
694    img_close(survey);
695
696    // Check we've actually loaded some legs or stations!
697    if (m_NumLegs == 0 && m_Labels.empty()) {
698        wxString m = wxString::Format(msg(/*No survey data in 3d file `%s'*/202), file.c_str());
699        wxGetApp().ReportError(m);
700        return false;
701    }
702
703    if (points.empty()) {
704        // No legs, so get survey extents from stations
705        list<LabelInfo*>::const_iterator i;
706        for (i = m_Labels.begin(); i != m_Labels.end(); ++i) {
707            if ((*i)->x < xmin) xmin = (*i)->x;
708            if ((*i)->x > xmax) xmax = (*i)->x;
709            if ((*i)->y < ymin) ymin = (*i)->y;
710            if ((*i)->y > ymax) ymax = (*i)->y;
711            if ((*i)->z < m_ZMin) m_ZMin = (*i)->z;
712            if ((*i)->z > zmax) zmax = (*i)->z;
713        }
714    } else {
715        // Delete any trailing move.
716        PointInfo* pt = points.back();
717        if (!pt->isLine) {
718            m_NumPoints--;
719            points.pop_back();
720            delete pt;
721        }
722    }
723
724    m_XExt = xmax - xmin;
725    m_YExt = ymax - ymin;
726    m_ZExt = zmax - m_ZMin;
727
728    // FIXME -- temporary bodge
729    m_XMin = xmin;
730    m_YMin = ymin;
731
732    // Sort the labels.
733    m_Labels.sort(LabelCmp(separator));
734
735    // Fill the tree of stations and prefixes.
736    FillTree();
737    m_Tree->Expand(m_TreeRoot);
738
739    // Sort labels so that entrances are displayed in preference,
740    // then fixed points, then exported points, then other points.
741    //
742    // Also sort by leaf name so that we'll tend to choose labels
743    // from different surveys, rather than labels from surveys which
744    // are earlier in the list.
745    m_Labels.sort(LabelPlotCmp(separator));
746
747    // Sort out depth colouring boundaries (before centering dataset!)
748    SortIntoDepthBands(points);
749
750    // Centre the dataset around the origin.
751    CentreDataset(xmin, ymin, m_ZMin);
752
753#if 0
754    printf("time to load = %.3f\n", (double)timer.Time());
755#endif
756
757    // Update window title.
758    SetTitle(wxString(APP_NAME" - [") + m_File + wxString("]"));
759
760    return true;
761}
762
763void MainFrm::FillTree()
764{
765    // Fill the tree of stations and prefixes.
766
767    stack<wxTreeItemId> previous_ids;
768    wxString current_prefix = "";
769    wxTreeItemId current_id = m_TreeRoot;
770
771    list<LabelInfo*>::iterator pos = m_Labels.begin();
772    while (pos != m_Labels.end()) {
773        LabelInfo* label = *pos++;
774
775        // Determine the current prefix.
776        wxString prefix = label->GetText().BeforeLast(separator);
777
778        // Determine if we're still on the same prefix.
779        if (prefix == current_prefix) {
780            // no need to fiddle with branches...
781        }
782        // If not, then see if we've descended to a new prefix.
783        else if (prefix.Length() > current_prefix.Length() &&
784                 prefix.StartsWith(current_prefix) &&
785                 (prefix[current_prefix.Length()] == separator ||
786                  current_prefix == "")) {
787            // We have, so start as many new branches as required.
788            int current_prefix_length = current_prefix.Length();
789            current_prefix = prefix;
790            if (current_prefix_length != 0) {
791                prefix = prefix.Mid(current_prefix_length + 1);
792            }
793            int next_dot;
794            do {
795                // Extract the next bit of prefix.
796                next_dot = prefix.Find(separator);
797
798                wxString bit = next_dot == -1 ? prefix : prefix.Left(next_dot);
799                assert(bit != "");
800
801                // Add the current tree ID to the stack.
802                previous_ids.push(current_id);
803
804                // Append the new item to the tree and set this as the current branch.
805                current_id = m_Tree->AppendItem(current_id, bit);
806                m_Tree->SetItemData(current_id, new TreeData(NULL));
807                prefix = prefix.Mid(next_dot + 1);
808            } while (next_dot != -1);
809        }
810        // Otherwise, we must have moved up, and possibly then down again.
811        else {
812            size_t count = 0;
813            bool ascent_only = (prefix.Length() < current_prefix.Length() &&
814                                current_prefix.StartsWith(prefix) &&
815                                (current_prefix[prefix.Length()] == separator ||
816                                 prefix == ""));
817            if (!ascent_only) {
818                // Find out how much of the current prefix and the new prefix
819                // are the same.
820                // Note that we require a match of a whole number of parts
821                // between dots!
822                for (size_t i = 0; prefix[i] == current_prefix[i]; ++i) {
823                    if (prefix[i] == separator) count = i + 1;
824                }
825            } else {
826                count = prefix.Length() + 1;
827            }
828
829            // Extract the part of the current prefix after the bit (if any)
830            // which has matched.
831            // This gives the prefixes to ascend over.
832            wxString prefixes_ascended = current_prefix.Mid(count);
833
834            // Count the number of prefixes to ascend over.
835            int num_prefixes = prefixes_ascended.Freq(separator);
836
837            // Reverse up over these prefixes.
838            for (int i = 1; i <= num_prefixes; i++) {
839                previous_ids.pop();
840            }
841            current_id = previous_ids.top();
842            previous_ids.pop();
843
844            if (!ascent_only) {
845                // Now extract the bit of new prefix.
846                wxString new_prefix = prefix.Mid(count);
847
848                // Add branches for this new part.
849                while (true) {
850                    // Extract the next bit of prefix.
851                    int next_dot = new_prefix.Find(separator);
852
853                    wxString bit;
854                    if (next_dot == -1) {
855                        bit = new_prefix;
856                    } else {
857                        bit = new_prefix.Left(next_dot);
858                    }
859
860                    // Add the current tree ID to the stack.
861                    previous_ids.push(current_id);
862
863                    // Append the new item to the tree and set this as the
864                    // current branch.
865                    current_id = m_Tree->AppendItem(current_id, bit);
866                    m_Tree->SetItemData(current_id, new TreeData(NULL));
867
868                    if (next_dot == -1) break;
869
870                    new_prefix = new_prefix.Mid(next_dot + 1);
871                }
872            }
873
874            current_prefix = prefix;
875        }
876
877        // Now add the leaf.
878        wxString bit = label->GetText().AfterLast(separator);
879        assert(bit != "");
880        wxTreeItemId id = m_Tree->AppendItem(current_id, bit);
881        m_Tree->SetItemData(id, new TreeData(label));
882        label->tree_id = id; // before calling SetTreeItemColour()...
883        SetTreeItemColour(label);
884    }
885}
886
887void MainFrm::SelectTreeItem(LabelInfo* label)
888{
889    m_Tree->SelectItem(label->tree_id);
890}
891
892void MainFrm::SetTreeItemColour(LabelInfo* label)
893{
894    // Set the colour for an item in the survey tree.
895
896    if (label->IsSurface()) {
897        m_Tree->SetItemTextColour(label->tree_id, wxColour(49, 158, 79));
898    }
899
900    if (label->IsEntrance()) {
901        // FIXME: making this red here doesn't match with entrance blobs
902        // being green...
903        m_Tree->SetItemTextColour(label->tree_id, wxColour(255, 0, 0));
904    }
905}
906
907void MainFrm::CentreDataset(Double xmin, Double ymin, Double zmin)
908{
909    // Centre the dataset around the origin.
910
911    Double xoff = m_Offsets.x = xmin + (m_XExt / 2.0);
912    Double yoff = m_Offsets.y = ymin + (m_YExt / 2.0);
913    Double zoff = m_Offsets.z = zmin + (m_ZExt / 2.0);
914
915    for (int band = 0; band < NUM_DEPTH_COLOURS + 1; band++) {
916        list<PointInfo*>::iterator pos = m_Points[band].begin();
917        list<PointInfo*>::iterator end = m_Points[band].end();
918        while (pos != end) {
919            PointInfo* point = *pos++;
920            point->x -= xoff;
921            point->y -= yoff;
922            point->z -= zoff;
923        }
924    }
925
926    list<LabelInfo*>::iterator lpos = m_Labels.begin();
927    while (lpos != m_Labels.end()) {
928        LabelInfo* label = *lpos++;
929        label->x -= xoff;
930        label->y -= yoff;
931        label->z -= zoff;
932    }
933}
934
935int MainFrm::GetDepthColour(Double z)
936{
937    // Return the (0-based) depth colour band index for a z-coordinate.
938    return int(((z - m_ZMin) / (m_ZExt == 0.0 ? 1.0 : m_ZExt)) * (NUM_DEPTH_COLOURS - 1));
939}
940
941Double MainFrm::GetDepthBoundaryBetweenBands(int a, int b)
942{
943    // Return the z-coordinate of the depth colour boundary between
944    // two adjacent depth colour bands (specified by 0-based indices).
945
946    assert((a == b - 1) || (a == b + 1));
947
948    int band = (a > b) ? a : b; // boundary N lies on the bottom of band N.
949    return m_ZMin + (m_ZExt * band / (NUM_DEPTH_COLOURS - 1));
950}
951
952void MainFrm::IntersectLineWithPlane(Double x0, Double y0, Double z0,
953                                     Double x1, Double y1, Double z1,
954                                     Double z, Double& x, Double& y)
955{
956    // Find the intersection point of the line (x0, y0, z0) -> (x1, y1, z1)
957    // with the plane parallel to the xy-plane with z-axis intersection z.
958    assert(z1 - z0 != 0.0);
959
960    Double t = (z - z0) / (z1 - z0);
961    // FIXME: this assertion fails on Windows - haven't investigated but I
962    // suspect it'll be crap rounding:
963    // assert(0.0 <= t && t <= 1.0);
964
965    x = x0 + t * (x1 - x0);
966    y = y0 + t * (y1 - y0);
967}
968
969void MainFrm::SortIntoDepthBands(list<PointInfo*>& points)
970{
971    // Split legs which cross depth colouring boundaries and classify all
972    // points into the correct depth bands.
973
974    list<PointInfo*>::iterator pos = points.begin();
975    PointInfo* prev_point = NULL;
976    while (pos != points.end()) {
977        PointInfo* point = *pos++;
978        assert(point);
979
980        // If this is a leg, then check if it intersects a depth
981        // colour boundary.
982        if (point->isLine) {
983            assert(prev_point);
984            int col1 = GetDepthColour(prev_point->z);
985            int col2 = GetDepthColour(point->z);
986            if (col1 != col2) {
987                // The leg does cross at least one boundary, so split it as
988                // many times as required...
989                int inc = (col1 > col2) ? -1 : 1;
990                for (int band = col1; band != col2; band += inc) {
991                    int next_band = band + inc;
992
993                    // Determine the z-coordinate of the boundary being
994                    // intersected.
995                    Double split_at_z = GetDepthBoundaryBetweenBands(band, next_band);
996                    Double split_at_x, split_at_y;
997
998                    // Find the coordinates of the intersection point.
999                    IntersectLineWithPlane(prev_point->x, prev_point->y, prev_point->z,
1000                                           point->x, point->y, point->z,
1001                                           split_at_z, split_at_x, split_at_y);
1002
1003                    // Create a new leg only as far as this point.
1004                    PointInfo* info = new PointInfo;
1005                    info->x = split_at_x;
1006                    info->y = split_at_y;
1007                    info->z = split_at_z;
1008                    info->isLine = true;
1009                    info->isSurface = point->isSurface;
1010                    m_Points[band].push_back(info);
1011
1012                    // Create a move to this point in the next band.
1013                    info = new PointInfo;
1014                    info->x = split_at_x;
1015                    info->y = split_at_y;
1016                    info->z = split_at_z;
1017                    info->isLine = false;
1018                    info->isSurface = point->isSurface;
1019                    m_Points[next_band].push_back(info);
1020
1021                    m_NumExtraLegs++;
1022                    m_NumPoints += 2;
1023                }
1024            }
1025
1026            // Add the last point of the (possibly split) leg.
1027            m_Points[col2].push_back(point);
1028        }
1029        else {
1030            // The first point, a surface point, or another move: put it in the
1031            // correct list according to depth.
1032            int band = GetDepthColour(point->z);
1033            m_Points[band].push_back(point);
1034        }
1035
1036        prev_point = point;
1037    }
1038}
1039
1040void MainFrm::OnMRUFile(wxCommandEvent& event)
1041{
1042    wxString f(m_history.GetHistoryFile(event.GetId() - wxID_FILE1));
1043    if (!f.empty()) OpenFile(f);
1044}
1045
1046void MainFrm::OpenFile(const wxString& file, wxString survey, bool delay)
1047{
1048    wxBusyCursor hourglass;
1049    if (LoadData(file, survey)) {
1050        if (wxIsAbsolutePath(m_File)) {
1051            m_history.AddFileToHistory(m_File);
1052        } else {
1053            wxString abs = wxGetCwd() + wxString(FNM_SEP_LEV) + m_File;
1054            m_history.AddFileToHistory(abs);
1055        }
1056        wxConfigBase *b = wxConfigBase::Get();
1057        m_history.Save(*b);
1058        b->Flush();
1059
1060        int x;
1061        int y;
1062        GetClientSize(&x, &y);
1063        if (x < 600)
1064            x /= 3;
1065        else if (x < 1000)
1066            x = 200;
1067        else
1068            x /= 5;
1069
1070        m_Splitter->SplitVertically(m_Panel, m_Gfx, x);
1071        m_SashPosition = x; // Save width of panel.
1072
1073        if (delay) {
1074            m_Gfx->InitialiseOnNextResize();
1075        } else {
1076            m_Gfx->Initialise();
1077        }
1078        m_Panel->Show(true);
1079
1080        m_Gfx->SetFocus();
1081    }
1082}
1083
1084//
1085//  UI event handlers
1086//
1087
1088#undef FILEDIALOG_MULTIGLOBS
1089// MS Windows supports "*.abc;*.def" natively; wxGtk supports them as of 2.3
1090#if defined(_WIN32) || wxCHECK_VERSION(2,3,0)
1091# define FILEDIALOG_MULTIGLOBS
1092#endif
1093
1094void MainFrm::OnOpen(wxCommandEvent&)
1095{
1096#ifdef __WXMOTIF__
1097    wxFileDialog dlg (this, wxString(msg(/*Select a 3d file to view*/206)), "", "",
1098                      "*.3d", wxOPEN);
1099#else
1100    wxFileDialog dlg(this, wxString(msg(/*Select a 3d file to view*/206)), "", "",
1101                     wxString::Format("%s|*.3d"
1102#ifdef FILEDIALOG_MULTIGLOBS
1103                                      ";*.3D"
1104#endif
1105#ifdef FILEDIALOG_MULTIGLOBS
1106                                      "|%s|*.plt;*.plf"
1107#ifndef _WIN32
1108                                      ";*.PLT;*.PLF"
1109#endif
1110#else
1111                                      "|%s|*.pl?" // not ideal...
1112#endif
1113                                      "|%s|*.xyz"
1114#ifdef FILEDIALOG_MULTIGLOBS
1115#ifndef _WIN32
1116                                      ";*.XYZ"
1117#endif
1118#endif
1119                                      "|%s|%s",
1120                                      msg(/*Survex 3d files*/207),
1121                                      msg(/*Compass PLT files*/324),
1122                                      msg(/*CMAP XYZ files*/325),
1123                                      msg(/*All files*/208),
1124                                      wxFileSelectorDefaultWildcardStr),
1125                     wxOPEN);
1126#endif
1127    if (dlg.ShowModal() == wxID_OK) {
1128        OpenFile(dlg.GetPath());
1129    }
1130}
1131
1132void MainFrm::OnPrint(wxCommandEvent&)
1133{
1134    m_Gfx->OnPrint(m_File, m_Title, m_DateStamp);
1135}
1136
1137void MainFrm::OnPageSetup(wxCommandEvent&)
1138{
1139    m_pageSetupData.SetPrintData(m_printData);
1140
1141    wxPageSetupDialog pageSetupDialog(this, &m_pageSetupData);
1142    pageSetupDialog.ShowModal();
1143
1144    m_printData = pageSetupDialog.GetPageSetupData().GetPrintData();
1145    m_pageSetupData = pageSetupDialog.GetPageSetupData();
1146}
1147
1148void MainFrm::OnExport(wxCommandEvent&)
1149{
1150    char *baseleaf = baseleaf_from_fnm(m_File.c_str());
1151    wxFileDialog dlg(this, wxString("Export as:"), "",
1152                     wxString(baseleaf),
1153                     "DXF files|*.dxf|SVG files|*.svg|Sketch files|*.sk|EPS files|*.eps|Compass PLT for use with Carto|*.plt",
1154                     wxSAVE|wxOVERWRITE_PROMPT);
1155    free(baseleaf);
1156    if (dlg.ShowModal() == wxID_OK) {
1157        wxString fnm = dlg.GetPath();
1158        if (!m_Gfx->OnExport(fnm, m_Title)) {
1159            wxGetApp().ReportError(wxString::Format("Couldn't write file `%s'", fnm.c_str()));
1160        }
1161    }
1162}
1163
1164void MainFrm::OnQuit(wxCommandEvent&)
1165{
1166    exit(0);
1167}
1168
1169void MainFrm::OnClose(wxCloseEvent&)
1170{
1171    exit(0);
1172}
1173
1174void MainFrm::OnAbout(wxCommandEvent&)
1175{
1176    AboutDlg dlg(this, icon_path);
1177    dlg.Centre();
1178    dlg.ShowModal();
1179}
1180
1181void MainFrm::GetColour(int band, Double& r, Double& g, Double& b) const
1182{
1183    assert(band >= 0 && band < NUM_DEPTH_COLOURS);
1184    r = Double(REDS[band]) / 255.0;
1185    g = Double(GREENS[band]) / 255.0;
1186    b = Double(BLUES[band]) / 255.0;
1187}
1188
1189void MainFrm::ClearTreeSelection()
1190{
1191    m_Tree->UnselectAll();
1192    m_Gfx->SetThere();
1193}
1194
1195void MainFrm::ClearCoords()
1196{
1197    m_Coords->SetLabel("");
1198}
1199
1200void MainFrm::SetCoords(Double x, Double y)
1201{
1202    wxString str;
1203    if (m_Gfx->m_Metric) {
1204        str.Printf(msg(/*  %d E, %d N*/338), int(x), int(y));
1205    } else {
1206        str.Printf(msg(/*  %d E, %d N*/338),
1207                   int(x / METRES_PER_FOOT), int(y / METRES_PER_FOOT));
1208    }
1209    m_Coords->SetLabel(str);
1210}
1211
1212void MainFrm::SetAltitude(Double z)
1213{
1214    wxString str;
1215    if (m_Gfx->m_Metric) {
1216        str.Printf("  %s %dm", msg(/*Altitude*/335),
1217                   int(z));
1218    } else {
1219        str.Printf("  %s %dft", msg(/*Altitude*/335),
1220                   int(z / METRES_PER_FOOT));
1221    }
1222    m_Coords->SetLabel(str);
1223}
1224
1225void MainFrm::ShowInfo(const LabelInfo *label)
1226{
1227    assert(m_Gfx);
1228
1229    wxString str;
1230    if (m_Gfx->m_Metric) {
1231        str.Printf(msg(/*  %d E, %d N*/338),
1232                   int(label->x + m_Offsets.x),
1233                   int(label->y + m_Offsets.y));
1234    } else {
1235        str.Printf(msg(/*  %d E, %d N*/338),
1236                   int((label->x + m_Offsets.x) / METRES_PER_FOOT),
1237                   int((label->y + m_Offsets.y) / METRES_PER_FOOT));
1238    }
1239    m_StnCoords->SetLabel(str);
1240    m_StnName->SetLabel(label->text);
1241
1242    if (m_Gfx->m_Metric) {
1243        str.Printf("  %s %dm", msg(/*Altitude*/335),
1244                   int(label->z + m_Offsets.z));
1245    } else {
1246        str.Printf("  %s %dft", msg(/*Altitude*/335),
1247                   int((label->z + m_Offsets.z) / METRES_PER_FOOT));
1248    }
1249    m_StnAlt->SetLabel(str);
1250    m_Gfx->SetHere(label->x, label->y, label->z);
1251
1252    wxTreeItemData* sel_wx;
1253    bool sel = m_Tree->GetSelectionData(&sel_wx);
1254    if (sel) {
1255        TreeData *data = (TreeData*) sel_wx;
1256
1257        if (data->IsStation()) {
1258            const LabelInfo* label2 = data->GetLabel();
1259            assert(label2);
1260
1261            Double x0 = label2->x;
1262            Double x1 = label->x;
1263            Double dx = x1 - x0;
1264            Double y0 = label2->y;
1265            Double y1 = label->y;
1266            Double dy = y1 - y0;
1267            Double z0 = label2->z;
1268            Double z1 = label->z;
1269            Double dz = z1 - z0;
1270
1271            Double d_horiz = sqrt(dx*dx + dy*dy);
1272            Double dr = sqrt(dx*dx + dy*dy + dz*dz);
1273
1274            Double brg = atan2(dx, dy) * 180.0 / M_PI;
1275            if (brg < 0) brg += 360;
1276
1277            str.Printf(msg(/*From %s*/339), label2->text.c_str());
1278            m_Dist1->SetLabel(str);
1279            if (m_Gfx->m_Metric) {
1280                str.Printf(msg(/*  H %d%s, V %d%s*/340),
1281                           int(d_horiz), "m",
1282                           int(dz), "m");
1283            } else {
1284                str.Printf(msg(/*  H %d%s, V %d%s*/340),
1285                           int(d_horiz / METRES_PER_FOOT), "ft",
1286                           int(dz / METRES_PER_FOOT), "ft");
1287            }
1288            m_Dist2->SetLabel(str);
1289            wxString brg_unit;
1290            if (m_Gfx->m_Degrees) {
1291                brg_unit = msg(/*&deg;*/344);
1292            } else {
1293                brg *= 400.0 / 360.0;
1294                brg_unit = msg(/*grad*/345);
1295            }
1296            if (m_Gfx->m_Metric) {
1297                str.Printf(msg(/*  Dist %d%s, Brg %03d%s*/341),
1298                           int(dr), "m", int(brg), brg_unit.c_str());
1299            } else {
1300                str.Printf(msg(/*  Dist %d%s, Brg %03d%s*/341),
1301                           int(dr / METRES_PER_FOOT), "ft", int(brg),
1302                           brg_unit.c_str());
1303            }
1304            m_Dist3->SetLabel(str);
1305            m_Gfx->SetThere(x0, y0, z0);
1306        } else {
1307            m_Gfx->SetThere(); // FIXME: not in SetMouseOverStation version?
1308        }
1309    }
1310}
1311
1312void MainFrm::DisplayTreeInfo(const wxTreeItemData* item)
1313{
1314    const TreeData* data = static_cast<const TreeData*>(item);
1315    if (data) {
1316        if (data->IsStation()) {
1317            const LabelInfo * l = data->GetLabel();
1318            ShowInfo(l);
1319            m_Gfx->SetHere(l->x, l->y, l->z);
1320        } else {
1321            m_StnName->SetLabel("");
1322            m_StnCoords->SetLabel("");
1323            m_StnAlt->SetLabel("");
1324            m_Gfx->SetHere();
1325            m_Dist1->SetLabel("");
1326            m_Dist2->SetLabel("");
1327            m_Dist3->SetLabel("");
1328            m_Gfx->SetHere();
1329        }
1330    }
1331}
1332
1333void MainFrm::TreeItemSelected(wxTreeItemData* item)
1334{
1335    TreeData* data = (TreeData*) item;
1336
1337    if (data && data->IsStation()) {
1338        const LabelInfo* label = data->GetLabel();
1339        m_Gfx->CentreOn(label->x, label->y, label->z);
1340        m_Gfx->SetThere(label->x, label->y, label->z);
1341    } else {
1342        m_Gfx->SetThere();
1343    }
1344
1345    m_Dist1->SetLabel("");
1346    m_Dist2->SetLabel("");
1347    m_Dist3->SetLabel("");
1348}
1349
1350void MainFrm::OnFind(wxCommandEvent&)
1351{
1352    wxBusyCursor hourglass;
1353    // Find stations specified by a string or regular expression.
1354
1355    wxString pattern = m_FindBox->GetValue();
1356    int re_flags = wxRE_NOSUB;
1357
1358    if (true /* case insensitive */) {
1359        re_flags |= wxRE_ICASE;
1360    }
1361
1362    bool substring = true;
1363    if (m_RegexpCheckBox->GetValue()) {
1364        re_flags |= wxRE_EXTENDED;
1365    } else if (false /* simple glob-style */) {
1366        wxString pat;
1367        for (size_t i = 0; i < pattern.size(); i++) {
1368           char ch = pattern[i];
1369           // ^ only special at start; $ at end.  But this is simpler...
1370           switch (ch) {
1371            case '^': case '$': case '.': case '[': case '\\':
1372              pat += '\\';
1373              pat += ch;
1374              break;
1375            case '*':
1376              pat += ".*";
1377              substring = false;
1378              break;
1379            case '?':
1380              pat += '.';
1381              substring = false;
1382              break;
1383            default:
1384              pat += ch;
1385           }
1386        }
1387        pattern = pat;
1388        re_flags |= wxRE_BASIC;
1389    } else {
1390        wxString pat;
1391        for (size_t i = 0; i < pattern.size(); i++) {
1392           char ch = pattern[i];
1393           // ^ only special at start; $ at end.  But this is simpler...
1394           switch (ch) {
1395            case '^': case '$': case '*': case '.': case '[': case '\\':
1396              pat += '\\';
1397           }
1398           pat += ch;
1399        }
1400        pattern = pat;
1401        re_flags |= wxRE_BASIC;
1402    }
1403
1404    if (!substring) {
1405        // FIXME "0u" required to avoid compilation error with g++-3.0
1406        if (pattern.empty() || pattern[0u] != '^') pattern = '^' + pattern;
1407        // FIXME: this fails to cope with "\$" at the end of pattern...
1408        if (pattern[pattern.size() - 1] != '$') pattern += '$';
1409    }
1410
1411    wxRegEx regex;
1412    if (!regex.Compile(pattern, re_flags)) {
1413        wxString m;
1414        m.Printf(msg(/*Invalid regular expression: %s*/404), pattern.c_str());
1415        wxGetApp().ReportError(m);
1416        return;
1417    }
1418
1419    list<LabelInfo*>::iterator pos = m_Labels.begin();
1420
1421    int found = 0;
1422    while (pos != m_Labels.end()) {
1423        LabelInfo* label = *pos++;
1424
1425        if (regex.Matches(label->text)) {
1426            label->flags |= LFLAG_HIGHLIGHTED;
1427            found++;
1428        } else {
1429            label->flags &= ~LFLAG_HIGHLIGHTED;
1430        }
1431    }
1432
1433    m_Found->SetLabel(wxString::Format(msg(/*%d found*/331), found));
1434#ifdef _WIN32
1435    m_Found->Refresh(); // FIXME
1436#endif
1437    // Re-sort so highlighted points get names in preference
1438    if (found) m_Labels.sort(LabelPlotCmp(separator));
1439    m_Gfx->ForceRefresh();
1440
1441#if 0
1442    if (!found) {
1443        wxGetApp().ReportError(msg(/*No matches were found.*/328));
1444    }
1445#endif
1446
1447    m_Gfx->SetFocus();
1448}
1449
1450void MainFrm::OnHide(wxCommandEvent&)
1451{
1452    // Hide any search result highlights.
1453    m_Found->SetLabel("");
1454    list<LabelInfo*>::iterator pos = m_Labels.begin();
1455    while (pos != m_Labels.end()) {
1456        LabelInfo* label = *pos++;
1457        label->flags &= ~LFLAG_HIGHLIGHTED;
1458    }
1459    m_Gfx->ForceRefresh();
1460}
1461
1462void MainFrm::SetMouseOverStation(LabelInfo* label)
1463{
1464    if (label) {
1465        ShowInfo(label);
1466    } else {
1467        m_StnName->SetLabel("");
1468        m_StnCoords->SetLabel("");
1469        m_StnAlt->SetLabel("");
1470        m_Gfx->SetHere();
1471        m_Dist1->SetLabel("");
1472        m_Dist2->SetLabel("");
1473        m_Dist3->SetLabel("");
1474    }
1475}
1476
1477void MainFrm::OnViewSidePanel(wxCommandEvent&)
1478{
1479    ToggleSidePanel();
1480}
1481
1482void MainFrm::ToggleSidePanel()
1483{
1484    // Toggle display of the side panel.
1485
1486    assert(m_Gfx);
1487
1488    if (m_Splitter->IsSplit()) {
1489        m_SashPosition = m_Splitter->GetSashPosition(); // save width of panel
1490        m_Splitter->Unsplit(m_Panel);
1491    } else {
1492        m_Panel->Show(true);
1493        m_Gfx->Show(true);
1494        m_Splitter->SplitVertically(m_Panel, m_Gfx, m_SashPosition);
1495    }
1496}
1497
1498void MainFrm::OnViewSidePanelUpdate(wxUpdateUIEvent& ui)
1499{
1500    ui.Enable(!m_File.empty());
1501    ui.Check(m_Splitter->IsSplit());
1502}
Note: See TracBrowser for help on using the repository browser.