<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/web_resource/promo_resource_service.h"

#include "base/string_number_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "base/time.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/sync_ui_util.h"
#include "chrome/common/pref_names.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_service.h"
#include "content/common/notification_type.h"

namespace {

// Delay on first fetch so we don't interfere with startup.
static const int kStartResourceFetchDelay = 5000;

// Delay between calls to update the cache (48 hours).
static const int kCacheUpdateDelay = 48 * 60 * 60 * 1000;

// Users are randomly assigned to one of kNTPPromoGroupSize buckets, in order
// to be able to roll out promos slowly, or display different promos to
// different groups.
static const int kNTPPromoGroupSize = 16;

// Maximum number of hours for each time slice (4 weeks).
static const int kMaxTimeSliceHours = 24 * 7 * 4;

// Used to determine which build type should be shown a given promo.
enum BuildType {
  DEV_BUILD = 1,
  BETA_BUILD = 1 << 1,
  STABLE_BUILD = 1 << 2,
};

}  // namespace

const char* PromoResourceService::kCurrentTipPrefName = "current_tip";
const char* PromoResourceService::kTipCachePrefName = "tips";

// Server for dynamically loaded NTP HTML elements. TODO(mirandac): append
// locale for future usage, when we're serving localizable strings.
const char* PromoResourceService::kDefaultPromoResourceServer =
    "https://www.google.com/support/chrome/bin/topic/1142433/inproduct?hl=";

PromoResourceService::PromoResourceService(Profile* profile)
    : WebResourceService(profile,
                         profile->GetPrefs(),
                         PromoResourceService::kDefaultPromoResourceServer,
                         true,  // append locale to URL
                         NotificationType::PROMO_RESOURCE_STATE_CHANGED,
                         prefs::kNTPPromoResourceCacheUpdate,
                         kStartResourceFetchDelay,
                         kCacheUpdateDelay),
      web_resource_cache_(NULL) {
  Init();
}

PromoResourceService::~PromoResourceService() { }

void PromoResourceService::Init() {
  prefs_->RegisterDoublePref(prefs::kNTPCustomLogoStart, 0);
  prefs_->RegisterDoublePref(prefs::kNTPCustomLogoEnd, 0);
  prefs_->RegisterDoublePref(prefs::kNTPPromoStart, 0);
  prefs_->RegisterDoublePref(prefs::kNTPPromoEnd, 0);
  prefs_->RegisterStringPref(prefs::kNTPPromoLine, std::string());
  prefs_->RegisterBooleanPref(prefs::kNTPPromoClosed, false);
  prefs_->RegisterIntegerPref(prefs::kNTPPromoGroup, -1);
  prefs_->RegisterIntegerPref(prefs::kNTPPromoBuild,
                              DEV_BUILD | BETA_BUILD | STABLE_BUILD);
  prefs_->RegisterIntegerPref(prefs::kNTPPromoGroupTimeSlice, 0);

  // If the promo start is in the future, set a notification task to invalidate
  // the NTP cache at the time of the promo start.
  double promo_start = prefs_->GetDouble(prefs::kNTPPromoStart);
  double promo_end = prefs_->GetDouble(prefs::kNTPPromoEnd);
  ScheduleNotification(promo_start, promo_end);
}

void PromoResourceService::Unpack(const DictionaryValue& parsed_json) {
  UnpackLogoSignal(parsed_json);
  UnpackPromoSignal(parsed_json);
}

void PromoResourceService::ScheduleNotification(double promo_start,
                                                double promo_end) {
  if (promo_start > 0 && promo_end > 0) {
    int64 ms_until_start =
        static_cast<int64>((base::Time::FromDoubleT(
            promo_start) - base::Time::Now()).InMilliseconds());
    int64 ms_until_end =
        static_cast<int64>((base::Time::FromDoubleT(
            promo_end) - base::Time::Now()).InMilliseconds());
    if (ms_until_start > 0)
      PostNotification(ms_until_start);
    if (ms_until_end > 0) {
      PostNotification(ms_until_end);
      if (ms_until_start <= 0) {
        // Notify immediately if time is between start and end.
        PostNotification(0);
      }
    }
  }
}

void PromoResourceService::UnpackTips(const DictionaryValue& parsed_json) {
  // Get dictionary of cached preferences.
  web_resource_cache_ =
      prefs_->GetMutableDictionary(prefs::kNTPPromoResourceCache);

  // The list of individual tips.
  ListValue* tip_holder = new ListValue();
  web_resource_cache_->Set(PromoResourceService::kTipCachePrefName, tip_holder);

  DictionaryValue* topic_dict;
  ListValue* answer_list;
  std::string topic_id;
  std::string answer_id;
  std::string inproduct;
  int tip_counter = 0;

  if (parsed_json.GetDictionary("topic", &topic_dict)) {
    if (topic_dict->GetString("topic_id", &topic_id))
      web_resource_cache_->SetString("topic_id", topic_id);
    if (topic_dict->GetList("answers", &answer_list)) {
      for (ListValue::const_iterator tip_iter = answer_list->begin();
           tip_iter != answer_list->end(); ++tip_iter) {
        if (!(*tip_iter)->IsType(Value::TYPE_DICTIONARY))
          continue;
        DictionaryValue* a_dic =
            static_cast<DictionaryValue*>(*tip_iter);
        if (a_dic->GetString("inproduct", &inproduct)) {
          tip_holder->Append(Value::CreateStringValue(inproduct));
        }
        tip_counter++;
      }
      // If tips exist, set current index to 0.
      if (!inproduct.empty()) {
        web_resource_cache_->SetInteger(
            PromoResourceService::kCurrentTipPrefName, 0);
      }
    }
  }
}

void PromoResourceService::UnpackPromoSignal(
    const DictionaryValue& parsed_json) {
  DictionaryValue* topic_dict;
  ListValue* answer_list;
  double old_promo_start = 0;
  double old_promo_end = 0;
  double promo_start = 0;
  double promo_end = 0;

  // Check for preexisting start and end values.
  if (prefs_->HasPrefPath(prefs::kNTPPromoStart) &&
      prefs_->HasPrefPath(prefs::kNTPPromoEnd)) {
    old_promo_start = prefs_->GetDouble(prefs::kNTPPromoStart);
    old_promo_end = prefs_->GetDouble(prefs::kNTPPromoEnd);
  }

  // Check for newly received start and end values.
  if (parsed_json.GetDictionary("topic", &topic_dict)) {
    if (topic_dict->GetList("answers", &answer_list)) {
      std::string promo_start_string = "";
      std::string promo_end_string = "";
      std::string promo_string = "";
      std::string promo_build = "";
      int promo_build_type = 0;
      int time_slice_hrs = 0;
      for (ListValue::const_iterator tip_iter = answer_list->begin();
           tip_iter != answer_list->end(); ++tip_iter) {
        if (!(*tip_iter)->IsType(Value::TYPE_DICTIONARY))
          continue;
        DictionaryValue* a_dic =
            static_cast<DictionaryValue*>(*tip_iter);
        std::string promo_signal;
        if (a_dic->GetString("name", &promo_signal)) {
          if (promo_signal == "promo_start") {
            a_dic->GetString("question", &promo_build);
            size_t split = promo_build.find(":");
            if (split != std::string::npos &&
                base::StringToInt(promo_build.substr(0, split),
                                  &promo_build_type) &&
                base::StringToInt(promo_build.substr(split+1),
                                  &time_slice_hrs) &&
                promo_build_type >= 0 &&
                promo_build_type <= (DEV_BUILD | BETA_BUILD | STABLE_BUILD) &&
                time_slice_hrs >= 0 &&
                time_slice_hrs <= kMaxTimeSliceHours) {
              prefs_->SetInteger(prefs::kNTPPromoBuild, promo_build_type);
              prefs_->SetInteger(prefs::kNTPPromoGroupTimeSlice,
                                 time_slice_hrs);
            } else {
              // If no time data or bad time data are set, show promo on all
              // builds with no time slicing.
              prefs_->SetInteger(prefs::kNTPPromoBuild,
                                 DEV_BUILD | BETA_BUILD | STABLE_BUILD);
              prefs_->SetInteger(prefs::kNTPPromoGroupTimeSlice, 0);
            }
            a_dic->GetString("inproduct", &promo_start_string);
            a_dic->GetString("tooltip", &promo_string);
            prefs_->SetString(prefs::kNTPPromoLine, promo_string);
            srand(static_cast<uint32>(time(NULL)));
            prefs_->SetInteger(prefs::kNTPPromoGroup,
                               rand() % kNTPPromoGroupSize);
          } else if (promo_signal == "promo_end") {
            a_dic->GetString("inproduct", &promo_end_string);
          }
        }
      }
      if (!promo_start_string.empty() &&
          promo_start_string.length() > 0 &&
          !promo_end_string.empty() &&
          promo_end_string.length() > 0) {
        base::Time start_time;
        base::Time end_time;
        if (base::Time::FromString(
                ASCIIToWide(promo_start_string).c_str(), &start_time) &&
            base::Time::FromString(
                ASCIIToWide(promo_end_string).c_str(), &end_time)) {
          // Add group time slice, adjusted from hours to seconds.
          promo_start = start_time.ToDoubleT() +
              (prefs_->FindPreference(prefs::kNTPPromoGroup) ?
                  prefs_->GetInteger(prefs::kNTPPromoGroup) *
                      time_slice_hrs * 60 * 60 : 0);
          promo_end = end_time.ToDoubleT();
        }
      }
    }
  }

  // If start or end times have changed, trigger a new web resource
  // notification, so that the logo on the NTP is updated. This check is
  // outside the reading of the web resource data, because the absence of
  // dates counts as a triggering change if there were dates before.
  // Also reset the promo closed preference, to signal a new promo.
  if (!(old_promo_start == promo_start) ||
      !(old_promo_end == promo_end)) {
    prefs_->SetDouble(prefs::kNTPPromoStart, promo_start);
    prefs_->SetDouble(prefs::kNTPPromoEnd, promo_end);
    prefs_->SetBoolean(prefs::kNTPPromoClosed, false);
    ScheduleNotification(promo_start, promo_end);
  }
}

void PromoResourceService::UnpackLogoSignal(
    const DictionaryValue& parsed_json) {
  DictionaryValue* topic_dict;
  ListValue* answer_list;
  double old_logo_start = 0;
  double old_logo_end = 0;
  double logo_start = 0;
  double logo_end = 0;

  // Check for preexisting start and end values.
  if (prefs_->HasPrefPath(prefs::kNTPCustomLogoStart) &&
      prefs_->HasPrefPath(prefs::kNTPCustomLogoEnd)) {
    old_logo_start = prefs_->GetDouble(prefs::kNTPCustomLogoStart);
    old_logo_end = prefs_->GetDouble(prefs::kNTPCustomLogoEnd);
  }

  // Check for newly received start and end values.
  if (parsed_json.GetDictionary("topic", &topic_dict)) {
    if (topic_dict->GetList("answers", &answer_list)) {
      std::string logo_start_string = "";
      std::string logo_end_string = "";
      for (ListValue::const_iterator tip_iter = answer_list->begin();
           tip_iter != answer_list->end(); ++tip_iter) {
        if (!(*tip_iter)->IsType(Value::TYPE_DICTIONARY))
          continue;
        DictionaryValue* a_dic =
            static_cast<DictionaryValue*>(*tip_iter);
        std::string logo_signal;
        if (a_dic->GetString("name", &logo_signal)) {
          if (logo_signal == "custom_logo_start") {
            a_dic->GetString("inproduct", &logo_start_string);
          } else if (logo_signal == "custom_logo_end") {
            a_dic->GetString("inproduct", &logo_end_string);
          }
        }
      }
      if (!logo_start_string.empty() &&
          logo_start_string.length() > 0 &&
          !logo_end_string.empty() &&
          logo_end_string.length() > 0) {
        base::Time start_time;
        base::Time end_time;
        if (base::Time::FromString(
                ASCIIToWide(logo_start_string).c_str(), &start_time) &&
            base::Time::FromString(
                ASCIIToWide(logo_end_string).c_str(), &end_time)) {
          logo_start = start_time.ToDoubleT();
          logo_end = end_time.ToDoubleT();
        }
      }
    }
  }

  // If logo start or end times have changed, trigger a new web resource
  // notification, so that the logo on the NTP is updated. This check is
  // outside the reading of the web resource data, because the absence of
  // dates counts as a triggering change if there were dates before.
  if (!(old_logo_start == logo_start) ||
      !(old_logo_end == logo_end)) {
    prefs_->SetDouble(prefs::kNTPCustomLogoStart, logo_start);
    prefs_->SetDouble(prefs::kNTPCustomLogoEnd, logo_end);
    NotificationService* service = NotificationService::current();
    service->Notify(NotificationType::PROMO_RESOURCE_STATE_CHANGED,
                    Source<WebResourceService>(this),
                    NotificationService::NoDetails());
  }
}

namespace PromoResourceServiceUtil {

bool CanShowPromo(Profile* profile) {
  bool promo_closed = false;
  PrefService* prefs = profile->GetPrefs();
  if (prefs->HasPrefPath(prefs::kNTPPromoClosed))
    promo_closed = prefs->GetBoolean(prefs::kNTPPromoClosed);

  // Only show if not synced.
  bool is_synced =
      (profile->HasProfileSyncService() &&
          sync_ui_util::GetStatus(
              profile->GetProfileSyncService()) == sync_ui_util::SYNCED);

  // GetVersionStringModifier hits the registry. See http://crbug.com/70898.
  base::ThreadRestrictions::ScopedAllowIO allow_io;
  const std::string channel = platform_util::GetVersionStringModifier();
  bool is_promo_build = false;
  if (prefs->HasPrefPath(prefs::kNTPPromoBuild)) {
    int builds_allowed = prefs->GetInteger(prefs::kNTPPromoBuild);
    if (channel == "dev" || channel == "dev-m") {
      is_promo_build = (DEV_BUILD & builds_allowed) != 0;
    } else if (channel == "beta" || channel == "beta-m") {
      is_promo_build = (BETA_BUILD & builds_allowed) != 0;
    } else if (channel == "" || channel == "m") {
      is_promo_build = (STABLE_BUILD & builds_allowed) != 0;
    } else {
      is_promo_build = false;
    }
  }

  return !promo_closed && !is_synced && is_promo_build;
}

}  // namespace PromoResourceServiceUtil

<commit_msg>Fix the PromoResourceService so that it does not display a promo if data from the server is corrupt.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/web_resource/promo_resource_service.h"

#include "base/string_number_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "base/time.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/sync_ui_util.h"
#include "chrome/common/pref_names.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_service.h"
#include "content/common/notification_type.h"

namespace {

// Delay on first fetch so we don't interfere with startup.
static const int kStartResourceFetchDelay = 5000;

// Delay between calls to update the cache (48 hours).
static const int kCacheUpdateDelay = 48 * 60 * 60 * 1000;

// Users are randomly assigned to one of kNTPPromoGroupSize buckets, in order
// to be able to roll out promos slowly, or display different promos to
// different groups.
static const int kNTPPromoGroupSize = 16;

// Maximum number of hours for each time slice (4 weeks).
static const int kMaxTimeSliceHours = 24 * 7 * 4;

// Used to determine which build type should be shown a given promo.
enum BuildType {
  NO_BUILD = 0,
  DEV_BUILD = 1,
  BETA_BUILD = 1 << 1,
  STABLE_BUILD = 1 << 2,
};

}  // namespace

const char* PromoResourceService::kCurrentTipPrefName = "current_tip";
const char* PromoResourceService::kTipCachePrefName = "tips";

// Server for dynamically loaded NTP HTML elements. TODO(mirandac): append
// locale for future usage, when we're serving localizable strings.
const char* PromoResourceService::kDefaultPromoResourceServer =
    "https://www.google.com/support/chrome/bin/topic/1142433/inproduct?hl=";

PromoResourceService::PromoResourceService(Profile* profile)
    : WebResourceService(profile,
                         profile->GetPrefs(),
                         PromoResourceService::kDefaultPromoResourceServer,
                         true,  // append locale to URL
                         NotificationType::PROMO_RESOURCE_STATE_CHANGED,
                         prefs::kNTPPromoResourceCacheUpdate,
                         kStartResourceFetchDelay,
                         kCacheUpdateDelay),
      web_resource_cache_(NULL) {
  Init();
}

PromoResourceService::~PromoResourceService() { }

void PromoResourceService::Init() {
  prefs_->RegisterDoublePref(prefs::kNTPCustomLogoStart, 0);
  prefs_->RegisterDoublePref(prefs::kNTPCustomLogoEnd, 0);
  prefs_->RegisterDoublePref(prefs::kNTPPromoStart, 0);
  prefs_->RegisterDoublePref(prefs::kNTPPromoEnd, 0);
  prefs_->RegisterStringPref(prefs::kNTPPromoLine, std::string());
  prefs_->RegisterBooleanPref(prefs::kNTPPromoClosed, false);
  prefs_->RegisterIntegerPref(prefs::kNTPPromoGroup, -1);
  prefs_->RegisterIntegerPref(prefs::kNTPPromoBuild,
                              DEV_BUILD | BETA_BUILD | STABLE_BUILD);
  prefs_->RegisterIntegerPref(prefs::kNTPPromoGroupTimeSlice, 0);

  // If the promo start is in the future, set a notification task to invalidate
  // the NTP cache at the time of the promo start.
  double promo_start = prefs_->GetDouble(prefs::kNTPPromoStart);
  double promo_end = prefs_->GetDouble(prefs::kNTPPromoEnd);
  ScheduleNotification(promo_start, promo_end);
}

void PromoResourceService::Unpack(const DictionaryValue& parsed_json) {
  UnpackLogoSignal(parsed_json);
  UnpackPromoSignal(parsed_json);
}

void PromoResourceService::ScheduleNotification(double promo_start,
                                                double promo_end) {
  if (promo_start > 0 && promo_end > 0) {
    int64 ms_until_start =
        static_cast<int64>((base::Time::FromDoubleT(
            promo_start) - base::Time::Now()).InMilliseconds());
    int64 ms_until_end =
        static_cast<int64>((base::Time::FromDoubleT(
            promo_end) - base::Time::Now()).InMilliseconds());
    if (ms_until_start > 0)
      PostNotification(ms_until_start);
    if (ms_until_end > 0) {
      PostNotification(ms_until_end);
      if (ms_until_start <= 0) {
        // Notify immediately if time is between start and end.
        PostNotification(0);
      }
    }
  }
}

void PromoResourceService::UnpackTips(const DictionaryValue& parsed_json) {
  // Get dictionary of cached preferences.
  web_resource_cache_ =
      prefs_->GetMutableDictionary(prefs::kNTPPromoResourceCache);

  // The list of individual tips.
  ListValue* tip_holder = new ListValue();
  web_resource_cache_->Set(PromoResourceService::kTipCachePrefName, tip_holder);

  DictionaryValue* topic_dict;
  ListValue* answer_list;
  std::string topic_id;
  std::string answer_id;
  std::string inproduct;
  int tip_counter = 0;

  if (parsed_json.GetDictionary("topic", &topic_dict)) {
    if (topic_dict->GetString("topic_id", &topic_id))
      web_resource_cache_->SetString("topic_id", topic_id);
    if (topic_dict->GetList("answers", &answer_list)) {
      for (ListValue::const_iterator tip_iter = answer_list->begin();
           tip_iter != answer_list->end(); ++tip_iter) {
        if (!(*tip_iter)->IsType(Value::TYPE_DICTIONARY))
          continue;
        DictionaryValue* a_dic =
            static_cast<DictionaryValue*>(*tip_iter);
        if (a_dic->GetString("inproduct", &inproduct)) {
          tip_holder->Append(Value::CreateStringValue(inproduct));
        }
        tip_counter++;
      }
      // If tips exist, set current index to 0.
      if (!inproduct.empty()) {
        web_resource_cache_->SetInteger(
            PromoResourceService::kCurrentTipPrefName, 0);
      }
    }
  }
}

void PromoResourceService::UnpackPromoSignal(
    const DictionaryValue& parsed_json) {
  DictionaryValue* topic_dict;
  ListValue* answer_list;
  double old_promo_start = 0;
  double old_promo_end = 0;
  double promo_start = 0;
  double promo_end = 0;

  // Check for preexisting start and end values.
  if (prefs_->HasPrefPath(prefs::kNTPPromoStart) &&
      prefs_->HasPrefPath(prefs::kNTPPromoEnd)) {
    old_promo_start = prefs_->GetDouble(prefs::kNTPPromoStart);
    old_promo_end = prefs_->GetDouble(prefs::kNTPPromoEnd);
  }

  // Check for newly received start and end values.
  if (parsed_json.GetDictionary("topic", &topic_dict)) {
    if (topic_dict->GetList("answers", &answer_list)) {
      std::string promo_start_string = "";
      std::string promo_end_string = "";
      std::string promo_string = "";
      std::string promo_build = "";
      int promo_build_type = 0;
      int time_slice_hrs = 0;
      for (ListValue::const_iterator tip_iter = answer_list->begin();
           tip_iter != answer_list->end(); ++tip_iter) {
        if (!(*tip_iter)->IsType(Value::TYPE_DICTIONARY))
          continue;
        DictionaryValue* a_dic =
            static_cast<DictionaryValue*>(*tip_iter);
        std::string promo_signal;
        if (a_dic->GetString("name", &promo_signal)) {
          if (promo_signal == "promo_start") {
            a_dic->GetString("question", &promo_build);
            size_t split = promo_build.find(":");
            if (split != std::string::npos &&
                base::StringToInt(promo_build.substr(0, split),
                                  &promo_build_type) &&
                base::StringToInt(promo_build.substr(split+1),
                                  &time_slice_hrs) &&
                promo_build_type >= 0 &&
                promo_build_type <= (DEV_BUILD | BETA_BUILD | STABLE_BUILD) &&
                time_slice_hrs >= 0 &&
                time_slice_hrs <= kMaxTimeSliceHours) {
              prefs_->SetInteger(prefs::kNTPPromoBuild, promo_build_type);
              prefs_->SetInteger(prefs::kNTPPromoGroupTimeSlice,
                                 time_slice_hrs);
            } else {
              // If no time data or bad time data are set, do not show promo.
              prefs_->SetInteger(prefs::kNTPPromoBuild, NO_BUILD);
              prefs_->SetInteger(prefs::kNTPPromoGroupTimeSlice, 0);
            }
            a_dic->GetString("inproduct", &promo_start_string);
            a_dic->GetString("tooltip", &promo_string);
            prefs_->SetString(prefs::kNTPPromoLine, promo_string);
            srand(static_cast<uint32>(time(NULL)));
            prefs_->SetInteger(prefs::kNTPPromoGroup,
                               rand() % kNTPPromoGroupSize);
          } else if (promo_signal == "promo_end") {
            a_dic->GetString("inproduct", &promo_end_string);
          }
        }
      }
      if (!promo_start_string.empty() &&
          promo_start_string.length() > 0 &&
          !promo_end_string.empty() &&
          promo_end_string.length() > 0) {
        base::Time start_time;
        base::Time end_time;
        if (base::Time::FromString(
                ASCIIToWide(promo_start_string).c_str(), &start_time) &&
            base::Time::FromString(
                ASCIIToWide(promo_end_string).c_str(), &end_time)) {
          // Add group time slice, adjusted from hours to seconds.
          promo_start = start_time.ToDoubleT() +
              (prefs_->FindPreference(prefs::kNTPPromoGroup) ?
                  prefs_->GetInteger(prefs::kNTPPromoGroup) *
                      time_slice_hrs * 60 * 60 : 0);
          promo_end = end_time.ToDoubleT();
        }
      }
    }
  }

  // If start or end times have changed, trigger a new web resource
  // notification, so that the logo on the NTP is updated. This check is
  // outside the reading of the web resource data, because the absence of
  // dates counts as a triggering change if there were dates before.
  // Also reset the promo closed preference, to signal a new promo.
  if (!(old_promo_start == promo_start) ||
      !(old_promo_end == promo_end)) {
    prefs_->SetDouble(prefs::kNTPPromoStart, promo_start);
    prefs_->SetDouble(prefs::kNTPPromoEnd, promo_end);
    prefs_->SetBoolean(prefs::kNTPPromoClosed, false);
    ScheduleNotification(promo_start, promo_end);
  }
}

void PromoResourceService::UnpackLogoSignal(
    const DictionaryValue& parsed_json) {
  DictionaryValue* topic_dict;
  ListValue* answer_list;
  double old_logo_start = 0;
  double old_logo_end = 0;
  double logo_start = 0;
  double logo_end = 0;

  // Check for preexisting start and end values.
  if (prefs_->HasPrefPath(prefs::kNTPCustomLogoStart) &&
      prefs_->HasPrefPath(prefs::kNTPCustomLogoEnd)) {
    old_logo_start = prefs_->GetDouble(prefs::kNTPCustomLogoStart);
    old_logo_end = prefs_->GetDouble(prefs::kNTPCustomLogoEnd);
  }

  // Check for newly received start and end values.
  if (parsed_json.GetDictionary("topic", &topic_dict)) {
    if (topic_dict->GetList("answers", &answer_list)) {
      std::string logo_start_string = "";
      std::string logo_end_string = "";
      for (ListValue::const_iterator tip_iter = answer_list->begin();
           tip_iter != answer_list->end(); ++tip_iter) {
        if (!(*tip_iter)->IsType(Value::TYPE_DICTIONARY))
          continue;
        DictionaryValue* a_dic =
            static_cast<DictionaryValue*>(*tip_iter);
        std::string logo_signal;
        if (a_dic->GetString("name", &logo_signal)) {
          if (logo_signal == "custom_logo_start") {
            a_dic->GetString("inproduct", &logo_start_string);
          } else if (logo_signal == "custom_logo_end") {
            a_dic->GetString("inproduct", &logo_end_string);
          }
        }
      }
      if (!logo_start_string.empty() &&
          logo_start_string.length() > 0 &&
          !logo_end_string.empty() &&
          logo_end_string.length() > 0) {
        base::Time start_time;
        base::Time end_time;
        if (base::Time::FromString(
                ASCIIToWide(logo_start_string).c_str(), &start_time) &&
            base::Time::FromString(
                ASCIIToWide(logo_end_string).c_str(), &end_time)) {
          logo_start = start_time.ToDoubleT();
          logo_end = end_time.ToDoubleT();
        }
      }
    }
  }

  // If logo start or end times have changed, trigger a new web resource
  // notification, so that the logo on the NTP is updated. This check is
  // outside the reading of the web resource data, because the absence of
  // dates counts as a triggering change if there were dates before.
  if (!(old_logo_start == logo_start) ||
      !(old_logo_end == logo_end)) {
    prefs_->SetDouble(prefs::kNTPCustomLogoStart, logo_start);
    prefs_->SetDouble(prefs::kNTPCustomLogoEnd, logo_end);
    NotificationService* service = NotificationService::current();
    service->Notify(NotificationType::PROMO_RESOURCE_STATE_CHANGED,
                    Source<WebResourceService>(this),
                    NotificationService::NoDetails());
  }
}

namespace PromoResourceServiceUtil {

bool CanShowPromo(Profile* profile) {
  bool promo_closed = false;
  PrefService* prefs = profile->GetPrefs();
  if (prefs->HasPrefPath(prefs::kNTPPromoClosed))
    promo_closed = prefs->GetBoolean(prefs::kNTPPromoClosed);

  // Only show if not synced.
  bool is_synced =
      (profile->HasProfileSyncService() &&
          sync_ui_util::GetStatus(
              profile->GetProfileSyncService()) == sync_ui_util::SYNCED);

  // GetVersionStringModifier hits the registry. See http://crbug.com/70898.
  base::ThreadRestrictions::ScopedAllowIO allow_io;
  const std::string channel = platform_util::GetVersionStringModifier();
  bool is_promo_build = false;
  if (prefs->HasPrefPath(prefs::kNTPPromoBuild)) {
    int builds_allowed = prefs->GetInteger(prefs::kNTPPromoBuild);
    if (builds_allowed == NO_BUILD)
      return false;
    if (channel == "dev" || channel == "dev-m") {
      is_promo_build = (DEV_BUILD & builds_allowed) != 0;
    } else if (channel == "beta" || channel == "beta-m") {
      is_promo_build = (BETA_BUILD & builds_allowed) != 0;
    } else if (channel == "" || channel == "m") {
      is_promo_build = (STABLE_BUILD & builds_allowed) != 0;
    } else {
      is_promo_build = false;
    }
  }

  return !promo_closed && !is_synced && is_promo_build;
}

}  // namespace PromoResourceServiceUtil

<|endoftext|>


<commit_before>#include "NumberTest.h"
#include "SimpleAudioEngine.h"
#include "LanguageManager.h"
#include "Planet.h"

USING_NS_CC;
using namespace CocosDenshion;

NumberTest * NumberTest::create(int _number, Sprite * _bg)
{
	NumberTest * tip = new NumberTest();

	if (tip && tip->init())
	{
		tip->autorelease();
		tip->initNumberTest(_number, _bg);
		return tip;
	}
	CC_SAFE_DELETE(tip);
	return NULL;
}

NumberTest::~NumberTest()
{

}

void NumberTest::initNumberTest(int _number, Sprite * _bg)
{
	number = _number;
	setupDirector();
	setupBoundary();
	setupSprite();
	setupAudio();
	setupLabel();
	consumed = false;
	this->setScale(0.9);
	bg = _bg;
	BaseObject::initObject();
}

void NumberTest::setupDirector()
{
	visibleSize = Director::getInstance()->getVisibleSize();
	winSize = Director::getInstance()->getWinSize(); //design size?
	frameSize = Director::getInstance()->getOpenGLView()->getFrameSize();
	origin = Director::getInstance()->getVisibleOrigin(); // common to all maps i hope
}

void NumberTest::setupBoundary()
{
	boundary.shape = SHAPE::circle;
	boundary.active = true;
	boundary.r = 20;
}

void NumberTest::setupSprite()
{
	auto sprite = Sprite::createWithSpriteFrameName("tip.png");
	auto moveUp = EaseInOut::create(MoveBy::create(2, Vec2(0, 5.0f)), 2);
	auto moveBack = EaseInOut::create(MoveBy::create(2, Vec2(0, -5.0f)), 2);
	auto seq1 = Sequence::create(moveUp, moveBack, nullptr);
	sprite->runAction(RepeatForever::create(seq1));
	this->addChild(sprite);
}

void NumberTest::setupAudio()
{
	auto audio = SimpleAudioEngine::getInstance();
	audio->preloadEffect("sfx/bot.wav");
	langCode = "en";
	if (CCApplication::getInstance()->getCurrentLanguage() == LanguageType::SWAHILI)
		langCode = "sw";
	for (int i = 0; i <= 10; i++)
	{
		audio->preloadEffect(("sfx/" + langCode + "/digit_" + std::to_string(i) + ".wav").c_str());
	}
}

void NumberTest::setupLabel()
{
	numberLabel = Label::createWithTTF(std::to_string(number), LanguageManager::getString("font"), 50);
	numberLabel->setPosition(Vec2(0, 100));
	auto action0 = ScaleTo::create(0.3f, 1.1f, 1.1f);
	auto action1 = ScaleTo::create(0.3f, 0.99f, 0.99f);
	ActionInterval *bouncingAction = Sequence::create(action0, action1, nullptr);
	auto action = RepeatForever::create(bouncingAction);
	numberLabel->runAction(action);
	numberLabel->setVisible(false);
	this->addChild(numberLabel);
}

void NumberTest::update(bool hit)
{
	if (hit && !isMessagevisible && !consumed)
	{
		auto audio = SimpleAudioEngine::getInstance();
		isMessagevisible = true;
		audio->playEffect("sfx/bot.wav");

		Vector<FiniteTimeAction *>  allActions;
		auto action = MoveTo::create(1.5, Vec2(0, 0));
		TargetedAction * t1 = TargetedAction::create(bg, action);
		allActions.pushBack(t1);

		Vector<MenuItem *> menuItems;
		auto wrongChoice = [this, audio](Ref * sender) {
			audio->playEffect("sfx/hurt.wav");
		};
		auto rightChoice = [this, audio](Ref * sender) {
			audio->playEffect("sfx/correct.wav");
			auto * moveUp = MoveTo::create(1.5, Vec2(0, visibleSize.height));
			bg->runAction(moveUp);
			consumed = true;
		};

		Label * labelA = Label::createWithTTF("", LanguageManager::getString("font"), 120);
		labelA->setScale(0.9);
		labelA->setString(std::to_string(RandomHelper::random_int(1,10)));
		MenuItemLabel * mLabelA = MenuItemLabel::create(labelA, wrongChoice);

		Label * labelB = Label::createWithTTF("", LanguageManager::getString("font"), 120);
		labelB->setScale(0.9);
		labelB->setString("2");
		MenuItemLabel * mLabelB = MenuItemLabel::create(labelB, rightChoice);

		Label * labelC = Label::createWithTTF("", LanguageManager::getString("font"), 120);
		labelC->setScale(0.9);
		labelC->setString("3");
		MenuItemLabel * mLabelC = MenuItemLabel::create(labelC, wrongChoice);

		auto scaleUp = ScaleTo::create(1, 1.01);
		auto scaleDown = ScaleTo::create(1, 1);
		auto seqScale = Sequence::create(scaleUp, scaleDown, NULL);
		auto repScale = RepeatForever::create(seqScale);
		labelA->runAction(repScale);
		labelB->runAction(repScale->clone());
		labelC->runAction(repScale->clone());

		menuItems.pushBack(mLabelA);
		menuItems.pushBack(mLabelB);
		menuItems.pushBack(mLabelC);
		Menu * menu = Menu::createWithArray(menuItems);
		menu->setPosition(Vec2(visibleSize.width - 100, visibleSize.height / 2));
		menu->alignItemsVerticallyWithPadding(-20);
		menu->setEnabled(false);

		menu->setOpacity(100);
		bg->addChild(menu);

		auto scaleTo1 = ScaleTo::create(1.3, 1);
		int vLevel = 2;
		int hLevel = 1;
		for (int i = 1; i <= number; i++)
		{
			auto planet = Planet::create(-1);
			planet->setPosition(Vec2(105 * hLevel - 40, vLevel * visibleSize.height / 3));
			bg->addChild(planet);
			hLevel++;
			if (i == 5)
			{
				vLevel = 1;
				hLevel = 1;
			}
			planet->setScale(0.01);
			TargetedAction * tA = TargetedAction::create(planet, scaleTo1);
			allActions.pushBack(tA);
			auto playAudio = CallFunc::create([this, i]() {
				auto audio = SimpleAudioEngine::getInstance();
				audio->playEffect(("sfx/" + langCode + "/digit_" + std::to_string(i) + ".wav").c_str());
			});
			allActions.pushBack(playAudio);
		}
		auto * delay = DelayTime::create(1);
		allActions.pushBack(delay);

		auto * fadeIn = FadeIn::create(0.5);
		TargetedAction * fadeInMenu = TargetedAction::create(menu, fadeIn);
		allActions.pushBack(fadeInMenu);

		auto * enableMenuLambda = CallFunc::create([menu]() {
			menu->setEnabled(true);
		});
		allActions.pushBack(enableMenuLambda);

		auto seq = Sequence::create(allActions);
		bg->stopAllActions();
		bg->runAction(seq);
		numberLabel->setVisible(true);
	}
	else if (!hit && isMessagevisible)
	{
		isMessagevisible = false;
		auto action = MoveTo::create(1.5, Vec2(0, visibleSize.height));
		bg->stopAllActions();
		bg->runAction(action);
		bg->removeAllChildren();
	}

	if (hit && !isMessagevisible && consumed)
	{
		isMessagevisible = true;
		auto audio = SimpleAudioEngine::getInstance();
		audio->playEffect(("sfx/" + langCode + "/digit_" + std::to_string(number) + ".wav").c_str());
	}
}

<commit_msg>Added code for randomizing the correct answer<commit_after>#include "NumberTest.h"
#include "SimpleAudioEngine.h"
#include "LanguageManager.h"
#include "Planet.h"

USING_NS_CC;
using namespace CocosDenshion;

NumberTest * NumberTest::create(int _number, Sprite * _bg)
{
	NumberTest * tip = new NumberTest();

	if (tip && tip->init())
	{
		tip->autorelease();
		tip->initNumberTest(_number, _bg);
		return tip;
	}
	CC_SAFE_DELETE(tip);
	return NULL;
}

NumberTest::~NumberTest()
{

}

void NumberTest::initNumberTest(int _number, Sprite * _bg)
{
	number = _number;
	setupDirector();
	setupBoundary();
	setupSprite();
	setupAudio();
	setupLabel();
	consumed = false;
	this->setScale(0.9);
	bg = _bg;
	BaseObject::initObject();
}

void NumberTest::setupDirector()
{
	visibleSize = Director::getInstance()->getVisibleSize();
	winSize = Director::getInstance()->getWinSize(); //design size?
	frameSize = Director::getInstance()->getOpenGLView()->getFrameSize();
	origin = Director::getInstance()->getVisibleOrigin(); // common to all maps i hope
}

void NumberTest::setupBoundary()
{
	boundary.shape = SHAPE::circle;
	boundary.active = true;
	boundary.r = 20;
}

void NumberTest::setupSprite()
{
	auto sprite = Sprite::createWithSpriteFrameName("tip.png");
	auto moveUp = EaseInOut::create(MoveBy::create(2, Vec2(0, 5.0f)), 2);
	auto moveBack = EaseInOut::create(MoveBy::create(2, Vec2(0, -5.0f)), 2);
	auto seq1 = Sequence::create(moveUp, moveBack, nullptr);
	sprite->runAction(RepeatForever::create(seq1));
	this->addChild(sprite);
}

void NumberTest::setupAudio()
{
	auto audio = SimpleAudioEngine::getInstance();
	audio->preloadEffect("sfx/bot.wav");
	langCode = "en";
	if (CCApplication::getInstance()->getCurrentLanguage() == LanguageType::SWAHILI)
		langCode = "sw";
	for (int i = 0; i <= 10; i++)
	{
		audio->preloadEffect(("sfx/" + langCode + "/digit_" + std::to_string(i) + ".wav").c_str());
	}
}

void NumberTest::setupLabel()
{
	numberLabel = Label::createWithTTF(std::to_string(number), LanguageManager::getString("font"), 50);
	numberLabel->setPosition(Vec2(0, 100));
	auto action0 = ScaleTo::create(0.3f, 1.1f, 1.1f);
	auto action1 = ScaleTo::create(0.3f, 0.99f, 0.99f);
	ActionInterval *bouncingAction = Sequence::create(action0, action1, nullptr);
	auto action = RepeatForever::create(bouncingAction);
	numberLabel->runAction(action);
	numberLabel->setVisible(false);
	this->addChild(numberLabel);
}

void NumberTest::update(bool hit)
{
	if (hit && !isMessagevisible && !consumed)
	{
		auto audio = SimpleAudioEngine::getInstance();
		isMessagevisible = true;
		audio->playEffect("sfx/bot.wav");

		Vector<FiniteTimeAction *>  allActions;
		auto action = MoveTo::create(1.5, Vec2(0, 0));
		TargetedAction * t1 = TargetedAction::create(bg, action);
		allActions.pushBack(t1);

		Vector<MenuItem *> menuItems;
		auto wrongChoice = [this, audio](Ref * sender) {
			audio->playEffect("sfx/hurt.wav");
		};
		auto rightChoice = [this, audio](Ref * sender) {
			audio->playEffect("sfx/correct.wav");
			auto * moveUp = MoveTo::create(1.5, Vec2(0, visibleSize.height));
			bg->runAction(moveUp);
			consumed = true;
		};

		int n = -1;
		int correctPicked = RandomHelper::random_int(1, 3);
		Label * labelA = Label::createWithTTF("", LanguageManager::getString("font"), 120);
		labelA->setScale(0.9);
		MenuItemLabel * mLabelA;
		if (correctPicked == 1)
		{
			labelA->setString(std::to_string(number));
			mLabelA = MenuItemLabel::create(labelA, rightChoice);
		}
		else
		{
			n = RandomHelper::random_int(1, 10);
			while (n == number)
			{
				n = RandomHelper::random_int(1, 10);
			}
			labelA->setString(std::to_string(n));
			mLabelA = MenuItemLabel::create(labelA, wrongChoice);
		}

		Label * labelB = Label::createWithTTF("", LanguageManager::getString("font"), 120);
		labelB->setScale(0.9);
		MenuItemLabel * mLabelB;
		if (correctPicked == 2)
		{
			labelB->setString(std::to_string(number));
			mLabelB = MenuItemLabel::create(labelB, rightChoice);
		}
		else
		{
			n = RandomHelper::random_int(1, 10);
			while (n == number)
			{
				n = RandomHelper::random_int(1, 10);
			}
			labelB->setString(std::to_string(n));
			mLabelB = MenuItemLabel::create(labelB, wrongChoice);
		}

		Label * labelC = Label::createWithTTF("", LanguageManager::getString("font"), 120);
		labelC->setScale(0.9);
		MenuItemLabel * mLabelC;
		if (correctPicked == 3)
		{
			labelC->setString(std::to_string(number));
			mLabelC = MenuItemLabel::create(labelC, rightChoice);
		}
		else
		{
			n = RandomHelper::random_int(1, 10);
			while (n == number)
			{
				n = RandomHelper::random_int(1, 10);
			}
			labelC->setString(std::to_string(n));
			mLabelC = MenuItemLabel::create(labelC, wrongChoice);
		}

		auto scaleUp = ScaleTo::create(1, 1.01);
		auto scaleDown = ScaleTo::create(1, 1);
		auto seqScale = Sequence::create(scaleUp, scaleDown, NULL);
		auto repScale = RepeatForever::create(seqScale);
		labelA->runAction(repScale);
		labelB->runAction(repScale->clone());
		labelC->runAction(repScale->clone());

		menuItems.pushBack(mLabelA);
		menuItems.pushBack(mLabelB);
		menuItems.pushBack(mLabelC);
		Menu * menu = Menu::createWithArray(menuItems);
		menu->setPosition(Vec2(visibleSize.width - 100, visibleSize.height / 2));
		menu->alignItemsVerticallyWithPadding(-20);
		menu->setEnabled(false);

		menu->setOpacity(100);
		bg->addChild(menu);

		auto scaleTo1 = ScaleTo::create(1.3, 1);
		int vLevel = 2;
		int hLevel = 1;
		for (int i = 1; i <= number; i++)
		{
			auto planet = Planet::create(-1);
			planet->setPosition(Vec2(105 * hLevel - 40, vLevel * visibleSize.height / 3));
			bg->addChild(planet);
			hLevel++;
			if (i == 5)
			{
				vLevel = 1;
				hLevel = 1;
			}
			planet->setScale(0.01);
			TargetedAction * tA = TargetedAction::create(planet, scaleTo1);
			allActions.pushBack(tA);
			auto playAudio = CallFunc::create([this, i]() {
				auto audio = SimpleAudioEngine::getInstance();
				audio->playEffect(("sfx/" + langCode + "/digit_" + std::to_string(i) + ".wav").c_str());
			});
			allActions.pushBack(playAudio);
		}
		auto * delay = DelayTime::create(1);
		allActions.pushBack(delay);

		auto * fadeIn = FadeIn::create(0.5);
		TargetedAction * fadeInMenu = TargetedAction::create(menu, fadeIn);
		allActions.pushBack(fadeInMenu);

		auto * enableMenuLambda = CallFunc::create([menu]() {
			menu->setEnabled(true);
		});
		allActions.pushBack(enableMenuLambda);

		auto seq = Sequence::create(allActions);
		bg->stopAllActions();
		bg->runAction(seq);
		numberLabel->setVisible(true);
	}
	else if (!hit && isMessagevisible)
	{
		isMessagevisible = false;
		auto action = MoveTo::create(1.5, Vec2(0, visibleSize.height));
		bg->stopAllActions();
		bg->runAction(action);
		bg->removeAllChildren();
	}

	if (hit && !isMessagevisible && consumed)
	{
		isMessagevisible = true;
		auto audio = SimpleAudioEngine::getInstance();
		audio->playEffect(("sfx/" + langCode + "/digit_" + std::to_string(number) + ".wav").c_str());
	}
}

<|endoftext|>


<commit_before>/**********************************************************************
 * $Id$
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.refractions.net
 *
 * Copyright (C) 2006 Refractions Research Inc.
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************/

#include <geos/indexQuadtree.h>
#include <sstream>
#include <vector>

#ifndef GEOS_DEBUG
#define GEOS_DEBUG 0
#endif

using namespace std;
namespace geos {
namespace index { // geos.index
namespace quadtree { // geos.index.quadtree

int
QuadTreeNodeBase::getSubnodeIndex(const Envelope *env, const Coordinate& centre)
{
	int subnodeIndex=-1;
	if (env->getMinX()>=centre.x) {
		if (env->getMinY()>=centre.y) subnodeIndex=3;
		if (env->getMaxY()<=centre.y) subnodeIndex=1;
	}
	if (env->getMaxX()<=centre.x) {
		if (env->getMinY()>=centre.y) subnodeIndex=2;
		if (env->getMaxY()<=centre.y) subnodeIndex=0;
	}
#if GEOS_DEBUG
	cerr<<"getSubNodeIndex("<<env->toString()<<", "<<centre.toString()<<") returning "<<subnodeIndex<<endl;
#endif
	return subnodeIndex;
}

QuadTreeNodeBase::QuadTreeNodeBase() {
	items=new vector<void*>();
	subnode[0]=NULL;
	subnode[1]=NULL;
	subnode[2]=NULL;
	subnode[3]=NULL;
}

QuadTreeNodeBase::~QuadTreeNodeBase() {
	delete subnode[0];
	delete subnode[1];
	delete subnode[2];
	delete subnode[3];
	subnode[0]=NULL;
	subnode[1]=NULL;
	subnode[2]=NULL;
	subnode[3]=NULL;
	delete items;
}

vector<void*>* QuadTreeNodeBase::getItems() {
	return items;
}

void QuadTreeNodeBase::add(void* item) {
	items->push_back(item);
	//GEOS_DEBUG itemCount++;
	//GEOS_DEBUG System.out.print(itemCount);
}

vector<void*>*
QuadTreeNodeBase::addAllItems(vector<void*> *resultItems)
{
	//<<TODO:ASSERT?>> Can we assert that this node cannot have both items
	//and subnodes? [Jon Aquino]
	resultItems->insert(resultItems->end(),items->begin(),items->end());
	for(int i=0;i<4;i++) {
		if (subnode[i]!=NULL) {
			subnode[i]->addAllItems(resultItems);
		}
	}
	return resultItems;
}

void
QuadTreeNodeBase::addAllItemsFromOverlapping(const Envelope *searchEnv, vector<void*> *resultItems)
{
	if (!isSearchMatch(searchEnv))
		return;

	//<<TODO:ASSERT?>> Can we assert that this node cannot have both items
	//and subnodes? [Jon Aquino]
	resultItems->insert(resultItems->end(),items->begin(),items->end());
	for(int i=0;i<4;i++) {
		if (subnode[i]!=NULL) {
			subnode[i]->addAllItemsFromOverlapping(searchEnv,resultItems);
		}
	}
}

//<<TODO:RENAME?>> In Samet's terminology, I think what we're returning here is
//actually level+1 rather than depth. (See p. 4 of his book) [Jon Aquino]
int QuadTreeNodeBase::depth() {
	int maxSubDepth=0;
	for(int i=0;i<4;i++) {
		if (subnode[i]!=NULL) {
			int sqd=subnode[i]->depth();
			if (sqd>maxSubDepth)
				maxSubDepth=sqd;
		}
	}
	return maxSubDepth+1;
}

//<<TODO:RENAME?>> "size" is a bit generic. How about "itemCount"? [Jon Aquino]
int QuadTreeNodeBase::size() {
	int subSize=0;
	for(int i=0;i<4;i++) {
		if (subnode[i]!=NULL) {
			subSize+=subnode[i]->size();
		}
	}
	return subSize+(int)items->size();
}

//<<TODO:RENAME?>> The Java Language Specification recommends that "Methods to
//get and set an attribute that might be thought of as a variable V should be
//named getV and setV" (6.8.3). Perhaps this and other methods should be
//renamed to "get..."? [Jon Aquino]
int QuadTreeNodeBase::nodeCount() {
	int subSize=0;
	for(int i=0;i<4;i++) {
		if (subnode[i]!=NULL) {
			subSize+=subnode[i]->size();
		}
	}
	return subSize+1;
}

string
QuadTreeNodeBase::toString() const
{
	ostringstream s;
	s<<"ITEMS:"<<items->size()<<endl;
	for (int i=0; i<4; i++)
	{
		s<<"subnode["<<i<<"] ";
		if ( subnode[i] == NULL ) s<<"NULL";
		else s<<subnode[i]->toString();
		s<<endl;
	}
	return s.str();
}

/*public*/
void
QuadTreeNodeBase::visit(const Envelope* searchEnv, ItemVisitor& visitor)
{
	if (! isSearchMatch(searchEnv)) return;

	// this node may have items as well as subnodes (since items may not
	// be wholely contained in any single subnode
	visitItems(searchEnv, visitor);

	for (int i = 0; i < 4; i++) {
		if (subnode[i] != NULL) {
			subnode[i]->visit(searchEnv, visitor);
		}
	}
}

/*private*/
void
QuadTreeNodeBase::visitItems(const Envelope* searchEnv, ItemVisitor& visitor)
{
	// would be nice to filter items based on search envelope, but can't
	// until they contain an envelope
	for (vector<void*>::iterator i=items->begin(), e=items->end();
			i!=e; i++)
	{
		visitor.visitItem(*i);
	}
}

/*public*/
bool
QuadTreeNodeBase::remove(const Envelope* itemEnv, void* item)
{
	// use envelope to restrict nodes scanned
	if (! isSearchMatch(itemEnv)) return false;

	bool found = false;
	for (int i = 0; i < 4; i++) {
		if (subnode[i] != NULL) {
			found = subnode[i]->remove(itemEnv, item);
			if (found) {
				// trim subtree if empty
				if (subnode[i]->isPrunable())
				subnode[i] = NULL;
				break;
			}
		}
	}
	// if item was found lower down, don't need to search for it here
	if (found) return found;

	// otherwise, try and remove the item from the list of items
	// in this node
	vector<void*>::iterator foundIter =
		find(items->begin(), items->end(), item);
	if ( foundIter != items->end() ) {
		items->erase(foundIter);
		return true;
	} else {
		return false;
	}
}

} // namespace geos.index.quadtree
} // namespace geos.index
} // namespace geos

/**********************************************************************
 * $Log$
 * Revision 1.16  2006/03/03 10:46:21  strk
 * Removed 'using namespace' from headers, added missing headers in .cpp files, removed useless includes in headers (bug#46)
 *
 * Revision 1.15  2006/03/02 12:12:00  strk
 * Renamed DEBUG macros to GEOS_DEBUG, all wrapped in #ifndef block to allow global override (bug#43)
 *
 * Revision 1.14  2006/02/23 11:54:20  strk
 * - MCIndexPointSnapper
 * - MCIndexSnapRounder
 * - SnapRounding BufferOp
 * - ScaledNoder
 * - GEOSException hierarchy cleanups
 * - SpatialIndex memory-friendly query interface
 * - GeometryGraph::getBoundaryNodes memory-friendly
 * - NodeMap::getBoundaryNodes memory-friendly
 * - Cleanups in geomgraph::Edge
 * - Added an XML test for snaprounding buffer (shows leaks, working on it)
 *
 * Revision 1.13  2006/02/20 21:04:37  strk
 * - namespace geos::index
 * - SpatialIndex interface synced
 *
 * Revision 1.12  2006/02/20 10:14:18  strk
 * - namespaces geos::index::*
 * - Doxygen documentation cleanup
 *
 * Revision 1.11  2005/01/28 09:47:51  strk
 * Replaced sprintf uses with ostringstream.
 *
 * Revision 1.10  2004/11/01 16:43:04  strk
 * Added Profiler code.
 * Temporarly patched a bug in DoubleBits (must check drawbacks).
 * Various cleanups and speedups.
 *
 * Revision 1.9  2004/07/27 16:35:46  strk
 * Geometry::getEnvelopeInternal() changed to return a const Envelope *.
 * This should reduce object copies as once computed the envelope of a
 * geometry remains the same.
 *
 * Revision 1.8  2004/07/02 13:28:27  strk
 * Fixed all #include lines to reflect headers layout change.
 * Added client application build tips in README.
 *
 * Revision 1.7  2003/11/07 01:23:42  pramsey
 * Add standard CVS headers licence notices and copyrights to all cpp and h
 * files.
 *
 *
 **********************************************************************/

<commit_msg>Added missing <algorithm> include (bug#56)<commit_after>/**********************************************************************
 * $Id$
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.refractions.net
 *
 * Copyright (C) 2006 Refractions Research Inc.
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************/

#include <sstream>
#include <vector>
#include <algorithm>

#include <geos/indexQuadtree.h>

#ifndef GEOS_DEBUG
#define GEOS_DEBUG 0
#endif

using namespace std;
namespace geos {
namespace index { // geos.index
namespace quadtree { // geos.index.quadtree

int
QuadTreeNodeBase::getSubnodeIndex(const Envelope *env, const Coordinate& centre)
{
	int subnodeIndex=-1;
	if (env->getMinX()>=centre.x) {
		if (env->getMinY()>=centre.y) subnodeIndex=3;
		if (env->getMaxY()<=centre.y) subnodeIndex=1;
	}
	if (env->getMaxX()<=centre.x) {
		if (env->getMinY()>=centre.y) subnodeIndex=2;
		if (env->getMaxY()<=centre.y) subnodeIndex=0;
	}
#if GEOS_DEBUG
	cerr<<"getSubNodeIndex("<<env->toString()<<", "<<centre.toString()<<") returning "<<subnodeIndex<<endl;
#endif
	return subnodeIndex;
}

QuadTreeNodeBase::QuadTreeNodeBase() {
	items=new vector<void*>();
	subnode[0]=NULL;
	subnode[1]=NULL;
	subnode[2]=NULL;
	subnode[3]=NULL;
}

QuadTreeNodeBase::~QuadTreeNodeBase() {
	delete subnode[0];
	delete subnode[1];
	delete subnode[2];
	delete subnode[3];
	subnode[0]=NULL;
	subnode[1]=NULL;
	subnode[2]=NULL;
	subnode[3]=NULL;
	delete items;
}

vector<void*>* QuadTreeNodeBase::getItems() {
	return items;
}

void QuadTreeNodeBase::add(void* item) {
	items->push_back(item);
	//GEOS_DEBUG itemCount++;
	//GEOS_DEBUG System.out.print(itemCount);
}

vector<void*>*
QuadTreeNodeBase::addAllItems(vector<void*> *resultItems)
{
	//<<TODO:ASSERT?>> Can we assert that this node cannot have both items
	//and subnodes? [Jon Aquino]
	resultItems->insert(resultItems->end(),items->begin(),items->end());
	for(int i=0;i<4;i++) {
		if (subnode[i]!=NULL) {
			subnode[i]->addAllItems(resultItems);
		}
	}
	return resultItems;
}

void
QuadTreeNodeBase::addAllItemsFromOverlapping(const Envelope *searchEnv, vector<void*> *resultItems)
{
	if (!isSearchMatch(searchEnv))
		return;

	//<<TODO:ASSERT?>> Can we assert that this node cannot have both items
	//and subnodes? [Jon Aquino]
	resultItems->insert(resultItems->end(),items->begin(),items->end());
	for(int i=0;i<4;i++) {
		if (subnode[i]!=NULL) {
			subnode[i]->addAllItemsFromOverlapping(searchEnv,resultItems);
		}
	}
}

//<<TODO:RENAME?>> In Samet's terminology, I think what we're returning here is
//actually level+1 rather than depth. (See p. 4 of his book) [Jon Aquino]
int QuadTreeNodeBase::depth() {
	int maxSubDepth=0;
	for(int i=0;i<4;i++) {
		if (subnode[i]!=NULL) {
			int sqd=subnode[i]->depth();
			if (sqd>maxSubDepth)
				maxSubDepth=sqd;
		}
	}
	return maxSubDepth+1;
}

//<<TODO:RENAME?>> "size" is a bit generic. How about "itemCount"? [Jon Aquino]
int QuadTreeNodeBase::size() {
	int subSize=0;
	for(int i=0;i<4;i++) {
		if (subnode[i]!=NULL) {
			subSize+=subnode[i]->size();
		}
	}
	return subSize+(int)items->size();
}

//<<TODO:RENAME?>> The Java Language Specification recommends that "Methods to
//get and set an attribute that might be thought of as a variable V should be
//named getV and setV" (6.8.3). Perhaps this and other methods should be
//renamed to "get..."? [Jon Aquino]
int QuadTreeNodeBase::nodeCount() {
	int subSize=0;
	for(int i=0;i<4;i++) {
		if (subnode[i]!=NULL) {
			subSize+=subnode[i]->size();
		}
	}
	return subSize+1;
}

string
QuadTreeNodeBase::toString() const
{
	ostringstream s;
	s<<"ITEMS:"<<items->size()<<endl;
	for (int i=0; i<4; i++)
	{
		s<<"subnode["<<i<<"] ";
		if ( subnode[i] == NULL ) s<<"NULL";
		else s<<subnode[i]->toString();
		s<<endl;
	}
	return s.str();
}

/*public*/
void
QuadTreeNodeBase::visit(const Envelope* searchEnv, ItemVisitor& visitor)
{
	if (! isSearchMatch(searchEnv)) return;

	// this node may have items as well as subnodes (since items may not
	// be wholely contained in any single subnode
	visitItems(searchEnv, visitor);

	for (int i = 0; i < 4; i++) {
		if (subnode[i] != NULL) {
			subnode[i]->visit(searchEnv, visitor);
		}
	}
}

/*private*/
void
QuadTreeNodeBase::visitItems(const Envelope* searchEnv, ItemVisitor& visitor)
{
	// would be nice to filter items based on search envelope, but can't
	// until they contain an envelope
	for (vector<void*>::iterator i=items->begin(), e=items->end();
			i!=e; i++)
	{
		visitor.visitItem(*i);
	}
}

/*public*/
bool
QuadTreeNodeBase::remove(const Envelope* itemEnv, void* item)
{
	// use envelope to restrict nodes scanned
	if (! isSearchMatch(itemEnv)) return false;

	bool found = false;
	for (int i = 0; i < 4; i++) {
		if (subnode[i] != NULL) {
			found = subnode[i]->remove(itemEnv, item);
			if (found) {
				// trim subtree if empty
				if (subnode[i]->isPrunable())
				subnode[i] = NULL;
				break;
			}
		}
	}
	// if item was found lower down, don't need to search for it here
	if (found) return found;

	// otherwise, try and remove the item from the list of items
	// in this node
	vector<void*>::iterator foundIter =
		find(items->begin(), items->end(), item);
	if ( foundIter != items->end() ) {
		items->erase(foundIter);
		return true;
	} else {
		return false;
	}
}

} // namespace geos.index.quadtree
} // namespace geos.index
} // namespace geos

/**********************************************************************
 * $Log$
 * Revision 1.17  2006/03/10 11:41:49  strk
 * Added missing <algorithm> include (bug#56)
 *
 * Revision 1.16  2006/03/03 10:46:21  strk
 * Removed 'using namespace' from headers, added missing headers in .cpp files, removed useless includes in headers (bug#46)
 *
 * Revision 1.15  2006/03/02 12:12:00  strk
 * Renamed DEBUG macros to GEOS_DEBUG, all wrapped in #ifndef block to allow global override (bug#43)
 *
 * Revision 1.14  2006/02/23 11:54:20  strk
 * - MCIndexPointSnapper
 * - MCIndexSnapRounder
 * - SnapRounding BufferOp
 * - ScaledNoder
 * - GEOSException hierarchy cleanups
 * - SpatialIndex memory-friendly query interface
 * - GeometryGraph::getBoundaryNodes memory-friendly
 * - NodeMap::getBoundaryNodes memory-friendly
 * - Cleanups in geomgraph::Edge
 * - Added an XML test for snaprounding buffer (shows leaks, working on it)
 *
 * Revision 1.13  2006/02/20 21:04:37  strk
 * - namespace geos::index
 * - SpatialIndex interface synced
 *
 * Revision 1.12  2006/02/20 10:14:18  strk
 * - namespaces geos::index::*
 * - Doxygen documentation cleanup
 *
 * Revision 1.11  2005/01/28 09:47:51  strk
 * Replaced sprintf uses with ostringstream.
 *
 * Revision 1.10  2004/11/01 16:43:04  strk
 * Added Profiler code.
 * Temporarly patched a bug in DoubleBits (must check drawbacks).
 * Various cleanups and speedups.
 *
 * Revision 1.9  2004/07/27 16:35:46  strk
 * Geometry::getEnvelopeInternal() changed to return a const Envelope *.
 * This should reduce object copies as once computed the envelope of a
 * geometry remains the same.
 *
 * Revision 1.8  2004/07/02 13:28:27  strk
 * Fixed all #include lines to reflect headers layout change.
 * Added client application build tips in README.
 *
 * Revision 1.7  2003/11/07 01:23:42  pramsey
 * Add standard CVS headers licence notices and copyrights to all cpp and h
 * files.
 *
 *
 **********************************************************************/

<|endoftext|>


<commit_before>/**********************************************************************
 * $Id$
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.refractions.net
 *
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************
 * $Log$
 * Revision 1.2  2004/05/03 12:09:22  strk
 * newline added at end of file
 *
 * Revision 1.1  2004/03/26 07:48:30  ybychkov
 * "noding" package ported (JTS 1.4)
 *
 *
 **********************************************************************/

#include "../headers/noding.h"

namespace geos {
bool nodingSegmentIntersector::isAdjacentSegments(int i1, int i2){
	return abs(i1-i2)==1;
}

nodingSegmentIntersector::nodingSegmentIntersector(LineIntersector *newLi) {
	li=newLi;
	hasIntersectionVar=false;
	hasProperVar = false;
	hasProperInteriorVar = false;
	hasInteriorVar = false;
	properIntersectionPoint=NULL;
	numIntersections = 0;
	numInteriorIntersections = 0;
	numProperIntersections = 0;
	numTests = 0;
}

LineIntersector* nodingSegmentIntersector::getLineIntersector() {
	return li;
}

/**
* @return the proper intersection point, or null if none was found
*/
Coordinate* nodingSegmentIntersector::getProperIntersectionPoint() {
	return properIntersectionPoint;
}

bool nodingSegmentIntersector::hasIntersection() {
	return hasIntersectionVar;
}
/**
* A proper intersection is an intersection which is interior to at least two
* line segments.  Note that a proper intersection is not necessarily
* in the interior of the entire Geometry, since another edge may have
* an endpoint equal to the intersection, which according to SFS semantics
* can result in the point being on the Boundary of the Geometry.
*/
bool nodingSegmentIntersector::hasProperIntersection() {
	return hasProperVar;
}
/**
* A proper interior intersection is a proper intersection which is not
* contained in the set of boundary nodes set for this nodingSegmentIntersector.
*/
bool nodingSegmentIntersector::hasProperInteriorIntersection() {
	return hasProperInteriorVar;
}
/**
* An interior intersection is an intersection which is
* in the interior of some segment.
*/
bool nodingSegmentIntersector::hasInteriorIntersection() {
	return hasInteriorVar;
}

/**
* A trivial intersection is an apparent self-intersection which in fact
* is simply the point shared by adjacent line segments.
* Note that closed edges require a special check for the point shared by the beginning
* and end segments.
*/
bool nodingSegmentIntersector::isTrivialIntersection(SegmentString *e0, int segIndex0, SegmentString *e1, int segIndex1){
	if (e0 == e1) {
		if (li->getIntersectionNum() == 1) {
			if (isAdjacentSegments(segIndex0, segIndex1))
				return true;
			if (e0->isClosed()) {
				int maxSegIndex = e0->size() - 1;
				if ((segIndex0 == 0 && segIndex1 == maxSegIndex) ||
					(segIndex1 == 0 && segIndex0 == maxSegIndex)) {
						return true;
				}
			}
		}
	}
	return false;
}

/**
* This method is called by clients
* of the {@link nodingSegmentIntersector} class to process
* intersections for two segments of the {@link SegmentStrings} being intersected.
* Note that some clients (such as {@link MonotoneChain}s) may optimize away
* this call for segment pairs which they have determined do not intersect
* (e.g. by an disjoint envelope test).
*/
void nodingSegmentIntersector::processIntersections(SegmentString *e0, int segIndex0,SegmentString *e1, int segIndex1){
	if (e0==e1 && segIndex0==segIndex1) return;
	numTests++;
	Coordinate p00=e0->getCoordinates()->getAt(segIndex0);
	Coordinate p01=e0->getCoordinates()->getAt(segIndex0+1);
	Coordinate p10=e1->getCoordinates()->getAt(segIndex1);
	Coordinate p11=e1->getCoordinates()->getAt(segIndex1+1);
	li->computeIntersection(p00, p01, p10, p11);
	//if (li.hasIntersection() && li.isProper()) Debug.println(li);
	if (li->hasIntersection()) {
		if (recordIsolated) {
			e0->setIsolated(false);
			e1->setIsolated(false);
		}
		//intersectionFound = true;
		numIntersections++;
		if (li->isInteriorIntersection()) {
			numInteriorIntersections++;
			hasInteriorVar = true;
			//System.out.println(li);
		}
		// if the segments are adjacent they have at least one trivial intersection,
		// the shared endpoint.  Don't bother adding it if it is the
		// only intersection.
		if (!isTrivialIntersection(e0, segIndex0, e1, segIndex1)) {
			hasIntersectionVar = true;
			//Debug.println(li);
			e0->addIntersections(li, segIndex0, 0);
			e1->addIntersections(li, segIndex1, 1);
			if (li->isProper()) {
				numProperIntersections++;
				//properIntersectionPoint = (Coordinate) li.getIntersection(0).clone();
				hasProperVar = true;
				hasProperInteriorVar = true;
			}
		}
	}
}
}
<commit_msg>set (useless?) recordIsolated member in constructor<commit_after>/**********************************************************************
 * $Id$
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.refractions.net
 *
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************
 * $Log$
 * Revision 1.3  2004/05/27 10:26:50  strk
 * set (useless?) recordIsolated member in constructor
 *
 * Revision 1.2  2004/05/03 12:09:22  strk
 * newline added at end of file
 *
 * Revision 1.1  2004/03/26 07:48:30  ybychkov
 * "noding" package ported (JTS 1.4)
 *
 *
 **********************************************************************/

#include "../headers/noding.h"

namespace geos {
bool nodingSegmentIntersector::isAdjacentSegments(int i1, int i2){
	return abs(i1-i2)==1;
}

nodingSegmentIntersector::nodingSegmentIntersector(LineIntersector *newLi) {
	li=newLi;
	hasIntersectionVar=false;
	hasProperVar = false;
	hasProperInteriorVar = false;
	hasInteriorVar = false;
	properIntersectionPoint=NULL;
	numIntersections = 0;
	numInteriorIntersections = 0;
	numProperIntersections = 0;
	numTests = 0;
	recordIsolated = false;
}

LineIntersector* nodingSegmentIntersector::getLineIntersector() {
	return li;
}

/**
* @return the proper intersection point, or null if none was found
*/
Coordinate* nodingSegmentIntersector::getProperIntersectionPoint() {
	return properIntersectionPoint;
}

bool nodingSegmentIntersector::hasIntersection() {
	return hasIntersectionVar;
}
/**
* A proper intersection is an intersection which is interior to at least two
* line segments.  Note that a proper intersection is not necessarily
* in the interior of the entire Geometry, since another edge may have
* an endpoint equal to the intersection, which according to SFS semantics
* can result in the point being on the Boundary of the Geometry.
*/
bool nodingSegmentIntersector::hasProperIntersection() {
	return hasProperVar;
}
/**
* A proper interior intersection is a proper intersection which is not
* contained in the set of boundary nodes set for this nodingSegmentIntersector.
*/
bool nodingSegmentIntersector::hasProperInteriorIntersection() {
	return hasProperInteriorVar;
}
/**
* An interior intersection is an intersection which is
* in the interior of some segment.
*/
bool nodingSegmentIntersector::hasInteriorIntersection() {
	return hasInteriorVar;
}

/**
* A trivial intersection is an apparent self-intersection which in fact
* is simply the point shared by adjacent line segments.
* Note that closed edges require a special check for the point shared by the beginning
* and end segments.
*/
bool nodingSegmentIntersector::isTrivialIntersection(SegmentString *e0, int segIndex0, SegmentString *e1, int segIndex1){
	if (e0 == e1) {
		if (li->getIntersectionNum() == 1) {
			if (isAdjacentSegments(segIndex0, segIndex1))
				return true;
			if (e0->isClosed()) {
				int maxSegIndex = e0->size() - 1;
				if ((segIndex0 == 0 && segIndex1 == maxSegIndex) ||
					(segIndex1 == 0 && segIndex0 == maxSegIndex)) {
						return true;
				}
			}
		}
	}
	return false;
}

/**
* This method is called by clients
* of the {@link nodingSegmentIntersector} class to process
* intersections for two segments of the {@link SegmentStrings} being intersected.
* Note that some clients (such as {@link MonotoneChain}s) may optimize away
* this call for segment pairs which they have determined do not intersect
* (e.g. by an disjoint envelope test).
*/
void nodingSegmentIntersector::processIntersections(SegmentString *e0, int segIndex0,SegmentString *e1, int segIndex1){
	if (e0==e1 && segIndex0==segIndex1) return;
	numTests++;
	Coordinate p00=e0->getCoordinates()->getAt(segIndex0);
	Coordinate p01=e0->getCoordinates()->getAt(segIndex0+1);
	Coordinate p10=e1->getCoordinates()->getAt(segIndex1);
	Coordinate p11=e1->getCoordinates()->getAt(segIndex1+1);
	li->computeIntersection(p00, p01, p10, p11);
	//if (li.hasIntersection() && li.isProper()) Debug.println(li);
	if (li->hasIntersection()) {
		if (recordIsolated) {
			e0->setIsolated(false);
			e1->setIsolated(false);
		}
		//intersectionFound = true;
		numIntersections++;
		if (li->isInteriorIntersection()) {
			numInteriorIntersections++;
			hasInteriorVar = true;
			//System.out.println(li);
		}
		// if the segments are adjacent they have at least one trivial intersection,
		// the shared endpoint.  Don't bother adding it if it is the
		// only intersection.
		if (!isTrivialIntersection(e0, segIndex0, e1, segIndex1)) {
			hasIntersectionVar = true;
			//Debug.println(li);
			e0->addIntersections(li, segIndex0, 0);
			e1->addIntersections(li, segIndex1, 1);
			if (li->isProper()) {
				numProperIntersections++;
				//properIntersectionPoint = (Coordinate) li.getIntersection(0).clone();
				hasProperVar = true;
				hasProperInteriorVar = true;
			}
		}
	}
}
}
<|endoftext|>


<commit_before>#include "clientmanager.h"

/**
 * Raises a client to the layer above it.
 * @param window The client to raise
 */
void ClientManager::raise_layer(Window window)
{
    Layer current = m_layers[current];
    if (current < MAX_LAYER)
    {
        m_layers[current]++;
        relayer();
    }
}

/**
 * Lowers a client to the layer below it.
 * @param window The client to lower
 */
void ClientManager::lower_layer(Window window)
{
    Layer current = m_layers[current];
    if (current > MIN_LAYER)
    {
        m_layers[current]--;
        relayer();
    }
}

/**
 * Sets the layer of a client.
 * @param window The window of a client
 * @param layer The layer to set the client to
 */
void ClientManager::set_layer(Window window, Layer layer)
{
    m_layers[window] = layer;
    relayer();
}

/**
 * Relayers all clients which are currently CS_VISIBLE or CS_ACTIVE. Puts all
 * CS_ICON clients, or the placeholder windows for any CS_MOVING or CS_RESIZING
 * clients on the very top.
 */
void ClientManager::relayer()
{
    // First, only layer the 'normal' windows which are not icons or placeholders.
    std::vector<Window> clients;
    for (std::map<Window,ClientState>::iterator client_iter = m_clients.begin();
            client_iter != m_clients.end();
            client_iter++)
    {
        if (client_iter->second == CS_VISIBLE || client_iter->second == CS_ACTIVE)
            clients.push_back(client_iter->first);
    }

    MappedVectorSorter<Window,Layer> sorter(m_layers, true);
    std::sort(clients.begin(), clients.end(), sorter);
    XRestackWindows(m_shared.display, &clients[0], clients.size());

    // Now, go back and put all of the icons on the top
    for (std::map<Window,Icon*>::iterator icon_iter = m_icons.begin();
            icon_iter != m_icons.end();
            icon_iter++)
    {
        XRaiseWindow(m_shared.display, icon_iter->first);
    }

    // Finally, put the placeholder on the top, if there is one
    if (m_mvr.window != None)
        XRaiseWindow(m_shared.display, m_mvr.window);
}
<commit_msg>Added the layer manager code to the repository.<commit_after>/** @file */
#include "clientmanager.h"

/**
 * Raises a client to the layer above it.
 * @param window The client to raise
 */
void ClientManager::raise_layer(Window window)
{
    if (!is_client(window))
        return;

    Layer current = m_layers[current];
    if (current < MAX_LAYER)
    {
        m_layers[current]++;
        relayer();
    }
}

/**
 * Lowers a client to the layer below it.
 * @param window The client to lower
 */
void ClientManager::lower_layer(Window window)
{
    if (!is_client(window))
        return;

    Layer current = m_layers[current];
    if (current > MIN_LAYER)
    {
        m_layers[current]--;
        relayer();
    }
}

/**
 * Sets the layer of a client.
 * @param window The window of a client
 * @param layer The layer to set the client to
 */
void ClientManager::set_layer(Window window, Layer layer)
{
    if (!is_client(window))
        return;

    m_layers[window] = layer;
    relayer();
}

/**
 * Relayers all clients which are currently CS_VISIBLE or CS_ACTIVE. Puts all
 * CS_ICON clients, or the placeholder windows for any CS_MOVING or CS_RESIZING
 * clients on the very top.
 */
void ClientManager::relayer()
{
    // First, only layer the 'normal' windows which are not icons or placeholders.
    std::vector<Window> clients;
    for (std::map<Window,ClientState>::iterator client_iter = m_clients.begin();
            client_iter != m_clients.end();
            client_iter++)
    {
        if (client_iter->second == CS_VISIBLE || client_iter->second == CS_ACTIVE)
            clients.push_back(client_iter->first);
    }

    MappedVectorSorter<Window,Layer> sorter(m_layers, true);
    std::sort(clients.begin(), clients.end(), sorter);
    XRestackWindows(m_shared.display, &clients[0], clients.size());

    // Now, go back and put all of the icons on the top
    for (std::map<Window,Icon*>::iterator icon_iter = m_icons.begin();
            icon_iter != m_icons.end();
            icon_iter++)
    {
        XRaiseWindow(m_shared.display, icon_iter->first);
    }

    // Finally, put the placeholder on the top, if there is one
    if (m_mvr.window != None)
        XRaiseWindow(m_shared.display, m_mvr.window);
}
<|endoftext|>


<commit_before>/* Copyright © 2001-2014, Canal TP and/or its affiliates. All rights reserved.

This file is part of Navitia,
    the software to build cool stuff with public transport.

Hope you'll enjoy and contribute to this project,
    powered by Canal TP (www.canaltp.fr).
Help us simplify mobility and open public transport:
    a non ending quest to the responsive locomotion way of traveling!

LICENCE: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

Stay tuned using
twitter @navitia
IRC #navitia on freenode
https://groups.google.com/d/forum/navitia
www.navitia.io
*/

#include "ptreferential.h"
#include "ptreferential_api.h"
#include "type/pb_converter.h"
#include "type/data.h"
#include "type/pt_data.h"
#include "type/meta_data.h"
#include "routing/dataraptor.h"

namespace pt = boost::posix_time;

namespace navitia{ namespace ptref{
/// build the protobuf response of a pt ref query

static pbnavitia::Response extract_data(const type::Data& data,
                                        const type::Type_e requested_type,
                                        const type::Indexes& rows,
                                        const int depth,
                                        const boost::posix_time::ptime& current_time) {
    const auto& action_period = data.meta->production_period();
        switch(requested_type){
        case Type_e::ValidityPattern:
            return get_response(data.get_data<nt::ValidityPattern>(rows), data, depth, current_time);
        case Type_e::Line:
            return get_response(data.get_data<nt::Line>(rows), data, depth, current_time);
        case Type_e::LineGroup:
            return get_response(data.get_data<nt::LineGroup>(rows), data, depth, current_time);
        case Type_e::JourneyPattern:{
            navitia::PbCreator pb_creator(data, current_time, action_period);
            for(const auto& idx : rows){
                const auto& pair_jp = data.dataRaptor->jp_container.get_jps()[idx];
                auto* pb_jp = pb_creator.add_journey_patterns();
                pb_creator.fill(&pair_jp, pb_jp, depth);
            }
            return pb_creator.get_response();
        }
        case Type_e::StopPoint:
            return get_response(data.get_data<nt::StopPoint>(rows), data, depth, current_time, action_period);
        case Type_e::StopArea:
            return get_response(data.get_data<nt::StopArea>(rows), data, depth, current_time, action_period);
        case Type_e::Network:
            return get_response(data.get_data<nt::Network>(rows), data, depth, current_time, action_period);
        case Type_e::PhysicalMode:
            return get_response(data.get_data<nt::PhysicalMode>(rows), data, depth, current_time, action_period);
        case Type_e::CommercialMode:
            return get_response(data.get_data<nt::CommercialMode>(rows), data, depth, current_time, action_period);
        case Type_e::JourneyPatternPoint:{
            navitia::PbCreator pb_creator(data, current_time, action_period);
            for(const auto& idx : rows){
                const auto& pair_jpp = data.dataRaptor->jp_container.get_jpps()[idx];
                auto* pb_jpp = pb_creator.add_journey_pattern_points();
                pb_creator.fill(&pair_jpp, pb_jpp, depth);
            }
            return pb_creator.get_response();
        }
        case Type_e::Company:
            return get_response(data.get_data<nt::Company>(rows), data, depth, current_time, action_period);
        case Type_e::Route:
            return get_response(data.get_data<nt::Route>(rows), data, depth, current_time, action_period);
        case Type_e::POI:
            return get_response(data.get_data<georef::POI>(rows), data, depth, current_time, action_period);
        case Type_e::POIType:
            return get_response(data.get_data<georef::POIType>(rows), data, depth, current_time, action_period);
        case Type_e::Connection:
            return get_response(data.get_data<nt::StopPointConnection>(rows), data, depth, current_time, action_period);
        case Type_e::VehicleJourney:
            return get_response(data.get_data<nt::VehicleJourney>(rows), data, depth, current_time, action_period);
        case Type_e::Calendar:
            return get_response(data.get_data<nt::Calendar>(rows), data, depth, current_time, action_period);
        case Type_e::MetaVehicleJourney:{
            navitia::PbCreator pb_creator(data, current_time, action_period);
            for(const auto& idx : rows){
                const auto* meta_vj = data.pt_data->meta_vjs[Idx<type::MetaVehicleJourney>(idx)];
                auto* pb_trip = pb_creator.add_trips();
                pb_creator.fill(meta_vj, pb_trip, depth);
            }
            return pb_creator.get_response();
        }
        case Type_e::Impact:{
            navitia::PbCreator pb_creator(data, current_time, action_period);
            for(const auto& idx : rows){
                auto impact = data.pt_data->disruption_holder.get_weak_impacts()[idx].lock();
                auto* pb_impact = pb_creator.add_impacts();
                pb_creator.fill(impact.get(), pb_impact, depth);

            }
            return pb_creator.get_response();
        }
        case Type_e::Contributor:
            return get_response(data.get_data<nt::Contributor>(rows), data, depth, current_time, action_period);
        case Type_e::Dataset:
            return get_response(data.get_data<nt::Dataset>(rows), data, depth, current_time, action_period);
        default: return {};
        }
}

pbnavitia::Response query_pb(const type::Type_e requested_type,
                             const std::string& request,
                             const std::vector<std::string>& forbidden_uris,
                             const type::OdtLevel_e odt_level,
                             const int depth,
                             const int startPage,
                             const int count,
                             const boost::optional<boost::posix_time::ptime>& since,
                             const boost::optional<boost::posix_time::ptime>& until,
                             const type::Data& data,
                             const boost::posix_time::ptime& current_datetime) {
    type::Indexes final_indexes;
    pbnavitia::Response pb_response;
    int total_result;
    try {
        final_indexes = make_query(requested_type, request, forbidden_uris, odt_level, since, until, data);
    } catch(const parsing_error &parse_error) {
        fill_pb_error(pbnavitia::Error::unable_to_parse, "Unable to parse :" + parse_error.more, pb_response.mutable_error());
        return pb_response;
    } catch(const ptref_error &pt_error) {
        fill_pb_error(pbnavitia::Error::bad_filter, "ptref : " + pt_error.more, pb_response.mutable_error());
        return pb_response;
    }
    total_result = final_indexes.size();
    final_indexes = paginate(final_indexes, count, startPage);

    pb_response = extract_data(data, requested_type, final_indexes, depth, current_datetime);
    auto pagination = pb_response.mutable_pagination();
    pagination->set_totalresult(total_result);
    pagination->set_startpage(startPage);
    pagination->set_itemsperpage(count);
    pagination->set_itemsonpage(final_indexes.size());
    return pb_response;
}

}}
<commit_msg>ptref: fix disruption status on line, line_group.<commit_after>/* Copyright © 2001-2014, Canal TP and/or its affiliates. All rights reserved.

This file is part of Navitia,
    the software to build cool stuff with public transport.

Hope you'll enjoy and contribute to this project,
    powered by Canal TP (www.canaltp.fr).
Help us simplify mobility and open public transport:
    a non ending quest to the responsive locomotion way of traveling!

LICENCE: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

Stay tuned using
twitter @navitia
IRC #navitia on freenode
https://groups.google.com/d/forum/navitia
www.navitia.io
*/

#include "ptreferential.h"
#include "ptreferential_api.h"
#include "type/pb_converter.h"
#include "type/data.h"
#include "type/pt_data.h"
#include "type/meta_data.h"
#include "routing/dataraptor.h"

namespace pt = boost::posix_time;

namespace navitia{ namespace ptref{
/// build the protobuf response of a pt ref query

static pbnavitia::Response extract_data(const type::Data& data,
                                        const type::Type_e requested_type,
                                        const type::Indexes& rows,
                                        const int depth,
                                        const boost::posix_time::ptime& current_time) {
    const auto& action_period = data.meta->production_period();
        switch(requested_type){
        case Type_e::ValidityPattern:
            return get_response(data.get_data<nt::ValidityPattern>(rows), data, depth, current_time, action_period);
        case Type_e::Line:
            return get_response(data.get_data<nt::Line>(rows), data, depth, current_time, action_period);
        case Type_e::LineGroup:
            return get_response(data.get_data<nt::LineGroup>(rows), data, depth, current_time, action_period);
        case Type_e::JourneyPattern:{
            navitia::PbCreator pb_creator(data, current_time, action_period);
            for(const auto& idx : rows){
                const auto& pair_jp = data.dataRaptor->jp_container.get_jps()[idx];
                auto* pb_jp = pb_creator.add_journey_patterns();
                pb_creator.fill(&pair_jp, pb_jp, depth);
            }
            return pb_creator.get_response();
        }
        case Type_e::StopPoint:
            return get_response(data.get_data<nt::StopPoint>(rows), data, depth, current_time, action_period);
        case Type_e::StopArea:
            return get_response(data.get_data<nt::StopArea>(rows), data, depth, current_time, action_period);
        case Type_e::Network:
            return get_response(data.get_data<nt::Network>(rows), data, depth, current_time, action_period);
        case Type_e::PhysicalMode:
            return get_response(data.get_data<nt::PhysicalMode>(rows), data, depth, current_time, action_period);
        case Type_e::CommercialMode:
            return get_response(data.get_data<nt::CommercialMode>(rows), data, depth, current_time, action_period);
        case Type_e::JourneyPatternPoint:{
            navitia::PbCreator pb_creator(data, current_time, action_period);
            for(const auto& idx : rows){
                const auto& pair_jpp = data.dataRaptor->jp_container.get_jpps()[idx];
                auto* pb_jpp = pb_creator.add_journey_pattern_points();
                pb_creator.fill(&pair_jpp, pb_jpp, depth);
            }
            return pb_creator.get_response();
        }
        case Type_e::Company:
            return get_response(data.get_data<nt::Company>(rows), data, depth, current_time, action_period);
        case Type_e::Route:
            return get_response(data.get_data<nt::Route>(rows), data, depth, current_time, action_period);
        case Type_e::POI:
            return get_response(data.get_data<georef::POI>(rows), data, depth, current_time, action_period);
        case Type_e::POIType:
            return get_response(data.get_data<georef::POIType>(rows), data, depth, current_time, action_period);
        case Type_e::Connection:
            return get_response(data.get_data<nt::StopPointConnection>(rows), data, depth, current_time, action_period);
        case Type_e::VehicleJourney:
            return get_response(data.get_data<nt::VehicleJourney>(rows), data, depth, current_time, action_period);
        case Type_e::Calendar:
            return get_response(data.get_data<nt::Calendar>(rows), data, depth, current_time, action_period);
        case Type_e::MetaVehicleJourney:{
            navitia::PbCreator pb_creator(data, current_time, action_period);
            for(const auto& idx : rows){
                const auto* meta_vj = data.pt_data->meta_vjs[Idx<type::MetaVehicleJourney>(idx)];
                auto* pb_trip = pb_creator.add_trips();
                pb_creator.fill(meta_vj, pb_trip, depth);
            }
            return pb_creator.get_response();
        }
        case Type_e::Impact:{
            navitia::PbCreator pb_creator(data, current_time, action_period);
            for(const auto& idx : rows){
                auto impact = data.pt_data->disruption_holder.get_weak_impacts()[idx].lock();
                auto* pb_impact = pb_creator.add_impacts();
                pb_creator.fill(impact.get(), pb_impact, depth);

            }
            return pb_creator.get_response();
        }
        case Type_e::Contributor:
            return get_response(data.get_data<nt::Contributor>(rows), data, depth, current_time, action_period);
        case Type_e::Dataset:
            return get_response(data.get_data<nt::Dataset>(rows), data, depth, current_time, action_period);
        default: return {};
        }
}

pbnavitia::Response query_pb(const type::Type_e requested_type,
                             const std::string& request,
                             const std::vector<std::string>& forbidden_uris,
                             const type::OdtLevel_e odt_level,
                             const int depth,
                             const int startPage,
                             const int count,
                             const boost::optional<boost::posix_time::ptime>& since,
                             const boost::optional<boost::posix_time::ptime>& until,
                             const type::Data& data,
                             const boost::posix_time::ptime& current_datetime) {
    type::Indexes final_indexes;
    pbnavitia::Response pb_response;
    int total_result;
    try {
        final_indexes = make_query(requested_type, request, forbidden_uris, odt_level, since, until, data);
    } catch(const parsing_error &parse_error) {
        fill_pb_error(pbnavitia::Error::unable_to_parse, "Unable to parse :" + parse_error.more, pb_response.mutable_error());
        return pb_response;
    } catch(const ptref_error &pt_error) {
        fill_pb_error(pbnavitia::Error::bad_filter, "ptref : " + pt_error.more, pb_response.mutable_error());
        return pb_response;
    }
    total_result = final_indexes.size();
    final_indexes = paginate(final_indexes, count, startPage);

    pb_response = extract_data(data, requested_type, final_indexes, depth, current_datetime);
    auto pagination = pb_response.mutable_pagination();
    pagination->set_totalresult(total_result);
    pagination->set_startpage(startPage);
    pagination->set_itemsperpage(count);
    pagination->set_itemsonpage(final_indexes.size());
    return pb_response;
}

}}
<|endoftext|>


<commit_before>#include "dtex2/CacheAPI.h"

namespace dtex
{

static CacheAPI::Callback CB;

void CacheAPI::InitCallback(const Callback& cb)
{
	CB = cb;
}

void CacheAPI::RelocatePkg(int src_pkg, int src_tex, int src_lod, int dst_tex_id, int dst_fmt, int dst_w, int dst_h, int dst_xmin, int dst_ymin, int dst_xmax, int dst_ymax)
{
	CB.relocate_pkg(src_pkg, src_tex, src_lod, dst_tex_id, dst_fmt, dst_w, dst_h, dst_xmin, dst_ymin, dst_xmax, dst_ymax);
}

void CacheAPI::RelocatePkgFinish()
{
	CB.relocate_pkg_finish();
}

void CacheAPI::RemoveTex(int tex_id)
{
	CB.remove_tex(tex_id);
}

void CacheAPI::OnClearSymBlock(int block_id)
{
	CB.on_clear_sym_block(block_id);
}

}<commit_msg>[FIXED] check if nullptr<commit_after>#include "dtex2/CacheAPI.h"

namespace dtex
{

static CacheAPI::Callback CB;

void CacheAPI::InitCallback(const Callback& cb)
{
	CB = cb;
}

void CacheAPI::RelocatePkg(int src_pkg, int src_tex, int src_lod, int dst_tex_id, int dst_fmt, int dst_w, int dst_h, int dst_xmin, int dst_ymin, int dst_xmax, int dst_ymax)
{
	if (CB.relocate_pkg) {
		CB.relocate_pkg(src_pkg, src_tex, src_lod, dst_tex_id, dst_fmt, dst_w, dst_h, dst_xmin, dst_ymin, dst_xmax, dst_ymax);
	}
}

void CacheAPI::RelocatePkgFinish()
{
	if (CB.relocate_pkg_finish) {
		CB.relocate_pkg_finish();
	}
}

void CacheAPI::RemoveTex(int tex_id)
{
	if (CB.remove_tex) {
		CB.remove_tex(tex_id);
	}
}

void CacheAPI::OnClearSymBlock(int block_id)
{
	if (CB.on_clear_sym_block) {
		CB.on_clear_sym_block(block_id);
	}
}

}<|endoftext|>