source: git/src/mainfrm.cc @ 3878cbc

RELEASE/1.0
Last change on this file since 3878cbc was 3878cbc, checked in by Olly Betts <olly@…>, 14 years ago

src/mainfrm.cc,src/wx.h: Fix to compile with wx2.8.

git-svn-id: file:///home/survex-svn/survex/branches/1.0@3448 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

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