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"
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)
58 #if !defined(WIN32) && !defined(ANDROID)
59 #include <unistd.h> /* for execl() */
62 MainWindow::MainWindow(const StatusMessageList
&status_messages
)
64 map(NULL
), widget(NULL
), vario(*this),
66 suppress_traffic_gauge(false), force_traffic_gauge(false),
67 thermal_assistant(*this),
68 popup(status_messages
, *this, CommonInterface::GetUISettings()),
71 airspace_warning_pending(false)
76 * Destructor of the MainWindow-Class
79 MainWindow::~MainWindow()
87 MainWindow::register_class(HINSTANCE hInstance
)
91 wc
.style
= CS_HREDRAW
| CS_VREDRAW
;
92 wc
.lpfnWndProc
= Window::WndProc
;
95 wc
.hInstance
= hInstance
;
96 wc
.hIcon
= LoadIcon(hInstance
, MAKEINTRESOURCE(IDI_XCSOAR
));
98 wc
.hbrBackground
= NULL
;
100 wc
.lpszClassName
= _T("XCSoarMain");
102 return (RegisterClass(&wc
)!= FALSE
);
108 MainWindow::Set(const TCHAR
* text
, PixelRect rc
, TopWindowStyle style
)
110 SingleWindow::set(_T("XCSoarMain"), text
, rc
, style
);
117 const TCHAR
*msg
= _T("Font initialisation failed");
120 LogStartUp(_T("%s"), msg
);
122 /* now try to get a GUI error message out to the user */
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
);
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()) {
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()) {
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"));
207 MainWindow::Deinitialise()
209 InfoBoxManager::Destroy();
210 ButtonLabel::Destroy();
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
;
222 traffic_gauge
.Clear();
223 thermal_assistant
.Clear();
230 MainWindow::ReinitialiseLayout_vario(const InfoBoxLayout::Layout
&layout
)
232 if (!layout
.HasVario()) {
237 if (!vario
.IsDefined())
238 vario
.Set(new GlueGaugeVario(CommonInterface::GetLiveBlackboard(),
239 look
->vario
, look
->units
));
241 vario
.Move(layout
.vario
);
244 // XXX vario->bring_to_top();
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
);
259 MainWindow::ReinitialiseLayout()
264 dialogs
.top()->ReinitialiseLayout(); // adapt simulator prompt
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 */
272 #ifndef ENABLE_OPENGL
273 if (draw_thread
== NULL
)
274 /* no layout changes during startup */
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
;
295 ReinitialiseLayout_vario(ib_layout
);
297 ReinitialiseLayout_flarm(rc
, ib_layout
);
299 ReinitialiseLayoutTA(rc
, ib_layout
);
303 InfoBoxManager::Hide();
305 InfoBoxManager::Show();
307 const PixelRect
¤t_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
);
314 if (widget
!= NULL
) {
315 const PixelRect
¤t_map
= FullScreen
? rc
: map_rect
;
316 widget
->Move(current_map
);
320 // move topmost dialog to fit into the current layout, or close it
322 dialogs
.top()->ReinitialiseLayout();
326 map
->BringToBottom();
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
;
341 case InfoBoxSettings::Geometry::LEFT_8
:
342 val
= TrafficSettings::GaugeLocation::BottomLeft
;
344 case InfoBoxSettings::Geometry::TOP_12
:
345 val
= TrafficSettings::GaugeLocation::TopLeft
;
348 val
= TrafficSettings::GaugeLocation::BottomRight
; // Assume bottom right unles...
354 case TrafficSettings::GaugeLocation::TopLeft
:
355 rc
.right
= rc
.left
+ ib_layout
.control_width
* 2;
357 rc
.bottom
= rc
.top
+ ib_layout
.control_height
* 2;
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;
367 case TrafficSettings::GaugeLocation::BottomLeft
:
368 rc
.right
= rc
.left
+ ib_layout
.control_width
* 2;
370 rc
.top
= rc
.bottom
- ib_layout
.control_height
* 2 + 1;
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;
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;
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;
392 traffic_gauge
.Move(rc
);
396 MainWindow::ReinitialisePosition()
398 PixelRect rc
= SystemWindowSize();
399 fast_move(rc
.left
, rc
.top
, rc
.right
- rc
.left
, rc
.bottom
- rc
.top
);
411 MainWindow::SuspendThreads()
414 map
->SuspendThreads();
418 MainWindow::ResumeThreads()
421 map
->ResumeThreads();
425 MainWindow::SetDefaultFocus()
427 if (map
!= NULL
&& widget
== NULL
)
429 else if (widget
== NULL
|| !widget
->SetFocus())
434 MainWindow::full_redraw()
440 // Windows event handlers
443 MainWindow::OnResize(UPixelScalar width
, UPixelScalar height
)
445 SingleWindow::OnResize(width
, height
);
447 Layout::Initialize(width
, height
);
449 ReinitialiseLayout();
452 /* the map being created already is an indicator that XCSoar is
453 running already, and so we assume the menu buttons have been
455 map
->BringToBottom();
458 ButtonLabel::OnResize(get_client_rect());
460 ProgressGlue::Resize(width
, height
);
464 MainWindow::OnActivate()
466 SingleWindow::OnActivate();
474 MainWindow::OnSetFocus()
476 SingleWindow::OnSetFocus();
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
)
484 else if (widget
!= NULL
)
490 MainWindow::OnKeyDown(unsigned key_code
)
492 return InputEvents::processKey(key_code
) ||
493 SingleWindow::OnKeyDown(key_code
);
497 MainWindow::OnTimer(WindowTimer
&_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();
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
)
540 airspace_warning_pending
= false;
541 if (dlgAirspaceWarningVisible())
542 /* already visible */
545 /* un-blank the display, play a sound and show the dialog */
546 ResetDisplayTimeOut();
548 PlayResource(_T("IDR_WAV_BEEPBWEEP"));
550 dlgAirspaceWarningsShowModal(*this, *airspace_warnings
, true);
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();
569 case CMD_CALCULATED_UPDATE
:
570 XCSoarInterface::ReceiveCalculated();
575 InfoBoxManager::SetDirty();
576 InfoBoxManager::ProcessTimer();
585 MainWindow::OnCreate()
587 SingleWindow::OnCreate();
589 timer
.Schedule(500); // 2 times per second
593 MainWindow::OnDestroy()
599 SingleWindow::OnDestroy();
602 bool MainWindow::OnClose() {
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();
615 MainWindow::SetFullScreen(bool _full_screen
)
617 if (_full_screen
== FullScreen
)
620 FullScreen
= _full_screen
;
623 InfoBoxManager::Hide();
625 InfoBoxManager::Show();
628 widget
->Move(FullScreen
? get_client_rect() : map_rect
);
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
639 MainWindow::SetTerrain(RasterTerrain
*terrain
)
642 map
->SetTerrain(terrain
);
646 MainWindow::SetTopography(TopographyStore
*topography
)
649 map
->SetTopography(topography
);
653 MainWindow::GetDisplayMode() const
656 ? map
->GetDisplayMode()
661 MainWindow::SetComputerSettings(const ComputerSettings
&settings_computer
)
664 map
->SetComputerSettings(settings_computer
);
668 MainWindow::SetMapSettings(const MapSettings
&settings_map
)
671 map
->SetMapSettings(settings_map
);
675 MainWindow::GetMapIfActive()
677 return IsMapActive() ? map
: NULL
;
681 MainWindow::ActivateMap()
686 if (widget
!= NULL
) {
696 MainWindow::KillWidget()
707 InputEvents::SetFlavour(NULL
);
711 MainWindow::SetWidget(Widget
*_widget
)
713 assert(_widget
!= NULL
);
715 /* delete the old widget */
718 /* hide the map (might be hidden already) */
724 const PixelRect rc
= FullScreen
? get_client_rect() : map_rect
;
725 widget
->Initialise(*this, rc
);
726 widget
->Prepare(*this, rc
);
729 if (!widget
->SetFocus())
734 MainWindow::UpdateGaugeVisibility()
736 bool full_screen
= GetFullScreen();
738 vario
.SetVisible(!full_screen
&&
739 !CommonInterface::GetUIState().screen_blanked
);
741 UpdateTrafficGaugeVisibility();
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;
762 traffic_visible
= false;
765 if (traffic_visible
) {
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();
780 traffic_gauge
.Hide();
783 const MapWindowProjection
&
784 MainWindow::GetProjection() const
789 return map
->VisibleProjection();
793 MainWindow::ToggleSuppressFLARMRadar()
795 suppress_traffic_gauge
= !suppress_traffic_gauge
;
799 MainWindow::ToggleForceFLARMRadar()
801 force_traffic_gauge
= !force_traffic_gauge
;
802 CommonInterface::SetUISettings().traffic
.enable_gauge
= force_traffic_gauge
;
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 */
815 SingleWindow::on_pause();