예제 #1
0
파일: Team.cpp 프로젝트: Adrien81/XCSoar
void
UpdateInfoBoxTeamBearingDiff(InfoBoxData &data)
{
  const TeamCodeSettings &settings =
    CommonInterface::GetComputerSettings().team_code;
  const NMEAInfo &basic = CommonInterface::Basic();
  const TrafficList &flarm = basic.flarm.traffic;
  const TeamInfo &teamcode_info = CommonInterface::Calculated();

  if (teamcode_info.teammate_available && basic.track_available) {
    // Set Value
    Angle Value = teamcode_info.teammate_vector.bearing - basic.track;
    data.SetValueFromBearingDifference(Value);
  } else
    data.SetValueInvalid();

  // Set Comment
  if (!settings.team_flarm_id.IsDefined())
    data.SetCommentInvalid();
  else if (!StringIsEmpty(settings.team_flarm_callsign))
    data.SetComment(settings.team_flarm_callsign);
  else
    data.SetComment(_T("???"));

  if (flarm.FindTraffic(settings.team_flarm_id) != NULL)
    data.SetCommentColor(2);
  else
    data.SetCommentColor(1);
}
예제 #2
0
파일: Team.cpp 프로젝트: Adrien81/XCSoar
void
UpdateInfoBoxTeamBearing(InfoBoxData &data)
{
  const TeamCodeSettings &settings =
    CommonInterface::GetComputerSettings().team_code;
  const TrafficList &flarm = CommonInterface::Basic().flarm.traffic;
  const TeamInfo &teamcode_info = CommonInterface::Calculated();

  if (teamcode_info.teammate_available) {
    // Set Value
    data.SetValue(teamcode_info.teammate_vector.bearing);
  }
  else
    data.SetValueInvalid();

  // Set Comment
  if (!settings.team_flarm_id.IsDefined())
    data.SetCommentInvalid();
  else if (!settings.team_flarm_callsign.empty())
    data.SetComment(settings.team_flarm_callsign.c_str());
  else
    data.SetComment(_T("???"));

  if (flarm.FindTraffic(settings.team_flarm_id) != NULL)
    data.SetCommentColor(2);
  else
    data.SetCommentColor(1);
}
예제 #3
0
파일: Team.cpp 프로젝트: Adrien81/XCSoar
void
InfoBoxContentTeamCode::Update(InfoBoxData &data)
{
  const TeamCodeSettings &settings =
    CommonInterface::GetComputerSettings().team_code;
  const TeamInfo &teamcode_info = CommonInterface::Calculated();

  if (!settings.team_code_reference_waypoint) {
    data.SetInvalid();
    return;
  }

  // Set Value
  data.SetValue(CommonInterface::Calculated().own_teammate_code.GetCode());

  // Set Comment
  if (teamcode_info.flarm_teammate_code.IsDefined()) {
    data.SetComment(teamcode_info.flarm_teammate_code.GetCode());
    data.SetCommentColor(teamcode_info.flarm_teammate_code_current ? 2 : 1);
  } else if (settings.team_code.IsDefined()) {
    data.SetComment(settings.team_code.GetCode());
    data.SetCommentColor(0);
  }
  else
    data.SetCommentInvalid();
}
예제 #4
0
파일: Speed.cpp 프로젝트: davidswelt/XCSoar
void
InfoBoxContentSpeedDolphin::Update(InfoBoxData &data)
{
  // Set Value
  const DerivedInfo &calculated = CommonInterface::Calculated();
  data.SetValueFromSpeed(calculated.V_stf, false);

  // Set Comment
  if (XCSoarInterface::GetComputerSettings().features.block_stf_enabled)
    data.SetComment(_("BLOCK"));
  else
    data.SetComment(_("DOLPHIN"));

}
예제 #5
0
void
UpdateInfoBoxAltitudeNav(InfoBoxData &data)
{
  const MoreData &basic = CommonInterface::Basic();

  if (!basic.NavAltitudeAvailable()) {
    data.SetInvalid();

    if (basic.pressure_altitude_available)
      data.SetComment(_("no QNH"));

    return;
  }

  const ComputerSettings &settings_computer = CommonInterface::GetComputerSettings();

  if (basic.baro_altitude_available &&
      settings_computer.features.nav_baro_altitude_enabled)
    data.SetTitle(InfoBoxFactory::GetCaption(InfoBoxFactory::e_H_Baro));
  else
    data.SetTitle(InfoBoxFactory::GetCaption(InfoBoxFactory::e_HeightGPS));

  data.SetValueFromAltitude(basic.nav_altitude);
  data.SetCommentFromAlternateAltitude(basic.nav_altitude);
}
예제 #6
0
파일: Task.cpp 프로젝트: davidswelt/XCSoar
void
InfoBoxContentTaskTimeUnderMaxHeight::Update(InfoBoxData &data)
{
  const CommonStats &common_stats = XCSoarInterface::Calculated().common_stats;
  const TaskStats &task_stats = XCSoarInterface::Calculated().task_stats;
  const fixed maxheight = fixed(protected_task_manager->
                                GetOrderedTaskBehaviour().start_max_height);

  if (!task_stats.task_valid || !positive(maxheight)
      || !protected_task_manager
      || !positive(common_stats.TimeUnderStartMaxHeight)) {
    data.SetInvalid();
    return;
  }

  const int dd = (int)(XCSoarInterface::Basic().time -
      common_stats.TimeUnderStartMaxHeight);

  TCHAR value[32];
  TCHAR comment[32];
  FormatTimeTwoLines(value, comment, dd);

  data.SetValue(value);
  data.SetComment(_("Time Below"));
}
예제 #7
0
void
InfoBoxContentFinalETEVMG::Update(InfoBoxData &data)
{
  const NMEAInfo &basic = CommonInterface::Basic();
  const TaskStats &task_stats = CommonInterface::Calculated().task_stats;

  if (!basic.ground_speed_available || task_stats.task_valid ||
      task_stats.total.remaining.IsDefined()) {
    data.SetInvalid();
    return;
  }

  const fixed d = task_stats.total.remaining.get_distance();
  const fixed &v = basic.ground_speed;

  if (!task_stats.task_valid ||
      !positive(d) ||
      !positive(v)) {
    data.SetInvalid();
    return;
  }

  TCHAR HHMMSSsmart[32];
  TCHAR SSsmart[32];
  const int dd = (int)(d/v);
  Units::TimeToTextSmart(HHMMSSsmart, SSsmart, dd);

  data.SetValue(HHMMSSsmart);
  data.SetComment(SSsmart);
}
예제 #8
0
파일: Task.cpp 프로젝트: Adrien81/XCSoar
void
UpdateInfoBoxTaskAATimeDelta(InfoBoxData &data)
{
  const auto &calculated = CommonInterface::Calculated();
  const TaskStats &task_stats = calculated.ordered_task_stats;
  const CommonStats &common_stats = calculated.common_stats;

  if (!task_stats.has_targets ||
      !task_stats.total.IsAchievable()) {
    data.SetInvalid();
    return;
  }

  assert(!negative(task_stats.total.time_remaining_start));

  fixed diff = task_stats.total.time_remaining_start -
    common_stats.aat_time_remaining;

  TCHAR value[32];
  TCHAR comment[32];
  const int dd = abs((int)diff);
  FormatTimeTwoLines(value, comment, dd);

  data.UnsafeFormatValue(negative(diff) ? _T("-%s") : _T("%s"), value);

  data.SetComment(comment);

  // Set Color (red/blue/black)
  data.SetValueColor(negative(diff) ? 1 :
                   task_stats.total.time_remaining_start >
                       common_stats.aat_time_remaining + fixed(5*60) ? 2 : 0);
}
예제 #9
0
void
InfoBoxContentTaskAATimeDelta::Update(InfoBoxData &data)
{
  const TaskStats &task_stats = XCSoarInterface::Calculated().task_stats;
  const CommonStats &common_stats = XCSoarInterface::Calculated().common_stats;

  if (!common_stats.ordered_has_targets ||
      !task_stats.task_valid || !task_stats.total.IsAchievable()) {
    data.SetInvalid();
    return;
  }

  assert(!negative(task_stats.total.time_remaining));

  fixed diff = task_stats.total.time_remaining -
    common_stats.aat_time_remaining;

  TCHAR HHMMSSsmart[32];
  TCHAR SSsmart[32];
  const int dd = abs((int)diff);
  Units::TimeToTextSmart(HHMMSSsmart, SSsmart, dd);

  data.UnsafeFormatValue(negative(diff) ? _T("-%s") : _T("%s"), HHMMSSsmart);

  data.SetComment(SSsmart);

  // Set Color (red/blue/black)
  data.SetValueColor(negative(diff) ? 1 :
                   task_stats.total.time_remaining >
                       common_stats.aat_time_remaining + fixed(5*60) ? 2 : 0);
}
예제 #10
0
파일: Task.cpp 프로젝트: Adrien81/XCSoar
void
UpdateInfoBoxTaskTimeUnderMaxHeight(InfoBoxData &data)
{
  const auto &calculated = CommonInterface::Calculated();
  const TaskStats &task_stats = calculated.ordered_task_stats;
  const CommonStats &common_stats = calculated.common_stats;
  const fixed maxheight = fixed(protected_task_manager->
                                GetOrderedTaskSettings().start_constraints.max_height);

  if (!task_stats.task_valid || !positive(maxheight)
      || !protected_task_manager
      || !positive(common_stats.TimeUnderStartMaxHeight)) {
    data.SetInvalid();
    return;
  }

  const int dd = (int)(CommonInterface::Basic().time -
      common_stats.TimeUnderStartMaxHeight);

  TCHAR value[32];
  TCHAR comment[32];
  FormatTimeTwoLines(value, comment, dd);

  data.SetValue(value);
  data.SetComment(_("Time Below"));
}
예제 #11
0
파일: Task.cpp 프로젝트: Adrien81/XCSoar
void
UpdateInfoBoxFinalETEVMG(InfoBoxData &data)
{
  const NMEAInfo &basic = CommonInterface::Basic();
  const TaskStats &task_stats = CommonInterface::Calculated().task_stats;

  if (!basic.ground_speed_available || !task_stats.task_valid ||
      !task_stats.total.remaining.IsDefined()) {
    data.SetInvalid();
    return;
  }

  const fixed d = task_stats.total.remaining.GetDistance();
  const fixed v = basic.ground_speed;

  if (!task_stats.task_valid ||
      !positive(d) ||
      !positive(v)) {
    data.SetInvalid();
    return;
  }

  TCHAR value[32];
  TCHAR comment[32];
  const int dd = (int)(d/v);
  FormatTimeTwoLines(value, comment, dd);

  data.SetValue(value);
  data.SetComment(comment);
}
예제 #12
0
파일: Task.cpp 프로젝트: Adrien81/XCSoar
void
UpdateInfoBoxNextDistanceNominal(InfoBoxData &data)
{
  const Waypoint* way_point = protected_task_manager != NULL
    ? protected_task_manager->GetActiveWaypoint()
    : NULL;

  if (!way_point) {
    data.SetInvalid();
    return;
  }

  const NMEAInfo &basic = CommonInterface::Basic();
  const TaskStats &task_stats = CommonInterface::Calculated().task_stats;

  if (!task_stats.task_valid || !basic.location_available) {
      data.SetInvalid();
      return;
  }

  const GeoVector vector(basic.location, way_point->location);

  if (!vector.IsValid()) {
      data.SetInvalid();
      return;
  }

  // Set Value
  data.SetValueFromDistance(vector.distance);
  data.SetValueColor(task_stats.inside_oz ? 3 : 0);
  data.SetComment(vector.bearing);
}
예제 #13
0
void
UpdateInfoBoxNbrSat(InfoBoxData &data)
{
    const NMEAInfo &basic = CommonInterface::Basic();
    const GPSState &gps = basic.gps;

    data.SetComment(gettext(GetGPSStatus(basic)));

    if (!basic.alive)
        data.SetComment(_("No GPS"));
    else if (gps.satellites_used_available) {
        // known number of sats
        data.FormatValue(_T("%u"), gps.satellites_used);
    } else {
        // valid but unknown number of sats
        data.SetValueInvalid();
    }
}
예제 #14
0
파일: Task.cpp 프로젝트: Adrien81/XCSoar
void
UpdateInfoBoxStartOpenArrival(InfoBoxData &data)
{
  const NMEAInfo &basic = CommonInterface::Basic();
  const auto &calculated = CommonInterface::Calculated();
  const TaskStats &task_stats = calculated.ordered_task_stats;
  const GlideResult &current_remaining =
    task_stats.current_leg.solution_remaining;
  const CommonStats &common_stats = CommonInterface::Calculated().common_stats;
  const RoughTimeSpan &open = common_stats.start_open_time_span;

  /* reset color that may have been set by a previous call */
  data.SetValueColor(0);

  if (!basic.time_available || !task_stats.task_valid ||
      common_stats.ordered_summary.active != 0 ||
      !open.IsDefined() ||
      !current_remaining.IsOk()) {
    data.SetInvalid();
    return;
  }

  const unsigned arrival_s(basic.time + current_remaining.time_elapsed);
  const RoughTime arrival = RoughTime::FromSecondOfDayChecked(arrival_s);

  if (open.HasEnded(arrival)) {
    data.SetValueInvalid();
    data.SetComment(_("Closed"));
  } else if (open.HasBegun(arrival)) {
    if (open.GetEnd().IsValid()) {
      unsigned seconds = SecondsUntil(arrival_s, open.GetEnd());
      data.UnsafeFormatValue(_T("%02u:%02u"), seconds / 60, seconds % 60);
      data.SetValueColor(3);
    } else
      data.SetValueInvalid();

    data.SetComment(_("Open"));
  } else {
    unsigned seconds = SecondsUntil(arrival_s, open.GetStart());
    data.UnsafeFormatValue(_T("%02u:%02u"), seconds / 60, seconds % 60);
    data.SetValueColor(2);
    data.SetComment(_("Waiting"));
  }
}
예제 #15
0
void
InfoBoxContentNearestAirspaceVertical::Update(InfoBoxData &data)
{
  NearestAirspace nearest = FindNearestVerticalAirspace();
  if (!nearest.IsDefined()) {
    data.SetInvalid();
    return;
  }

  data.SetValueFromArrival(nearest.distance);
  data.SetComment(nearest.airspace->GetName());
}
예제 #16
0
파일: Team.cpp 프로젝트: Adrien81/XCSoar
void
UpdateInfoBoxTeamDistance(InfoBoxData &data)
{
  const TeamCodeSettings &settings =
    CommonInterface::GetComputerSettings().team_code;
  const TeamInfo &teamcode_info = CommonInterface::Calculated();

  // Set Value
  if (teamcode_info.teammate_available)
    data.SetValueFromDistance(teamcode_info.teammate_vector.distance);
  else
    data.SetValueInvalid();

  // Set Comment
  if (!settings.team_flarm_id.IsDefined())
    data.SetCommentInvalid();
  else if (!StringIsEmpty(settings.team_flarm_callsign))
    data.SetComment(settings.team_flarm_callsign);
  else
    data.SetComment(_T("???"));

  data.SetCommentColor(teamcode_info.flarm_teammate_code_current ? 2 : 1);
}
예제 #17
0
파일: Trace.cpp 프로젝트: StefanL74/XCSoar
void
InfoBoxContentSpark::SetVSpeedComment(InfoBoxData &data,
                                      const TraceVariableHistory &var)
{
  if (var.empty())
    return;

  TCHAR sTmp[32];
  FormatUserVerticalSpeed(var.last(), sTmp,
                          ARRAY_SIZE(sTmp));
  data.SetComment(sTmp);

  data.SetCustom();
}
예제 #18
0
파일: Trace.cpp 프로젝트: StefanL74/XCSoar
void
InfoBoxContentBarogram::Update(InfoBoxData &data)
{
  const MoreData &basic = CommonInterface::Basic();
  TCHAR sTmp[32];

  if (basic.NavAltitudeAvailable()) {
    FormatUserAltitude(basic.nav_altitude, sTmp,
                       ARRAY_SIZE(sTmp));
    data.SetComment(sTmp);
  } else
    data.SetCommentInvalid();

  data.SetCustom();
}
예제 #19
0
파일: Weather.cpp 프로젝트: Adrien81/XCSoar
void
UpdateInfoBoxWindBearing(InfoBoxData &data)
{
  const DerivedInfo &info = CommonInterface::Calculated();
  if (!info.wind_available) {
    data.SetInvalid();
    return;
  }

  data.SetValue(info.wind.bearing);

  TCHAR buffer[16];
  FormatUserWindSpeed(info.wind.norm, buffer, true, false);
  data.SetComment(buffer);
}
예제 #20
0
파일: Other.cpp 프로젝트: rjsikarwar/XCSoar
void
InfoBoxContentHorizon::Update(InfoBoxData &data)
{
#ifdef NO_HORIZON
  data.SetInvalid();
  data.SetComment(_("Disabled"));
#else
  if (!CommonInterface::Basic().attitude.bank_angle_available &&
      !CommonInterface::Basic().attitude.pitch_angle_available) {
    data.SetInvalid();
    return;
  }

  data.SetCustom();
#endif
}
예제 #21
0
void
InfoBoxContentAlternateGR::Update(InfoBoxData &data)
{
  if (protected_task_manager == NULL) {
    data.SetInvalid();
    return;
  }

  ProtectedTaskManager::Lease lease(*protected_task_manager);
  const AlternateList &alternates = lease->GetAlternates();

  const AlternatePoint *alternate;
  if (!alternates.empty()) {
    if (index >= alternates.size())
      index = alternates.size() - 1;

    alternate = &alternates[index];
  } else {
    alternate = NULL;
  }

  data.FormatTitle(_T("Altn %d GR"), index + 1);

  if (alternate == NULL) {
    data.SetInvalid();
    return;
  }

  data.SetComment(alternate->waypoint.name.c_str());

  fixed gradient =
    ::AngleToGradient(alternate->solution.DestinationAngleGround());

  if (negative(gradient)) {
    data.SetValueColor(0);
    data.SetValue(_T("+++"));
    return;
  }
  if (::GradientValid(gradient)) {
    data.SetValueFromGlideRatio(gradient);
  } else {
    data.SetInvalid();
  }

  // Set Color (blue/black)
  data.SetValueColor(alternate->solution.IsFinalGlide() ? 2 : 0);
}
예제 #22
0
void
InfoBoxContentAltitudeBaro::Update(InfoBoxData &data)
{
  const NMEAInfo &basic = CommonInterface::Basic();

  if (!basic.baro_altitude_available) {
    data.SetInvalid();

    if (basic.pressure_altitude_available)
      data.SetComment(_("no QNH"));

    return;
  }

  data.SetValueFromAltitude(basic.baro_altitude);
  data.SetCommentFromAlternateAltitude(basic.baro_altitude);
}
예제 #23
0
void
UpdateInfoBoxThermalLastTime(InfoBoxData &data)
{
  const OneClimbInfo &thermal = CommonInterface::Calculated().last_thermal;
  if (!thermal.IsDefined()) {
    data.SetInvalid();
    return;
  }

  // Set Value

  TCHAR value[32], comment[32];
  FormatTimeTwoLines(value, comment, (int)thermal.duration);

  data.SetValue(value);
  data.SetComment(comment);
}
예제 #24
0
파일: Time.cpp 프로젝트: MindMil/XCSoar
void
UpdateInfoBoxTimeFlight(InfoBoxData &data)
{
  const FlyingState &flight = CommonInterface::Calculated().flight;

  if (!positive(flight.flight_time)) {
    data.SetInvalid();
    return;
  }

  // Set Value
  TCHAR value[32], comment[32];
  FormatTimeTwoLines(value, comment, (int)flight.flight_time);

  data.SetValue(value);
  data.SetComment(comment);
}
예제 #25
0
파일: Task.cpp 프로젝트: Adrien81/XCSoar
void
InfoBoxContentNextWaypoint::Update(InfoBoxData &data)
{
  // use proper non-terminal next task stats

  const Waypoint* way_point = protected_task_manager != NULL
    ? protected_task_manager->GetActiveWaypoint()
    : NULL;

  if (!way_point) {
    data.SetTitle(_("Next"));
    data.SetInvalid();
    return;
  }

  data.SetTitle(way_point->name.c_str());

  // Set Comment
  if (way_point->radio_frequency.IsDefined()) {
    const unsigned freq = way_point->radio_frequency.GetKiloHertz();
    data.FormatComment(_T("%u.%03u %s"),
                       freq / 1000, freq % 1000, way_point->comment.c_str());
  }
  else
    data.SetComment(way_point->comment.c_str());

  const NMEAInfo &basic = CommonInterface::Basic();
  const TaskStats &task_stats = CommonInterface::Calculated().task_stats;
  const GlideResult &solution_remaining =
    task_stats.current_leg.solution_remaining;
  const GeoVector &vector_remaining = task_stats.current_leg.vector_remaining;
  if (!basic.track_available || !task_stats.task_valid ||
      !vector_remaining.IsValid()) {
    data.SetValueInvalid();
    return;
  }

  // Set Value
  Angle Value = vector_remaining.bearing - basic.track;
  data.SetValueFromBearingDifference(Value);

  // Set Color (blue/black)
  data.SetValueColor(solution_remaining.IsFinalGlide() ? 2 : 0);
}
예제 #26
0
파일: Weather.cpp 프로젝트: Adrien81/XCSoar
void
UpdateInfoBoxWindSpeed(InfoBoxData &data)
{
  const DerivedInfo &info = CommonInterface::Calculated();
  if (!info.wind_available) {
    data.SetInvalid();
    return;
  }

  // Set Value
  data.SetValue(_T("%2.0f"),
                    Units::ToUserWindSpeed(info.wind.norm));

  // Set Unit
  data.SetValueUnit(Units::current.wind_speed_unit);

  // Set Comment
  data.SetComment(info.wind.bearing);
}
예제 #27
0
파일: Weather.cpp 프로젝트: Adrien81/XCSoar
void
InfoBoxContentWindArrow::Update(InfoBoxData &data)
{
  const DerivedInfo &info = CommonInterface::Calculated();
  if (!info.wind_available || info.wind.IsZero()) {
    data.SetInvalid();
    return;
  }

  data.SetCustom();

  TCHAR speed_buffer[16], bearing_buffer[16];
  FormatUserWindSpeed(info.wind.norm, speed_buffer, true, false);
  FormatBearing(bearing_buffer, ARRAY_SIZE(bearing_buffer), info.wind.bearing);

  StaticString<32> buffer;
  buffer.Format(_T("%s / %s"), bearing_buffer, speed_buffer);
  data.SetComment(buffer);
}
예제 #28
0
void
InfoBoxContentFlightLevel::Update(InfoBoxData &data)
{
  const NMEAInfo &basic = CommonInterface::Basic();
  const ComputerSettings &settings_computer =
    CommonInterface::GetComputerSettings();

  if (basic.pressure_altitude_available) {
    fixed Altitude = Units::ToUserUnit(basic.pressure_altitude, Unit::FEET);

    // Title color black
    data.SetTitleColor(0);

    // Set Value
    data.UnsafeFormatValue(_T("%03d"), iround(Altitude / 100));

    // Set Comment
    data.UnsafeFormatComment(_T("%dft"), iround(Altitude));

  } else if (basic.gps_altitude_available &&
             settings_computer.pressure_available) {
    // Take gps altitude as baro altitude. This is inaccurate but still fits our needs.
    const AtmosphericPressure &qnh = settings_computer.pressure;
    fixed Altitude = Units::ToUserUnit(qnh.QNHAltitudeToPressureAltitude(basic.gps_altitude), Unit::FEET);

    // Title color red
    data.SetTitleColor(1);

    // Set Value
    data.UnsafeFormatValue(_T("%03d"), iround(Altitude / 100));

    // Set Comment
    data.UnsafeFormatComment(_T("%dft"), iround(Altitude));

  } else if ((basic.baro_altitude_available || basic.gps_altitude_available) &&
             !settings_computer.pressure_available) {
    data.SetInvalid();
    data.SetComment(_("no QNH"));
  } else {
    data.SetInvalid();
  }
}
예제 #29
0
void
InfoBoxContentFinalETE::Update(InfoBoxData &data)
{
  const TaskStats &task_stats = XCSoarInterface::Calculated().task_stats;

  if (!task_stats.task_valid || !task_stats.total.IsAchievable()) {
    data.SetInvalid();
    return;
  }

  assert(!negative(task_stats.total.time_remaining));

  TCHAR HHMMSSsmart[32];
  TCHAR SSsmart[32];
  const int dd = abs((int)task_stats.total.time_remaining);
  Units::TimeToTextSmart(HHMMSSsmart, SSsmart, dd);

  data.SetValue(HHMMSSsmart);
  data.SetComment(SSsmart);
}
예제 #30
0
파일: Task.cpp 프로젝트: Adrien81/XCSoar
void
UpdateInfoBoxFinalETE(InfoBoxData &data)
{
  const TaskStats &task_stats = CommonInterface::Calculated().task_stats;

  if (!task_stats.task_valid || !task_stats.total.IsAchievable()) {
    data.SetInvalid();
    return;
  }

  assert(!negative(task_stats.total.time_remaining_now));

  TCHAR value[32];
  TCHAR comment[32];
  const int dd = abs((int)task_stats.total.time_remaining_now);
  FormatTimeTwoLines(value, comment, dd);

  data.SetValue(value);
  data.SetComment(comment);
}