<commit_before>// Copyright (c) 2013 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/component_updater/widevine_cdm_component_installer.h"

#include <string.h>

#include <vector>

#include "base/base_paths.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/string_util.h"
#include "base/values.h"
#include "base/version.h"
#include "build/build_config.h"
#include "chrome/browser/component_updater/component_updater_service.h"
#include "chrome/browser/plugins/plugin_prefs.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/widevine_cdm_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/common/pepper_plugin_info.h"
#include "third_party/widevine/cdm/widevine_cdm_common.h"

#include "widevine_cdm_version.h"  // In SHARED_INTERMEDIATE_DIR.

using content::BrowserThread;
using content::PluginService;

namespace {

// TODO(xhwang): Move duplicate code among all component installer
// implementations to some common place.

// TODO(xhwang): This is the sha256sum of "Widevine CDM". Get the real extension
// ID for Widevine CDM.
// CRX hash. The extension id is: lgafhhiijclkaikclkjjofekikijofcm.
const uint8 kSha2Hash[] = {0xb6, 0x05, 0x77, 0x88, 0x92, 0xba, 0x08, 0xa2,
                           0xba, 0x99, 0xe5, 0x4a, 0x8a, 0x89, 0xe5, 0x2c,
                           0x88, 0x38, 0x2f, 0xf5, 0xa7, 0x7b, 0x93, 0xe7,
                           0xf1, 0x84, 0xcc, 0x37, 0xe1, 0xe5, 0x7a, 0xbd};

// File name of the Widevine CDM component manifest on different platforms.
const char kWidevineCdmManifestName[] = "WidevineCdm";

// Name of the Widevine CDM OS in the component manifest.
const char kWidevineCdmOperatingSystem[] =
#if defined(OS_MACOSX)
    "mac";
#elif defined(OS_WIN)
    "win";
#else  // OS_LINUX, etc. TODO(viettrungluu): Separate out Chrome OS and Android?
    "linux";
#endif

// Name of the Widevine CDM architecture in the component manifest.
const char kWidevineCdmArch[] =
#if defined(ARCH_CPU_X86)
    "ia32";
#elif defined(ARCH_CPU_X86_64)
    "x64";
#else  // TODO(viettrungluu): Support an ARM check?
    "???";
#endif

// If we don't have a Widevine CDM component, this is the version we claim.
const char kNullVersion[] = "0.0.0.0";

// The base directory on Windows looks like:
// <profile>\AppData\Local\Google\Chrome\User Data\WidevineCdm\.
base::FilePath GetWidevineCdmBaseDirectory() {
  base::FilePath result;
  PathService::Get(chrome::DIR_WIDEVINE_CDM, &result);
  return result;
}

#if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
// Widevine CDM plugins have the version encoded in the path itself
// so we need to enumerate the directories to find the full path.
// On success, |latest_dir| returns something like:
// <profile>\AppData\Local\Google\Chrome\User Data\WidevineCdm\10.3.44.555\.
// |latest_version| returns the corresponding version number. |older_dirs|
// returns directories of all older versions.
bool GetWidevineCdmDirectory(base::FilePath* latest_dir,
                             base::Version* latest_version,
                             std::vector<base::FilePath>* older_dirs) {
  base::FilePath base_dir = GetWidevineCdmBaseDirectory();
  bool found = false;
  file_util::FileEnumerator file_enumerator(
      base_dir, false, file_util::FileEnumerator::DIRECTORIES);
  for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
       path = file_enumerator.Next()) {
    base::Version version(path.BaseName().MaybeAsASCII());
    if (!version.IsValid())
      continue;
    if (found) {
      if (version.CompareTo(*latest_version) > 0) {
        older_dirs->push_back(*latest_dir);
        *latest_dir = path;
        *latest_version = version;
      } else {
        older_dirs->push_back(path);
      }
    } else {
      *latest_dir = path;
      *latest_version = version;
      found = true;
    }
  }
  return found;
}
#endif  // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)

bool MakeWidevineCdmPluginInfo(const base::FilePath& path,
                               const base::Version& version,
                               content::PepperPluginInfo* plugin_info) {
  if (!version.IsValid() ||
      version.components().size() !=
          static_cast<size_t>(kWidevineCdmVersionNumComponents)) {
    return false;
  }

  plugin_info->is_internal = false;
  // Widevine CDM must run out of process.
  plugin_info->is_out_of_process = true;
  plugin_info->path = path;
  plugin_info->name = kWidevineCdmPluginName;
  plugin_info->description = kWidevineCdmPluginDescription;
  plugin_info->version = version.GetString();
  webkit::WebPluginMimeType widevine_cdm_mime_type(
      kWidevineCdmPluginMimeType,
      kWidevineCdmPluginExtension,
      kWidevineCdmPluginMimeTypeDescription);
  plugin_info->mime_types.push_back(widevine_cdm_mime_type);
  plugin_info->permissions = kWidevineCdmPluginPermissions;

  return true;
}

void RegisterWidevineCdmWithChrome(const base::FilePath& path,
                                   const base::Version& version) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  content::PepperPluginInfo plugin_info;
  if (!MakeWidevineCdmPluginInfo(path, version, &plugin_info))
    return;

  PluginService::GetInstance()->RegisterInternalPlugin(
      plugin_info.ToWebPluginInfo(), true);
  PluginService::GetInstance()->RefreshPlugins();
}

// Returns true if this browser is compatible with the given Widevine CDM
// manifest, with the version specified in the manifest in |version_out|.
bool CheckWidevineCdmManifest(const base::DictionaryValue& manifest,
                              base::Version* version_out) {
  std::string name;
  manifest.GetStringASCII("name", &name);

  if (name != kWidevineCdmManifestName)
    return false;

  std::string proposed_version;
  manifest.GetStringASCII("version", &proposed_version);
  base::Version version(proposed_version.c_str());
  if (!version.IsValid())
    return false;

  std::string os;
  manifest.GetStringASCII("x-widevine-cdm-os", &os);
  if (os != kWidevineCdmOperatingSystem)
    return false;

  std::string arch;
  manifest.GetStringASCII("x-widevine-cdm-arch", &arch);
  if (arch != kWidevineCdmArch)
    return false;

  *version_out = version;
  return true;
}

}  // namespace

class WidevineCdmComponentInstaller : public ComponentInstaller {
 public:
  explicit WidevineCdmComponentInstaller(const base::Version& version);
  virtual ~WidevineCdmComponentInstaller() {}

  virtual void OnUpdateError(int error) OVERRIDE;
  virtual bool Install(const base::DictionaryValue& manifest,
                       const base::FilePath& unpack_path) OVERRIDE;

 private:
  base::Version current_version_;
};

WidevineCdmComponentInstaller::WidevineCdmComponentInstaller(
    const base::Version& version)
    : current_version_(version) {
  DCHECK(version.IsValid());
}

void WidevineCdmComponentInstaller::OnUpdateError(int error) {
  NOTREACHED() << "Widevine CDM update error: " << error;
}

bool WidevineCdmComponentInstaller::Install(
    const base::DictionaryValue& manifest,
    const base::FilePath& unpack_path) {
  base::Version version;
  if (!CheckWidevineCdmManifest(manifest, &version))
    return false;
  if (current_version_.CompareTo(version) > 0)
    return false;

  // TODO(xhwang): Also check if Widevine CDM binary exists.
  if (!file_util::PathExists(unpack_path.Append(kWidevineCdmPluginFileName)))
    return false;

  // Passed the basic tests. Time to install it.
  base::FilePath path =
      GetWidevineCdmBaseDirectory().AppendASCII(version.GetString());
  if (file_util::PathExists(path))
    return false;
  if (!file_util::Move(unpack_path, path))
    return false;

  // Installation is done. Now register the Widevine CDM with chrome.
  current_version_ = version;
  path = path.Append(kWidevineCdmPluginFileName);
  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
      base::Bind(&RegisterWidevineCdmWithChrome, path, version));
  return true;
}

namespace {

#if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
void FinishWidevineCdmUpdateRegistration(ComponentUpdateService* cus,
                                         const base::Version& version) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  CrxComponent widevine_cdm;
  widevine_cdm.name = "WidevineCdm";
  widevine_cdm.installer = new WidevineCdmComponentInstaller(version);
  widevine_cdm.version = version;
  widevine_cdm.pk_hash.assign(kSha2Hash, &kSha2Hash[sizeof(kSha2Hash)]);
  if (cus->RegisterComponent(widevine_cdm) != ComponentUpdateService::kOk) {
    NOTREACHED() << "Widevine CDM component registration failed.";
    return;
  }
}

void StartWidevineCdmUpdateRegistration(ComponentUpdateService* cus) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  base::FilePath path = GetWidevineCdmBaseDirectory();
  if (!file_util::PathExists(path) && !file_util::CreateDirectory(path)) {
    NOTREACHED() << "Could not create Widevine CDM directory.";
    return;
  }

  base::Version version(kNullVersion);
  std::vector<base::FilePath> older_dirs;
  if (GetWidevineCdmDirectory(&path, &version, &older_dirs)) {
    path = path.Append(kWidevineCdmPluginFileName);
    if (file_util::PathExists(path)) {
      BrowserThread::PostTask(
          BrowserThread::UI, FROM_HERE,
          base::Bind(&RegisterWidevineCdmWithChrome, path, version));
    } else {
      version = base::Version(kNullVersion);
    }
  }

  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      base::Bind(&FinishWidevineCdmUpdateRegistration, cus, version));

  // Remove older versions of Widevine CDM.
  for (std::vector<base::FilePath>::iterator iter = older_dirs.begin();
       iter != older_dirs.end(); ++iter) {
    file_util::Delete(*iter, true);
  }
}
#endif  // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)

}  // namespace

void RegisterWidevineCdmComponent(ComponentUpdateService* cus) {
#if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
                          base::Bind(&StartWidevineCdmUpdateRegistration, cus));
#endif  // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
}
<commit_msg>Update extension ID and CRX hash for Widevine CDM component.<commit_after>// Copyright (c) 2013 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/component_updater/widevine_cdm_component_installer.h"

#include <string.h>

#include <vector>

#include "base/base_paths.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/string_util.h"
#include "base/values.h"
#include "base/version.h"
#include "build/build_config.h"
#include "chrome/browser/component_updater/component_updater_service.h"
#include "chrome/browser/plugins/plugin_prefs.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/widevine_cdm_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/plugin_service.h"
#include "content/public/common/pepper_plugin_info.h"
#include "third_party/widevine/cdm/widevine_cdm_common.h"

#include "widevine_cdm_version.h"  // In SHARED_INTERMEDIATE_DIR.

using content::BrowserThread;
using content::PluginService;

namespace {

// TODO(xhwang): Move duplicate code among all component installer
// implementations to some common place.

// CRX hash. The extension id is: pdkaonnflpjcgibpgaanildgengnihcm.
const uint8 kSha2Hash[] = { 0xf3, 0xa0, 0xed, 0xd5, 0xbf, 0x92, 0x68, 0x1f,
                            0x60, 0x0d, 0x8b, 0x36, 0x4d, 0x6d, 0x87, 0x2c,
                            0x86, 0x61, 0x12, 0x20, 0x21, 0xf8, 0x94, 0xdd,
                            0xe1, 0xb6, 0xb4, 0x55, 0x34, 0x8c, 0x2e, 0x20 };

// File name of the Widevine CDM component manifest on different platforms.
const char kWidevineCdmManifestName[] = "WidevineCdm";

// Name of the Widevine CDM OS in the component manifest.
const char kWidevineCdmOperatingSystem[] =
#if defined(OS_MACOSX)
    "mac";
#elif defined(OS_WIN)
    "win";
#else  // OS_LINUX, etc. TODO(viettrungluu): Separate out Chrome OS and Android?
    "linux";
#endif

// Name of the Widevine CDM architecture in the component manifest.
const char kWidevineCdmArch[] =
#if defined(ARCH_CPU_X86)
    "ia32";
#elif defined(ARCH_CPU_X86_64)
    "x64";
#else  // TODO(viettrungluu): Support an ARM check?
    "???";
#endif

// If we don't have a Widevine CDM component, this is the version we claim.
const char kNullVersion[] = "0.0.0.0";

// The base directory on Windows looks like:
// <profile>\AppData\Local\Google\Chrome\User Data\WidevineCdm\.
base::FilePath GetWidevineCdmBaseDirectory() {
  base::FilePath result;
  PathService::Get(chrome::DIR_WIDEVINE_CDM, &result);
  return result;
}

#if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
// Widevine CDM plugins have the version encoded in the path itself
// so we need to enumerate the directories to find the full path.
// On success, |latest_dir| returns something like:
// <profile>\AppData\Local\Google\Chrome\User Data\WidevineCdm\10.3.44.555\.
// |latest_version| returns the corresponding version number. |older_dirs|
// returns directories of all older versions.
bool GetWidevineCdmDirectory(base::FilePath* latest_dir,
                             base::Version* latest_version,
                             std::vector<base::FilePath>* older_dirs) {
  base::FilePath base_dir = GetWidevineCdmBaseDirectory();
  bool found = false;
  file_util::FileEnumerator file_enumerator(
      base_dir, false, file_util::FileEnumerator::DIRECTORIES);
  for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
       path = file_enumerator.Next()) {
    base::Version version(path.BaseName().MaybeAsASCII());
    if (!version.IsValid())
      continue;
    if (found) {
      if (version.CompareTo(*latest_version) > 0) {
        older_dirs->push_back(*latest_dir);
        *latest_dir = path;
        *latest_version = version;
      } else {
        older_dirs->push_back(path);
      }
    } else {
      *latest_dir = path;
      *latest_version = version;
      found = true;
    }
  }
  return found;
}
#endif  // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)

bool MakeWidevineCdmPluginInfo(const base::FilePath& path,
                               const base::Version& version,
                               content::PepperPluginInfo* plugin_info) {
  if (!version.IsValid() ||
      version.components().size() !=
          static_cast<size_t>(kWidevineCdmVersionNumComponents)) {
    return false;
  }

  plugin_info->is_internal = false;
  // Widevine CDM must run out of process.
  plugin_info->is_out_of_process = true;
  plugin_info->path = path;
  plugin_info->name = kWidevineCdmPluginName;
  plugin_info->description = kWidevineCdmPluginDescription;
  plugin_info->version = version.GetString();
  webkit::WebPluginMimeType widevine_cdm_mime_type(
      kWidevineCdmPluginMimeType,
      kWidevineCdmPluginExtension,
      kWidevineCdmPluginMimeTypeDescription);
  plugin_info->mime_types.push_back(widevine_cdm_mime_type);
  plugin_info->permissions = kWidevineCdmPluginPermissions;

  return true;
}

void RegisterWidevineCdmWithChrome(const base::FilePath& path,
                                   const base::Version& version) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  content::PepperPluginInfo plugin_info;
  if (!MakeWidevineCdmPluginInfo(path, version, &plugin_info))
    return;

  PluginService::GetInstance()->RegisterInternalPlugin(
      plugin_info.ToWebPluginInfo(), true);
  PluginService::GetInstance()->RefreshPlugins();
}

// Returns true if this browser is compatible with the given Widevine CDM
// manifest, with the version specified in the manifest in |version_out|.
bool CheckWidevineCdmManifest(const base::DictionaryValue& manifest,
                              base::Version* version_out) {
  std::string name;
  manifest.GetStringASCII("name", &name);

  if (name != kWidevineCdmManifestName)
    return false;

  std::string proposed_version;
  manifest.GetStringASCII("version", &proposed_version);
  base::Version version(proposed_version.c_str());
  if (!version.IsValid())
    return false;

  std::string os;
  manifest.GetStringASCII("x-widevine-cdm-os", &os);
  if (os != kWidevineCdmOperatingSystem)
    return false;

  std::string arch;
  manifest.GetStringASCII("x-widevine-cdm-arch", &arch);
  if (arch != kWidevineCdmArch)
    return false;

  *version_out = version;
  return true;
}

}  // namespace

class WidevineCdmComponentInstaller : public ComponentInstaller {
 public:
  explicit WidevineCdmComponentInstaller(const base::Version& version);
  virtual ~WidevineCdmComponentInstaller() {}

  virtual void OnUpdateError(int error) OVERRIDE;
  virtual bool Install(const base::DictionaryValue& manifest,
                       const base::FilePath& unpack_path) OVERRIDE;

 private:
  base::Version current_version_;
};

WidevineCdmComponentInstaller::WidevineCdmComponentInstaller(
    const base::Version& version)
    : current_version_(version) {
  DCHECK(version.IsValid());
}

void WidevineCdmComponentInstaller::OnUpdateError(int error) {
  NOTREACHED() << "Widevine CDM update error: " << error;
}

bool WidevineCdmComponentInstaller::Install(
    const base::DictionaryValue& manifest,
    const base::FilePath& unpack_path) {
  base::Version version;
  if (!CheckWidevineCdmManifest(manifest, &version))
    return false;
  if (current_version_.CompareTo(version) > 0)
    return false;

  // TODO(xhwang): Also check if Widevine CDM binary exists.
  if (!file_util::PathExists(unpack_path.Append(kWidevineCdmPluginFileName)))
    return false;

  // Passed the basic tests. Time to install it.
  base::FilePath path =
      GetWidevineCdmBaseDirectory().AppendASCII(version.GetString());
  if (file_util::PathExists(path))
    return false;
  if (!file_util::Move(unpack_path, path))
    return false;

  // Installation is done. Now register the Widevine CDM with chrome.
  current_version_ = version;
  path = path.Append(kWidevineCdmPluginFileName);
  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
      base::Bind(&RegisterWidevineCdmWithChrome, path, version));
  return true;
}

namespace {

#if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
void FinishWidevineCdmUpdateRegistration(ComponentUpdateService* cus,
                                         const base::Version& version) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  CrxComponent widevine_cdm;
  widevine_cdm.name = "WidevineCdm";
  widevine_cdm.installer = new WidevineCdmComponentInstaller(version);
  widevine_cdm.version = version;
  widevine_cdm.pk_hash.assign(kSha2Hash, &kSha2Hash[sizeof(kSha2Hash)]);
  if (cus->RegisterComponent(widevine_cdm) != ComponentUpdateService::kOk) {
    NOTREACHED() << "Widevine CDM component registration failed.";
    return;
  }
}

void StartWidevineCdmUpdateRegistration(ComponentUpdateService* cus) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  base::FilePath path = GetWidevineCdmBaseDirectory();
  if (!file_util::PathExists(path) && !file_util::CreateDirectory(path)) {
    NOTREACHED() << "Could not create Widevine CDM directory.";
    return;
  }

  base::Version version(kNullVersion);
  std::vector<base::FilePath> older_dirs;
  if (GetWidevineCdmDirectory(&path, &version, &older_dirs)) {
    path = path.Append(kWidevineCdmPluginFileName);
    if (file_util::PathExists(path)) {
      BrowserThread::PostTask(
          BrowserThread::UI, FROM_HERE,
          base::Bind(&RegisterWidevineCdmWithChrome, path, version));
    } else {
      version = base::Version(kNullVersion);
    }
  }

  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      base::Bind(&FinishWidevineCdmUpdateRegistration, cus, version));

  // Remove older versions of Widevine CDM.
  for (std::vector<base::FilePath>::iterator iter = older_dirs.begin();
       iter != older_dirs.end(); ++iter) {
    file_util::Delete(*iter, true);
  }
}
#endif  // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)

}  // namespace

void RegisterWidevineCdmComponent(ComponentUpdateService* cus) {
#if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
                          base::Bind(&StartWidevineCdmUpdateRegistration, cus));
#endif  // defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
}
<|endoftext|>


<commit_before>#include "CurlInitializer.h"
#include "ExceptionHelpers.h"

using namespace Instagram;

CurlInitializer::CurlInitializer(CurlApiPtr curlApi) : mCurlApi(curlApi)
{
    if (mCurlApi->curl_global_init(CURL_GLOBAL_ALL))
        Throw(CURL_GLOBAL_INIT_FAILED);
}

CurlInitializer::~CurlInitializer()
{
    mCurlApi->curl_global_cleanup();
}
<commit_msg>Always instantiate CurlInitializer in static memory when cpp-instagram is linked.<commit_after>#include "CurlApiImpl.h"
#include "CurlInitializer.h"
#include "ExceptionHelpers.h"

using namespace Instagram;

namespace
{
    // Initializing will be called during global variables initialization
    CurlInitializer curlInitializer(CurlApiPtr(new CurlApiImpl));
}

CurlInitializer::CurlInitializer(CurlApiPtr curlApi) : mCurlApi(curlApi)
{
    if (mCurlApi->curl_global_init(CURL_GLOBAL_ALL))
        Throw(CURL_GLOBAL_INIT_FAILED);
}

CurlInitializer::~CurlInitializer()
{
    mCurlApi->curl_global_cleanup();
}
<|endoftext|>


<commit_before>/***************************************************************************
 *   Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  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 Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"
#ifndef _PreComp_
# include <QContextMenuEvent>
# include <QMenu>
#endif

#include <Base/Console.h>
#include <App/Application.h>

#include "PrefWidgets.h"
#include "FileDialog.h"
#include <cstring>

using Base::Console;
using namespace Gui;

/** Constructs a preference widget.
 */
PrefWidget::PrefWidget()
 : WindowParameter("")
{
}

/**
 * Destroys the widget and detaches it from its parameter group.
 */
PrefWidget::~PrefWidget()
{
  if (getWindowParameter().isValid())
    getWindowParameter()->Detach(this);
}

/** Sets the preference name to \a name. */
void PrefWidget::setEntryName( const QByteArray& name )
{
  m_sPrefName = name;
}

/** Returns the widget's preference name. */
QByteArray PrefWidget::entryName() const
{
  return m_sPrefName;
}

/** Sets the preference path to \a path. */
void PrefWidget::setParamGrpPath( const QByteArray& path )
{
#ifdef FC_DEBUG
  if (getWindowParameter().isValid())
  {
    if ( paramGrpPath() != path )
      Base::Console().Warning("Widget already attached\n");
  }
#endif

  if ( paramGrpPath() != path )
  {
    if ( setGroupName( path ) )
    {
      m_sPrefGrp = path;
      assert(getWindowParameter().isValid());
      getWindowParameter()->Attach(this);
    }
  }
}

/** Returns the widget's preferences path. */
QByteArray PrefWidget::paramGrpPath() const
{
  return m_sPrefGrp;
}

/**
 * This method is called if one ore more values in the parameter settings are changed
 * where getParamGrp() points to.
 * Note: This method is called for each parameter inside the parameter group. So
 * you have to filter out the appropriate parameter with the name \a sReason.
 * \a rCaller calls this method.
 */
void PrefWidget::OnChange(Base::Subject<const char*> &rCaller, const char * sReason)
{
    if (std::strcmp(sReason,m_sPrefName) == 0)
        restorePreferences();
}

/**
 * Saves the current preferences of the widget.
 * All preference widget attached to the same parameter group are notified.
 */
void PrefWidget::onSave()
{
  savePreferences();
  if (getWindowParameter().isValid())
    getWindowParameter()->Notify( entryName() );
#ifdef FC_DEBUG
  else
    qFatal( "No parameter group specified!" );
#endif
}

/**
 * Restores the preferences of the widget.
 */
void PrefWidget::onRestore()
{
#ifdef FC_DEBUG
  if (getWindowParameter().isNull())
    qWarning( "No parameter group specified!" );
#endif
  restorePreferences();
}

// --------------------------------------------------------------------

PrefSpinBox::PrefSpinBox ( QWidget * parent )
  : QSpinBox(parent), PrefWidget()
{
}

PrefSpinBox::~PrefSpinBox()
{
}

void PrefSpinBox::restorePreferences()
{
  if ( getWindowParameter().isNull() )
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  int nVal = getWindowParameter()->GetInt( entryName(), QSpinBox::value() );
  setValue( nVal );
}

void PrefSpinBox::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  getWindowParameter()->SetInt( entryName() , (int)value() );
}

// --------------------------------------------------------------------

PrefDoubleSpinBox::PrefDoubleSpinBox ( QWidget * parent )
  : QDoubleSpinBox(parent), PrefWidget()
{
}

PrefDoubleSpinBox::~PrefDoubleSpinBox()
{
}

void PrefDoubleSpinBox::restorePreferences()
{
  if ( getWindowParameter().isNull() )
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  double fVal = (double)getWindowParameter()->GetFloat( entryName() , value() );
  setValue(fVal);
}

void PrefDoubleSpinBox::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  getWindowParameter()->SetFloat( entryName(), value() );
}

// --------------------------------------------------------------------

PrefLineEdit::PrefLineEdit ( QWidget * parent )
  : QLineEdit(parent), PrefWidget()
{
}

PrefLineEdit::~PrefLineEdit()
{
}

void PrefLineEdit::restorePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  QString text = this->text();
  text = QString::fromUtf8(getWindowParameter()->GetASCII(entryName(), text.toUtf8()).c_str());
  setText(text);
}

void PrefLineEdit::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  getWindowParameter()->SetASCII(entryName(), text().toUtf8());
}

// --------------------------------------------------------------------

PrefFileChooser::PrefFileChooser ( QWidget * parent )
  : FileChooser(parent), PrefWidget()
{
}

PrefFileChooser::~PrefFileChooser()
{
}

void PrefFileChooser::restorePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  QString txt = QString::fromUtf8(getWindowParameter()->GetASCII(entryName(), fileName().toUtf8()).c_str());
  setFileName(txt);
}

void PrefFileChooser::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  getWindowParameter()->SetASCII(entryName(), fileName().toUtf8());
}

// --------------------------------------------------------------------

PrefComboBox::PrefComboBox ( QWidget * parent )
  : QComboBox(parent), PrefWidget()
{
}

PrefComboBox::~PrefComboBox()
{
}

void PrefComboBox::restorePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  int index = getWindowParameter()->GetInt(entryName(), currentIndex());
  setCurrentIndex(index);
}

void PrefComboBox::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  getWindowParameter()->SetInt(entryName() , currentIndex());
}

// --------------------------------------------------------------------

PrefCheckBox::PrefCheckBox ( QWidget * parent )
  : QCheckBox(parent), PrefWidget()
{
}

PrefCheckBox::~PrefCheckBox()
{
}

void PrefCheckBox::restorePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  bool enable = getWindowParameter()->GetBool( entryName(), isChecked() );
  setChecked(enable);
}

void PrefCheckBox::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  getWindowParameter()->SetBool( entryName(), isChecked() );
}

// --------------------------------------------------------------------

PrefRadioButton::PrefRadioButton ( QWidget * parent )
  : QRadioButton(parent), PrefWidget()
{
}

PrefRadioButton::~PrefRadioButton()
{
}

void PrefRadioButton::restorePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  bool enable = getWindowParameter()->GetBool( entryName(), isChecked() );
  setChecked(enable);
}

void PrefRadioButton::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  getWindowParameter()->SetBool( entryName() , isChecked() );
}

// --------------------------------------------------------------------

PrefSlider::PrefSlider ( QWidget * parent )
  : QSlider(parent), PrefWidget()
{
}

PrefSlider::~PrefSlider()
{
}

void PrefSlider::restorePreferences()
{
  if ( getWindowParameter().isNull() )
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  int nVal = getWindowParameter()->GetInt(entryName(), QSlider::value());
  setValue(nVal);
}

void PrefSlider::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  getWindowParameter()->SetInt(entryName() , (int)value());
}

// --------------------------------------------------------------------

PrefColorButton::PrefColorButton ( QWidget * parent )
  : ColorButton(parent), PrefWidget()
{
}

PrefColorButton::~PrefColorButton()
{
}

void PrefColorButton::restorePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  QColor col = color();

  unsigned long lcol = (col.red() << 24) | (col.green() << 16) | (col.blue() << 8);

  lcol = getWindowParameter()->GetUnsigned( entryName(), lcol );
  int r = (lcol >> 24)&0xff;
  int g = (lcol >> 16)&0xff;
  int b = (lcol >>  8)&0xff;

  setColor(QColor(r,g,b));
}

void PrefColorButton::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  QColor col = color();
  // (r,g,b,a) with a = 255 (opaque)
  unsigned long lcol = (col.red() << 24) | (col.green() << 16) | (col.blue() << 8) | 255;

  getWindowParameter()->SetUnsigned( entryName(), lcol );
}

// --------------------------------------------------------------------

namespace Gui {
class PrefQuantitySpinBoxPrivate
{
public:
    PrefQuantitySpinBoxPrivate() :
      historySize(5)
    {
    }
    ~PrefQuantitySpinBoxPrivate()
    {
    }

    QByteArray prefGrp;
    ParameterGrp::handle handle;
    int historySize;
};
}

PrefQuantitySpinBox::PrefQuantitySpinBox (QWidget * parent)
  : QuantitySpinBox(parent), d_ptr(new PrefQuantitySpinBoxPrivate())
{
}

PrefQuantitySpinBox::~PrefQuantitySpinBox()
{
}

void PrefQuantitySpinBox::contextMenuEvent(QContextMenuEvent *event)
{
    Q_D(PrefQuantitySpinBox);

    QMenu *editMenu = lineEdit()->createStandardContextMenu();
    editMenu->setTitle(tr("Edit"));
    QMenu* menu = new QMenu(QString::fromAscii("PrefQuantitySpinBox"));

    menu->addMenu(editMenu);
    menu->addSeparator();

    // datastructure to remember actions for values
    std::vector<QString> values;
    std::vector<QAction *> actions;

    // add the history menu part...
    QStringList history = getHistory();

    for (QStringList::const_iterator it = history.begin();it!= history.end();++it) {
        actions.push_back(menu->addAction(*it));
        values.push_back(*it);
    }

    // add the save value portion of the menu
    menu->addSeparator();
    QAction *saveValueAction = menu->addAction(tr("Save value"));
    QAction *clearListAction = menu->addAction(tr("Clear list"));
    clearListAction->setDisabled(history.empty());

    // call the menu and wait until its back
    QAction *userAction = menu->exec(event->globalPos());

    // look what the user has choosen
    if (userAction == saveValueAction) {
        pushToHistory(this->text());
    }
    else if (userAction == clearListAction) {
        d->handle->Clear();
    }
    else {
        int i=0;
        for (std::vector<QAction *>::const_iterator it = actions.begin();it!=actions.end();++it,i++) {
            if (*it == userAction) {
                lineEdit()->setText(values[i]);
                break;
            }
        }
    }

    delete menu;
}

void PrefQuantitySpinBox::pushToHistory(const QString &valueq)
{
    Q_D(PrefQuantitySpinBox);

    QString val;
    if (valueq.isEmpty())
        val = this->text();
    else
        val = valueq;

    std::string value(val.toUtf8());
    if (d->handle.isValid()) {
        // do nothing if the given value is on top of the history
        std::string tHist = d->handle->GetASCII("Hist0");
        if (tHist != val.toUtf8().constData()) {
            for (int i = d->historySize -1 ; i>=0 ;i--) {
                QByteArray hist1 = "Hist";
                QByteArray hist0 = "Hist";
                hist1.append(QByteArray::number(i+1));
                hist0.append(QByteArray::number(i));
                std::string tHist = d->handle->GetASCII(hist0);
                if (!tHist.empty())
                    d->handle->SetASCII(hist1,tHist.c_str());
            }
            d->handle->SetASCII("Hist0",value.c_str());
        }
    }
}

QStringList PrefQuantitySpinBox::getHistory() const
{
    Q_D(const PrefQuantitySpinBox);
    QStringList res;

    if (d->handle.isValid()) {
        std::string tmp;
        for (int i = 0 ; i< d->historySize ;i++) {
            QByteArray hist = "Hist";
            hist.append(QByteArray::number(i));
            tmp = d->handle->GetASCII(hist);
            if (!tmp.empty())
                res.push_back(QString::fromUtf8(tmp.c_str()));
            else
                break; // end of history reached
        }
    }

    return res;
}

void PrefQuantitySpinBox::setToLastUsedValue()
{
    QStringList hist = getHistory();
    if (!hist.empty())
        lineEdit()->setText(hist[0]);
}

void PrefQuantitySpinBox::setParamGrpPath(const QByteArray& path)
{
    Q_D(PrefQuantitySpinBox);
    d->handle = App::GetApplication().GetParameterGroupByPath(path);
    if (d->handle.isValid())
        d->prefGrp = path;
}

QByteArray PrefQuantitySpinBox::paramGrpPath() const
{
    Q_D(const PrefQuantitySpinBox);
    if (d->handle.isValid())
        return d->prefGrp;
    return QByteArray();
}

int PrefQuantitySpinBox::historySize() const
{
    Q_D(const PrefQuantitySpinBox);
    return d->historySize;
}

void PrefQuantitySpinBox::setHistorySize(int i)
{
    Q_D(PrefQuantitySpinBox);
    d->historySize = i;
}

#include "moc_PrefWidgets.cpp"
<commit_msg>properly cast to unsigned before the shift<commit_after>/***************************************************************************
 *   Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  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 Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"
#ifndef _PreComp_
# include <QContextMenuEvent>
# include <QMenu>
#endif

#include <Base/Console.h>
#include <App/Application.h>

#include "PrefWidgets.h"
#include "FileDialog.h"
#include <cstring>

using Base::Console;
using namespace Gui;

/** Constructs a preference widget.
 */
PrefWidget::PrefWidget()
 : WindowParameter("")
{
}

/**
 * Destroys the widget and detaches it from its parameter group.
 */
PrefWidget::~PrefWidget()
{
  if (getWindowParameter().isValid())
    getWindowParameter()->Detach(this);
}

/** Sets the preference name to \a name. */
void PrefWidget::setEntryName( const QByteArray& name )
{
  m_sPrefName = name;
}

/** Returns the widget's preference name. */
QByteArray PrefWidget::entryName() const
{
  return m_sPrefName;
}

/** Sets the preference path to \a path. */
void PrefWidget::setParamGrpPath( const QByteArray& path )
{
#ifdef FC_DEBUG
  if (getWindowParameter().isValid())
  {
    if ( paramGrpPath() != path )
      Base::Console().Warning("Widget already attached\n");
  }
#endif

  if ( paramGrpPath() != path )
  {
    if ( setGroupName( path ) )
    {
      m_sPrefGrp = path;
      assert(getWindowParameter().isValid());
      getWindowParameter()->Attach(this);
    }
  }
}

/** Returns the widget's preferences path. */
QByteArray PrefWidget::paramGrpPath() const
{
  return m_sPrefGrp;
}

/**
 * This method is called if one ore more values in the parameter settings are changed
 * where getParamGrp() points to.
 * Note: This method is called for each parameter inside the parameter group. So
 * you have to filter out the appropriate parameter with the name \a sReason.
 * \a rCaller calls this method.
 */
void PrefWidget::OnChange(Base::Subject<const char*> &rCaller, const char * sReason)
{
    if (std::strcmp(sReason,m_sPrefName) == 0)
        restorePreferences();
}

/**
 * Saves the current preferences of the widget.
 * All preference widget attached to the same parameter group are notified.
 */
void PrefWidget::onSave()
{
  savePreferences();
  if (getWindowParameter().isValid())
    getWindowParameter()->Notify( entryName() );
#ifdef FC_DEBUG
  else
    qFatal( "No parameter group specified!" );
#endif
}

/**
 * Restores the preferences of the widget.
 */
void PrefWidget::onRestore()
{
#ifdef FC_DEBUG
  if (getWindowParameter().isNull())
    qWarning( "No parameter group specified!" );
#endif
  restorePreferences();
}

// --------------------------------------------------------------------

PrefSpinBox::PrefSpinBox ( QWidget * parent )
  : QSpinBox(parent), PrefWidget()
{
}

PrefSpinBox::~PrefSpinBox()
{
}

void PrefSpinBox::restorePreferences()
{
  if ( getWindowParameter().isNull() )
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  int nVal = getWindowParameter()->GetInt( entryName(), QSpinBox::value() );
  setValue( nVal );
}

void PrefSpinBox::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  getWindowParameter()->SetInt( entryName() , (int)value() );
}

// --------------------------------------------------------------------

PrefDoubleSpinBox::PrefDoubleSpinBox ( QWidget * parent )
  : QDoubleSpinBox(parent), PrefWidget()
{
}

PrefDoubleSpinBox::~PrefDoubleSpinBox()
{
}

void PrefDoubleSpinBox::restorePreferences()
{
  if ( getWindowParameter().isNull() )
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  double fVal = (double)getWindowParameter()->GetFloat( entryName() , value() );
  setValue(fVal);
}

void PrefDoubleSpinBox::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  getWindowParameter()->SetFloat( entryName(), value() );
}

// --------------------------------------------------------------------

PrefLineEdit::PrefLineEdit ( QWidget * parent )
  : QLineEdit(parent), PrefWidget()
{
}

PrefLineEdit::~PrefLineEdit()
{
}

void PrefLineEdit::restorePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  QString text = this->text();
  text = QString::fromUtf8(getWindowParameter()->GetASCII(entryName(), text.toUtf8()).c_str());
  setText(text);
}

void PrefLineEdit::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  getWindowParameter()->SetASCII(entryName(), text().toUtf8());
}

// --------------------------------------------------------------------

PrefFileChooser::PrefFileChooser ( QWidget * parent )
  : FileChooser(parent), PrefWidget()
{
}

PrefFileChooser::~PrefFileChooser()
{
}

void PrefFileChooser::restorePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  QString txt = QString::fromUtf8(getWindowParameter()->GetASCII(entryName(), fileName().toUtf8()).c_str());
  setFileName(txt);
}

void PrefFileChooser::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  getWindowParameter()->SetASCII(entryName(), fileName().toUtf8());
}

// --------------------------------------------------------------------

PrefComboBox::PrefComboBox ( QWidget * parent )
  : QComboBox(parent), PrefWidget()
{
}

PrefComboBox::~PrefComboBox()
{
}

void PrefComboBox::restorePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  int index = getWindowParameter()->GetInt(entryName(), currentIndex());
  setCurrentIndex(index);
}

void PrefComboBox::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  getWindowParameter()->SetInt(entryName() , currentIndex());
}

// --------------------------------------------------------------------

PrefCheckBox::PrefCheckBox ( QWidget * parent )
  : QCheckBox(parent), PrefWidget()
{
}

PrefCheckBox::~PrefCheckBox()
{
}

void PrefCheckBox::restorePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  bool enable = getWindowParameter()->GetBool( entryName(), isChecked() );
  setChecked(enable);
}

void PrefCheckBox::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  getWindowParameter()->SetBool( entryName(), isChecked() );
}

// --------------------------------------------------------------------

PrefRadioButton::PrefRadioButton ( QWidget * parent )
  : QRadioButton(parent), PrefWidget()
{
}

PrefRadioButton::~PrefRadioButton()
{
}

void PrefRadioButton::restorePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  bool enable = getWindowParameter()->GetBool( entryName(), isChecked() );
  setChecked(enable);
}

void PrefRadioButton::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  getWindowParameter()->SetBool( entryName() , isChecked() );
}

// --------------------------------------------------------------------

PrefSlider::PrefSlider ( QWidget * parent )
  : QSlider(parent), PrefWidget()
{
}

PrefSlider::~PrefSlider()
{
}

void PrefSlider::restorePreferences()
{
  if ( getWindowParameter().isNull() )
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  int nVal = getWindowParameter()->GetInt(entryName(), QSlider::value());
  setValue(nVal);
}

void PrefSlider::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  getWindowParameter()->SetInt(entryName() , (int)value());
}

// --------------------------------------------------------------------

PrefColorButton::PrefColorButton ( QWidget * parent )
  : ColorButton(parent), PrefWidget()
{
}

PrefColorButton::~PrefColorButton()
{
}

void PrefColorButton::restorePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot restore!\n");
    return;
  }

  QColor col = color();

  unsigned long lcol = (col.red() << 24) | (col.green() << 16) | (col.blue() << 8);

  lcol = getWindowParameter()->GetUnsigned( entryName(), lcol );
  int r = (lcol >> 24)&0xff;
  int g = (lcol >> 16)&0xff;
  int b = (lcol >>  8)&0xff;

  setColor(QColor(r,g,b));
}

void PrefColorButton::savePreferences()
{
  if (getWindowParameter().isNull())
  {
    Console().Warning("Cannot save!\n");
    return;
  }

  QColor col = color();
  // (r,g,b,a) with a = 255 (opaque)
  unsigned long lcol = (static_cast<unsigned long> (col.red()) << 24)
          | (static_cast<unsigned long> (col.green()) << 16)
          | (static_cast<unsigned long> (col.blue()) << 8) | 255;
  getWindowParameter()->SetUnsigned( entryName(), lcol );
}

// --------------------------------------------------------------------

namespace Gui {
class PrefQuantitySpinBoxPrivate
{
public:
    PrefQuantitySpinBoxPrivate() :
      historySize(5)
    {
    }
    ~PrefQuantitySpinBoxPrivate()
    {
    }

    QByteArray prefGrp;
    ParameterGrp::handle handle;
    int historySize;
};
}

PrefQuantitySpinBox::PrefQuantitySpinBox (QWidget * parent)
  : QuantitySpinBox(parent), d_ptr(new PrefQuantitySpinBoxPrivate())
{
}

PrefQuantitySpinBox::~PrefQuantitySpinBox()
{
}

void PrefQuantitySpinBox::contextMenuEvent(QContextMenuEvent *event)
{
    Q_D(PrefQuantitySpinBox);

    QMenu *editMenu = lineEdit()->createStandardContextMenu();
    editMenu->setTitle(tr("Edit"));
    QMenu* menu = new QMenu(QString::fromAscii("PrefQuantitySpinBox"));

    menu->addMenu(editMenu);
    menu->addSeparator();

    // datastructure to remember actions for values
    std::vector<QString> values;
    std::vector<QAction *> actions;

    // add the history menu part...
    QStringList history = getHistory();

    for (QStringList::const_iterator it = history.begin();it!= history.end();++it) {
        actions.push_back(menu->addAction(*it));
        values.push_back(*it);
    }

    // add the save value portion of the menu
    menu->addSeparator();
    QAction *saveValueAction = menu->addAction(tr("Save value"));
    QAction *clearListAction = menu->addAction(tr("Clear list"));
    clearListAction->setDisabled(history.empty());

    // call the menu and wait until its back
    QAction *userAction = menu->exec(event->globalPos());

    // look what the user has choosen
    if (userAction == saveValueAction) {
        pushToHistory(this->text());
    }
    else if (userAction == clearListAction) {
        d->handle->Clear();
    }
    else {
        int i=0;
        for (std::vector<QAction *>::const_iterator it = actions.begin();it!=actions.end();++it,i++) {
            if (*it == userAction) {
                lineEdit()->setText(values[i]);
                break;
            }
        }
    }

    delete menu;
}

void PrefQuantitySpinBox::pushToHistory(const QString &valueq)
{
    Q_D(PrefQuantitySpinBox);

    QString val;
    if (valueq.isEmpty())
        val = this->text();
    else
        val = valueq;

    std::string value(val.toUtf8());
    if (d->handle.isValid()) {
        // do nothing if the given value is on top of the history
        std::string tHist = d->handle->GetASCII("Hist0");
        if (tHist != val.toUtf8().constData()) {
            for (int i = d->historySize -1 ; i>=0 ;i--) {
                QByteArray hist1 = "Hist";
                QByteArray hist0 = "Hist";
                hist1.append(QByteArray::number(i+1));
                hist0.append(QByteArray::number(i));
                std::string tHist = d->handle->GetASCII(hist0);
                if (!tHist.empty())
                    d->handle->SetASCII(hist1,tHist.c_str());
            }
            d->handle->SetASCII("Hist0",value.c_str());
        }
    }
}

QStringList PrefQuantitySpinBox::getHistory() const
{
    Q_D(const PrefQuantitySpinBox);
    QStringList res;

    if (d->handle.isValid()) {
        std::string tmp;
        for (int i = 0 ; i< d->historySize ;i++) {
            QByteArray hist = "Hist";
            hist.append(QByteArray::number(i));
            tmp = d->handle->GetASCII(hist);
            if (!tmp.empty())
                res.push_back(QString::fromUtf8(tmp.c_str()));
            else
                break; // end of history reached
        }
    }

    return res;
}

void PrefQuantitySpinBox::setToLastUsedValue()
{
    QStringList hist = getHistory();
    if (!hist.empty())
        lineEdit()->setText(hist[0]);
}

void PrefQuantitySpinBox::setParamGrpPath(const QByteArray& path)
{
    Q_D(PrefQuantitySpinBox);
    d->handle = App::GetApplication().GetParameterGroupByPath(path);
    if (d->handle.isValid())
        d->prefGrp = path;
}

QByteArray PrefQuantitySpinBox::paramGrpPath() const
{
    Q_D(const PrefQuantitySpinBox);
    if (d->handle.isValid())
        return d->prefGrp;
    return QByteArray();
}

int PrefQuantitySpinBox::historySize() const
{
    Q_D(const PrefQuantitySpinBox);
    return d->historySize;
}

void PrefQuantitySpinBox::setHistorySize(int i)
{
    Q_D(PrefQuantitySpinBox);
    d->historySize = i;
}

#include "moc_PrefWidgets.cpp"
<|endoftext|>


<commit_before>/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */

#include "LayerPlanBuffer.h"
#include "gcodeExport.h"
#include "utils/logoutput.h"
#include "FffProcessor.h"

namespace cura {

void LayerPlanBuffer::flush()
{
    if (buffer.size() > 0)
    {
        insertPreheatCommands(); // insert preheat commands of the very last layer
    }
    while (!buffer.empty())
    {
        buffer.front().writeGCode(gcode);
        if (CommandSocket::isInstantiated())
        {
            CommandSocket::getInstance()->flushGcode();
        }
        buffer.pop_front();
    }

}

void LayerPlanBuffer::insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_after_extruder_plan_start, int extruder, double temp)
{
    double acc_time = 0.0;
    for (unsigned int path_idx = 0; path_idx < extruder_plan_before.paths.size(); path_idx++)
    {
        GCodePath& path = extruder_plan_before.paths[path_idx];
        acc_time += path.estimates.getTotalTime();
        if (acc_time > time_after_extruder_plan_start)
        {
//                 logError("Inserting %f\t seconds too early!\n", acc_time - time_after_extruder_plan_start);
            extruder_plan_before.insertCommand(path_idx, extruder, temp, false, acc_time - time_after_extruder_plan_start);
            return;
        }
    }
    // TODO: walk over paths from back to front instead of front to back!
    extruder_plan_before.insertCommand(extruder_plan_before.paths.size(), extruder, temp, false); // insert at end of extruder plan if time_after_extruder_plan_start > extruder_plan.time
    // = special insert after all extruder plans
}

Preheat::WarmUpResult LayerPlanBuffer::timeBeforeExtruderPlanToInsert(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
{
    ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
    int extruder = extruder_plan.extruder;
    double required_temp = extruder_plan.required_temp;

    unsigned int extruder_plan_before_idx = extruder_plan_idx - 1;
    bool first_it = true;
    double in_between_time = 0.0;
    for (unsigned int layer_idx = layer_plan_idx; int(layer_idx) >= 0; layer_idx--)
    { // find a previous extruder plan where the same extruder is used to see what time this extruder wasn't used
        GCodePlanner& layer = *layers[layer_idx];
        if (!first_it)
        {
            extruder_plan_before_idx = layer.extruder_plans.size() - 1;
        }
        for ( ; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
        {
            ExtruderPlan& extruder_plan = layer.extruder_plans[extruder_plan_before_idx];
            if (extruder_plan.extruder == extruder)
            {
                Preheat::WarmUpResult warm_up = preheat_config.timeBeforeEndToInsertPreheatCommand_coolDownWarmUp(in_between_time, extruder, required_temp);
                warm_up.heating_time = std::min(in_between_time, warm_up.heating_time + time_to_start_warmup_earlier_to_be_extra_sure_we_dont_have_to_wait);
                return warm_up;
            }
            in_between_time += extruder_plan.estimates.getTotalTime();
        }
        first_it = false;
    }
    // The last extruder plan with the same extruder falls outside of the buffer
    // assume the nozzle has cooled down to strandby temperature already.
    Preheat::WarmUpResult warm_up;
    warm_up.total_time_window = in_between_time;
    warm_up.lowest_temperature = preheat_config.getStandbyTemp(extruder);
    warm_up.heating_time = preheat_config.timeBeforeEndToInsertPreheatCommand_warmUp(warm_up.lowest_temperature, extruder, required_temp, false);
    if (warm_up.heating_time > in_between_time)
    {
        warm_up.heating_time = in_between_time;
        warm_up.lowest_temperature = in_between_time / preheat_config.getTimeToHeatup1Degree(extruder);
    }
    warm_up.heating_time = warm_up.heating_time + time_to_start_warmup_earlier_to_be_extra_sure_we_dont_have_to_wait;
    return warm_up;

}

void LayerPlanBuffer::insertPreheatCommand_singleExtrusion(ExtruderPlan& prev_extruder_plan, int extruder, double required_temp)
{
    // time_before_extruder_plan_end is halved, so that at the layer change the temperature will be half way betewen the two requested temperatures
    double time_before_extruder_plan_end = 0.5 * preheat_config.timeBeforeEndToInsertPreheatCommand_warmUp(prev_extruder_plan.required_temp, extruder, required_temp, true);
    double time_after_extruder_plan_start = prev_extruder_plan.estimates.getTotalTime() - time_before_extruder_plan_end;
    if (time_after_extruder_plan_start < 0)
    {
        time_after_extruder_plan_start = 0; // don't override the extruder plan with same extruder of the previous layer
    }

    insertPreheatCommand(prev_extruder_plan, time_after_extruder_plan_start, extruder, required_temp);
}

void LayerPlanBuffer::handleStandbyTemp(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx, double standby_temp)
{
    ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
    int extruder = extruder_plan.extruder;
    unsigned int extruder_plan_before_idx = extruder_plan_idx - 1;
    bool first_it = true;
    for (unsigned int layer_idx = layer_plan_idx; int(layer_idx) >= 0; layer_idx--)
    { // find a previous extruder plan where the same extruder is used to see what time this extruder wasn't used
        GCodePlanner& layer = *layers[layer_idx];
        if (!first_it)
        {
            extruder_plan_before_idx = layer.extruder_plans.size() - 1;
        }
        for ( ; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
        {
            ExtruderPlan& extruder_plan = layer.extruder_plans[extruder_plan_before_idx];
            if (extruder_plan.extruder == extruder)
            {
                extruder_plan.prev_extruder_standby_temp = standby_temp;
                return;
            }
        }
        first_it = false;
    }
}

void LayerPlanBuffer::insertPreheatCommand_multiExtrusion(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
{
    ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
    int extruder = extruder_plan.extruder;
    double required_temp = extruder_plan.required_temp;

    Preheat::WarmUpResult heating_time_and_from_temp = timeBeforeExtruderPlanToInsert(layers, layer_plan_idx, extruder_plan_idx);

    handleStandbyTemp(layers, layer_plan_idx, extruder_plan_idx, heating_time_and_from_temp.lowest_temperature);

    double time_before_extruder_plan_to_insert = heating_time_and_from_temp.heating_time;
    unsigned int extruder_plan_before_idx = extruder_plan_idx - 1;
    bool first_it = true; // Whether it's the first iteration of the for loop below
    for (unsigned int layer_idx = layer_plan_idx; int(layer_idx) >= 0; layer_idx--)
    {
        GCodePlanner& layer = *layers[layer_idx];
        if (!first_it)
        { // go over all extruder plans in the layer except for the layer of this extruder plan
            extruder_plan_before_idx = layer.extruder_plans.size() - 1;
        }
        for ( ; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
        {
            ExtruderPlan& extruder_plan_before = layer.extruder_plans[extruder_plan_before_idx];
            assert (extruder_plan_before.extruder != extruder);

            double time_here = extruder_plan_before.estimates.getTotalTime();
            if (time_here >= time_before_extruder_plan_to_insert)
            {
                insertPreheatCommand(extruder_plan_before, time_here - time_before_extruder_plan_to_insert, extruder, required_temp);
                return;
            }
            time_before_extruder_plan_to_insert -= time_here;

        }
        first_it = false;
    }

    // time_before_extruder_plan_to_insert falls before all plans in the buffer
    ExtruderPlan& first_extruder_plan = layers[0]->extruder_plans[0];
    first_extruder_plan.insertCommand(0, extruder, required_temp, false); // insert preheat command at verfy beginning of buffer
}

void LayerPlanBuffer::insertPreheatCommand(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
{
    ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
    int extruder = extruder_plan.extruder;
    double required_temp = extruder_plan.required_temp;

    ExtruderPlan* prev_extruder_plan = nullptr;
    if (extruder_plan_idx == 0)
    {
        if (layer_plan_idx == 0)
        { // the very first extruder plan of the current meshgroup
            for (int extruder_idx = 0; extruder_idx < getSettingAsCount("machine_extruder_count"); extruder_idx++)
            { // set temperature of the first nozzle, turn other nozzles down
                if (FffProcessor::getInstance()->getMeshgroupNr() == 0)
                {
                    // override values from GCodeExport::setInitialTemps
                    // the first used extruder should be set to the required temp in the start gcode
                    // see  FffGcodeWriter::processStartingCode
                    if (extruder_idx == extruder)
                    {
                        gcode.setInitialTemp(extruder_idx, required_temp);
                    }
                    else
                    {
                        gcode.setInitialTemp(extruder_idx, preheat_config.getStandbyTemp(extruder_idx));
                    }
                }
                else
                {
                    if (extruder_idx == extruder)
                    {
                        extruder_plan.required_temp = required_temp;
                    }
                    else
                    { // TODO: do we need to do this?
                        extruder_plan.prev_extruder_standby_temp = preheat_config.getStandbyTemp(extruder_idx);
                    }
                }
            }
            return;
        }
        prev_extruder_plan = &layers[layer_plan_idx - 1]->extruder_plans.back();
    }
    else
    {
        prev_extruder_plan = &layers[layer_plan_idx]->extruder_plans[extruder_plan_idx - 1];
    }
    assert(prev_extruder_plan != nullptr);

    int prev_extruder = prev_extruder_plan->extruder;

    if (prev_extruder != extruder)
    { // set previous extruder to standby temperature
        extruder_plan.prev_extruder_standby_temp = preheat_config.getStandbyTemp(prev_extruder);
    }

    if (prev_extruder == extruder)
    {
        if (preheat_config.usesFlowDependentTemp(extruder))
        {
            insertPreheatCommand_singleExtrusion(*prev_extruder_plan, extruder, required_temp);
        }
    }
    else
    {
        insertPreheatCommand_multiExtrusion(layers, layer_plan_idx, extruder_plan_idx);
    }

}

void LayerPlanBuffer::insertPreheatCommands()
{
    if (buffer.back().extruder_plans.size() == 0 || (buffer.back().extruder_plans.size() == 1 && buffer.back().extruder_plans[0].paths.size() == 0))
    { // disregard empty layer
        buffer.pop_back();
        return;
    }

    std::vector<GCodePlanner*> layers;
    layers.reserve(buffer.size());
    for (GCodePlanner& layer_plan : buffer)
    {
        layers.push_back(&layer_plan);
    }

    unsigned int layer_idx = layers.size() - 1;

    // insert commands for all extruder plans on this layer
    GCodePlanner& layer_plan = *layers[layer_idx];
    for (unsigned int extruder_plan_idx = 0; extruder_plan_idx < layer_plan.extruder_plans.size(); extruder_plan_idx++)
    {
        ExtruderPlan& extruder_plan = layer_plan.extruder_plans[extruder_plan_idx];
        double time = extruder_plan.estimates.getTotalUnretractedTime();
        if (time <= 0.0
            || extruder_plan.estimates.getMaterial() == 0.0 // extruder plan only consists of moves (when an extruder switch occurs at the beginning of a layer)
        )
        {
            continue;
        }
        double avg_flow = extruder_plan.estimates.getMaterial() / time; // TODO: subtract retracted travel time
        extruder_plan.required_temp = preheat_config.getTemp(extruder_plan.extruder, avg_flow);

        insertPreheatCommand(layers, layer_idx, extruder_plan_idx);
    }
}

} // namespace cura<commit_msg>fix: removed superfluous if case (CURA-1731)<commit_after>/** Copyright (C) 2015 Ultimaker - Released under terms of the AGPLv3 License */

#include "LayerPlanBuffer.h"
#include "gcodeExport.h"
#include "utils/logoutput.h"
#include "FffProcessor.h"

namespace cura {

void LayerPlanBuffer::flush()
{
    if (buffer.size() > 0)
    {
        insertPreheatCommands(); // insert preheat commands of the very last layer
    }
    while (!buffer.empty())
    {
        buffer.front().writeGCode(gcode);
        if (CommandSocket::isInstantiated())
        {
            CommandSocket::getInstance()->flushGcode();
        }
        buffer.pop_front();
    }

}

void LayerPlanBuffer::insertPreheatCommand(ExtruderPlan& extruder_plan_before, double time_after_extruder_plan_start, int extruder, double temp)
{
    double acc_time = 0.0;
    for (unsigned int path_idx = 0; path_idx < extruder_plan_before.paths.size(); path_idx++)
    {
        GCodePath& path = extruder_plan_before.paths[path_idx];
        acc_time += path.estimates.getTotalTime();
        if (acc_time > time_after_extruder_plan_start)
        {
//                 logError("Inserting %f\t seconds too early!\n", acc_time - time_after_extruder_plan_start);
            extruder_plan_before.insertCommand(path_idx, extruder, temp, false, acc_time - time_after_extruder_plan_start);
            return;
        }
    }
    // TODO: walk over paths from back to front instead of front to back!
    extruder_plan_before.insertCommand(extruder_plan_before.paths.size(), extruder, temp, false); // insert at end of extruder plan if time_after_extruder_plan_start > extruder_plan.time
    // = special insert after all extruder plans
}

Preheat::WarmUpResult LayerPlanBuffer::timeBeforeExtruderPlanToInsert(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
{
    ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
    int extruder = extruder_plan.extruder;
    double required_temp = extruder_plan.required_temp;

    unsigned int extruder_plan_before_idx = extruder_plan_idx - 1;
    bool first_it = true;
    double in_between_time = 0.0;
    for (unsigned int layer_idx = layer_plan_idx; int(layer_idx) >= 0; layer_idx--)
    { // find a previous extruder plan where the same extruder is used to see what time this extruder wasn't used
        GCodePlanner& layer = *layers[layer_idx];
        if (!first_it)
        {
            extruder_plan_before_idx = layer.extruder_plans.size() - 1;
        }
        for ( ; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
        {
            ExtruderPlan& extruder_plan = layer.extruder_plans[extruder_plan_before_idx];
            if (extruder_plan.extruder == extruder)
            {
                Preheat::WarmUpResult warm_up = preheat_config.timeBeforeEndToInsertPreheatCommand_coolDownWarmUp(in_between_time, extruder, required_temp);
                warm_up.heating_time = std::min(in_between_time, warm_up.heating_time + time_to_start_warmup_earlier_to_be_extra_sure_we_dont_have_to_wait);
                return warm_up;
            }
            in_between_time += extruder_plan.estimates.getTotalTime();
        }
        first_it = false;
    }
    // The last extruder plan with the same extruder falls outside of the buffer
    // assume the nozzle has cooled down to strandby temperature already.
    Preheat::WarmUpResult warm_up;
    warm_up.total_time_window = in_between_time;
    warm_up.lowest_temperature = preheat_config.getStandbyTemp(extruder);
    warm_up.heating_time = preheat_config.timeBeforeEndToInsertPreheatCommand_warmUp(warm_up.lowest_temperature, extruder, required_temp, false);
    if (warm_up.heating_time > in_between_time)
    {
        warm_up.heating_time = in_between_time;
        warm_up.lowest_temperature = in_between_time / preheat_config.getTimeToHeatup1Degree(extruder);
    }
    warm_up.heating_time = warm_up.heating_time + time_to_start_warmup_earlier_to_be_extra_sure_we_dont_have_to_wait;
    return warm_up;

}

void LayerPlanBuffer::insertPreheatCommand_singleExtrusion(ExtruderPlan& prev_extruder_plan, int extruder, double required_temp)
{
    // time_before_extruder_plan_end is halved, so that at the layer change the temperature will be half way betewen the two requested temperatures
    double time_before_extruder_plan_end = 0.5 * preheat_config.timeBeforeEndToInsertPreheatCommand_warmUp(prev_extruder_plan.required_temp, extruder, required_temp, true);
    double time_after_extruder_plan_start = prev_extruder_plan.estimates.getTotalTime() - time_before_extruder_plan_end;
    if (time_after_extruder_plan_start < 0)
    {
        time_after_extruder_plan_start = 0; // don't override the extruder plan with same extruder of the previous layer
    }

    insertPreheatCommand(prev_extruder_plan, time_after_extruder_plan_start, extruder, required_temp);
}

void LayerPlanBuffer::handleStandbyTemp(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx, double standby_temp)
{
    ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
    int extruder = extruder_plan.extruder;
    unsigned int extruder_plan_before_idx = extruder_plan_idx - 1;
    bool first_it = true;
    for (unsigned int layer_idx = layer_plan_idx; int(layer_idx) >= 0; layer_idx--)
    { // find a previous extruder plan where the same extruder is used to see what time this extruder wasn't used
        GCodePlanner& layer = *layers[layer_idx];
        if (!first_it)
        {
            extruder_plan_before_idx = layer.extruder_plans.size() - 1;
        }
        for ( ; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
        {
            ExtruderPlan& extruder_plan = layer.extruder_plans[extruder_plan_before_idx];
            if (extruder_plan.extruder == extruder)
            {
                extruder_plan.prev_extruder_standby_temp = standby_temp;
                return;
            }
        }
        first_it = false;
    }
}

void LayerPlanBuffer::insertPreheatCommand_multiExtrusion(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
{
    ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
    int extruder = extruder_plan.extruder;
    double required_temp = extruder_plan.required_temp;

    Preheat::WarmUpResult heating_time_and_from_temp = timeBeforeExtruderPlanToInsert(layers, layer_plan_idx, extruder_plan_idx);

    handleStandbyTemp(layers, layer_plan_idx, extruder_plan_idx, heating_time_and_from_temp.lowest_temperature);

    double time_before_extruder_plan_to_insert = heating_time_and_from_temp.heating_time;
    unsigned int extruder_plan_before_idx = extruder_plan_idx - 1;
    bool first_it = true; // Whether it's the first iteration of the for loop below
    for (unsigned int layer_idx = layer_plan_idx; int(layer_idx) >= 0; layer_idx--)
    {
        GCodePlanner& layer = *layers[layer_idx];
        if (!first_it)
        { // go over all extruder plans in the layer except for the layer of this extruder plan
            extruder_plan_before_idx = layer.extruder_plans.size() - 1;
        }
        for ( ; int(extruder_plan_before_idx) >= 0; extruder_plan_before_idx--)
        {
            ExtruderPlan& extruder_plan_before = layer.extruder_plans[extruder_plan_before_idx];
            assert (extruder_plan_before.extruder != extruder);

            double time_here = extruder_plan_before.estimates.getTotalTime();
            if (time_here >= time_before_extruder_plan_to_insert)
            {
                insertPreheatCommand(extruder_plan_before, time_here - time_before_extruder_plan_to_insert, extruder, required_temp);
                return;
            }
            time_before_extruder_plan_to_insert -= time_here;

        }
        first_it = false;
    }

    // time_before_extruder_plan_to_insert falls before all plans in the buffer
    ExtruderPlan& first_extruder_plan = layers[0]->extruder_plans[0];
    first_extruder_plan.insertCommand(0, extruder, required_temp, false); // insert preheat command at verfy beginning of buffer
}

void LayerPlanBuffer::insertPreheatCommand(std::vector<GCodePlanner*>& layers, unsigned int layer_plan_idx, unsigned int extruder_plan_idx)
{
    ExtruderPlan& extruder_plan = layers[layer_plan_idx]->extruder_plans[extruder_plan_idx];
    int extruder = extruder_plan.extruder;
    double required_temp = extruder_plan.required_temp;

    ExtruderPlan* prev_extruder_plan = nullptr;
    if (extruder_plan_idx == 0)
    {
        if (layer_plan_idx == 0)
        { // the very first extruder plan of the current meshgroup
            for (int extruder_idx = 0; extruder_idx < getSettingAsCount("machine_extruder_count"); extruder_idx++)
            { // set temperature of the first nozzle, turn other nozzles down
                if (FffProcessor::getInstance()->getMeshgroupNr() == 0)
                {
                    // override values from GCodeExport::setInitialTemps
                    // the first used extruder should be set to the required temp in the start gcode
                    // see  FffGcodeWriter::processStartingCode
                    if (extruder_idx == extruder)
                    {
                        gcode.setInitialTemp(extruder_idx, required_temp);
                    }
                    else
                    {
                        gcode.setInitialTemp(extruder_idx, preheat_config.getStandbyTemp(extruder_idx));
                    }
                }
                else
                {
                    if (extruder_idx != extruder)
                    { // TODO: do we need to do this?
                        extruder_plan.prev_extruder_standby_temp = preheat_config.getStandbyTemp(extruder_idx);
                    }
                }
            }
            return;
        }
        prev_extruder_plan = &layers[layer_plan_idx - 1]->extruder_plans.back();
    }
    else
    {
        prev_extruder_plan = &layers[layer_plan_idx]->extruder_plans[extruder_plan_idx - 1];
    }
    assert(prev_extruder_plan != nullptr);

    int prev_extruder = prev_extruder_plan->extruder;

    if (prev_extruder != extruder)
    { // set previous extruder to standby temperature
        extruder_plan.prev_extruder_standby_temp = preheat_config.getStandbyTemp(prev_extruder);
    }

    if (prev_extruder == extruder)
    {
        if (preheat_config.usesFlowDependentTemp(extruder))
        {
            insertPreheatCommand_singleExtrusion(*prev_extruder_plan, extruder, required_temp);
        }
    }
    else
    {
        insertPreheatCommand_multiExtrusion(layers, layer_plan_idx, extruder_plan_idx);
    }

}

void LayerPlanBuffer::insertPreheatCommands()
{
    if (buffer.back().extruder_plans.size() == 0 || (buffer.back().extruder_plans.size() == 1 && buffer.back().extruder_plans[0].paths.size() == 0))
    { // disregard empty layer
        buffer.pop_back();
        return;
    }

    std::vector<GCodePlanner*> layers;
    layers.reserve(buffer.size());
    for (GCodePlanner& layer_plan : buffer)
    {
        layers.push_back(&layer_plan);
    }

    unsigned int layer_idx = layers.size() - 1;

    // insert commands for all extruder plans on this layer
    GCodePlanner& layer_plan = *layers[layer_idx];
    for (unsigned int extruder_plan_idx = 0; extruder_plan_idx < layer_plan.extruder_plans.size(); extruder_plan_idx++)
    {
        ExtruderPlan& extruder_plan = layer_plan.extruder_plans[extruder_plan_idx];
        double time = extruder_plan.estimates.getTotalUnretractedTime();
        if (time <= 0.0
            || extruder_plan.estimates.getMaterial() == 0.0 // extruder plan only consists of moves (when an extruder switch occurs at the beginning of a layer)
        )
        {
            continue;
        }
        double avg_flow = extruder_plan.estimates.getMaterial() / time; // TODO: subtract retracted travel time
        extruder_plan.required_temp = preheat_config.getTemp(extruder_plan.extruder, avg_flow);

        insertPreheatCommand(layers, layer_idx, extruder_plan_idx);
    }
}

} // namespace cura<|endoftext|>


<commit_before>//===- tools/dsymutil/CompileUnit.h - Dwarf compile unit ------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "CompileUnit.h"
#include "DeclContext.h"

namespace llvm {
namespace dsymutil {

/// Check if the DIE at \p Idx is in the scope of a function.
static bool inFunctionScope(CompileUnit &U, unsigned Idx) {
  while (Idx) {
    if (U.getOrigUnit().getDIEAtIndex(Idx).getTag() == dwarf::DW_TAG_subprogram)
      return true;
    Idx = U.getInfo(Idx).ParentIdx;
  }
  return false;
}

void CompileUnit::markEverythingAsKept() {
  unsigned Idx = 0;

  setHasInterestingContent();

  for (auto &I : Info) {
    // Mark everything that wasn't explicit marked for pruning.
    I.Keep = !I.Prune;
    auto DIE = OrigUnit.getDIEAtIndex(Idx++);

    // Try to guess which DIEs must go to the accelerator tables. We do that
    // just for variables, because functions will be handled depending on
    // whether they carry a DW_AT_low_pc attribute or not.
    if (DIE.getTag() != dwarf::DW_TAG_variable &&
        DIE.getTag() != dwarf::DW_TAG_constant)
      continue;

    Optional<DWARFFormValue> Value;
    if (!(Value = DIE.find(dwarf::DW_AT_location))) {
      if ((Value = DIE.find(dwarf::DW_AT_const_value)) &&
          !inFunctionScope(*this, I.ParentIdx))
        I.InDebugMap = true;
      continue;
    }
    if (auto Block = Value->getAsBlock()) {
      if (Block->size() > OrigUnit.getAddressByteSize() &&
          (*Block)[0] == dwarf::DW_OP_addr)
        I.InDebugMap = true;
    }
  }
}

uint64_t CompileUnit::computeNextUnitOffset() {
  NextUnitOffset = StartOffset + 11 /* Header size */;
  // The root DIE might be null, meaning that the Unit had nothing to
  // contribute to the linked output. In that case, we will emit the
  // unit header without any actual DIE.
  if (NewUnit)
    NextUnitOffset += NewUnit->getUnitDie().getSize();
  return NextUnitOffset;
}

/// Keep track of a forward cross-cu reference from this unit
/// to \p Die that lives in \p RefUnit.
void CompileUnit::noteForwardReference(DIE *Die, const CompileUnit *RefUnit,
                                       DeclContext *Ctxt, PatchLocation Attr) {
  ForwardDIEReferences.emplace_back(Die, RefUnit, Ctxt, Attr);
}

void CompileUnit::fixupForwardReferences() {
  for (const auto &Ref : ForwardDIEReferences) {
    DIE *RefDie;
    const CompileUnit *RefUnit;
    PatchLocation Attr;
    DeclContext *Ctxt;
    std::tie(RefDie, RefUnit, Ctxt, Attr) = Ref;
    if (Ctxt && Ctxt->getCanonicalDIEOffset())
      Attr.set(Ctxt->getCanonicalDIEOffset());
    else
      Attr.set(RefDie->getOffset() + RefUnit->getStartOffset());
  }
}

void CompileUnit::addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset) {
  Labels.insert({LabelLowPc, PcOffset});
}

void CompileUnit::addFunctionRange(uint64_t FuncLowPc, uint64_t FuncHighPc,
                                   int64_t PcOffset) {
  Ranges.insert(FuncLowPc, FuncHighPc, PcOffset);
  this->LowPc = std::min(LowPc, FuncLowPc + PcOffset);
  this->HighPc = std::max(HighPc, FuncHighPc + PcOffset);
}

void CompileUnit::noteRangeAttribute(const DIE &Die, PatchLocation Attr) {
  if (Die.getTag() != dwarf::DW_TAG_compile_unit)
    RangeAttributes.push_back(Attr);
  else
    UnitRangeAttribute = Attr;
}

void CompileUnit::noteLocationAttribute(PatchLocation Attr, int64_t PcOffset) {
  LocationAttributes.emplace_back(Attr, PcOffset);
}

void CompileUnit::addNamespaceAccelerator(const DIE *Die,
                                          DwarfStringPoolEntryRef Name) {
  Namespaces.emplace_back(Name, Die);
}

void CompileUnit::addObjCAccelerator(const DIE *Die,
                                     DwarfStringPoolEntryRef Name,
                                     bool SkipPubSection) {
  ObjC.emplace_back(Name, Die, SkipPubSection);
}

void CompileUnit::addNameAccelerator(const DIE *Die,
                                     DwarfStringPoolEntryRef Name,
                                     bool SkipPubSection) {
  Pubnames.emplace_back(Name, Die, SkipPubSection);
}

void CompileUnit::addTypeAccelerator(const DIE *Die,
                                     DwarfStringPoolEntryRef Name,
                                     bool ObjcClassImplementation,
                                     uint32_t QualifiedNameHash) {
  Pubtypes.emplace_back(Name, Die, QualifiedNameHash, ObjcClassImplementation);
}

} // namespace dsymutil
} // namespace llvm
<commit_msg>[dsymutil] Fix assertion triggered by empty address range.<commit_after>//===- tools/dsymutil/CompileUnit.h - Dwarf compile unit ------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "CompileUnit.h"
#include "DeclContext.h"

namespace llvm {
namespace dsymutil {

/// Check if the DIE at \p Idx is in the scope of a function.
static bool inFunctionScope(CompileUnit &U, unsigned Idx) {
  while (Idx) {
    if (U.getOrigUnit().getDIEAtIndex(Idx).getTag() == dwarf::DW_TAG_subprogram)
      return true;
    Idx = U.getInfo(Idx).ParentIdx;
  }
  return false;
}

void CompileUnit::markEverythingAsKept() {
  unsigned Idx = 0;

  setHasInterestingContent();

  for (auto &I : Info) {
    // Mark everything that wasn't explicit marked for pruning.
    I.Keep = !I.Prune;
    auto DIE = OrigUnit.getDIEAtIndex(Idx++);

    // Try to guess which DIEs must go to the accelerator tables. We do that
    // just for variables, because functions will be handled depending on
    // whether they carry a DW_AT_low_pc attribute or not.
    if (DIE.getTag() != dwarf::DW_TAG_variable &&
        DIE.getTag() != dwarf::DW_TAG_constant)
      continue;

    Optional<DWARFFormValue> Value;
    if (!(Value = DIE.find(dwarf::DW_AT_location))) {
      if ((Value = DIE.find(dwarf::DW_AT_const_value)) &&
          !inFunctionScope(*this, I.ParentIdx))
        I.InDebugMap = true;
      continue;
    }
    if (auto Block = Value->getAsBlock()) {
      if (Block->size() > OrigUnit.getAddressByteSize() &&
          (*Block)[0] == dwarf::DW_OP_addr)
        I.InDebugMap = true;
    }
  }
}

uint64_t CompileUnit::computeNextUnitOffset() {
  NextUnitOffset = StartOffset + 11 /* Header size */;
  // The root DIE might be null, meaning that the Unit had nothing to
  // contribute to the linked output. In that case, we will emit the
  // unit header without any actual DIE.
  if (NewUnit)
    NextUnitOffset += NewUnit->getUnitDie().getSize();
  return NextUnitOffset;
}

/// Keep track of a forward cross-cu reference from this unit
/// to \p Die that lives in \p RefUnit.
void CompileUnit::noteForwardReference(DIE *Die, const CompileUnit *RefUnit,
                                       DeclContext *Ctxt, PatchLocation Attr) {
  ForwardDIEReferences.emplace_back(Die, RefUnit, Ctxt, Attr);
}

void CompileUnit::fixupForwardReferences() {
  for (const auto &Ref : ForwardDIEReferences) {
    DIE *RefDie;
    const CompileUnit *RefUnit;
    PatchLocation Attr;
    DeclContext *Ctxt;
    std::tie(RefDie, RefUnit, Ctxt, Attr) = Ref;
    if (Ctxt && Ctxt->getCanonicalDIEOffset())
      Attr.set(Ctxt->getCanonicalDIEOffset());
    else
      Attr.set(RefDie->getOffset() + RefUnit->getStartOffset());
  }
}

void CompileUnit::addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset) {
  Labels.insert({LabelLowPc, PcOffset});
}

void CompileUnit::addFunctionRange(uint64_t FuncLowPc, uint64_t FuncHighPc,
                                   int64_t PcOffset) {
  //  Don't add empty ranges to the interval map.  They are a problem because
  //  the interval map expects half open intervals. This is safe because they
  //  are empty anyway.
  if (FuncHighPc != FuncLowPc)
    Ranges.insert(FuncLowPc, FuncHighPc, PcOffset);
  this->LowPc = std::min(LowPc, FuncLowPc + PcOffset);
  this->HighPc = std::max(HighPc, FuncHighPc + PcOffset);
}

void CompileUnit::noteRangeAttribute(const DIE &Die, PatchLocation Attr) {
  if (Die.getTag() != dwarf::DW_TAG_compile_unit)
    RangeAttributes.push_back(Attr);
  else
    UnitRangeAttribute = Attr;
}

void CompileUnit::noteLocationAttribute(PatchLocation Attr, int64_t PcOffset) {
  LocationAttributes.emplace_back(Attr, PcOffset);
}

void CompileUnit::addNamespaceAccelerator(const DIE *Die,
                                          DwarfStringPoolEntryRef Name) {
  Namespaces.emplace_back(Name, Die);
}

void CompileUnit::addObjCAccelerator(const DIE *Die,
                                     DwarfStringPoolEntryRef Name,
                                     bool SkipPubSection) {
  ObjC.emplace_back(Name, Die, SkipPubSection);
}

void CompileUnit::addNameAccelerator(const DIE *Die,
                                     DwarfStringPoolEntryRef Name,
                                     bool SkipPubSection) {
  Pubnames.emplace_back(Name, Die, SkipPubSection);
}

void CompileUnit::addTypeAccelerator(const DIE *Die,
                                     DwarfStringPoolEntryRef Name,
                                     bool ObjcClassImplementation,
                                     uint32_t QualifiedNameHash) {
  Pubtypes.emplace_back(Name, Die, QualifiedNameHash, ObjcClassImplementation);
}

} // namespace dsymutil
} // namespace llvm
<|endoftext|>


<commit_before>
#include "Globals.h"

#include "LoggerListeners.h"

#include <chrono>

#if defined(_WIN32)
	#include <io.h>  // Needed for _isatty(), not available on Linux
	#include <time.h>
#elif defined(__linux) && !defined(ANDROID_NDK)
	#include <unistd.h>  // Needed for isatty() on Linux
#elif defined(ANDROID_NDK)
	#include <android/log.h>
#endif

#if defined(_WIN32) || (defined (__linux) && !defined(ANDROID_NDK))
	class cColouredConsoleListener
		: public cLogger::cListener
	{
	protected:

		virtual void SetLogColour(cLogger::eLogLevel a_LogLevel) = 0;
		virtual void SetDefaultLogColour() = 0;

		virtual void Log(AString a_Message, cLogger::eLogLevel a_LogLevel) override
		{
			SetLogColour(a_LogLevel);
			fputs(a_Message.c_str(), stdout);
			SetDefaultLogColour();
		}
	};
#endif

#ifdef _WIN32
	class cWindowsConsoleListener
		: public cColouredConsoleListener
	{
		typedef cColouredConsoleListener super;
	public:
		cWindowsConsoleListener(HANDLE a_Console, WORD a_DefaultConsoleAttrib) :
			m_Console(a_Console),
			m_DefaultConsoleAttrib(a_DefaultConsoleAttrib)
		{
		}

		#ifdef _DEBUG
			virtual void Log(AString a_Message, cLogger::eLogLevel a_LogLevel) override
			{
				super::Log(a_Message, a_LogLevel);
				// In a Windows Debug build, output the log to debug console as well:
				OutputDebugStringA(a_Message.c_str());
			}
		#endif

		virtual void SetLogColour(cLogger::eLogLevel a_LogLevel) override
		{
			// by default, gray on black
			WORD Attrib = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
			switch (a_LogLevel)
			{
				case cLogger::llRegular:
				{
					// Gray on black
					Attrib = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
					break;
				}
				case cLogger::llInfo:
				{
					// Yellow on black
					Attrib = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
					break;
				}
				case cLogger::llWarning:
				{
					// Red on black
					Attrib = FOREGROUND_RED | FOREGROUND_INTENSITY;
					break;
				}
				case cLogger::llError:
				{
					// Black on red
					Attrib = BACKGROUND_RED | BACKGROUND_INTENSITY;
					break;
				}
			}
			SetConsoleTextAttribute(m_Console, Attrib);
		}

		virtual void SetDefaultLogColour() override
		{
			SetConsoleTextAttribute(m_Console, m_DefaultConsoleAttrib);
		}

	private:

		HANDLE m_Console;
		WORD m_DefaultConsoleAttrib;
	};

#elif defined (__linux) && !defined(ANDROID_NDK)

	class cLinuxConsoleListener
		: public cColouredConsoleListener
	{
	public:
		virtual void SetLogColour(cLogger::eLogLevel a_LogLevel) override
		{
			switch (a_LogLevel)
			{
				case cLogger::llRegular:
				{
					// Whatever the console default is
					printf("\x1b[0m");
					break;
				}
				case cLogger::llInfo:
				{
					// Yellow on black
					printf("\x1b[33;1m");
					break;
				}
				case cLogger::llWarning:
				{
					// Red on black
					printf("\x1b[31;1m");
					break;
				}
				case cLogger::llError:
				{
					// Yellow on red
					printf("\x1b[1;33;41;1m");
					break;
				}
			}
		}

		virtual void SetDefaultLogColour() override
		{
			// Whatever the console default is
			printf("\x1b[0m");
		}
	};

#elif defined(ANDROID_NDK)

	class cAndroidConsoleListener
		: public cLogger::cListener
	{
	public:
		virtual void Log(AString a_Message, cLogger::eLogLevel a_LogLevel) override
		{
			android_LogPriority AndroidLogLevel;
			switch (a_LogLevel)
			{
				case cLogger::llRegular:
				{
					AndroidLogLevel = ANDROID_LOG_VERBOSE;
					break;
				}
				case cLogger::llInfo:
				{
					AndroidLogLevel = ANDROID_LOG_INFO;
					break;
				}
				case cLogger::llWarning:
				{
					AndroidLogLevel = ANDROID_LOG_WARNING;
					break;
				}
				case cLogger::llError:
				{
					AndroidLogLevel = ANDROID_LOG_ERROR;
					break;
				}
			}
			__android_log_print(AndroidLogLevel, "MCServer", "%s", a_Message.c_str());
		}
	};

#endif

class cVanillaCPPConsoleListener
	: public cLogger::cListener
{
public:
	virtual void Log(AString a_Message, cLogger::eLogLevel a_LogLevel) override
	{
		AString LogLevelString;
		switch (a_LogLevel)
		{
			case cLogger::llRegular:
			{
				LogLevelString = "Log";
				break;
			}
			case cLogger::llInfo:
			{
				LogLevelString = "Info";
				break;
			}
			case cLogger::llWarning:
			{
				LogLevelString = "Warning";
				break;
			}
			case cLogger::llError:
			{
				LogLevelString = "Error";
				break;
			}
		}
		printf("%s: %s", LogLevelString.c_str(), a_Message.c_str());
	}
};

// Listener for when stdout is closed, i.e. When running as a daemon.
class cNullConsoleListener
	: public cLogger::cListener
{
	virtual void Log(AString a_Message, cLogger::eLogLevel a_LogLevel) override
	{
	}
};

cLogger::cListener * MakeConsoleListener(bool a_IsService)
{
	if (a_IsService)
	{
		return new cNullConsoleListener;
	}

	#ifdef _WIN32
		// See whether we are writing to a console the default console attrib:
		bool ShouldColorOutput = (_isatty(_fileno(stdin)) != 0);
		if (ShouldColorOutput)
		{
			CONSOLE_SCREEN_BUFFER_INFO sbi;
			HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE);
			GetConsoleScreenBufferInfo(Console, &sbi);
			WORD DefaultConsoleAttrib = sbi.wAttributes;
			return new cWindowsConsoleListener(Console, DefaultConsoleAttrib);
		}
		else
		{
			return new cVanillaCPPConsoleListener;
		}

	#elif defined (__linux) && !defined(ANDROID_NDK)
		// TODO: lookup terminal in terminfo
		if (isatty(fileno(stdout)))
		{
			return new cLinuxConsoleListener();
		}
		else
		{
			return new cVanillaCPPConsoleListener();
		}
	#else
		return new cVanillaCPPConsoleListener();
	#endif
}

////////////////////////////////////////////////////////////////////////////////
// cFileListener:

cFileListener::cFileListener(void)
{
	cFile::CreateFolder(FILE_IO_PREFIX + AString("logs"));
	m_File.Open(
		FILE_IO_PREFIX + Printf(
			"logs/LOG_%lld.txt",
			std::chrono::duration_cast<std::chrono::seconds>(
				std::chrono::system_clock::now().time_since_epoch()
			).count()
		),
		cFile::fmAppend
	);
}

void cFileListener::Log(AString a_Message, cLogger::eLogLevel a_LogLevel)
{
	const char * LogLevelPrefix = "Unkn ";
	switch (a_LogLevel)
	{
		case cLogger::llRegular:
		{
			LogLevelPrefix = "     ";
			break;
		}
		case cLogger::llInfo:
		{
			LogLevelPrefix = "Info ";
			break;
		}
		case cLogger::llWarning:
		{
			LogLevelPrefix = "Warn ";
			break;
		}
		case cLogger::llError:
		{
			LogLevelPrefix = "Err  ";
			break;
		}
	}
	m_File.Printf("%s%s", LogLevelPrefix, a_Message.c_str());
}

<commit_msg>Fixed tigers format string<commit_after>
#include "Globals.h"

#include "LoggerListeners.h"

#include <chrono>

#if defined(_WIN32)
	#include <io.h>  // Needed for _isatty(), not available on Linux
	#include <time.h>
#elif defined(__linux) && !defined(ANDROID_NDK)
	#include <unistd.h>  // Needed for isatty() on Linux
#elif defined(ANDROID_NDK)
	#include <android/log.h>
#endif

#if defined(_WIN32) || (defined (__linux) && !defined(ANDROID_NDK))
	class cColouredConsoleListener
		: public cLogger::cListener
	{
	protected:

		virtual void SetLogColour(cLogger::eLogLevel a_LogLevel) = 0;
		virtual void SetDefaultLogColour() = 0;

		virtual void Log(AString a_Message, cLogger::eLogLevel a_LogLevel) override
		{
			SetLogColour(a_LogLevel);
			fputs(a_Message.c_str(), stdout);
			SetDefaultLogColour();
		}
	};
#endif

#ifdef _WIN32
	class cWindowsConsoleListener
		: public cColouredConsoleListener
	{
		typedef cColouredConsoleListener super;
	public:
		cWindowsConsoleListener(HANDLE a_Console, WORD a_DefaultConsoleAttrib) :
			m_Console(a_Console),
			m_DefaultConsoleAttrib(a_DefaultConsoleAttrib)
		{
		}

		#ifdef _DEBUG
			virtual void Log(AString a_Message, cLogger::eLogLevel a_LogLevel) override
			{
				super::Log(a_Message, a_LogLevel);
				// In a Windows Debug build, output the log to debug console as well:
				OutputDebugStringA(a_Message.c_str());
			}
		#endif

		virtual void SetLogColour(cLogger::eLogLevel a_LogLevel) override
		{
			// by default, gray on black
			WORD Attrib = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
			switch (a_LogLevel)
			{
				case cLogger::llRegular:
				{
					// Gray on black
					Attrib = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
					break;
				}
				case cLogger::llInfo:
				{
					// Yellow on black
					Attrib = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
					break;
				}
				case cLogger::llWarning:
				{
					// Red on black
					Attrib = FOREGROUND_RED | FOREGROUND_INTENSITY;
					break;
				}
				case cLogger::llError:
				{
					// Black on red
					Attrib = BACKGROUND_RED | BACKGROUND_INTENSITY;
					break;
				}
			}
			SetConsoleTextAttribute(m_Console, Attrib);
		}

		virtual void SetDefaultLogColour() override
		{
			SetConsoleTextAttribute(m_Console, m_DefaultConsoleAttrib);
		}

	private:

		HANDLE m_Console;
		WORD m_DefaultConsoleAttrib;
	};

#elif defined (__linux) && !defined(ANDROID_NDK)

	class cLinuxConsoleListener
		: public cColouredConsoleListener
	{
	public:
		virtual void SetLogColour(cLogger::eLogLevel a_LogLevel) override
		{
			switch (a_LogLevel)
			{
				case cLogger::llRegular:
				{
					// Whatever the console default is
					printf("\x1b[0m");
					break;
				}
				case cLogger::llInfo:
				{
					// Yellow on black
					printf("\x1b[33;1m");
					break;
				}
				case cLogger::llWarning:
				{
					// Red on black
					printf("\x1b[31;1m");
					break;
				}
				case cLogger::llError:
				{
					// Yellow on red
					printf("\x1b[1;33;41;1m");
					break;
				}
			}
		}

		virtual void SetDefaultLogColour() override
		{
			// Whatever the console default is
			printf("\x1b[0m");
		}
	};

#elif defined(ANDROID_NDK)

	class cAndroidConsoleListener
		: public cLogger::cListener
	{
	public:
		virtual void Log(AString a_Message, cLogger::eLogLevel a_LogLevel) override
		{
			android_LogPriority AndroidLogLevel;
			switch (a_LogLevel)
			{
				case cLogger::llRegular:
				{
					AndroidLogLevel = ANDROID_LOG_VERBOSE;
					break;
				}
				case cLogger::llInfo:
				{
					AndroidLogLevel = ANDROID_LOG_INFO;
					break;
				}
				case cLogger::llWarning:
				{
					AndroidLogLevel = ANDROID_LOG_WARNING;
					break;
				}
				case cLogger::llError:
				{
					AndroidLogLevel = ANDROID_LOG_ERROR;
					break;
				}
			}
			__android_log_print(AndroidLogLevel, "MCServer", "%s", a_Message.c_str());
		}
	};

#endif

class cVanillaCPPConsoleListener
	: public cLogger::cListener
{
public:
	virtual void Log(AString a_Message, cLogger::eLogLevel a_LogLevel) override
	{
		AString LogLevelString;
		switch (a_LogLevel)
		{
			case cLogger::llRegular:
			{
				LogLevelString = "Log";
				break;
			}
			case cLogger::llInfo:
			{
				LogLevelString = "Info";
				break;
			}
			case cLogger::llWarning:
			{
				LogLevelString = "Warning";
				break;
			}
			case cLogger::llError:
			{
				LogLevelString = "Error";
				break;
			}
		}
		printf("%s: %s", LogLevelString.c_str(), a_Message.c_str());
	}
};

// Listener for when stdout is closed, i.e. When running as a daemon.
class cNullConsoleListener
	: public cLogger::cListener
{
	virtual void Log(AString a_Message, cLogger::eLogLevel a_LogLevel) override
	{
	}
};

cLogger::cListener * MakeConsoleListener(bool a_IsService)
{
	if (a_IsService)
	{
		return new cNullConsoleListener;
	}

	#ifdef _WIN32
		// See whether we are writing to a console the default console attrib:
		bool ShouldColorOutput = (_isatty(_fileno(stdin)) != 0);
		if (ShouldColorOutput)
		{
			CONSOLE_SCREEN_BUFFER_INFO sbi;
			HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE);
			GetConsoleScreenBufferInfo(Console, &sbi);
			WORD DefaultConsoleAttrib = sbi.wAttributes;
			return new cWindowsConsoleListener(Console, DefaultConsoleAttrib);
		}
		else
		{
			return new cVanillaCPPConsoleListener;
		}

	#elif defined (__linux) && !defined(ANDROID_NDK)
		// TODO: lookup terminal in terminfo
		if (isatty(fileno(stdout)))
		{
			return new cLinuxConsoleListener();
		}
		else
		{
			return new cVanillaCPPConsoleListener();
		}
	#else
		return new cVanillaCPPConsoleListener();
	#endif
}

////////////////////////////////////////////////////////////////////////////////
// cFileListener:

cFileListener::cFileListener(void)
{
	cFile::CreateFolder(FILE_IO_PREFIX + AString("logs"));
	m_File.Open(
		FILE_IO_PREFIX + Printf(
			"logs/LOG_%d.txt",
			std::chrono::duration_cast<std::chrono::duration<int, std::ratio<1>>>(
				std::chrono::system_clock::now().time_since_epoch()
			).count()
		),
		cFile::fmAppend
	);
}

void cFileListener::Log(AString a_Message, cLogger::eLogLevel a_LogLevel)
{
	const char * LogLevelPrefix = "Unkn ";
	switch (a_LogLevel)
	{
		case cLogger::llRegular:
		{
			LogLevelPrefix = "     ";
			break;
		}
		case cLogger::llInfo:
		{
			LogLevelPrefix = "Info ";
			break;
		}
		case cLogger::llWarning:
		{
			LogLevelPrefix = "Warn ";
			break;
		}
		case cLogger::llError:
		{
			LogLevelPrefix = "Err  ";
			break;
		}
	}
	m_File.Printf("%s%s", LogLevelPrefix, a_Message.c_str());
}

<|endoftext|>