[5809313] | 1 | // |
---|
| 2 | // gfxcore.h |
---|
| 3 | // |
---|
| 4 | // Core drawing code for Aven. |
---|
| 5 | // |
---|
| 6 | // Copyright (C) 2000-2001, Mark R. Shinwell. |
---|
[301bbc1] | 7 | // Copyright (C) 2001-2002,2004,2005 Olly Betts |
---|
[5809313] | 8 | // |
---|
| 9 | // This program is free software; you can redistribute it and/or modify |
---|
| 10 | // it under the terms of the GNU General Public License as published by |
---|
| 11 | // the Free Software Foundation; either version 2 of the License, or |
---|
| 12 | // (at your option) any later version. |
---|
| 13 | // |
---|
| 14 | // This program is distributed in the hope that it will be useful, |
---|
| 15 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
| 16 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
| 17 | // GNU General Public License for more details. |
---|
| 18 | // |
---|
| 19 | // You should have received a copy of the GNU General Public License |
---|
| 20 | // along with this program; if not, write to the Free Software |
---|
[1b71c05] | 21 | // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
---|
[5809313] | 22 | // |
---|
| 23 | |
---|
| 24 | #ifndef gfxcore_h |
---|
| 25 | #define gfxcore_h |
---|
| 26 | |
---|
[7aa15c0] | 27 | #include <float.h> |
---|
| 28 | |
---|
[5809313] | 29 | #include "quaternion.h" |
---|
| 30 | #include "wx.h" |
---|
[156dc16] | 31 | #include <utility> |
---|
| 32 | #include <list> |
---|
[5809313] | 33 | |
---|
[003d953] | 34 | using std::list; |
---|
| 35 | |
---|
[137bf99] | 36 | class MainFrm; |
---|
[e61185d] | 37 | class PointInfo; |
---|
[5809313] | 38 | |
---|
[156dc16] | 39 | struct ColourTriple { |
---|
| 40 | // RGB triple: values are from 0-255 inclusive for each component. |
---|
| 41 | int r; |
---|
| 42 | int g; |
---|
| 43 | int b; |
---|
| 44 | }; |
---|
| 45 | |
---|
| 46 | // Colours for drawing. |
---|
[fa42426] | 47 | // These must be in the same order as the entries in COLOURS[] in gfxcore.cc. |
---|
[156dc16] | 48 | enum AvenColour { |
---|
| 49 | col_BLACK = 0, |
---|
| 50 | col_GREY, |
---|
| 51 | col_LIGHT_GREY, |
---|
| 52 | col_LIGHT_GREY_2, |
---|
| 53 | col_DARK_GREY, |
---|
| 54 | col_WHITE, |
---|
| 55 | col_TURQUOISE, |
---|
| 56 | col_GREEN, |
---|
| 57 | col_INDICATOR_1, |
---|
| 58 | col_INDICATOR_2, |
---|
| 59 | col_YELLOW, |
---|
| 60 | col_RED, |
---|
| 61 | col_LAST // must be the last entry here |
---|
| 62 | }; |
---|
| 63 | |
---|
[0784240] | 64 | // Mac OS X headers pollute the global namespace with generic names like |
---|
| 65 | // "class Point", which clashes with our "class Point". So for __WXMAC__ |
---|
| 66 | // put our class in a namespace and define Point as a macro. |
---|
| 67 | #ifdef __WXMAC__ |
---|
| 68 | namespace svx { |
---|
| 69 | #endif |
---|
| 70 | |
---|
[cd6ea75] | 71 | class Point { |
---|
[2a02de2] | 72 | friend class GfxCore; |
---|
| 73 | Double x, y, z; |
---|
[cd6ea75] | 74 | public: |
---|
| 75 | Point() {} |
---|
| 76 | Point(Double x_, Double y_, Double z_) : x(x_), y(y_), z(z_) {} |
---|
[2a02de2] | 77 | }; |
---|
| 78 | |
---|
[0784240] | 79 | #ifdef __WXMAC__ |
---|
| 80 | } |
---|
| 81 | #define Point svx::Point |
---|
| 82 | #endif |
---|
| 83 | |
---|
[39e460c9] | 84 | class LabelInfo; |
---|
| 85 | |
---|
[156dc16] | 86 | extern const ColourTriple COLOURS[]; // defined in gfxcore.cc |
---|
| 87 | |
---|
[5809313] | 88 | class GfxCore : public wxWindow { |
---|
| 89 | struct params { |
---|
[421b7d2] | 90 | Quaternion rotation; |
---|
| 91 | Double scale; |
---|
| 92 | struct { |
---|
[c6d95d8] | 93 | Double x; |
---|
| 94 | Double y; |
---|
| 95 | Double z; |
---|
[5809313] | 96 | } translation; |
---|
| 97 | } m_Params; |
---|
| 98 | |
---|
[de7a879] | 99 | enum LockFlags { |
---|
[421b7d2] | 100 | lock_NONE = 0, |
---|
| 101 | lock_X = 1, |
---|
[de7a879] | 102 | lock_Y = 2, |
---|
| 103 | lock_Z = 4, |
---|
| 104 | lock_POINT = lock_X | lock_Y | lock_Z, |
---|
| 105 | lock_XZ = lock_X | lock_Z, |
---|
| 106 | lock_YZ = lock_Y | lock_Z, |
---|
| 107 | lock_XY = lock_X | lock_Y |
---|
| 108 | }; |
---|
[5809313] | 109 | |
---|
| 110 | struct PlotData { |
---|
[cd6ea75] | 111 | Point *vertices; |
---|
| 112 | Point *surface_vertices; |
---|
[421b7d2] | 113 | int* num_segs; |
---|
| 114 | int* surface_num_segs; |
---|
[5809313] | 115 | }; |
---|
| 116 | |
---|
[42adb19] | 117 | struct { |
---|
[421b7d2] | 118 | int offset_x; |
---|
| 119 | int offset_y; |
---|
| 120 | int width; |
---|
| 121 | int drag_start_offset_x; |
---|
| 122 | int drag_start_offset_y; |
---|
[42adb19] | 123 | } m_ScaleBar; |
---|
| 124 | |
---|
[156dc16] | 125 | double m_MaxExtent; // twice the maximum of the {x,y,z}-extents, in survey coordinates. |
---|
[d5de678] | 126 | char *m_LabelGrid; |
---|
[303a525] | 127 | bool m_RotationOK; |
---|
[de7a879] | 128 | LockFlags m_Lock; |
---|
[5809313] | 129 | Matrix4 m_RotationMatrix; |
---|
| 130 | bool m_DraggingLeft; |
---|
| 131 | bool m_DraggingMiddle; |
---|
| 132 | bool m_DraggingRight; |
---|
[137bf99] | 133 | MainFrm* m_Parent; |
---|
[5809313] | 134 | wxPoint m_DragStart; |
---|
[2effbf1] | 135 | wxPoint m_DragRealStart; |
---|
[26f3bd8] | 136 | wxPoint m_DragLast; |
---|
[d8622e1] | 137 | wxBitmap* m_OffscreenBitmap; |
---|
[5809313] | 138 | wxMemoryDC m_DrawDC; |
---|
| 139 | bool m_DoneFirstShow; |
---|
| 140 | bool m_RedrawOffscreen; |
---|
| 141 | int* m_Polylines; |
---|
[11a266e] | 142 | int* m_SurfacePolylines; |
---|
[5809313] | 143 | int m_Bands; |
---|
[c6d95d8] | 144 | Double m_InitialScale; |
---|
| 145 | Double m_TiltAngle; |
---|
| 146 | Double m_PanAngle; |
---|
[5809313] | 147 | bool m_Rotating; |
---|
[c6d95d8] | 148 | Double m_RotationStep; |
---|
[3d00693] | 149 | int m_SwitchingTo; |
---|
[5809313] | 150 | bool m_ReverseControls; |
---|
| 151 | bool m_Crosses; |
---|
| 152 | bool m_Legs; |
---|
| 153 | bool m_Names; |
---|
| 154 | bool m_Scalebar; |
---|
[84cab34] | 155 | wxFont m_Font; |
---|
[5809313] | 156 | bool m_Depthbar; |
---|
| 157 | bool m_OverlappingNames; |
---|
| 158 | bool m_Compass; |
---|
[c300a04] | 159 | bool m_Clino; |
---|
[5809313] | 160 | int m_XSize; |
---|
| 161 | int m_YSize; |
---|
| 162 | int m_XCentre; |
---|
| 163 | int m_YCentre; |
---|
| 164 | PlotData* m_PlotData; |
---|
[068b4f2] | 165 | bool m_InitialisePending; |
---|
[42adb19] | 166 | enum { drag_NONE, drag_MAIN, drag_COMPASS, drag_ELEV, drag_SCALE } m_LastDrag; |
---|
[b56df45] | 167 | bool m_MouseOutsideCompass; |
---|
| 168 | bool m_MouseOutsideElev; |
---|
[e8bc148] | 169 | bool m_UndergroundLegs; |
---|
| 170 | bool m_SurfaceLegs; |
---|
| 171 | bool m_Surface; |
---|
| 172 | bool m_SurfaceDashed; |
---|
| 173 | bool m_SurfaceDepth; |
---|
[fe444b8] | 174 | bool m_Entrances; |
---|
| 175 | bool m_FixedPts; |
---|
| 176 | bool m_ExportedPts; |
---|
[c1cf79d] | 177 | bool m_Grid; |
---|
[fa42426] | 178 | |
---|
| 179 | list<LabelInfo*> *m_PointGrid; |
---|
[dfe4454c] | 180 | bool m_HitTestGridValid; |
---|
[5809313] | 181 | |
---|
[ef07952] | 182 | Point m_here, m_there; |
---|
[156dc16] | 183 | |
---|
[a8e9fde] | 184 | wxStopWatch timer; |
---|
| 185 | long drawtime; |
---|
[b6de07d] | 186 | |
---|
| 187 | bool clipping; |
---|
[a8e9fde] | 188 | |
---|
[156dc16] | 189 | wxPen* m_Pens; |
---|
| 190 | wxBrush* m_Brushes; |
---|
| 191 | |
---|
| 192 | void SetColour(AvenColour col, bool background = false /* true => foreground */) { |
---|
[421b7d2] | 193 | assert(col >= (AvenColour) 0 && col < col_LAST); |
---|
[156dc16] | 194 | if (background) { |
---|
| 195 | assert(m_Brushes[col].Ok()); |
---|
[421b7d2] | 196 | m_DrawDC.SetBrush(m_Brushes[col]); |
---|
[156dc16] | 197 | } |
---|
| 198 | else { |
---|
| 199 | assert(m_Pens[col].Ok()); |
---|
| 200 | m_DrawDC.SetPen(m_Pens[col]); |
---|
| 201 | } |
---|
| 202 | } |
---|
| 203 | |
---|
[c6d95d8] | 204 | Double XToScreen(Double x, Double y, Double z) { |
---|
[421b7d2] | 205 | return Double(x * m_RotationMatrix.get(0, 0) + |
---|
[003d953] | 206 | y * m_RotationMatrix.get(0, 1) + |
---|
| 207 | z * m_RotationMatrix.get(0, 2)); |
---|
[5809313] | 208 | } |
---|
| 209 | |
---|
[c6d95d8] | 210 | Double YToScreen(Double x, Double y, Double z) { |
---|
[421b7d2] | 211 | return Double(x * m_RotationMatrix.get(1, 0) + |
---|
[003d953] | 212 | y * m_RotationMatrix.get(1, 1) + |
---|
| 213 | z * m_RotationMatrix.get(1, 2)); |
---|
[5809313] | 214 | } |
---|
| 215 | |
---|
[c6d95d8] | 216 | Double ZToScreen(Double x, Double y, Double z) { |
---|
[421b7d2] | 217 | return Double(x * m_RotationMatrix.get(2, 0) + |
---|
[003d953] | 218 | y * m_RotationMatrix.get(2, 1) + |
---|
| 219 | z * m_RotationMatrix.get(2, 2)); |
---|
[5809313] | 220 | } |
---|
[168df28] | 221 | |
---|
[c6d95d8] | 222 | Double GridXToScreen(Double x, Double y, Double z); |
---|
| 223 | Double GridYToScreen(Double x, Double y, Double z); |
---|
[2173dbd] | 224 | Double GridXToScreen(const Point &p) { |
---|
| 225 | return GridXToScreen(p.x, p.y, p.z); |
---|
| 226 | } |
---|
| 227 | Double GridYToScreen(const Point &p) { |
---|
| 228 | return GridYToScreen(p.x, p.y, p.z); |
---|
| 229 | } |
---|
[c1cf79d] | 230 | |
---|
[2effbf1] | 231 | void CheckHitTestGrid(wxPoint& point, bool centre); |
---|
| 232 | |
---|
[c300a04] | 233 | wxCoord GetClinoOffset(); |
---|
[c6d95d8] | 234 | wxPoint CompassPtToScreen(Double x, Double y, Double z); |
---|
[b56df45] | 235 | void DrawTick(wxCoord cx, wxCoord cy, int angle_cw); |
---|
[c6d95d8] | 236 | wxString FormatLength(Double, bool scalebar = true); |
---|
[5809313] | 237 | |
---|
[cd6ea75] | 238 | void SetScale(Double scale); |
---|
| 239 | void SetScaleInitial(Double scale); |
---|
[b6de07d] | 240 | void DrawBand(int num_polylines, const int *num_segs, const Point *vertices, |
---|
| 241 | Double m_00, Double m_01, Double m_02, |
---|
| 242 | Double m_20, Double m_21, Double m_22); |
---|
[5809313] | 243 | void RedrawOffscreen(); |
---|
| 244 | void TryToFreeArrays(); |
---|
| 245 | void FirstShow(); |
---|
[84cab34] | 246 | |
---|
| 247 | void DrawScalebar(); |
---|
| 248 | void DrawDepthbar(); |
---|
[6606406] | 249 | void Draw2dIndicators(); |
---|
[c1cf79d] | 250 | void DrawGrid(); |
---|
[6606406] | 251 | |
---|
| 252 | wxPoint IndicatorCompassToScreenPan(int angle); |
---|
| 253 | wxPoint IndicatorCompassToScreenElev(int angle); |
---|
[5809313] | 254 | |
---|
| 255 | void HandleScaleRotate(bool, wxPoint); |
---|
| 256 | void HandleTilt(wxPoint); |
---|
| 257 | void HandleTranslate(wxPoint); |
---|
| 258 | |
---|
[c8c116c] | 259 | void TranslateCave(int dx, int dy); |
---|
[c6d95d8] | 260 | void TiltCave(Double tilt_angle); |
---|
| 261 | void TurnCave(Double angle); |
---|
| 262 | void TurnCaveTo(Double angle); |
---|
[f626e1f] | 263 | |
---|
[84cab34] | 264 | void DrawNames(); |
---|
| 265 | void NattyDrawNames(); |
---|
| 266 | void SimpleDrawNames(); |
---|
| 267 | |
---|
[0e864da5] | 268 | void Repaint(); |
---|
| 269 | |
---|
[dfe4454c] | 270 | void CreateHitTestGrid(); |
---|
[421b7d2] | 271 | |
---|
[5809313] | 272 | public: |
---|
[7a89dc2] | 273 | bool m_Degrees; |
---|
| 274 | bool m_Metric; |
---|
| 275 | |
---|
[156dc16] | 276 | GfxCore(MainFrm* parent, wxWindow* parent_window); |
---|
[5809313] | 277 | ~GfxCore(); |
---|
| 278 | |
---|
| 279 | void Initialise(); |
---|
[137bf99] | 280 | void InitialiseOnNextResize() { m_InitialisePending = true; } |
---|
[8000d8f] | 281 | void InitialiseTerrain(); |
---|
[a87b1f7] | 282 | |
---|
[fa42426] | 283 | void ForceRefresh(); |
---|
[156dc16] | 284 | |
---|
[6009a17] | 285 | void RefreshLine(const Point &a, const Point &b, const Point &c); |
---|
[2173dbd] | 286 | |
---|
[fd6e0d5] | 287 | void SetHere(); |
---|
| 288 | void SetHere(Double x, Double y, Double z); |
---|
| 289 | void SetThere(); |
---|
| 290 | void SetThere(Double x, Double y, Double z); |
---|
| 291 | |
---|
[156dc16] | 292 | void CentreOn(Double x, Double y, Double z); |
---|
| 293 | |
---|
[4b1fc48] | 294 | void OnDefaults(); |
---|
| 295 | void OnPlan(); |
---|
| 296 | void OnElevation(); |
---|
| 297 | void OnDisplayOverlappingNames(); |
---|
| 298 | void OnShowCrosses(); |
---|
| 299 | void OnShowStationNames(); |
---|
| 300 | void OnShowSurveyLegs(); |
---|
| 301 | void OnShowSurface(); |
---|
| 302 | void OnShowSurfaceDepth(); |
---|
| 303 | void OnShowSurfaceDashed(); |
---|
| 304 | void OnMoveEast(); |
---|
| 305 | void OnMoveNorth(); |
---|
| 306 | void OnMoveSouth(); |
---|
| 307 | void OnMoveWest(); |
---|
| 308 | void OnStartRotation(); |
---|
| 309 | void OnToggleRotation(); |
---|
| 310 | void OnStopRotation(); |
---|
| 311 | void OnReverseControls(); |
---|
[ba2c5b1] | 312 | void OnSlowDown(bool accel = false); |
---|
| 313 | void OnSpeedUp(bool accel = false); |
---|
| 314 | void OnStepOnceAnticlockwise(bool accel = false); |
---|
| 315 | void OnStepOnceClockwise(bool accel = false); |
---|
| 316 | void OnHigherViewpoint(bool accel = false); |
---|
| 317 | void OnLowerViewpoint(bool accel = false); |
---|
| 318 | void OnShiftDisplayDown(bool accel = false); |
---|
| 319 | void OnShiftDisplayLeft(bool accel = false); |
---|
| 320 | void OnShiftDisplayRight(bool accel = false); |
---|
| 321 | void OnShiftDisplayUp(bool accel = false); |
---|
| 322 | void OnZoomIn(bool accel = false); |
---|
| 323 | void OnZoomOut(bool accel = false); |
---|
[4b1fc48] | 324 | void OnToggleScalebar(); |
---|
| 325 | void OnToggleDepthbar(); |
---|
| 326 | void OnViewCompass(); |
---|
| 327 | void OnViewClino(); |
---|
| 328 | void OnViewGrid(); |
---|
| 329 | void OnReverseDirectionOfRotation(); |
---|
| 330 | void OnShowEntrances(); |
---|
| 331 | void OnShowFixedPts(); |
---|
| 332 | void OnShowExportedPts(); |
---|
[6d109e5] | 333 | void OnCancelDistLine(); |
---|
[84cab34] | 334 | |
---|
[5809313] | 335 | void OnPaint(wxPaintEvent&); |
---|
[168df28] | 336 | void OnSize(wxSizeEvent& event); |
---|
| 337 | void OnIdle(wxIdleEvent& event); |
---|
| 338 | |
---|
[5809313] | 339 | void OnMouseMove(wxMouseEvent& event); |
---|
[168df28] | 340 | void OnLeaveWindow(wxMouseEvent& event); |
---|
| 341 | |
---|
[5809313] | 342 | void OnLButtonDown(wxMouseEvent& event); |
---|
| 343 | void OnLButtonUp(wxMouseEvent& event); |
---|
| 344 | void OnMButtonDown(wxMouseEvent& event); |
---|
| 345 | void OnMButtonUp(wxMouseEvent& event); |
---|
| 346 | void OnRButtonDown(wxMouseEvent& event); |
---|
| 347 | void OnRButtonUp(wxMouseEvent& event); |
---|
[582b10b] | 348 | bool Animate(); |
---|
[5809313] | 349 | |
---|
[4b1fc48] | 350 | void OnKeyPress(wxKeyEvent &e); |
---|
| 351 | |
---|
[84cab34] | 352 | void OnDisplayOverlappingNamesUpdate(wxUpdateUIEvent&); |
---|
| 353 | void OnShowCrossesUpdate(wxUpdateUIEvent&); |
---|
| 354 | void OnShowStationNamesUpdate(wxUpdateUIEvent&); |
---|
| 355 | void OnShowSurveyLegsUpdate(wxUpdateUIEvent&); |
---|
[e8bc148] | 356 | void OnShowSurfaceUpdate(wxUpdateUIEvent&); |
---|
| 357 | void OnShowSurfaceDepthUpdate(wxUpdateUIEvent&); |
---|
| 358 | void OnShowSurfaceDashedUpdate(wxUpdateUIEvent&); |
---|
[84cab34] | 359 | void OnMoveEastUpdate(wxUpdateUIEvent&); |
---|
| 360 | void OnMoveNorthUpdate(wxUpdateUIEvent&); |
---|
| 361 | void OnMoveSouthUpdate(wxUpdateUIEvent&); |
---|
| 362 | void OnMoveWestUpdate(wxUpdateUIEvent&); |
---|
| 363 | void OnStartRotationUpdate(wxUpdateUIEvent&); |
---|
[7ebc3d1] | 364 | void OnToggleRotationUpdate(wxUpdateUIEvent&); |
---|
[84cab34] | 365 | void OnStopRotationUpdate(wxUpdateUIEvent&); |
---|
| 366 | void OnReverseControlsUpdate(wxUpdateUIEvent&); |
---|
| 367 | void OnReverseDirectionOfRotationUpdate(wxUpdateUIEvent&); |
---|
| 368 | void OnSlowDownUpdate(wxUpdateUIEvent&); |
---|
| 369 | void OnSpeedUpUpdate(wxUpdateUIEvent&); |
---|
| 370 | void OnStepOnceAnticlockwiseUpdate(wxUpdateUIEvent&); |
---|
| 371 | void OnStepOnceClockwiseUpdate(wxUpdateUIEvent&); |
---|
| 372 | void OnDefaultsUpdate(wxUpdateUIEvent&); |
---|
| 373 | void OnElevationUpdate(wxUpdateUIEvent&); |
---|
| 374 | void OnHigherViewpointUpdate(wxUpdateUIEvent&); |
---|
| 375 | void OnLowerViewpointUpdate(wxUpdateUIEvent&); |
---|
| 376 | void OnPlanUpdate(wxUpdateUIEvent&); |
---|
| 377 | void OnShiftDisplayDownUpdate(wxUpdateUIEvent&); |
---|
| 378 | void OnShiftDisplayLeftUpdate(wxUpdateUIEvent&); |
---|
| 379 | void OnShiftDisplayRightUpdate(wxUpdateUIEvent&); |
---|
| 380 | void OnShiftDisplayUpUpdate(wxUpdateUIEvent&); |
---|
| 381 | void OnZoomInUpdate(wxUpdateUIEvent&); |
---|
| 382 | void OnZoomOutUpdate(wxUpdateUIEvent&); |
---|
| 383 | void OnToggleScalebarUpdate(wxUpdateUIEvent&); |
---|
| 384 | void OnToggleDepthbarUpdate(wxUpdateUIEvent&); |
---|
| 385 | void OnViewCompassUpdate(wxUpdateUIEvent&); |
---|
[c300a04] | 386 | void OnViewClinoUpdate(wxUpdateUIEvent&); |
---|
[c1cf79d] | 387 | void OnViewGridUpdate(wxUpdateUIEvent&); |
---|
[fe444b8] | 388 | void OnShowEntrancesUpdate(wxUpdateUIEvent&); |
---|
| 389 | void OnShowExportedPtsUpdate(wxUpdateUIEvent&); |
---|
| 390 | void OnShowFixedPtsUpdate(wxUpdateUIEvent&); |
---|
[e3513a5] | 391 | void OnIndicatorsUpdate(wxUpdateUIEvent&); |
---|
[1fd2edb] | 392 | void OnCancelDistLineUpdate(wxUpdateUIEvent&); |
---|
[84cab34] | 393 | |
---|
[7a89dc2] | 394 | void OnToggleMetric(); |
---|
| 395 | void OnToggleMetricUpdate(wxUpdateUIEvent& cmd); |
---|
| 396 | |
---|
| 397 | void OnToggleDegrees(); |
---|
| 398 | void OnToggleDegreesUpdate(wxUpdateUIEvent& cmd); |
---|
[5757725] | 399 | |
---|
[09d50cc] | 400 | void OnPrint(const wxString &filename, const wxString &title, |
---|
| 401 | const wxString &datestamp); |
---|
[301bbc1] | 402 | bool OnExport(const wxString &filename, const wxString &title); |
---|
[09d50cc] | 403 | |
---|
[5809313] | 404 | private: |
---|
[84cab34] | 405 | DECLARE_EVENT_TABLE() |
---|
[5809313] | 406 | }; |
---|
| 407 | |
---|
| 408 | #endif |
---|