Renderer, ...: use PixelRect::GetCenter()
[xcsoar.git] / src / MapWindow / GlueMapWindowOverlays.cpp
blobd9665c36a463a2f6c646676f57ca3e7e2b1e6fac
1 /*
2 Copyright_License {
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"
49 #include <stdio.h>
51 void
52 GlueMapWindow::DrawGesture(Canvas &canvas) const
54 if (!gestures.HasPoints())
55 return;
57 const TCHAR *gesture = gestures.GetGesture();
58 if (gesture != NULL && !InputEvents::IsGesture(gesture))
59 canvas.Select(gesture_look.invalid_pen);
60 else
61 canvas.Select(gesture_look.pen);
63 canvas.SelectHollowBrush();
65 const auto &points = gestures.GetPoints();
66 auto it = points.begin();
67 auto it_last = it++;
68 for (auto it_end = points.end(); it != it_end; it_last = it++)
69 canvas.DrawLinePiece(*it_last, *it);
72 void
73 GlueMapWindow::DrawCrossHairs(Canvas &canvas) const
75 if (!render_projection.IsValid())
76 return;
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);
89 void
90 GlueMapWindow::DrawPanInfo(Canvas &canvas) const
92 if (!render_projection.IsValid())
93 return;
95 GeoPoint location = render_projection.GetGeoLocation();
97 TextInBoxMode mode;
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;
108 if (compass_visible)
109 /* don't obscure the north arrow */
110 /* TODO: obtain offset from CompassRenderer */
111 y += Layout::Scale(19) + Layout::FastScale(13);
113 if (terrain) {
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());
127 y += height;
131 TCHAR buffer[256];
132 FormatGeoPoint(location, buffer, ARRAY_SIZE(buffer), _T('\n'));
134 TCHAR *start = buffer;
135 while (true) {
136 TCHAR *newline = _tcschr(start, _T('\n'));
137 if (newline != NULL)
138 *newline = _T('\0');
140 TextInBox(canvas, start, x, y, mode,
141 render_projection.GetScreenWidth(),
142 render_projection.GetScreenHeight());
144 y += height;
146 if (newline == NULL)
147 break;
149 start = newline + 1;
153 void
154 GlueMapWindow::DrawGPSStatus(Canvas &canvas, const PixelRect &rc,
155 const NMEAInfo &info) const
157 const TCHAR *txt;
158 const MaskedIcon *icon;
160 if (!info.alive) {
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");
166 } else
167 // early exit
168 return;
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);
177 TextInBoxMode mode;
178 mode.shape = LabelShape::ROUNDED_BLACK;
180 canvas.Select(Fonts::map_bold);
181 TextInBox(canvas, txt, x, y, mode, rc, NULL);
184 void
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);
197 // draw flight mode
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;
206 else
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));
214 // draw flarm status
215 if (!GetMapSettings().show_flarm_alarm_level)
216 // Don't show indicator when the gauge is indicating the traffic anyway
217 return;
219 const FlarmStatus &flarm = Basic().flarm.status;
220 if (!flarm.available)
221 return;
223 switch (flarm.alarm_level) {
224 case FlarmTraffic::AlarmType::NONE:
225 bmp = &look.traffic_safe_icon;
226 break;
227 case FlarmTraffic::AlarmType::LOW:
228 case FlarmTraffic::AlarmType::INFO_ALERT:
229 bmp = &look.traffic_warning_icon;
230 break;
231 case FlarmTraffic::AlarmType::IMPORTANT:
232 case FlarmTraffic::AlarmType::URGENT:
233 bmp = &look.traffic_alarm_icon;
234 break;
237 offset += bmp->GetSize().cx + Layout::Scale(6);
239 bmp->Draw(canvas, rc.right - offset,
240 rc.bottom - bmp->GetSize().cy - Layout::Scale(2));
243 void
244 GlueMapWindow::DrawFinalGlide(Canvas &canvas, const PixelRect &rc) const
247 if (GetMapSettings().final_glide_bar_display_mode==FinalGlideBarDisplayMode::OFF)
248 return;
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())
258 return;
260 if (solution_mc0.SelectAltitudeDifference(glide_settings) < fixed(-1000) &&
261 solution.SelectAltitudeDifference(glide_settings) < fixed(-1000))
262 return;
265 final_glide_bar_renderer.Draw(canvas, rc, Calculated(),
266 GetComputerSettings().task.glide,
267 GetMapSettings().final_glide_bar_mc0_enabled);
270 void
271 GlueMapWindow::DrawVario(Canvas &canvas, const PixelRect &rc) const
273 if (!GetMapSettings().vario_bar_enabled)
274 return;
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 ;
281 void
282 GlueMapWindow::DrawMapScale(Canvas &canvas, const PixelRect &rc,
283 const MapWindowProjection &projection) const
285 if (!projection.IsValid())
286 return;
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();
300 PixelScalar x = 0;
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);
310 x += text_padding_x;
311 canvas.DrawText(x,
312 rc.bottom - Fonts::map_bold.GetAscentHeight() - Layout::Scale(1),
313 buffer);
315 x += text_padding_x + text_size.cx;
316 look.map_scale_right_icon.Draw(canvas, x, rc.bottom - height);
318 buffer.clear();
319 if (GetMapSettings().auto_zoom_enabled)
320 buffer = _T("AUTO ");
322 switch (follow_mode) {
323 case FOLLOW_SELF:
324 break;
326 case FOLLOW_PAN:
327 buffer += _T("PAN ");
328 break;
331 const UIState &ui_state = GetUIState();
332 if (ui_state.auxiliary_enabled) {
333 buffer += ui_state.panel_name;
334 buffer += _T(" ");
337 if (Basic().gps.replay)
338 buffer += _T("REPLAY ");
339 else if (Basic().gps.simulator) {
340 buffer += _("Simulator");
341 buffer += _T(" ");
344 if (GetComputerSettings().polar.ballast_timer_active)
345 buffer.AppendFormat(
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());
351 if (label != NULL)
352 buffer += label;
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);
365 void
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) {
373 RasterPoint sc;
374 if (projection.GeoToScreenIfVisible(thermal_locator.estimate_location, sc)) {
375 look.thermal_source_icon.Draw(canvas, sc);
378 } else {
379 MapWindow::DrawThermalEstimate(canvas);
383 void
384 GlueMapWindow::RenderTrail(Canvas &canvas, const RasterPoint aircraft_pos)
386 unsigned min_time;
387 switch(GetMapSettings().trail.length) {
388 case TrailSettings::Length::OFF:
389 return;
390 case TrailSettings::Length::LONG:
391 min_time = std::max(0, (int)Basic().time - 3600);
392 break;
393 case TrailSettings::Length::SHORT:
394 min_time = std::max(0, (int)Basic().time - 600);
395 break;
396 case TrailSettings::Length::FULL:
397 min_time = 0; // full
398 break;
401 DrawTrail(canvas, aircraft_pos, min_time,
402 GetMapSettings().trail.wind_drift_enabled && InCirclingMode());
405 void
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)
411 return;
413 PixelRect tb_rect;
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;
420 if (task != NULL) {
421 ProtectedTaskManager::Lease task_manager(*task);
422 renderer.DrawThermalBand(Basic(),
423 Calculated(),
424 GetComputerSettings(),
425 canvas,
426 tb_rect,
427 GetComputerSettings().task,
428 true,
429 &task_manager->GetOrderedTask().GetOrderedTaskBehaviour());
430 } else {
431 renderer.DrawThermalBand(Basic(),
432 Calculated(),
433 GetComputerSettings(),
434 canvas,
435 tb_rect,
436 GetComputerSettings().task,
437 true);
441 void
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);