po: update French translation
[xcsoar.git] / src / MainWindow.cpp
blob3dcc6ee7d6033f17fe23b4ac3cb27cf6b3528c69
1 /*
2 Copyright_License {
4 XCSoar Glide Computer - http://www.xcsoar.org/
5 Copyright (C) 2000-2012 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 "MainWindow.hpp"
25 #include "MapWindow/GlueMapWindow.hpp"
26 #include "resource.h"
27 #include "Protection.hpp"
28 #include "InfoBoxes/InfoBoxManager.hpp"
29 #include "InfoBoxes/InfoBoxLayout.hpp"
30 #include "Interface.hpp"
31 #include "Input/InputEvents.hpp"
32 #include "Menu/ButtonLabel.hpp"
33 #include "Screen/Layout.hpp"
34 #include "Screen/Blank.hpp"
35 #include "Dialogs/AirspaceWarningDialog.hpp"
36 #include "Audio/Sound.hpp"
37 #include "Components.hpp"
38 #include "ProcessTimer.hpp"
39 #include "LogFile.hpp"
40 #include "Screen/Fonts.hpp"
41 #include "Gauge/GaugeFLARM.hpp"
42 #include "Gauge/GaugeThermalAssistant.hpp"
43 #include "Gauge/GlueGaugeVario.hpp"
44 #include "Menu/MenuBar.hpp"
45 #include "Form/Form.hpp"
46 #include "Form/Widget.hpp"
47 #include "UtilsSystem.hpp"
48 #include "Look/Look.hpp"
49 #include "Profile/ProfileKeys.hpp"
50 #include "Profile/Profile.hpp"
51 #include "ProgressGlue.hpp"
52 #include "UIState.hpp"
54 #if !defined(WIN32) && !defined(ANDROID)
55 #include <unistd.h>
56 #endif
58 #if !defined(WIN32) && !defined(ANDROID)
59 #include <unistd.h> /* for execl() */
60 #endif
62 MainWindow::MainWindow(const StatusMessageList &status_messages)
63 :look(NULL),
64 map(NULL), widget(NULL), vario(*this),
65 traffic_gauge(*this),
66 suppress_traffic_gauge(false), force_traffic_gauge(false),
67 thermal_assistant(*this),
68 popup(status_messages, *this, CommonInterface::GetUISettings()),
69 timer(*this),
70 FullScreen(false),
71 airspace_warning_pending(false)
75 /**
76 * Destructor of the MainWindow-Class
77 * @return
79 MainWindow::~MainWindow()
81 reset();
84 #ifdef USE_GDI
86 bool
87 MainWindow::register_class(HINSTANCE hInstance)
89 WNDCLASS wc;
91 wc.style = CS_HREDRAW | CS_VREDRAW;
92 wc.lpfnWndProc = Window::WndProc;
93 wc.cbClsExtra = 0;
94 wc.cbWndExtra = 0;
95 wc.hInstance = hInstance;
96 wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_XCSOAR));
97 wc.hCursor = 0;
98 wc.hbrBackground = NULL;
99 wc.lpszMenuName = 0;
100 wc.lpszClassName = _T("XCSoarMain");
102 return (RegisterClass(&wc)!= FALSE);
105 #endif /* USE_GDI */
107 void
108 MainWindow::Set(const TCHAR* text, PixelRect rc, TopWindowStyle style)
110 SingleWindow::set(_T("XCSoarMain"), text, rc, style);
113 gcc_noreturn
114 static void
115 NoFontsAvailable()
117 const TCHAR *msg = _T("Font initialisation failed");
119 /* log the error */
120 LogStartUp(_T("%s"), msg);
122 /* now try to get a GUI error message out to the user */
123 #ifdef WIN32
124 MessageBox(NULL, msg, _T("XCSoar"), MB_ICONEXCLAMATION|MB_OK);
125 #elif !defined(ANDROID)
126 execl("/usr/bin/xmessage", "xmessage", msg, NULL);
127 execl("/usr/X11/bin/xmessage", "xmessage", msg, NULL);
128 #endif
129 exit(EXIT_FAILURE);
132 void
133 MainWindow::Initialise()
135 PixelRect rc = get_client_rect();
137 Layout::Initialize(rc.right - rc.left, rc.bottom - rc.top);
139 LogStartUp(_T("Initialise fonts"));
140 if (!Fonts::Initialize()) {
141 reset();
142 NoFontsAvailable();
145 if (look == NULL)
146 look = new Look();
148 look->Initialise();
151 void
152 MainWindow::InitialiseConfigured()
154 const UISettings &ui_settings = CommonInterface::GetUISettings();
156 PixelRect rc = get_client_rect();
158 LogStartUp(_T("InfoBox geometry"));
159 const InfoBoxLayout::Layout ib_layout =
160 InfoBoxLayout::Calculate(rc, ui_settings.info_boxes.geometry);
162 Fonts::SizeInfoboxFont(ib_layout.control_width);
164 if (ui_settings.custom_fonts) {
165 LogStartUp(_T("Load custom fonts"));
166 if (!Fonts::LoadCustom()) {
167 LogStartUp(_T("Failed to load custom fonts"));
168 if (!Fonts::Initialize()) {
169 reset();
170 NoFontsAvailable();
175 assert(look != NULL);
176 look->InitialiseConfigured(CommonInterface::GetUISettings());
178 LogStartUp(_T("Create info boxes"));
179 InfoBoxManager::Create(rc, ib_layout, look->info_box, look->units);
180 map_rect = ib_layout.remaining;
182 LogStartUp(_T("Create button labels"));
183 ButtonLabel::CreateButtonLabels(*this);
184 ButtonLabel::SetFont(Fonts::map_bold);
186 ReinitialiseLayout_vario(ib_layout);
188 ReinitialiseLayoutTA(rc, ib_layout);
190 WindowStyle hidden_border;
191 hidden_border.Hide();
192 hidden_border.Border();
194 ReinitialiseLayout_flarm(rc, ib_layout);
196 map = new GlueMapWindow(*look);
197 map->SetComputerSettings(CommonInterface::GetComputerSettings());
198 map->SetMapSettings(CommonInterface::GetMapSettings());
199 map->set(*this, map_rect);
200 map->set_font(Fonts::map);
202 LogStartUp(_T("Initialise message system"));
203 popup.set(rc);
206 void
207 MainWindow::Deinitialise()
209 InfoBoxManager::Destroy();
210 ButtonLabel::Destroy();
212 popup.reset();
214 // During destruction of GlueMapWindow WM_SETFOCUS gets called for
215 // MainWindow which tries to set the focus to GlueMapWindow. Prevent
216 // this issue by setting map to NULL before calling delete.
217 GlueMapWindow *temp_map = map;
218 map = NULL;
219 delete temp_map;
221 vario.Clear();
222 traffic_gauge.Clear();
223 thermal_assistant.Clear();
225 delete look;
226 look = NULL;
229 void
230 MainWindow::ReinitialiseLayout_vario(const InfoBoxLayout::Layout &layout)
232 if (!layout.HasVario()) {
233 vario.Clear();
234 return;
237 if (!vario.IsDefined())
238 vario.Set(new GlueGaugeVario(CommonInterface::GetLiveBlackboard(),
239 look->vario, look->units));
241 vario.Move(layout.vario);
242 vario.Show();
244 // XXX vario->bring_to_top();
247 void
248 MainWindow::ReinitialiseLayoutTA(PixelRect rc,
249 const InfoBoxLayout::Layout &layout)
251 UPixelScalar sz = std::min(layout.control_height,
252 layout.control_width) * 2;
253 rc.right = rc.left + sz;
254 rc.top = rc.bottom - sz;
255 thermal_assistant.Move(rc);
258 void
259 MainWindow::ReinitialiseLayout()
261 if (map == NULL) {
262 #ifdef ANDROID
263 if (HasDialog())
264 dialogs.top()->ReinitialiseLayout(); // adapt simulator prompt
265 #endif
266 /* without the MapWindow, it is safe to assume that the MainWindow
267 is just being initialized, and the InfoBoxes aren't initialized
268 yet either, so there is nothing to do here */
269 return;
272 #ifndef ENABLE_OPENGL
273 if (draw_thread == NULL)
274 /* no layout changes during startup */
275 return;
276 #endif
278 InfoBoxManager::Destroy();
280 const UISettings &ui_settings = CommonInterface::GetUISettings();
282 PixelRect rc = get_client_rect();
283 const InfoBoxLayout::Layout ib_layout =
284 InfoBoxLayout::Calculate(rc, ui_settings.info_boxes.geometry);
286 Fonts::SizeInfoboxFont(ib_layout.control_width);
288 InfoBoxManager::Create(rc, ib_layout, look->info_box, look->units);
289 InfoBoxManager::ProcessTimer();
290 map_rect = ib_layout.remaining;
292 popup.reset();
293 popup.set(rc);
295 ReinitialiseLayout_vario(ib_layout);
297 ReinitialiseLayout_flarm(rc, ib_layout);
299 ReinitialiseLayoutTA(rc, ib_layout);
301 if (map != NULL) {
302 if (FullScreen)
303 InfoBoxManager::Hide();
304 else
305 InfoBoxManager::Show();
307 const PixelRect &current_map = FullScreen ? rc : map_rect;
308 map->move(current_map.left, current_map.top,
309 current_map.right - current_map.left,
310 current_map.bottom - current_map.top);
311 map->FullRedraw();
314 if (widget != NULL) {
315 const PixelRect &current_map = FullScreen ? rc : map_rect;
316 widget->Move(current_map);
319 #ifdef ANDROID
320 // move topmost dialog to fit into the current layout, or close it
321 if (HasDialog())
322 dialogs.top()->ReinitialiseLayout();
323 #endif
325 if (map != NULL)
326 map->BringToBottom();
329 void
330 MainWindow::ReinitialiseLayout_flarm(PixelRect rc, const InfoBoxLayout::Layout ib_layout)
332 TrafficSettings::GaugeLocation val =
333 CommonInterface::GetUISettings().traffic.gauge_location;
335 // Automatic mode - follow info boxes
336 if (val == TrafficSettings::GaugeLocation::Auto) {
337 switch (InfoBoxManager::layout.geometry) {
338 case InfoBoxSettings::Geometry::TOP_8:
339 val = TrafficSettings::GaugeLocation::TopRight;
340 break;
341 case InfoBoxSettings::Geometry::LEFT_8:
342 val = TrafficSettings::GaugeLocation::BottomLeft;
343 break;
344 case InfoBoxSettings::Geometry::TOP_12:
345 val = TrafficSettings::GaugeLocation::TopLeft;
346 break;
347 default:
348 val = TrafficSettings::GaugeLocation::BottomRight; // Assume bottom right unles...
349 break;
353 switch (val) {
354 case TrafficSettings::GaugeLocation::TopLeft:
355 rc.right = rc.left + ib_layout.control_width * 2;
356 ++rc.left;
357 rc.bottom = rc.top + ib_layout.control_height * 2;
358 ++rc.top;
359 break;
361 case TrafficSettings::GaugeLocation::TopRight:
362 rc.left = rc.right - ib_layout.control_width * 2 + 1;
363 rc.bottom = rc.top + ib_layout.control_height * 2;
364 ++rc.top;
365 break;
367 case TrafficSettings::GaugeLocation::BottomLeft:
368 rc.right = rc.left + ib_layout.control_width * 2;
369 ++rc.left;
370 rc.top = rc.bottom - ib_layout.control_height * 2 + 1;
371 break;
373 case TrafficSettings::GaugeLocation::CentreTop:
374 rc.left = (rc.left + rc.right) / 2 - ib_layout.control_width;
375 rc.right = rc.left + ib_layout.control_width * 2 - 1;
376 rc.bottom = rc.top + ib_layout.control_height * 2;
377 ++rc.top;
378 break;
380 case TrafficSettings::GaugeLocation::CentreBottom:
381 rc.left = (rc.left + rc.right) / 2 - ib_layout.control_width;
382 rc.right = rc.left + ib_layout.control_width * 2 - 1;
383 rc.top = rc.bottom - ib_layout.control_height * 2 + 1;
384 break;
386 default: // aka flBottomRight
387 rc.left = rc.right - ib_layout.control_width * 2 + 1;
388 rc.top = rc.bottom - ib_layout.control_height * 2 + 1;
389 break;
392 traffic_gauge.Move(rc);
395 void
396 MainWindow::ReinitialisePosition()
398 PixelRect rc = SystemWindowSize();
399 fast_move(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
402 void
403 MainWindow::reset()
405 Deinitialise();
407 TopWindow::reset();
410 void
411 MainWindow::SuspendThreads()
413 if (map != NULL)
414 map->SuspendThreads();
417 void
418 MainWindow::ResumeThreads()
420 if (map != NULL)
421 map->ResumeThreads();
424 void
425 MainWindow::SetDefaultFocus()
427 if (map != NULL && widget == NULL)
428 map->set_focus();
429 else if (widget == NULL || !widget->SetFocus())
430 set_focus();
433 void
434 MainWindow::full_redraw()
436 if (map != NULL)
437 map->FullRedraw();
440 // Windows event handlers
442 void
443 MainWindow::OnResize(UPixelScalar width, UPixelScalar height)
445 SingleWindow::OnResize(width, height);
447 Layout::Initialize(width, height);
449 ReinitialiseLayout();
451 if (map != NULL) {
452 /* the map being created already is an indicator that XCSoar is
453 running already, and so we assume the menu buttons have been
454 created, too */
455 map->BringToBottom();
458 ButtonLabel::OnResize(get_client_rect());
460 ProgressGlue::Resize(width, height);
463 bool
464 MainWindow::OnActivate()
466 SingleWindow::OnActivate();
468 Fullscreen();
470 return true;
473 void
474 MainWindow::OnSetFocus()
476 SingleWindow::OnSetFocus();
478 if (!HasDialog()) {
479 /* the main window should never have the keyboard focus; if we
480 happen to get the focus despite of that, forward it to the map
481 window to make keyboard shortcuts work */
482 if (map != NULL && widget == NULL)
483 map->set_focus();
484 else if (widget != NULL)
485 widget->SetFocus();
489 bool
490 MainWindow::OnKeyDown(unsigned key_code)
492 return InputEvents::processKey(key_code) ||
493 SingleWindow::OnKeyDown(key_code);
496 bool
497 MainWindow::OnTimer(WindowTimer &_timer)
499 if (_timer != timer)
500 return SingleWindow::OnTimer(_timer);
502 if (globalRunningEvent.Test()) {
503 battery_timer.Process();
505 ProcessTimer::Process();
507 if (!CommonInterface::GetUISettings().enable_thermal_assistant_gauge) {
508 thermal_assistant.Clear();
509 } else if (!CommonInterface::Calculated().circling ||
510 InputEvents::IsFlavour(_T("TA"))) {
511 thermal_assistant.Hide();
512 } else if (!HasDialog()) {
513 if (!thermal_assistant.IsDefined())
514 thermal_assistant.Set(new GaugeThermalAssistant(CommonInterface::GetLiveBlackboard()));
516 if (!thermal_assistant.IsVisible()) {
517 thermal_assistant.Show();
519 GaugeThermalAssistant *widget =
520 (GaugeThermalAssistant *)thermal_assistant.Get();
521 widget->Raise();
526 return true;
529 bool
530 MainWindow::OnUser(unsigned id)
532 ProtectedAirspaceWarningManager *airspace_warnings;
534 switch ((enum cmd)id) {
535 case CMD_AIRSPACE_WARNING:
536 airspace_warnings = GetAirspaceWarnings();
537 if (!airspace_warning_pending || airspace_warnings == NULL)
538 return true;
540 airspace_warning_pending = false;
541 if (dlgAirspaceWarningVisible())
542 /* already visible */
543 return true;
545 /* un-blank the display, play a sound and show the dialog */
546 ResetDisplayTimeOut();
547 #ifndef GNAV
548 PlayResource(_T("IDR_WAV_BEEPBWEEP"));
549 #endif
550 dlgAirspaceWarningsShowModal(*this, *airspace_warnings, true);
551 return true;
553 case CMD_GPS_UPDATE:
554 XCSoarInterface::ReceiveGPS();
557 * Update the infoboxes if no location is available
559 * (if the location is available the CalculationThread will send the
560 * CMD_CALCULATED_UPDATE message which will update them)
562 if (!CommonInterface::Basic().location_available) {
563 InfoBoxManager::SetDirty();
564 InfoBoxManager::ProcessTimer();
567 return true;
569 case CMD_CALCULATED_UPDATE:
570 XCSoarInterface::ReceiveCalculated();
572 if (map != NULL)
573 map->FullRedraw();
575 InfoBoxManager::SetDirty();
576 InfoBoxManager::ProcessTimer();
578 return true;
581 return false;
584 void
585 MainWindow::OnCreate()
587 SingleWindow::OnCreate();
589 timer.Schedule(500); // 2 times per second
592 void
593 MainWindow::OnDestroy()
595 timer.Cancel();
597 KillWidget();
599 SingleWindow::OnDestroy();
602 bool MainWindow::OnClose() {
603 if (!IsRunning())
604 /* no shutdown dialog if XCSoar hasn't completed initialization
605 yet (e.g. if we are in the simulator prompt) */
606 return SingleWindow::OnClose();
608 if (XCSoarInterface::CheckShutdown()) {
609 XCSoarInterface::Shutdown();
611 return true;
614 void
615 MainWindow::SetFullScreen(bool _full_screen)
617 if (_full_screen == FullScreen)
618 return;
620 FullScreen = _full_screen;
622 if (FullScreen)
623 InfoBoxManager::Hide();
624 else
625 InfoBoxManager::Show();
627 if (widget != NULL)
628 widget->Move(FullScreen ? get_client_rect() : map_rect);
630 if (map != NULL) {
631 const PixelRect rc = FullScreen ? get_client_rect() : map_rect;
632 map->fast_move(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
635 // the repaint will be triggered by the DrawThread
638 void
639 MainWindow::SetTerrain(RasterTerrain *terrain)
641 if (map != NULL)
642 map->SetTerrain(terrain);
645 void
646 MainWindow::SetTopography(TopographyStore *topography)
648 if (map != NULL)
649 map->SetTopography(topography);
652 DisplayMode
653 MainWindow::GetDisplayMode() const
655 return map != NULL
656 ? map->GetDisplayMode()
657 : DM_NONE;
660 void
661 MainWindow::SetComputerSettings(const ComputerSettings &settings_computer)
663 if (map != NULL)
664 map->SetComputerSettings(settings_computer);
667 void
668 MainWindow::SetMapSettings(const MapSettings &settings_map)
670 if (map != NULL)
671 map->SetMapSettings(settings_map);
674 GlueMapWindow *
675 MainWindow::GetMapIfActive()
677 return IsMapActive() ? map : NULL;
680 GlueMapWindow *
681 MainWindow::ActivateMap()
683 if (map == NULL)
684 return NULL;
686 if (widget != NULL) {
687 KillWidget();
688 map->show();
689 map->set_focus();
692 return map;
695 void
696 MainWindow::KillWidget()
698 if (widget == NULL)
699 return;
701 widget->Leave();
702 widget->Hide();
703 widget->Unprepare();
704 delete widget;
705 widget = NULL;
707 InputEvents::SetFlavour(NULL);
710 void
711 MainWindow::SetWidget(Widget *_widget)
713 assert(_widget != NULL);
715 /* delete the old widget */
716 KillWidget();
718 /* hide the map (might be hidden already) */
719 if (map != NULL)
720 map->fast_hide();
722 widget = _widget;
724 const PixelRect rc = FullScreen ? get_client_rect() : map_rect;
725 widget->Initialise(*this, rc);
726 widget->Prepare(*this, rc);
727 widget->Show(rc);
729 if (!widget->SetFocus())
730 set_focus();
733 void
734 MainWindow::UpdateGaugeVisibility()
736 bool full_screen = GetFullScreen();
738 vario.SetVisible(!full_screen &&
739 !CommonInterface::GetUIState().screen_blanked);
741 UpdateTrafficGaugeVisibility();
744 void
745 MainWindow::UpdateTrafficGaugeVisibility()
747 const FlarmState &flarm = CommonInterface::Basic().flarm;
748 bool traffic_visible =
749 (force_traffic_gauge ||
750 (CommonInterface::GetUISettings().traffic.enable_gauge &&
751 flarm.available && !flarm.traffic.empty())) &&
752 !CommonInterface::GetUIState().screen_blanked &&
753 /* hide the traffic gauge while the traffic widget is visible, to
754 avoid showing the same information twice */
755 !InputEvents::IsFlavour(_T("Traffic"));
757 if (traffic_visible && suppress_traffic_gauge) {
758 if (flarm.available &&
759 flarm.alarm_level != FlarmTraffic::AlarmType::NONE)
760 suppress_traffic_gauge = false;
761 else
762 traffic_visible = false;
765 if (traffic_visible) {
766 if (HasDialog())
767 return;
769 if (!traffic_gauge.IsDefined())
770 traffic_gauge.Set(new GaugeFLARM(CommonInterface::GetLiveBlackboard(),
771 GetLook().flarm_gauge));
773 if (!traffic_gauge.IsVisible()) {
774 traffic_gauge.Show();
776 GaugeFLARM *widget = (GaugeFLARM *)traffic_gauge.Get();
777 widget->Raise();
779 } else
780 traffic_gauge.Hide();
783 const MapWindowProjection &
784 MainWindow::GetProjection() const
786 AssertThread();
787 assert(map != NULL);
789 return map->VisibleProjection();
792 void
793 MainWindow::ToggleSuppressFLARMRadar()
795 suppress_traffic_gauge = !suppress_traffic_gauge;
798 void
799 MainWindow::ToggleForceFLARMRadar()
801 force_traffic_gauge = !force_traffic_gauge;
802 CommonInterface::SetUISettings().traffic.enable_gauge = force_traffic_gauge;
805 #ifdef ANDROID
807 void
808 MainWindow::on_pause()
810 if (!IsRunning() && HasDialog())
811 /* suspending before initialization has finished doesn't leave
812 anything worth resuming, so let's just quit now */
813 CancelDialog();
815 SingleWindow::on_pause();
818 #endif /* ANDROID */