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 "InfoBoxWindow.hpp"
25 #include "InfoBoxSettings.hpp"
27 #include "Look/InfoBoxLook.hpp"
28 #include "Look/UnitsLook.hpp"
29 #include "Input/InputEvents.hpp"
30 #include "Renderer/GlassRenderer.hpp"
31 #include "Renderer/UnitSymbolRenderer.hpp"
32 #include "Screen/UnitSymbol.hpp"
33 #include "Screen/Layout.hpp"
34 #include "Screen/BufferCanvas.hpp"
35 #include "Screen/Key.h"
36 #include "Dialogs/dlgInfoBoxAccess.hpp"
39 #include "Screen/SubCanvas.hpp"
46 #define SELECTORWIDTH Layout::Scale(5)
48 InfoBoxWindow::InfoBoxWindow(ContainerWindow
&parent
, PixelRect rc
,
49 unsigned border_flags
,
50 const InfoBoxSettings
&_settings
,
51 const InfoBoxLook
&_look
,
52 const UnitsLook
&_units_look
,
56 settings(_settings
), look(_look
), units_look(_units_look
),
57 border_kind(border_flags
),
59 dragging(false), pressed(false),
60 force_draw_selector(false),
61 focus_timer(*this), dialog_timer(*this)
65 style
.EnableDoubleClicks();
66 Create(parent
, rc
, style
);
69 InfoBoxWindow::~InfoBoxWindow() {
75 InfoBoxWindow::SetTitle(const TCHAR
*_title
)
77 data
.SetTitle(_title
);
78 Invalidate(title_rect
);
82 InfoBoxWindow::PaintTitle(Canvas
&canvas
)
84 if (data
.title
.empty())
87 if (!pressed
&& !HasFocus() && !dragging
&& !force_draw_selector
&&
88 settings
.border_style
== InfoBoxSettings::BorderStyle::SHADED
)
89 canvas
.DrawFilledRectangle(title_rect
, look
.caption_background_color
);
91 canvas
.SetTextColor(look
.GetTitleColor(data
.title_color
));
93 const Font
&font
= *look
.title
.font
;
96 PixelSize tsize
= canvas
.CalcTextSize(data
.title
);
98 PixelScalar halftextwidth
= (title_rect
.left
+ title_rect
.right
- tsize
.cx
) / 2;
99 PixelScalar x
= std::max(PixelScalar(1),
100 PixelScalar(title_rect
.left
+ halftextwidth
));
101 PixelScalar y
= title_rect
.top
;
103 canvas
.TextAutoClipped(x
, y
, data
.title
);
105 if (settings
.border_style
== InfoBoxSettings::BorderStyle::TAB
&&
106 halftextwidth
> Layout::Scale(3)) {
107 PixelScalar ytop
= title_rect
.top
+ font
.GetCapitalHeight() / 2;
108 PixelScalar ytopedge
= ytop
+ Layout::GetTextPadding();
109 PixelScalar ybottom
= title_rect
.top
+ Layout::Scale(6)
110 + font
.GetCapitalHeight();
112 canvas
.Select(look
.border_pen
);
115 tab
[0].x
= tab
[1].x
= title_rect
.left
;
116 tab
[0].y
= tab
[7].y
= ybottom
;
117 tab
[2].x
= title_rect
.left
+ Layout::GetTextPadding();
118 tab
[2].y
= tab
[5].y
= tab
[3].y
= tab
[4].y
= ytop
;
119 tab
[1].y
= tab
[6].y
= ytopedge
;
120 tab
[5].x
= title_rect
.right
- Layout::GetTextPadding();
121 tab
[6].x
= tab
[7].x
= title_rect
.right
;
122 tab
[3].x
= title_rect
.left
+ halftextwidth
- Layout::Scale(1);
123 tab
[4].x
= title_rect
.right
- halftextwidth
+ Layout::Scale(1);
125 canvas
.DrawPolyline(tab
, 4);
126 canvas
.DrawPolyline(tab
+ 4, 4);
131 InfoBoxWindow::PaintValue(Canvas
&canvas
)
133 if (data
.value
.empty())
136 canvas
.SetTextColor(look
.GetValueColor(data
.value_color
));
139 // Do text-based unit rendering on higher resolutions
140 if (Layout::FastScale(10) > 18) {
141 canvas
.Select(*look
.unit_font
);
142 PixelScalar unit_width
=
143 UnitSymbolRenderer::GetSize(canvas
, data
.value_unit
).cx
;
145 canvas
.Select(*look
.value
.font
);
146 int ascent_height
= look
.value
.font
->GetAscentHeight();
148 PixelSize value_size
= canvas
.CalcTextSize(data
.value
);
149 if (value_size
.cx
> value_rect
.right
- value_rect
.left
) {
150 canvas
.Select(*look
.small_font
);
151 ascent_height
= look
.small_font
->GetAscentHeight();
152 value_size
= canvas
.CalcTextSize(data
.value
);
155 PixelScalar x
= std::max(PixelScalar(0),
156 PixelScalar((value_rect
.left
+ value_rect
.right
157 - value_size
.cx
- unit_width
) / 2));
159 PixelScalar y
= (value_rect
.top
+ value_rect
.bottom
- value_size
.cy
) / 2;
161 canvas
.TextAutoClipped(x
, y
, data
.value
);
163 if (unit_width
!= 0) {
164 const int unit_height
=
165 UnitSymbolRenderer::GetAscentHeight(*look
.unit_font
, data
.value_unit
);
167 canvas
.Select(*look
.unit_font
);
168 UnitSymbolRenderer::Draw(canvas
,
170 y
+ ascent_height
- unit_height
},
171 data
.value_unit
, look
.unit_fraction_pen
);
177 canvas
.Select(*look
.value
.font
);
178 UPixelScalar ascent_height
= look
.value
.font
->GetAscentHeight();
179 UPixelScalar capital_height
= look
.value
.font
->GetCapitalHeight();
181 PixelSize value_size
= canvas
.CalcTextSize(data
.value
);
182 if (value_size
.cx
> value_rect
.right
- value_rect
.left
) {
183 canvas
.Select(*look
.small_font
);
184 ascent_height
= look
.small_font
->GetAscentHeight();
185 capital_height
= look
.small_font
->GetCapitalHeight();
186 value_size
= canvas
.CalcTextSize(data
.value
);
190 const UnitSymbol
*unit_symbol
= units_look
.GetSymbol(data
.value_unit
);
191 if (unit_symbol
!= NULL
) {
192 unit_size
= unit_symbol
->GetSize();
198 PixelScalar x
= std::max(PixelScalar(1),
199 PixelScalar((value_rect
.left
+ value_rect
.right
201 - Layout::FastScale(unit_size
.cx
)) / 2));
203 PixelScalar y
= value_rect
.top
+ 1 - ascent_height
+
204 (value_rect
.bottom
- value_rect
.top
+ capital_height
) / 2;
206 canvas
.TextAutoClipped(x
, y
, data
.value
);
208 if (unit_symbol
!= NULL
) {
209 #ifndef HAVE_CLIPPING
210 /* sort-of clipping */
211 if (x
+ value_size
.cx
>= (int)canvas
.GetWidth())
215 unit_symbol
->Draw(canvas
, x
+ value_size
.cx
,
216 y
+ ascent_height
- unit_symbol
->GetScreenSize().cy
,
217 look
.inverse
? UnitSymbol::INVERSE
: UnitSymbol::NORMAL
);
222 InfoBoxWindow::PaintComment(Canvas
&canvas
)
224 if (data
.comment
.empty())
227 canvas
.SetTextColor(look
.GetCommentColor(data
.comment_color
));
229 const Font
&font
= *look
.comment
.font
;
232 PixelSize tsize
= canvas
.CalcTextSize(data
.comment
);
234 PixelScalar x
= std::max(PixelScalar(1),
235 PixelScalar((comment_rect
.left
+ comment_rect
.right
237 PixelScalar y
= comment_rect
.top
;
239 canvas
.TextAutoClipped(x
, y
, data
.comment
);
243 InfoBoxWindow::Paint(Canvas
&canvas
)
245 const Color background_color
= pressed
246 ? look
.pressed_background_color
247 : (HasFocus() || dragging
|| force_draw_selector
248 ? look
.focused_background_color
249 : look
.background_color
);
250 if (settings
.border_style
== InfoBoxSettings::BorderStyle::GLASS
)
251 DrawGlassBackground(canvas
, canvas
.GetRect(), background_color
);
253 canvas
.Clear(background_color
);
255 if (data
.GetCustom() && content
!= NULL
) {
256 /* if there's no comment, the content object may paint that area,
258 const PixelRect
&rc
= data
.comment
.empty()
259 ? value_and_comment_rect
261 content
->OnCustomPaint(canvas
, rc
);
264 canvas
.SetBackgroundTransparent();
267 PaintComment(canvas
);
270 if (border_kind
!= 0) {
271 canvas
.Select(look
.border_pen
);
273 const UPixelScalar width
= canvas
.GetWidth(),
274 height
= canvas
.GetHeight();
276 if (border_kind
& BORDERTOP
) {
277 canvas
.DrawExactLine(0, 0, width
- 1, 0);
280 if (border_kind
& BORDERRIGHT
) {
281 canvas
.DrawExactLine(width
- 1, 0, width
- 1, height
);
284 if (border_kind
& BORDERBOTTOM
) {
285 canvas
.DrawExactLine(0, height
- 1, width
- 1, height
- 1);
288 if (border_kind
& BORDERLEFT
) {
289 canvas
.DrawExactLine(0, 0, 0, height
- 1);
295 InfoBoxWindow::PaintInto(Canvas
&dest
, PixelScalar xoff
, PixelScalar yoff
,
296 UPixelScalar width
, UPixelScalar height
)
299 SubCanvas
canvas(dest
, RasterPoint(xoff
, yoff
), PixelSize(width
, height
));
302 const PixelSize size
= GetSize();
303 BufferCanvas
buffer(dest
, size
);
306 dest
.Stretch(xoff
, yoff
, width
, height
, buffer
, 0, 0, size
.cx
, size
.cy
);
311 InfoBoxWindow::SetContentProvider(InfoBoxContent
*_content
)
321 InfoBoxWindow::UpdateContent()
326 InfoBoxData old
= data
;
327 content
->Update(data
);
329 if (old
.GetCustom() || data
.GetCustom())
330 /* must Invalidate everything when custom painting is/was
335 if (!data
.CompareTitle(old
) || !data
.CompareValue(old
) ||
336 !data
.CompareComment(old
))
339 if (!data
.CompareTitle(old
))
340 Invalidate(title_rect
);
341 if (!data
.CompareValue(old
))
342 Invalidate(value_rect
);
343 if (!data
.CompareComment(old
))
344 Invalidate(comment_rect
);
350 InfoBoxWindow::ShowDialog()
352 force_draw_selector
= true;
354 dlgInfoBoxAccessShowModeless(id
, GetDialogContent());
356 force_draw_selector
= false;
360 InfoBoxWindow::HandleKey(InfoBoxContent::InfoBoxKeyCodes keycode
)
362 if (content
!= NULL
&& content
->HandleKey(keycode
)) {
370 InfoBoxWindow::GetDialogContent() const
373 return content
->GetDialogContent();
379 InfoBoxWindow::OnDestroy()
381 focus_timer
.Cancel();
382 PaintWindow::OnDestroy();
386 InfoBoxWindow::OnResize(PixelSize new_size
)
388 PaintWindow::OnResize(new_size
);
390 PixelRect rc
= GetClientRect();
392 if (border_kind
& BORDERLEFT
)
393 rc
.left
+= look
.BORDER_WIDTH
;
395 if (border_kind
& BORDERRIGHT
)
396 rc
.right
-= look
.BORDER_WIDTH
;
398 if (border_kind
& BORDERTOP
)
399 rc
.top
+= look
.BORDER_WIDTH
;
401 if (border_kind
& BORDERBOTTOM
)
402 rc
.bottom
-= look
.BORDER_WIDTH
;
405 title_rect
.bottom
= rc
.top
+ look
.title
.font
->GetHeight();
408 comment_rect
.top
= comment_rect
.bottom
- (look
.comment
.font
->GetHeight() + 2);
410 value_rect
.top
= title_rect
.bottom
;
411 value_rect
.bottom
= comment_rect
.top
;
413 value_and_comment_rect
= value_rect
;
414 value_and_comment_rect
.bottom
= comment_rect
.bottom
;
418 InfoBoxWindow::OnKeyDown(unsigned key_code
)
420 /* handle local hot key */
424 focus_timer
.Schedule(FOCUS_TIMEOUT_MAX
);
425 return HandleKey(InfoBoxContent::ibkUp
);
428 focus_timer
.Schedule(FOCUS_TIMEOUT_MAX
);
429 return HandleKey(InfoBoxContent::ibkDown
);
432 focus_timer
.Schedule(FOCUS_TIMEOUT_MAX
);
433 return HandleKey(InfoBoxContent::ibkLeft
);
436 focus_timer
.Schedule(FOCUS_TIMEOUT_MAX
);
437 return HandleKey(InfoBoxContent::ibkRight
);
444 focus_timer
.Cancel();
449 /* handle global hot key */
451 if (InputEvents::ProcessKey(InputEvents::MODE_INFOBOX
, key_code
))
454 /* call super class */
456 return PaintWindow::OnKeyDown(key_code
);
460 InfoBoxWindow::OnMouseDown(PixelScalar x
, PixelScalar y
)
462 dialog_timer
.Cancel();
471 /* start "long click" detection */
472 dialog_timer
.Schedule(1000);
479 InfoBoxWindow::OnMouseUp(PixelScalar x
, PixelScalar y
)
481 dialog_timer
.Cancel();
484 const bool was_pressed
= pressed
;
495 if (GetDialogContent() != nullptr)
496 /* delay the dialog, so double click detection works */
497 dialog_timer
.Schedule(300);
507 InfoBoxWindow::OnMouseDouble(PixelScalar x
, PixelScalar y
)
509 dialog_timer
.Cancel();
512 InputEvents::ShowMenu();
518 InfoBoxWindow::OnMouseMove(PixelScalar x
, PixelScalar y
, unsigned keys
)
521 SetPressed(IsInside(x
, y
));
523 dialog_timer
.Cancel();
531 InfoBoxWindow::OnPaint(Canvas
&canvas
)
537 InfoBoxWindow::OnCancelMode()
546 dialog_timer
.Cancel();
548 PaintWindow::OnCancelMode();
552 InfoBoxWindow::OnSetFocus()
554 // Call the parent function
555 PaintWindow::OnSetFocus();
557 // Start the focus-auto-return timer
558 // to automatically return focus back to MapWindow if idle
559 focus_timer
.Schedule(FOCUS_TIMEOUT_MAX
);
561 // Redraw fast to paint the selector
566 InfoBoxWindow::OnKillFocus()
568 // Call the parent function
569 PaintWindow::OnKillFocus();
571 // Destroy the time if it exists
572 focus_timer
.Cancel();
574 // Redraw fast to remove the selector
579 InfoBoxWindow::OnTimer(WindowTimer
&timer
)
581 if (timer
== focus_timer
) {
582 focus_timer
.Cancel();
585 } else if (timer
== dialog_timer
) {
586 dragging
= pressed
= false;
590 dialog_timer
.Cancel();
594 return PaintWindow::OnTimer(timer
);