Renderer, ...: use PixelRect::GetCenter()
[xcsoar.git] / src / InfoBoxes / InfoBoxWindow.cpp
blob86f789c53bff50a5d4a2760cffa4a9659a8a3d2a
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 "InfoBoxWindow.hpp"
25 #include "InfoBoxSettings.hpp"
26 #include "Border.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"
38 #ifdef ENABLE_OPENGL
39 #include "Screen/SubCanvas.hpp"
40 #endif
42 #include <algorithm>
44 using std::max;
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,
53 unsigned _id,
54 WindowStyle style)
55 :content(NULL),
56 settings(_settings), look(_look), units_look(_units_look),
57 border_kind(border_flags),
58 id(_id),
59 dragging(false), pressed(false),
60 force_draw_selector(false),
61 focus_timer(*this), dialog_timer(*this)
63 data.Clear();
65 style.EnableDoubleClicks();
66 Create(parent, rc, style);
69 InfoBoxWindow::~InfoBoxWindow() {
70 delete content;
71 Destroy();
74 void
75 InfoBoxWindow::SetTitle(const TCHAR *_title)
77 data.SetTitle(_title);
78 Invalidate(title_rect);
81 void
82 InfoBoxWindow::PaintTitle(Canvas &canvas)
84 if (data.title.empty())
85 return;
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;
94 canvas.Select(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);
114 RasterPoint tab[8];
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);
130 void
131 InfoBoxWindow::PaintValue(Canvas &canvas)
133 if (data.value.empty())
134 return;
136 canvas.SetTextColor(look.GetValueColor(data.value_color));
138 #ifndef GNAV
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,
169 { x + value_size.cx,
170 y + ascent_height - unit_height },
171 data.value_unit, look.unit_fraction_pen);
173 return;
175 #endif
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);
189 PixelSize unit_size;
190 const UnitSymbol *unit_symbol = units_look.GetSymbol(data.value_unit);
191 if (unit_symbol != NULL) {
192 unit_size = unit_symbol->GetSize();
193 } else {
194 unit_size.cx = 0;
195 unit_size.cy = 0;
198 PixelScalar x = std::max(PixelScalar(1),
199 PixelScalar((value_rect.left + value_rect.right
200 - value_size.cx
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())
212 return;
213 #endif
215 unit_symbol->Draw(canvas, x + value_size.cx,
216 y + ascent_height - unit_symbol->GetScreenSize().cy,
217 look.inverse ? UnitSymbol::INVERSE : UnitSymbol::NORMAL);
221 void
222 InfoBoxWindow::PaintComment(Canvas &canvas)
224 if (data.comment.empty())
225 return;
227 canvas.SetTextColor(look.GetCommentColor(data.comment_color));
229 const Font &font = *look.comment.font;
230 canvas.Select(font);
232 PixelSize tsize = canvas.CalcTextSize(data.comment);
234 PixelScalar x = std::max(PixelScalar(1),
235 PixelScalar((comment_rect.left + comment_rect.right
236 - tsize.cx) / 2));
237 PixelScalar y = comment_rect.top;
239 canvas.TextAutoClipped(x, y, data.comment);
242 void
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);
252 else
253 canvas.Clear(background_color);
255 if (data.GetCustom() && content != NULL) {
256 /* if there's no comment, the content object may paint that area,
257 too */
258 const PixelRect &rc = data.comment.empty()
259 ? value_and_comment_rect
260 : value_rect;
261 content->OnCustomPaint(canvas, rc);
264 canvas.SetBackgroundTransparent();
266 PaintTitle(canvas);
267 PaintComment(canvas);
268 PaintValue(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);
294 void
295 InfoBoxWindow::PaintInto(Canvas &dest, PixelScalar xoff, PixelScalar yoff,
296 UPixelScalar width, UPixelScalar height)
298 #ifdef ENABLE_OPENGL
299 SubCanvas canvas(dest, RasterPoint(xoff, yoff), PixelSize(width, height));
300 Paint(canvas);
301 #else
302 const PixelSize size = GetSize();
303 BufferCanvas buffer(dest, size);
305 Paint(buffer);
306 dest.Stretch(xoff, yoff, width, height, buffer, 0, 0, size.cx, size.cy);
307 #endif
310 void
311 InfoBoxWindow::SetContentProvider(InfoBoxContent *_content)
313 delete content;
314 content = _content;
316 data.SetInvalid();
317 Invalidate();
320 void
321 InfoBoxWindow::UpdateContent()
323 if (content == NULL)
324 return;
326 InfoBoxData old = data;
327 content->Update(data);
329 if (old.GetCustom() || data.GetCustom())
330 /* must Invalidate everything when custom painting is/was
331 enabled */
332 Invalidate();
333 else {
334 #ifdef ENABLE_OPENGL
335 if (!data.CompareTitle(old) || !data.CompareValue(old) ||
336 !data.CompareComment(old))
337 Invalidate();
338 #else
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);
345 #endif
349 void
350 InfoBoxWindow::ShowDialog()
352 force_draw_selector = true;
354 dlgInfoBoxAccessShowModeless(id, GetDialogContent());
356 force_draw_selector = false;
359 bool
360 InfoBoxWindow::HandleKey(InfoBoxContent::InfoBoxKeyCodes keycode)
362 if (content != NULL && content->HandleKey(keycode)) {
363 UpdateContent();
364 return true;
366 return false;
369 const InfoBoxPanel *
370 InfoBoxWindow::GetDialogContent() const
372 if (content != NULL)
373 return content->GetDialogContent();
375 return NULL;
378 void
379 InfoBoxWindow::OnDestroy()
381 focus_timer.Cancel();
382 PaintWindow::OnDestroy();
385 void
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;
404 title_rect = rc;
405 title_rect.bottom = rc.top + look.title.font->GetHeight();
407 comment_rect = rc;
408 comment_rect.top = comment_rect.bottom - (look.comment.font->GetHeight() + 2);
409 value_rect = rc;
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;
417 bool
418 InfoBoxWindow::OnKeyDown(unsigned key_code)
420 /* handle local hot key */
422 switch (key_code) {
423 case KEY_UP:
424 focus_timer.Schedule(FOCUS_TIMEOUT_MAX);
425 return HandleKey(InfoBoxContent::ibkUp);
427 case KEY_DOWN:
428 focus_timer.Schedule(FOCUS_TIMEOUT_MAX);
429 return HandleKey(InfoBoxContent::ibkDown);
431 case KEY_LEFT:
432 focus_timer.Schedule(FOCUS_TIMEOUT_MAX);
433 return HandleKey(InfoBoxContent::ibkLeft);
435 case KEY_RIGHT:
436 focus_timer.Schedule(FOCUS_TIMEOUT_MAX);
437 return HandleKey(InfoBoxContent::ibkRight);
439 case KEY_RETURN:
440 ShowDialog();
441 return true;
443 case KEY_ESCAPE:
444 focus_timer.Cancel();
445 FocusParent();
446 return true;
449 /* handle global hot key */
451 if (InputEvents::ProcessKey(InputEvents::MODE_INFOBOX, key_code))
452 return true;
454 /* call super class */
456 return PaintWindow::OnKeyDown(key_code);
459 bool
460 InfoBoxWindow::OnMouseDown(PixelScalar x, PixelScalar y)
462 dialog_timer.Cancel();
464 if (!dragging) {
465 dragging = true;
466 SetCapture();
468 pressed = true;
469 Invalidate();
471 /* start "long click" detection */
472 dialog_timer.Schedule(1000);
475 return true;
478 bool
479 InfoBoxWindow::OnMouseUp(PixelScalar x, PixelScalar y)
481 dialog_timer.Cancel();
483 if (dragging) {
484 const bool was_pressed = pressed;
486 dragging = false;
487 pressed = false;
488 Invalidate();
490 ReleaseCapture();
492 if (was_pressed) {
493 SetFocus();
495 if (GetDialogContent() != nullptr)
496 /* delay the dialog, so double click detection works */
497 dialog_timer.Schedule(300);
500 return true;
503 return false;
506 bool
507 InfoBoxWindow::OnMouseDouble(PixelScalar x, PixelScalar y)
509 dialog_timer.Cancel();
511 if (!IsAltair())
512 InputEvents::ShowMenu();
514 return true;
517 bool
518 InfoBoxWindow::OnMouseMove(PixelScalar x, PixelScalar y, unsigned keys)
520 if (dragging) {
521 SetPressed(IsInside(x, y));
522 if (!pressed)
523 dialog_timer.Cancel();
524 return true;
527 return false;
530 void
531 InfoBoxWindow::OnPaint(Canvas &canvas)
533 Paint(canvas);
536 void
537 InfoBoxWindow::OnCancelMode()
539 if (dragging) {
540 dragging = false;
541 pressed = false;
542 Invalidate();
543 ReleaseCapture();
546 dialog_timer.Cancel();
548 PaintWindow::OnCancelMode();
551 void
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
562 Invalidate();
565 void
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
575 Invalidate();
578 bool
579 InfoBoxWindow::OnTimer(WindowTimer &timer)
581 if (timer == focus_timer) {
582 focus_timer.Cancel();
583 FocusParent();
584 return true;
585 } else if (timer == dialog_timer) {
586 dragging = pressed = false;
587 Invalidate();
588 ReleaseCapture();
590 dialog_timer.Cancel();
591 ShowDialog();
592 return true;
593 } else
594 return PaintWindow::OnTimer(timer);