4 XCSoar Glide Computer - http://www.xcsoar.org/
5 Copyright (C) 2000-2013 The XCSoar Project
6 A detailed list of copyright holders can be found in the file "AUTHORS".
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "GlueMapWindow.hpp"
25 #include "Look/MapLook.hpp"
26 #include "Look/TaskLook.hpp"
27 #include "Look/Fonts.hpp"
28 #include "Screen/Icon.hpp"
29 #include "Language/Language.hpp"
30 #include "Screen/Layout.hpp"
31 #include "Logger/Logger.hpp"
32 #include "Task/ProtectedTaskManager.hpp"
33 #include "Engine/Task/Ordered/OrderedTask.hpp"
34 #include "Renderer/TextInBox.hpp"
35 #include "Screen/UnitSymbol.hpp"
36 #include "Terrain/RasterWeather.hpp"
37 #include "Formatter/UserUnits.hpp"
38 #include "Formatter/UserGeoPointFormatter.hpp"
39 #include "InfoBoxes/InfoBoxManager.hpp"
40 #include "UIState.hpp"
41 #include "Renderer/FinalGlideBarRenderer.hpp"
42 #include "Units/Units.hpp"
43 #include "Terrain/RasterTerrain.hpp"
44 #include "Util/Macros.hpp"
45 #include "Util/Clamp.hpp"
46 #include "Look/GestureLook.hpp"
47 #include "Input/InputEvents.hpp"
52 GlueMapWindow::DrawGesture(Canvas
&canvas
) const
54 if (!gestures
.HasPoints())
57 const TCHAR
*gesture
= gestures
.GetGesture();
58 if (gesture
!= NULL
&& !InputEvents::IsGesture(gesture
))
59 canvas
.Select(gesture_look
.invalid_pen
);
61 canvas
.Select(gesture_look
.pen
);
63 canvas
.SelectHollowBrush();
65 const auto &points
= gestures
.GetPoints();
66 auto it
= points
.begin();
68 for (auto it_end
= points
.end(); it
!= it_end
; it_last
= it
++)
69 canvas
.DrawLinePiece(*it_last
, *it
);
73 GlueMapWindow::DrawCrossHairs(Canvas
&canvas
) const
75 if (!render_projection
.IsValid())
78 Pen
dash_pen(Pen::DASH
, 1, COLOR_DARK_GRAY
);
79 canvas
.Select(dash_pen
);
81 const RasterPoint center
= render_projection
.GetScreenOrigin();
83 canvas
.DrawLine(center
.x
+ 20, center
.y
,
84 center
.x
- 20, center
.y
);
85 canvas
.DrawLine(center
.x
, center
.y
+ 20,
86 center
.x
, center
.y
- 20);
90 GlueMapWindow::DrawPanInfo(Canvas
&canvas
) const
92 if (!render_projection
.IsValid())
95 GeoPoint location
= render_projection
.GetGeoLocation();
98 mode
.shape
= LabelShape::OUTLINED_INVERTED
;
99 mode
.align
= TextInBoxMode::Alignment::RIGHT
;
101 canvas
.Select(Fonts::map_bold
);
103 UPixelScalar padding
= Layout::FastScale(4);
104 UPixelScalar height
= Fonts::map_bold
.GetHeight();
105 PixelScalar y
= 0 + padding
;
106 PixelScalar x
= render_projection
.GetScreenWidth() - padding
;
109 /* don't obscure the north arrow */
110 /* TODO: obtain offset from CompassRenderer */
111 y
+= Layout::Scale(19) + Layout::FastScale(13);
114 short elevation
= terrain
->GetTerrainHeight(location
);
115 if (!RasterBuffer::IsSpecial(elevation
)) {
116 StaticString
<64> elevation_short
, elevation_long
;
117 FormatUserAltitude(fixed(elevation
),
118 elevation_short
.buffer(), elevation_short
.MAX_SIZE
);
120 elevation_long
= _("Elevation: ");
121 elevation_long
+= elevation_short
;
123 TextInBox(canvas
, elevation_long
, x
, y
, mode
,
124 render_projection
.GetScreenWidth(),
125 render_projection
.GetScreenHeight());
132 FormatGeoPoint(location
, buffer
, ARRAY_SIZE(buffer
), _T('\n'));
134 TCHAR
*start
= buffer
;
136 TCHAR
*newline
= _tcschr(start
, _T('\n'));
140 TextInBox(canvas
, start
, x
, y
, mode
,
141 render_projection
.GetScreenWidth(),
142 render_projection
.GetScreenHeight());
154 GlueMapWindow::DrawGPSStatus(Canvas
&canvas
, const PixelRect
&rc
,
155 const NMEAInfo
&info
) const
158 const MaskedIcon
*icon
;
161 icon
= &look
.no_gps_icon
;
162 txt
= _("GPS not connected");
163 } else if (!info
.location_available
) {
164 icon
= &look
.waiting_for_fix_icon
;
165 txt
= _("GPS waiting for fix");
170 PixelScalar x
= rc
.left
+ Layout::FastScale(2);
171 PixelScalar y
= rc
.bottom
- Layout::FastScale(35);
172 icon
->Draw(canvas
, x
, y
);
174 x
+= icon
->GetSize().cx
+ Layout::FastScale(4);
175 y
= rc
.bottom
- Layout::FastScale(34);
178 mode
.shape
= LabelShape::ROUNDED_BLACK
;
180 canvas
.Select(Fonts::map_bold
);
181 TextInBox(canvas
, txt
, x
, y
, mode
, rc
, NULL
);
185 GlueMapWindow::DrawFlightMode(Canvas
&canvas
, const PixelRect
&rc
) const
187 PixelScalar offset
= 0;
189 // draw logger status
190 if (logger
!= NULL
&& logger
->IsLoggerActive()) {
191 bool flip
= (Basic().date_time_utc
.second
% 2) == 0;
192 const MaskedIcon
&icon
= flip
? look
.logger_on_icon
: look
.logger_off_icon
;
193 offset
= icon
.GetSize().cx
;
194 icon
.Draw(canvas
, rc
.right
- offset
, rc
.bottom
- icon
.GetSize().cy
);
198 const MaskedIcon
*bmp
;
200 if (Calculated().common_stats
.task_type
== TaskType::ABORT
)
201 bmp
= &look
.abort_mode_icon
;
202 else if (GetDisplayMode() == DisplayMode::CIRCLING
)
203 bmp
= &look
.climb_mode_icon
;
204 else if (GetDisplayMode() == DisplayMode::FINAL_GLIDE
)
205 bmp
= &look
.final_glide_mode_icon
;
207 bmp
= &look
.cruise_mode_icon
;
209 offset
+= bmp
->GetSize().cx
+ Layout::Scale(6);
211 bmp
->Draw(canvas
, rc
.right
- offset
,
212 rc
.bottom
- bmp
->GetSize().cy
- Layout::Scale(4));
215 if (!GetMapSettings().show_flarm_alarm_level
)
216 // Don't show indicator when the gauge is indicating the traffic anyway
219 const FlarmStatus
&flarm
= Basic().flarm
.status
;
220 if (!flarm
.available
)
223 switch (flarm
.alarm_level
) {
224 case FlarmTraffic::AlarmType::NONE
:
225 bmp
= &look
.traffic_safe_icon
;
227 case FlarmTraffic::AlarmType::LOW
:
228 case FlarmTraffic::AlarmType::INFO_ALERT
:
229 bmp
= &look
.traffic_warning_icon
;
231 case FlarmTraffic::AlarmType::IMPORTANT
:
232 case FlarmTraffic::AlarmType::URGENT
:
233 bmp
= &look
.traffic_alarm_icon
;
237 offset
+= bmp
->GetSize().cx
+ Layout::Scale(6);
239 bmp
->Draw(canvas
, rc
.right
- offset
,
240 rc
.bottom
- bmp
->GetSize().cy
- Layout::Scale(2));
244 GlueMapWindow::DrawFinalGlide(Canvas
&canvas
, const PixelRect
&rc
) const
247 if (GetMapSettings().final_glide_bar_display_mode
==FinalGlideBarDisplayMode::OFF
)
250 if (GetMapSettings().final_glide_bar_display_mode
==FinalGlideBarDisplayMode::AUTO
) {
251 const TaskStats
&task_stats
= Calculated().task_stats
;
252 const ElementStat
&total
= task_stats
.total
;
253 const GlideResult
&solution
= total
.solution_remaining
;
254 const GlideResult
&solution_mc0
= total
.solution_mc0
;
255 const GlideSettings
&glide_settings
= GetComputerSettings().task
.glide
;
257 if (!task_stats
.task_valid
|| !solution
.IsOk() || !solution_mc0
.IsDefined())
260 if (solution_mc0
.SelectAltitudeDifference(glide_settings
) < fixed(-1000) &&
261 solution
.SelectAltitudeDifference(glide_settings
) < fixed(-1000))
265 final_glide_bar_renderer
.Draw(canvas
, rc
, Calculated(),
266 GetComputerSettings().task
.glide
,
267 GetMapSettings().final_glide_bar_mc0_enabled
);
271 GlueMapWindow::DrawVario(Canvas
&canvas
, const PixelRect
&rc
) const
273 if (!GetMapSettings().vario_bar_enabled
)
276 vario_bar_renderer
.Draw(canvas
, rc
, Basic(), Calculated(),
277 GetComputerSettings().polar
.glide_polar_task
,
278 true); //NOTE: AVG enabled for now, make it configurable ;
282 GlueMapWindow::DrawMapScale(Canvas
&canvas
, const PixelRect
&rc
,
283 const MapWindowProjection
&projection
) const
285 if (!projection
.IsValid())
288 StaticString
<80> buffer
;
290 fixed map_width
= projection
.GetScreenWidthMeters();
292 canvas
.Select(Fonts::map_bold
);
293 FormatUserMapScale(map_width
, buffer
.buffer(), true);
294 PixelSize text_size
= canvas
.CalcTextSize(buffer
);
296 const PixelScalar text_padding_x
= Layout::GetTextPadding();
297 const PixelScalar height
= Fonts::map_bold
.GetCapitalHeight()
298 + Layout::GetTextPadding();
301 look
.map_scale_left_icon
.Draw(canvas
, 0, rc
.bottom
- height
);
303 x
+= look
.map_scale_left_icon
.GetSize().cx
;
304 canvas
.DrawFilledRectangle(x
, rc
.bottom
- height
,
305 x
+ 2 * text_padding_x
+ text_size
.cx
,
306 rc
.bottom
, COLOR_WHITE
);
308 canvas
.SetBackgroundTransparent();
309 canvas
.SetTextColor(COLOR_BLACK
);
312 rc
.bottom
- Fonts::map_bold
.GetAscentHeight() - Layout::Scale(1),
315 x
+= text_padding_x
+ text_size
.cx
;
316 look
.map_scale_right_icon
.Draw(canvas
, x
, rc
.bottom
- height
);
319 if (GetMapSettings().auto_zoom_enabled
)
320 buffer
= _T("AUTO ");
322 switch (follow_mode
) {
327 buffer
+= _T("PAN ");
331 const UIState
&ui_state
= GetUIState();
332 if (ui_state
.auxiliary_enabled
) {
333 buffer
+= ui_state
.panel_name
;
337 if (Basic().gps
.replay
)
338 buffer
+= _T("REPLAY ");
339 else if (Basic().gps
.simulator
) {
340 buffer
+= _("Simulator");
344 if (GetComputerSettings().polar
.ballast_timer_active
)
346 _T("BALLAST %d LITERS "),
347 (int)GetComputerSettings().polar
.glide_polar_task
.GetBallastLitres());
349 if (weather
!= NULL
&& weather
->GetParameter() > 0) {
350 const TCHAR
*label
= weather
->ItemLabel(weather
->GetParameter());
355 if (!buffer
.empty()) {
356 int y
= rc
.bottom
- height
;
358 canvas
.SetBackgroundOpaque();
359 canvas
.SetBackgroundColor(COLOR_WHITE
);
361 canvas
.DrawText(0, y
- canvas
.CalcTextSize(buffer
).cy
, buffer
);
366 GlueMapWindow::DrawThermalEstimate(Canvas
&canvas
) const
368 if (InCirclingMode() && IsNearSelf()) {
369 // in circling mode, draw thermal at actual estimated location
370 const MapWindowProjection
&projection
= render_projection
;
371 const ThermalLocatorInfo
&thermal_locator
= Calculated().thermal_locator
;
372 if (thermal_locator
.estimate_valid
) {
374 if (projection
.GeoToScreenIfVisible(thermal_locator
.estimate_location
, sc
)) {
375 look
.thermal_source_icon
.Draw(canvas
, sc
);
379 MapWindow::DrawThermalEstimate(canvas
);
384 GlueMapWindow::RenderTrail(Canvas
&canvas
, const RasterPoint aircraft_pos
)
387 switch(GetMapSettings().trail
.length
) {
388 case TrailSettings::Length::OFF
:
390 case TrailSettings::Length::LONG
:
391 min_time
= std::max(0, (int)Basic().time
- 3600);
393 case TrailSettings::Length::SHORT
:
394 min_time
= std::max(0, (int)Basic().time
- 600);
396 case TrailSettings::Length::FULL
:
397 min_time
= 0; // full
401 DrawTrail(canvas
, aircraft_pos
, min_time
,
402 GetMapSettings().trail
.wind_drift_enabled
&& InCirclingMode());
406 GlueMapWindow::DrawThermalBand(Canvas
&canvas
, const PixelRect
&rc
) const
408 if (Calculated().task_stats
.total
.solution_remaining
.IsOk() &&
409 Calculated().task_stats
.total
.solution_remaining
.altitude_difference
> fixed(50)
410 && GetDisplayMode() == DisplayMode::FINAL_GLIDE
)
414 tb_rect
.left
= rc
.left
;
415 tb_rect
.right
= rc
.left
+Layout::Scale(20);
416 tb_rect
.top
= Layout::Scale(2);
417 tb_rect
.bottom
= (rc
.bottom
-rc
.top
)/5 - Layout::Scale(2);
419 const ThermalBandRenderer
&renderer
= thermal_band_renderer
;
421 ProtectedTaskManager::Lease
task_manager(*task
);
422 renderer
.DrawThermalBand(Basic(),
424 GetComputerSettings(),
427 GetComputerSettings().task
,
429 &task_manager
->GetOrderedTask().GetOrderedTaskBehaviour());
431 renderer
.DrawThermalBand(Basic(),
433 GetComputerSettings(),
436 GetComputerSettings().task
,
442 GlueMapWindow::DrawStallRatio(Canvas
&canvas
, const PixelRect
&rc
) const
444 if (Basic().stall_ratio_available
) {
445 // JMW experimental, display stall sensor
446 fixed s
= Clamp(Basic().stall_ratio
, fixed(0), fixed(1));
447 PixelScalar
m((rc
.bottom
- rc
.top
) * s
* s
);
449 canvas
.SelectBlackPen();
450 canvas
.DrawLine(rc
.right
- 1, rc
.bottom
- m
, rc
.right
- 11, rc
.bottom
- m
);