<commit_before>// Copyright (c) the JPEG XL Project 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 "lib/extras/enc/apng.h"

// Parts of this code are taken from apngdis, which has the following license:
/* APNG Disassembler 2.8
 *
 * Deconstructs APNG files into individual frames.
 *
 * http://apngdis.sourceforge.net
 *
 * Copyright (c) 2010-2015 Max Stepin
 * maxst at users.sourceforge.net
 *
 * zlib license
 * ------------
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 *
 */

#include <stdio.h>
#include <string.h>

#include <string>
#include <vector>

#include "jxl/encode.h"
#include "lib/extras/packed_image_convert.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/dec_external_image.h"
#include "lib/jxl/enc_color_management.h"
#include "lib/jxl/enc_image_bundle.h"
#include "lib/jxl/exif.h"
#include "lib/jxl/frame_header.h"
#include "lib/jxl/headers.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
#include "png.h" /* original (unpatched) libpng is ok */

namespace jxl {
namespace extras {

namespace {

class APNGEncoder : public Encoder {
 public:
  std::vector<JxlPixelFormat> AcceptedFormats() const override {
    std::vector<JxlPixelFormat> formats;
    for (const uint32_t num_channels : {1, 2, 3, 4}) {
      for (const JxlDataType data_type : {JXL_TYPE_UINT8, JXL_TYPE_UINT16}) {
        for (JxlEndianness endianness : {JXL_BIG_ENDIAN, JXL_LITTLE_ENDIAN}) {
          formats.push_back(JxlPixelFormat{/*num_channels=*/num_channels,
                                           /*data_type=*/data_type,
                                           /*endianness=*/endianness,
                                           /*align=*/0});
        }
      }
    }
    return formats;
  }
  Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image,
                ThreadPool* pool) const override {
    encoded_image->icc.clear();
    encoded_image->bitstreams.resize(1);
    CodecInOut io;
    JXL_RETURN_IF_ERROR(ConvertPackedPixelFileToCodecInOut(ppf, pool, &io));
    return EncodeImageAPNG(&io, io.metadata.m.color_encoding,
                           ppf.info.bits_per_sample, pool,
                           &encoded_image->bitstreams.front());
  }
};

static void PngWrite(png_structp png_ptr, png_bytep data, png_size_t length) {
  std::vector<uint8_t>* bytes =
      static_cast<std::vector<uint8_t>*>(png_get_io_ptr(png_ptr));
  bytes->insert(bytes->end(), data, data + length);
}

// Stores XMP and EXIF/IPTC into key/value strings for PNG
class BlobsWriterPNG {
 public:
  static Status Encode(const Blobs& blobs, std::vector<std::string>* strings) {
    if (!blobs.exif.empty()) {
      // PNG viewers typically ignore Exif orientation but not all of them do
      // (and e.g. cjxl doesn't), so we overwrite the Exif orientation to the
      // identity to avoid repeated orientation.
      std::vector<uint8_t> exif = blobs.exif;
      ResetExifOrientation(exif);
      JXL_RETURN_IF_ERROR(EncodeBase16("exif", exif, strings));
    }
    if (!blobs.iptc.empty()) {
      JXL_RETURN_IF_ERROR(EncodeBase16("iptc", blobs.iptc, strings));
    }
    if (!blobs.xmp.empty()) {
      JXL_RETURN_IF_ERROR(EncodeBase16("xmp", blobs.xmp, strings));
    }
    return true;
  }

 private:
  static JXL_INLINE char EncodeNibble(const uint8_t nibble) {
    JXL_ASSERT(nibble < 16);
    return (nibble < 10) ? '0' + nibble : 'a' + nibble - 10;
  }

  static Status EncodeBase16(const std::string& type,
                             const std::vector<uint8_t>& bytes,
                             std::vector<std::string>* strings) {
    // Encoding: base16 with newline after 72 chars.
    const size_t base16_size =
        2 * bytes.size() + DivCeil(bytes.size(), size_t(36)) + 1;
    std::string base16;
    base16.reserve(base16_size);
    for (size_t i = 0; i < bytes.size(); ++i) {
      if (i % 36 == 0) base16.push_back('\n');
      base16.push_back(EncodeNibble(bytes[i] >> 4));
      base16.push_back(EncodeNibble(bytes[i] & 0x0F));
    }
    base16.push_back('\n');
    JXL_ASSERT(base16.length() == base16_size);

    char key[30];
    snprintf(key, sizeof(key), "Raw profile type %s", type.c_str());

    char header[30];
    snprintf(header, sizeof(header), "\n%s\n%8" PRIuS, type.c_str(),
             bytes.size());

    strings->push_back(std::string(key));
    strings->push_back(std::string(header) + base16);
    return true;
  }
};

}  // namespace

std::unique_ptr<Encoder> GetAPNGEncoder() {
  return jxl::make_unique<APNGEncoder>();
}

Status EncodeImageAPNG(const CodecInOut* io, const ColorEncoding& c_desired,
                       size_t bits_per_sample, ThreadPool* pool,
                       std::vector<uint8_t>* bytes) {
  if (bits_per_sample > 8) {
    bits_per_sample = 16;
  } else if (bits_per_sample < 8) {
    // PNG can also do 4, 2, and 1 bits per sample, but it isn't implemented
    bits_per_sample = 8;
  }

  size_t count = 0;
  bool have_anim = io->metadata.m.have_animation;
  size_t anim_chunks = 0;
  int W = 0, H = 0;

  for (size_t i = 0; i < io->frames.size(); i++) {
    auto& frame = io->frames[i];
    if (!have_anim && i + 1 < io->frames.size()) continue;
    png_structp png_ptr;
    png_infop info_ptr;

    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

    if (!png_ptr) return JXL_FAILURE("Could not init png encoder");

    info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) return JXL_FAILURE("Could not init png info struct");

    png_set_write_fn(png_ptr, bytes, PngWrite, NULL);
    png_set_flush(png_ptr, 0);

    ImageBundle ib = frame.Copy();
    const size_t alpha_bits = ib.HasAlpha() ? bits_per_sample : 0;
    ImageMetadata metadata = io->metadata.m;
    ImageBundle store(&metadata);
    const ImageBundle* transformed;
    JXL_RETURN_IF_ERROR(TransformIfNeeded(ib, c_desired, GetJxlCms(), pool,
                                          &store, &transformed));
    size_t stride = ib.oriented_xsize() *
                    DivCeil(c_desired.Channels() * bits_per_sample + alpha_bits,
                            kBitsPerByte);
    std::vector<uint8_t> raw_bytes(stride * ib.oriented_ysize());
    JXL_RETURN_IF_ERROR(ConvertToExternal(
        *transformed, bits_per_sample, /*float_out=*/false,
        c_desired.Channels() + (ib.HasAlpha() ? 1 : 0), JXL_BIG_ENDIAN, stride,
        pool, raw_bytes.data(), raw_bytes.size(),
        /*out_callback=*/{}, metadata.GetOrientation()));

    int width = ib.oriented_xsize();
    int height = ib.oriented_ysize();

    png_byte color_type =
        (c_desired.Channels() == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_GRAY);
    if (ib.HasAlpha()) color_type |= PNG_COLOR_MASK_ALPHA;
    png_byte bit_depth = bits_per_sample;

    png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type,
                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
                 PNG_FILTER_TYPE_BASE);
    if (count == 0) {
      W = width;
      H = height;

      // TODO(jon): instead of always setting an iCCP, could try to avoid that
      // have to avoid warnings on the ICC profile becoming fatal
      png_set_benign_errors(png_ptr, 1);
      png_set_iCCP(png_ptr, info_ptr, "1", 0, c_desired.ICC().data(),
                   c_desired.ICC().size());

      std::vector<std::string> textstrings;
      JXL_RETURN_IF_ERROR(BlobsWriterPNG::Encode(io->blobs, &textstrings));
      for (size_t kk = 0; kk + 1 < textstrings.size(); kk += 2) {
        png_text text;
        text.key = const_cast<png_charp>(textstrings[kk].c_str());
        text.text = const_cast<png_charp>(textstrings[kk + 1].c_str());
        text.compression = PNG_TEXT_COMPRESSION_zTXt;
        png_set_text(png_ptr, info_ptr, &text, 1);
      }

      png_write_info(png_ptr, info_ptr);
    } else {
      // fake writing a header, otherwise libpng gets confused
      size_t pos = bytes->size();
      png_write_info(png_ptr, info_ptr);
      bytes->resize(pos);
    }

    if (have_anim) {
      if (count == 0) {
        png_byte adata[8];
        png_save_uint_32(adata, io->frames.size());
        png_save_uint_32(adata + 4, io->metadata.m.animation.num_loops);
        png_byte actl[5] = "acTL";
        png_write_chunk(png_ptr, actl, adata, 8);
      }
      png_byte fdata[26];
      JXL_ASSERT(W == width);
      JXL_ASSERT(H == height);
      // TODO(jon): also make this work for the non-coalesced case
      png_save_uint_32(fdata, anim_chunks++);
      png_save_uint_32(fdata + 4, width);
      png_save_uint_32(fdata + 8, height);
      png_save_uint_32(fdata + 12, 0);
      png_save_uint_32(fdata + 16, 0);
      png_save_uint_16(
          fdata + 20,
          frame.duration * io->metadata.m.animation.tps_denominator);
      png_save_uint_16(fdata + 22, io->metadata.m.animation.tps_numerator);
      fdata[24] = 1;
      fdata[25] = 0;
      png_byte fctl[5] = "fcTL";
      png_write_chunk(png_ptr, fctl, fdata, 26);
    }

    std::vector<uint8_t*> rows(height);
    for (int y = 0; y < height; ++y) {
      rows[y] = raw_bytes.data() + y * stride;
    }

    png_write_flush(png_ptr);
    const size_t pos = bytes->size();
    png_write_image(png_ptr, &rows[0]);
    png_write_flush(png_ptr);
    if (count > 0) {
      std::vector<uint8_t> fdata(4);
      png_save_uint_32(fdata.data(), anim_chunks++);
      size_t p = pos;
      while (p + 8 < bytes->size()) {
        size_t len = png_get_uint_32(bytes->data() + p);
        JXL_ASSERT(bytes->operator[](p + 4) == 'I');
        JXL_ASSERT(bytes->operator[](p + 5) == 'D');
        JXL_ASSERT(bytes->operator[](p + 6) == 'A');
        JXL_ASSERT(bytes->operator[](p + 7) == 'T');
        fdata.insert(fdata.end(), bytes->data() + p + 8,
                     bytes->data() + p + 8 + len);
        p += len + 12;
      }
      bytes->resize(pos);

      png_byte fdat[5] = "fdAT";
      png_write_chunk(png_ptr, fdat, fdata.data(), fdata.size());
    }

    count++;
    if (count == io->frames.size() || !have_anim) png_write_end(png_ptr, NULL);

    png_destroy_write_struct(&png_ptr, &info_ptr);
  }

  return true;
}

}  // namespace extras
}  // namespace jxl
<commit_msg>Add support for producing PNGs with the cICP chunk.<commit_after>// Copyright (c) the JPEG XL Project 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 "lib/extras/enc/apng.h"

// Parts of this code are taken from apngdis, which has the following license:
/* APNG Disassembler 2.8
 *
 * Deconstructs APNG files into individual frames.
 *
 * http://apngdis.sourceforge.net
 *
 * Copyright (c) 2010-2015 Max Stepin
 * maxst at users.sourceforge.net
 *
 * zlib license
 * ------------
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 *
 */

#include <stdio.h>
#include <string.h>

#include <string>
#include <vector>

#include "jxl/encode.h"
#include "lib/extras/packed_image_convert.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/dec_external_image.h"
#include "lib/jxl/enc_color_management.h"
#include "lib/jxl/enc_image_bundle.h"
#include "lib/jxl/exif.h"
#include "lib/jxl/frame_header.h"
#include "lib/jxl/headers.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
#include "png.h" /* original (unpatched) libpng is ok */

namespace jxl {
namespace extras {

namespace {

class APNGEncoder : public Encoder {
 public:
  std::vector<JxlPixelFormat> AcceptedFormats() const override {
    std::vector<JxlPixelFormat> formats;
    for (const uint32_t num_channels : {1, 2, 3, 4}) {
      for (const JxlDataType data_type : {JXL_TYPE_UINT8, JXL_TYPE_UINT16}) {
        for (JxlEndianness endianness : {JXL_BIG_ENDIAN, JXL_LITTLE_ENDIAN}) {
          formats.push_back(JxlPixelFormat{/*num_channels=*/num_channels,
                                           /*data_type=*/data_type,
                                           /*endianness=*/endianness,
                                           /*align=*/0});
        }
      }
    }
    return formats;
  }
  Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image,
                ThreadPool* pool) const override {
    encoded_image->icc.clear();
    encoded_image->bitstreams.resize(1);
    CodecInOut io;
    JXL_RETURN_IF_ERROR(ConvertPackedPixelFileToCodecInOut(ppf, pool, &io));
    return EncodeImageAPNG(&io, io.metadata.m.color_encoding,
                           ppf.info.bits_per_sample, pool,
                           &encoded_image->bitstreams.front());
  }
};

static void PngWrite(png_structp png_ptr, png_bytep data, png_size_t length) {
  std::vector<uint8_t>* bytes =
      static_cast<std::vector<uint8_t>*>(png_get_io_ptr(png_ptr));
  bytes->insert(bytes->end(), data, data + length);
}

// Stores XMP and EXIF/IPTC into key/value strings for PNG
class BlobsWriterPNG {
 public:
  static Status Encode(const Blobs& blobs, std::vector<std::string>* strings) {
    if (!blobs.exif.empty()) {
      // PNG viewers typically ignore Exif orientation but not all of them do
      // (and e.g. cjxl doesn't), so we overwrite the Exif orientation to the
      // identity to avoid repeated orientation.
      std::vector<uint8_t> exif = blobs.exif;
      ResetExifOrientation(exif);
      JXL_RETURN_IF_ERROR(EncodeBase16("exif", exif, strings));
    }
    if (!blobs.iptc.empty()) {
      JXL_RETURN_IF_ERROR(EncodeBase16("iptc", blobs.iptc, strings));
    }
    if (!blobs.xmp.empty()) {
      JXL_RETURN_IF_ERROR(EncodeBase16("xmp", blobs.xmp, strings));
    }
    return true;
  }

 private:
  static JXL_INLINE char EncodeNibble(const uint8_t nibble) {
    JXL_ASSERT(nibble < 16);
    return (nibble < 10) ? '0' + nibble : 'a' + nibble - 10;
  }

  static Status EncodeBase16(const std::string& type,
                             const std::vector<uint8_t>& bytes,
                             std::vector<std::string>* strings) {
    // Encoding: base16 with newline after 72 chars.
    const size_t base16_size =
        2 * bytes.size() + DivCeil(bytes.size(), size_t(36)) + 1;
    std::string base16;
    base16.reserve(base16_size);
    for (size_t i = 0; i < bytes.size(); ++i) {
      if (i % 36 == 0) base16.push_back('\n');
      base16.push_back(EncodeNibble(bytes[i] >> 4));
      base16.push_back(EncodeNibble(bytes[i] & 0x0F));
    }
    base16.push_back('\n');
    JXL_ASSERT(base16.length() == base16_size);

    char key[30];
    snprintf(key, sizeof(key), "Raw profile type %s", type.c_str());

    char header[30];
    snprintf(header, sizeof(header), "\n%s\n%8" PRIuS, type.c_str(),
             bytes.size());

    strings->push_back(std::string(key));
    strings->push_back(std::string(header) + base16);
    return true;
  }
};

}  // namespace

std::unique_ptr<Encoder> GetAPNGEncoder() {
  return jxl::make_unique<APNGEncoder>();
}

Status EncodeImageAPNG(const CodecInOut* io, const ColorEncoding& c_desired,
                       size_t bits_per_sample, ThreadPool* pool,
                       std::vector<uint8_t>* bytes) {
  if (bits_per_sample > 8) {
    bits_per_sample = 16;
  } else if (bits_per_sample < 8) {
    // PNG can also do 4, 2, and 1 bits per sample, but it isn't implemented
    bits_per_sample = 8;
  }

  png_byte cicp_data[4] = {};
  png_unknown_chunk cicp_chunk;

  size_t count = 0;
  bool have_anim = io->metadata.m.have_animation;
  size_t anim_chunks = 0;
  int W = 0, H = 0;

  for (size_t i = 0; i < io->frames.size(); i++) {
    auto& frame = io->frames[i];
    if (!have_anim && i + 1 < io->frames.size()) continue;
    png_structp png_ptr;
    png_infop info_ptr;

    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

    if (!png_ptr) return JXL_FAILURE("Could not init png encoder");

    info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) return JXL_FAILURE("Could not init png info struct");

    png_set_write_fn(png_ptr, bytes, PngWrite, NULL);
    png_set_flush(png_ptr, 0);

    ImageBundle ib = frame.Copy();
    const size_t alpha_bits = ib.HasAlpha() ? bits_per_sample : 0;
    ImageMetadata metadata = io->metadata.m;
    ImageBundle store(&metadata);
    const ImageBundle* transformed;
    JXL_RETURN_IF_ERROR(TransformIfNeeded(ib, c_desired, GetJxlCms(), pool,
                                          &store, &transformed));
    size_t stride = ib.oriented_xsize() *
                    DivCeil(c_desired.Channels() * bits_per_sample + alpha_bits,
                            kBitsPerByte);
    std::vector<uint8_t> raw_bytes(stride * ib.oriented_ysize());
    JXL_RETURN_IF_ERROR(ConvertToExternal(
        *transformed, bits_per_sample, /*float_out=*/false,
        c_desired.Channels() + (ib.HasAlpha() ? 1 : 0), JXL_BIG_ENDIAN, stride,
        pool, raw_bytes.data(), raw_bytes.size(),
        /*out_callback=*/{}, metadata.GetOrientation()));

    int width = ib.oriented_xsize();
    int height = ib.oriented_ysize();

    png_byte color_type =
        (c_desired.Channels() == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_GRAY);
    if (ib.HasAlpha()) color_type |= PNG_COLOR_MASK_ALPHA;
    png_byte bit_depth = bits_per_sample;

    png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type,
                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
                 PNG_FILTER_TYPE_BASE);
    if (count == 0) {
      W = width;
      H = height;

      if (!c_desired.WantICC() && c_desired.primaries != Primaries::kCustom &&
          !c_desired.tf.IsUnknown() && !c_desired.tf.IsGamma() &&
          (c_desired.white_point == WhitePoint::kD65 ||
           (c_desired.white_point == WhitePoint::kDCI &&
            c_desired.primaries == Primaries::kP3))) {
        cicp_data[0] = c_desired.primaries != Primaries::kP3
                           ? static_cast<png_byte>(c_desired.primaries)
                       : c_desired.white_point == WhitePoint::kD65 ? 12
                                                                   : 11;
        cicp_data[1] =
            static_cast<png_byte>(c_desired.tf.GetTransferFunction());
        cicp_data[3] = 1;
        cicp_chunk.data = cicp_data;
        cicp_chunk.size = sizeof(cicp_data);
        cicp_chunk.location = PNG_HAVE_PLTE;
        memcpy(cicp_chunk.name, "cICP", 5);
        png_set_keep_unknown_chunks(
            png_ptr, 3, reinterpret_cast<const png_byte*>("cICP"), 1);
        png_set_unknown_chunks(png_ptr, info_ptr, &cicp_chunk, 1);
      }
      // TODO(jon): instead of always setting an iCCP, could try to avoid that
      // have to avoid warnings on the ICC profile becoming fatal
      png_set_benign_errors(png_ptr, 1);
      png_set_iCCP(png_ptr, info_ptr, "1", 0, c_desired.ICC().data(),
                   c_desired.ICC().size());

      std::vector<std::string> textstrings;
      JXL_RETURN_IF_ERROR(BlobsWriterPNG::Encode(io->blobs, &textstrings));
      for (size_t kk = 0; kk + 1 < textstrings.size(); kk += 2) {
        png_text text;
        text.key = const_cast<png_charp>(textstrings[kk].c_str());
        text.text = const_cast<png_charp>(textstrings[kk + 1].c_str());
        text.compression = PNG_TEXT_COMPRESSION_zTXt;
        png_set_text(png_ptr, info_ptr, &text, 1);
      }

      png_write_info(png_ptr, info_ptr);
    } else {
      // fake writing a header, otherwise libpng gets confused
      size_t pos = bytes->size();
      png_write_info(png_ptr, info_ptr);
      bytes->resize(pos);
    }

    if (have_anim) {
      if (count == 0) {
        png_byte adata[8];
        png_save_uint_32(adata, io->frames.size());
        png_save_uint_32(adata + 4, io->metadata.m.animation.num_loops);
        png_byte actl[5] = "acTL";
        png_write_chunk(png_ptr, actl, adata, 8);
      }
      png_byte fdata[26];
      JXL_ASSERT(W == width);
      JXL_ASSERT(H == height);
      // TODO(jon): also make this work for the non-coalesced case
      png_save_uint_32(fdata, anim_chunks++);
      png_save_uint_32(fdata + 4, width);
      png_save_uint_32(fdata + 8, height);
      png_save_uint_32(fdata + 12, 0);
      png_save_uint_32(fdata + 16, 0);
      png_save_uint_16(
          fdata + 20,
          frame.duration * io->metadata.m.animation.tps_denominator);
      png_save_uint_16(fdata + 22, io->metadata.m.animation.tps_numerator);
      fdata[24] = 1;
      fdata[25] = 0;
      png_byte fctl[5] = "fcTL";
      png_write_chunk(png_ptr, fctl, fdata, 26);
    }

    std::vector<uint8_t*> rows(height);
    for (int y = 0; y < height; ++y) {
      rows[y] = raw_bytes.data() + y * stride;
    }

    png_write_flush(png_ptr);
    const size_t pos = bytes->size();
    png_write_image(png_ptr, &rows[0]);
    png_write_flush(png_ptr);
    if (count > 0) {
      std::vector<uint8_t> fdata(4);
      png_save_uint_32(fdata.data(), anim_chunks++);
      size_t p = pos;
      while (p + 8 < bytes->size()) {
        size_t len = png_get_uint_32(bytes->data() + p);
        JXL_ASSERT(bytes->operator[](p + 4) == 'I');
        JXL_ASSERT(bytes->operator[](p + 5) == 'D');
        JXL_ASSERT(bytes->operator[](p + 6) == 'A');
        JXL_ASSERT(bytes->operator[](p + 7) == 'T');
        fdata.insert(fdata.end(), bytes->data() + p + 8,
                     bytes->data() + p + 8 + len);
        p += len + 12;
      }
      bytes->resize(pos);

      png_byte fdat[5] = "fdAT";
      png_write_chunk(png_ptr, fdat, fdata.data(), fdata.size());
    }

    count++;
    if (count == io->frames.size() || !have_anim) png_write_end(png_ptr, NULL);

    png_destroy_write_struct(&png_ptr, &info_ptr);
  }

  return true;
}

}  // namespace extras
}  // namespace jxl
<|endoftext|>


<commit_before>/***********************************************************************
 tcp_connection.cpp - Implements the TCPConnection class.

 Copyright (c) 2007 by Educational Technology Resources, Inc.
 Others may also hold copyrights on code in this file.  See the
 CREDITS file in the top directory of the distribution for details.

 This file is part of MySQL++.

 MySQL++ is free software; you can redistribute it and/or modify it
 under the terms of the GNU Lesser General Public License as published
 by the Free Software Foundation; either version 2.1 of the License, or
 (at your option) any later version.

 MySQL++ 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 Lesser General Public
 License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with MySQL++; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
 USA
***********************************************************************/

#define MYSQLPP_NOT_HEADER
#include "common.h"
#include "tcp_connection.h"

#include "exceptions.h"

#if !defined(MYSQLPP_PLATFORM_WINDOWS)
#	include <netdb.h>
#endif

#include <ctype.h>

using namespace std;

namespace mysqlpp {

bool
TCPConnection::connect(cchar* addr, cchar* db, cchar* user, cchar* pass)
{
	error_message_.clear();

	unsigned int port = 0;
	string address;
	if (addr) {
		address = addr;
		if (!parse_address(address, port, error_message_)) {
			return false;
		}
	}

	if (error_message_.empty()) {
		return Connection::connect(db, address.c_str(), user, pass, port);
	}
	else {
		if (throw_exceptions()) {
			throw ConnectionFailed(error_message_.c_str());
		}
		else {
			return false;
		}
	}
}

bool
TCPConnection::parse_address(string& addr, unsigned int& port,
		string& error)
{
	error.clear();

	// Pull off service name or port number, if any
	string service;
	if (addr[0] == '[') {
		// Might be IPv6 address plus port/service in RFC 2732 form.
		string::size_type pos = addr.find(']');
		if ((pos == string::npos) ||
				(addr.find(':', pos + 1) != (pos + 1)) ||
				(addr.find_first_of("[]", pos + 2) != string::npos)) {
			error = "Malformed IPv6 [address]:service combination";
			return false;
		}

		// We can separate address from port/service now
		service = addr.substr(pos + 2);
		addr = addr.substr(1, pos - 1);

		// Ensure that address part is empty or has at least two colons
		if (addr.size() &&
				(((pos = addr.find(':')) == string::npos) ||
				(addr.find(':', pos + 1) == string::npos))) {
			error = "IPv6 literal needs at least two colons";
			return false;
		}
	}
	else {
		// Can only be IPv4 address, so check for 0-1 colons
		string::size_type pos = addr.find(':');
		if (pos != string::npos) {
			if (addr.find(':', pos + 1) != string::npos) {
				error = "IPv4 address:service combo can have only one colon";
				return false;
			}

			service = addr.substr(pos + 1);
			addr = addr.substr(0, pos);
		}
	}

	// Turn service into a port number
	if (service.empty()) {
		port = 0;
	}
	else {
		if (isdigit(service[0])) {
			port = atoi(service.c_str());
			if ((port < 1) || (port > USHRT_MAX)) {
				error = "Invalid TCP port number " + service;
				return false;
			}
		}
		else {
			servent* pse = getservbyname(service.c_str(), "tcp");
			if (pse) {
				port = ntohs(pse->s_port);
			}
			else {
				error = "Failed to look up TCP service " + service;
				return false;
			}
		}
	}

	// Ensure that there are only alphanumeric characters, dots,
	// dashes and colons in address.  Anything else must be an error.
	for (string::const_iterator it = addr.begin(); it != addr.end(); ++it) {
		string::value_type c = *it;
		if (!(isalnum(c) || (c == '.') || (c == '-') || (c == ':'))) {
			error = "Bad character '";
			error += c;
			error += "' in TCP/IP address";
			return false;
		}
	}

	return true;
}

} // end namespace mysqlpp

<commit_msg>Fixed a bug with non-default port numbers passed separately from server spec.  It was getting zeroed incorrectly.  Fix by Bart Verstraete.<commit_after>/***********************************************************************
 tcp_connection.cpp - Implements the TCPConnection class.

 Copyright (c) 2007 by Educational Technology Resources, Inc.
 Others may also hold copyrights on code in this file.  See the
 CREDITS file in the top directory of the distribution for details.

 This file is part of MySQL++.

 MySQL++ is free software; you can redistribute it and/or modify it
 under the terms of the GNU Lesser General Public License as published
 by the Free Software Foundation; either version 2.1 of the License, or
 (at your option) any later version.

 MySQL++ 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 Lesser General Public
 License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with MySQL++; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
 USA
***********************************************************************/

#define MYSQLPP_NOT_HEADER
#include "common.h"
#include "tcp_connection.h"

#include "exceptions.h"

#if !defined(MYSQLPP_PLATFORM_WINDOWS)
#	include <netdb.h>
#endif

#include <ctype.h>

using namespace std;

namespace mysqlpp {

bool
TCPConnection::connect(cchar* addr, cchar* db, cchar* user, cchar* pass)
{
	error_message_.clear();

	unsigned int port = 0;
	string address;
	if (addr) {
		address = addr;
		if (!parse_address(address, port, error_message_)) {
			return false;
		}
	}

	if (error_message_.empty()) {
		return Connection::connect(db, address.c_str(), user, pass, port);
	}
	else {
		if (throw_exceptions()) {
			throw ConnectionFailed(error_message_.c_str());
		}
		else {
			return false;
		}
	}
}

bool
TCPConnection::parse_address(string& addr, unsigned int& port,
		string& error)
{
	error.clear();

	// Pull off service name or port number, if any
	string service;
	if (addr[0] == '[') {
		// Might be IPv6 address plus port/service in RFC 2732 form.
		string::size_type pos = addr.find(']');
		if ((pos == string::npos) ||
				(addr.find(':', pos + 1) != (pos + 1)) ||
				(addr.find_first_of("[]", pos + 2) != string::npos)) {
			error = "Malformed IPv6 [address]:service combination";
			return false;
		}

		// We can separate address from port/service now
		service = addr.substr(pos + 2);
		addr = addr.substr(1, pos - 1);

		// Ensure that address part is empty or has at least two colons
		if (addr.size() &&
				(((pos = addr.find(':')) == string::npos) ||
				(addr.find(':', pos + 1) == string::npos))) {
			error = "IPv6 literal needs at least two colons";
			return false;
		}
	}
	else {
		// Can only be IPv4 address, so check for 0-1 colons
		string::size_type pos = addr.find(':');
		if (pos != string::npos) {
			if (addr.find(':', pos + 1) != string::npos) {
				error = "IPv4 address:service combo can have only one colon";
				return false;
			}

			service = addr.substr(pos + 1);
			addr = addr.substr(0, pos);
		}
	}

	// Turn service into a port number, if it was given.  If not, don't
	// overwrite port because it could have a legal value passed in from
	// Connection.
	if (!service.empty()) {
		if (isdigit(service[0])) {
			port = atoi(service.c_str());
			if ((port < 1) || (port > USHRT_MAX)) {
				error = "Invalid TCP port number " + service;
				return false;
			}
		}
		else {
			servent* pse = getservbyname(service.c_str(), "tcp");
			if (pse) {
				port = ntohs(pse->s_port);
			}
			else {
				error = "Failed to look up TCP service " + service;
				return false;
			}
		}
	}

	// Ensure that there are only alphanumeric characters, dots,
	// dashes and colons in address.  Anything else must be an error.
	for (string::const_iterator it = addr.begin(); it != addr.end(); ++it) {
		string::value_type c = *it;
		if (!(isalnum(c) || (c == '.') || (c == '-') || (c == ':'))) {
			error = "Bad character '";
			error += c;
			error += "' in TCP/IP address";
			return false;
		}
	}

	return true;
}

} // end namespace mysqlpp

<|endoftext|>


<commit_before>// Copyright 2014 BitPay Inc.
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <iomanip>
#include <sstream>
#include <stdio.h>
#include "univalue.h"
#include "univalue_escapes.h"

using namespace std;

static string json_escape(const string& inS)
{
    string outS;
    outS.reserve(inS.size() * 2);

    for (unsigned int i = 0; i < inS.size(); i++) {
        unsigned char ch = inS[i];
        const char *escStr = escapes[ch];

        if (escStr)
            outS += escStr;
        else
            outS += ch;
    }

    return outS;
}

string UniValue::write(unsigned int prettyIndent,
                       unsigned int indentLevel) const
{
    string s;
    s.reserve(1024);

    unsigned int modIndent = indentLevel;
    if (modIndent == 0)
        modIndent = 1;

    switch (typ) {
    case VNULL:
        s += "null";
        break;
    case VOBJ:
        writeObject(prettyIndent, modIndent, s);
        break;
    case VARR:
        writeArray(prettyIndent, modIndent, s);
        break;
    case VSTR:
        s += "\"" + json_escape(val) + "\"";
        break;
    case VNUM:
        s += val;
        break;
    case VBOOL:
        s += (val == "1" ? "true" : "false");
        break;
    }

    return s;
}

static void indentStr(unsigned int prettyIndent, unsigned int indentLevel, string& s)
{
    s.append(prettyIndent * indentLevel, ' ');
}

void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, string& s) const
{
    s += "[";
    if (prettyIndent)
        s += "\n";

    for (unsigned int i = 0; i < values.size(); i++) {
        if (prettyIndent)
            indentStr(prettyIndent, indentLevel, s);
        s += values[i].write(prettyIndent, indentLevel + 1);
        if (i != (values.size() - 1)) {
            s += ",";
            if (prettyIndent)
                s += " ";
        }
        if (prettyIndent)
            s += "\n";
    }

    if (prettyIndent)
        indentStr(prettyIndent, indentLevel - 1, s);
    s += "]";
}

void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, string& s) const
{
    s += "{";
    if (prettyIndent)
        s += "\n";

    for (unsigned int i = 0; i < keys.size(); i++) {
        if (prettyIndent)
            indentStr(prettyIndent, indentLevel, s);
        s += "\"" + json_escape(keys[i]) + "\":";
        if (prettyIndent)
            s += " ";
        s += values.at(i).write(prettyIndent, indentLevel + 1);
        if (i != (values.size() - 1))
            s += ",";
        if (prettyIndent)
            s += "\n";
    }

    if (prettyIndent)
        indentStr(prettyIndent, indentLevel - 1, s);
    s += "}";
}

<commit_msg>Remove trailing whitespace from JSON export<commit_after>// Copyright 2014 BitPay Inc.
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <iomanip>
#include <sstream>
#include <stdio.h>
#include "univalue.h"
#include "univalue_escapes.h"

using namespace std;

static string json_escape(const string& inS)
{
    string outS;
    outS.reserve(inS.size() * 2);

    for (unsigned int i = 0; i < inS.size(); i++) {
        unsigned char ch = inS[i];
        const char *escStr = escapes[ch];

        if (escStr)
            outS += escStr;
        else
            outS += ch;
    }

    return outS;
}

string UniValue::write(unsigned int prettyIndent,
                       unsigned int indentLevel) const
{
    string s;
    s.reserve(1024);

    unsigned int modIndent = indentLevel;
    if (modIndent == 0)
        modIndent = 1;

    switch (typ) {
    case VNULL:
        s += "null";
        break;
    case VOBJ:
        writeObject(prettyIndent, modIndent, s);
        break;
    case VARR:
        writeArray(prettyIndent, modIndent, s);
        break;
    case VSTR:
        s += "\"" + json_escape(val) + "\"";
        break;
    case VNUM:
        s += val;
        break;
    case VBOOL:
        s += (val == "1" ? "true" : "false");
        break;
    }

    return s;
}

static void indentStr(unsigned int prettyIndent, unsigned int indentLevel, string& s)
{
    s.append(prettyIndent * indentLevel, ' ');
}

void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, string& s) const
{
    s += "[";
    if (prettyIndent)
        s += "\n";

    for (unsigned int i = 0; i < values.size(); i++) {
        if (prettyIndent)
            indentStr(prettyIndent, indentLevel, s);
        s += values[i].write(prettyIndent, indentLevel + 1);
        if (i != (values.size() - 1)) {
            s += ",";
        }
        if (prettyIndent)
            s += "\n";
    }

    if (prettyIndent)
        indentStr(prettyIndent, indentLevel - 1, s);
    s += "]";
}

void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, string& s) const
{
    s += "{";
    if (prettyIndent)
        s += "\n";

    for (unsigned int i = 0; i < keys.size(); i++) {
        if (prettyIndent)
            indentStr(prettyIndent, indentLevel, s);
        s += "\"" + json_escape(keys[i]) + "\":";
        if (prettyIndent)
            s += " ";
        s += values.at(i).write(prettyIndent, indentLevel + 1);
        if (i != (values.size() - 1))
            s += ",";
        if (prettyIndent)
            s += "\n";
    }

    if (prettyIndent)
        indentStr(prettyIndent, indentLevel - 1, s);
    s += "}";
}

<|endoftext|>


<commit_before>/*
    Copyright (c) 2006 Volker Krause <volker.krause@rwth-aachen.de>

    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., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "monitor.h"
#include "notificationmanagerinterface.h"

#include <dbus/qdbusinterface.h>
#include <dbus/qdbusconnection.h>

#include <QDebug>

using namespace PIM;

class PIM::MonitorPrivate
{
  public:
    org::kde::Akonadi::NotificationManager *nm;
    QList<QByteArray> collections;

    bool isCollectionMonitored( const QByteArray &path )
    {
      foreach ( const QByteArray ba, collections ) {
        if ( path.startsWith( ba ) )
          return true;
      }
      return false;
    }
};

PIM::Monitor::Monitor( QObject *parent ) :
    QObject( parent ),
    d( new MonitorPrivate() )
{
  connectToNotificationManager();
}

PIM::Monitor::~Monitor()
{
  delete d->nm;
  delete d;
}

void PIM::Monitor::monitorCollection( const QByteArray & path )
{
  d->collections.append( path );
  if ( connectToNotificationManager() )
    d->nm->monitorCollection( path );
}

void PIM::Monitor::monitorItem( const DataReference & ref )
{
  if ( connectToNotificationManager() )
    d->nm->monitorItem( ref.persistanceID().toLatin1() );
}

void PIM::Monitor::slotItemChanged( const QByteArray & uid, const QByteArray & collection )
{
  if ( d->isCollectionMonitored( collection ) )
    emit itemChanged( DataReference( uid, QString() ) );
}

void PIM::Monitor::slotItemAdded( const QByteArray & uid, const QByteArray & collection )
{
  if ( d->isCollectionMonitored( collection ) )
    emit itemAdded( DataReference( uid, QString() ) );
}

void PIM::Monitor::slotItemRemoved( const QByteArray & uid, const QByteArray & collection )
{
  if ( d->isCollectionMonitored( collection ) )
    emit itemRemoved( DataReference( uid, QString() ) );
}

void PIM::Monitor::slotCollectionChanged( const QByteArray & path )
{
  if ( d->isCollectionMonitored( path ) )
    emit collectionChanged( path );
}

void PIM::Monitor::slotCollectionAdded( const QByteArray & path )
{
  if ( d->isCollectionMonitored( path ) )
    emit collectionAdded( path );
}

void PIM::Monitor::slotCollectionRemoved( const QByteArray & path )
{
  if ( d->isCollectionMonitored( path ) )
    emit collectionRemoved( path );
}

bool PIM::Monitor::connectToNotificationManager( )
{
  if ( !d->nm )
    d->nm = QDBus::sessionBus().findInterface<org::kde::Akonadi::NotificationManager>("org.kde.pim.Akonadi.NotificationManager", "/");
  else
    return true;

  if ( !d->nm ) {
    qWarning() << "Unable to connect to notification manager";
  } else {
    connect( d->nm, SIGNAL(itemChanged(QByteArray,QByteArray)), SLOT(slotItemChanged(QByteArray,QByteArray)) );
    connect( d->nm, SIGNAL(itemAdded(QByteArray,QByteArray)), SLOT(slotItemAdded(QByteArray,QByteArray)) );
    connect( d->nm, SIGNAL(itemRemoved(QByteArray,QByteArray)), SLOT(slotItemRemoved(QByteArray,QByteArray)) );
    connect( d->nm, SIGNAL(collectionChanged(QByteArray)), SLOT(slotCollectionChanged(QByteArray)) );
    connect( d->nm, SIGNAL(collectionAdded(QByteArray)), SLOT(slotCollectionAdded(QByteArray)) );
    connect( d->nm, SIGNAL(collectionRemoved(QByteArray)), SLOT(slotCollectionRemoved(QByteArray)) );
    return true;
  }
  return false;
}

#include "monitor.moc"
<commit_msg>Fix typo.<commit_after>/*
    Copyright (c) 2006 Volker Krause <volker.krause@rwth-aachen.de>

    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., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "monitor.h"
#include "notificationmanagerinterface.h"

#include <dbus/qdbusinterface.h>
#include <dbus/qdbusconnection.h>

#include <QDebug>

using namespace PIM;

class PIM::MonitorPrivate
{
  public:
    org::kde::Akonadi::NotificationManager *nm;
    QList<QByteArray> collections;

    bool isCollectionMonitored( const QByteArray &path )
    {
      foreach ( const QByteArray ba, collections ) {
        if ( path.startsWith( ba ) )
          return true;
      }
      return false;
    }
};

PIM::Monitor::Monitor( QObject *parent ) :
    QObject( parent ),
    d( new MonitorPrivate() )
{
  connectToNotificationManager();
}

PIM::Monitor::~Monitor()
{
  delete d->nm;
  delete d;
}

void PIM::Monitor::monitorCollection( const QByteArray & path )
{
  d->collections.append( path );
  if ( connectToNotificationManager() )
    d->nm->monitorCollection( path );
}

void PIM::Monitor::monitorItem( const DataReference & ref )
{
  if ( connectToNotificationManager() )
    d->nm->monitorItem( ref.persistanceID().toLatin1() );
}

void PIM::Monitor::slotItemChanged( const QByteArray & uid, const QByteArray & collection )
{
  if ( d->isCollectionMonitored( collection ) )
    emit itemChanged( DataReference( uid, QString() ) );
}

void PIM::Monitor::slotItemAdded( const QByteArray & uid, const QByteArray & collection )
{
  if ( d->isCollectionMonitored( collection ) )
    emit itemAdded( DataReference( uid, QString() ) );
}

void PIM::Monitor::slotItemRemoved( const QByteArray & uid, const QByteArray & collection )
{
  if ( d->isCollectionMonitored( collection ) )
    emit itemRemoved( DataReference( uid, QString() ) );
}

void PIM::Monitor::slotCollectionChanged( const QByteArray & path )
{
  if ( d->isCollectionMonitored( path ) )
    emit collectionChanged( path );
}

void PIM::Monitor::slotCollectionAdded( const QByteArray & path )
{
  if ( d->isCollectionMonitored( path ) )
    emit collectionAdded( path );
}

void PIM::Monitor::slotCollectionRemoved( const QByteArray & path )
{
  if ( d->isCollectionMonitored( path ) )
    emit collectionRemoved( path );
}

bool PIM::Monitor::connectToNotificationManager( )
{
  if ( !d->nm )
    d->nm = QDBus::sessionBus().findInterface<org::kde::Akonadi::NotificationManager>("org.kde.Akonadi.NotificationManager", "/");
  else
    return true;

  if ( !d->nm ) {
    qWarning() << "Unable to connect to notification manager";
  } else {
    connect( d->nm, SIGNAL(itemChanged(QByteArray,QByteArray)), SLOT(slotItemChanged(QByteArray,QByteArray)) );
    connect( d->nm, SIGNAL(itemAdded(QByteArray,QByteArray)), SLOT(slotItemAdded(QByteArray,QByteArray)) );
    connect( d->nm, SIGNAL(itemRemoved(QByteArray,QByteArray)), SLOT(slotItemRemoved(QByteArray,QByteArray)) );
    connect( d->nm, SIGNAL(collectionChanged(QByteArray)), SLOT(slotCollectionChanged(QByteArray)) );
    connect( d->nm, SIGNAL(collectionAdded(QByteArray)), SLOT(slotCollectionAdded(QByteArray)) );
    connect( d->nm, SIGNAL(collectionRemoved(QByteArray)), SLOT(slotCollectionRemoved(QByteArray)) );
    return true;
  }
  return false;
}

#include "monitor.moc"
<|endoftext|>


<commit_before>#include "Precompiled.h"
#include "Slider.h"
#include "ElementManager.h"

namespace libgui
{
	void Slider::Init()
	{
		m_track = make_shared<Track>();
		this->AddChild(m_track);

		m_thumb = make_shared<Thumb>(
			dynamic_pointer_cast<Slider>(shared_from_this()),
			m_track);
		m_track->AddChild(m_thumb);
	}

	const double& Slider::GetValue() const
	{
		return m_value;
	}

	void Slider::SetValue(double value)
	{
		m_value = value;
	}

	const double& Slider::GetThumbHeight() const
	{
		return m_thumbHeight;
	}

	void Slider::SetThumbHeight(double thumbHeight)
	{
		m_thumbHeight = thumbHeight;
	}

	const function<void(shared_ptr<Slider>)>& Slider::GetValueChangedCallback() const
	{
		return m_valueChangedCallback;
	}

	void Slider::SetValueChangedCallback(const function<void(shared_ptr<Slider>)>& valueChangedCallback)
	{
		m_valueChangedCallback = valueChangedCallback;
	}

	const shared_ptr<Slider::Thumb>& Slider::GetThumb() const
	{
		return m_thumb;
	}

	const shared_ptr<Slider::Track>& Slider::GetTrack() const
	{
		return m_track;
	}

	Slider::Thumb::Thumb(weak_ptr<Slider> slider, weak_ptr<Track> track)
		: m_slider(slider),
		m_track(track)
	{
	}

	void Slider::Thumb::Arrange()
	{
		if (auto slider = m_slider.lock())
		{
			auto p = GetParent();
			SetLeft(p->GetLeft()); SetRight(p->GetRight());
			auto thumbHeight = slider->GetThumbHeight();
			auto boundsTop = p->GetTop();
			auto boundsHeight = p->GetHeight() - thumbHeight;
			auto top = boundsTop + (slider->GetRawFromValue() * boundsHeight);
			SetTop(round(top)); SetBottom(round(top + thumbHeight));
		}
	}

	void Slider::Thumb::NotifyMouseEnter()
	{
		m_isOver = true;

		if (IsCapturing())
		{
			m_isPressed = true;
		}
		else
		{
			m_isHot = true;
		}
	}

	void Slider::Thumb::NotifyMouseLeave()
	{
		m_isOver = false;

		if (IsCapturing())
		{
			m_isPressed = false;
		}
		else
		{
			m_isHot = false;
		}
	}

	void Slider::Thumb::NotifyMouseDown(Location location)
	{
		m_isHot = true;
		m_isPressed = true;

		m_anchorOffset = location.y - GetTop();

		GetElementManager()->RequestCapture(dynamic_pointer_cast<Control>(shared_from_this()));
	}

	void Slider::Thumb::NotifyMouseUp(Location location)
	{
		m_isPressed = false;
		if (IsCapturing())
		{
			if (!m_isOver)
			{
				m_isHot = false;
			}

			GetElementManager()->ReleaseCapture();
		}
	}

	void Slider::Thumb::NotifyMouseMove(Location location, bool& updateScreen)
	{
		if (IsCapturing())
		{
			auto slider = m_slider.lock();
			auto track = m_track.lock();
			if (slider && track)
			{
				auto offsetPercent = ((location.y - m_anchorOffset) - track->GetTop()) /
					(track->GetHeight() - slider->GetThumbHeight());
				offsetPercent = max(0.0, offsetPercent);
				offsetPercent = min(1.0, offsetPercent);
				if (slider->GetRawFromValue() != offsetPercent)
				{
					slider->SetValueFromRaw(offsetPercent);
					updateScreen = true;
					return;
				}
			}
		}
		updateScreen = false;
		return;
	}

	const bool& Slider::Thumb::GetIsPressed() const
	{
		return m_isPressed;
	}

	const bool& Slider::Thumb::GetIsHot() const
	{
		return m_isHot;
	}

	const weak_ptr<Slider>& Slider::Thumb::GetSlider() const
	{
		return m_slider;
	}

	void Slider::OnValueChanged()
	{
		if (m_valueChangedCallback)
		{
			m_valueChangedCallback(dynamic_pointer_cast<Slider>(shared_from_this()));
		}
	}

	double Slider::GetRawFromValue()
	{
		return 1.0 - GetValue();
	}

	void Slider::SetValueFromRaw(double raw)
	{
		SetValue(1.0 - raw);
	}
}<commit_msg>Slider properly calls OnValueChanged()<commit_after>#include "Precompiled.h"
#include "Slider.h"
#include "ElementManager.h"

namespace libgui
{
	void Slider::Init()
	{
		m_track = make_shared<Track>();
		this->AddChild(m_track);

		m_thumb = make_shared<Thumb>(
			dynamic_pointer_cast<Slider>(shared_from_this()),
			m_track);
		m_track->AddChild(m_thumb);
	}

	const double& Slider::GetValue() const
	{
		return m_value;
	}

	void Slider::SetValue(double value)
	{
		if (value != m_value)
		{
			OnValueChanged();
		}

		m_value = value;
	}

	const double& Slider::GetThumbHeight() const
	{
		return m_thumbHeight;
	}

	void Slider::SetThumbHeight(double thumbHeight)
	{
		m_thumbHeight = thumbHeight;
	}

	const function<void(shared_ptr<Slider>)>& Slider::GetValueChangedCallback() const
	{
		return m_valueChangedCallback;
	}

	void Slider::SetValueChangedCallback(const function<void(shared_ptr<Slider>)>& valueChangedCallback)
	{
		m_valueChangedCallback = valueChangedCallback;
	}

	const shared_ptr<Slider::Thumb>& Slider::GetThumb() const
	{
		return m_thumb;
	}

	const shared_ptr<Slider::Track>& Slider::GetTrack() const
	{
		return m_track;
	}

	Slider::Thumb::Thumb(weak_ptr<Slider> slider, weak_ptr<Track> track)
		: m_slider(slider),
		m_track(track)
	{
	}

	void Slider::Thumb::Arrange()
	{
		if (auto slider = m_slider.lock())
		{
			auto p = GetParent();
			SetLeft(p->GetLeft()); SetRight(p->GetRight());
			auto thumbHeight = slider->GetThumbHeight();
			auto boundsTop = p->GetTop();
			auto boundsHeight = p->GetHeight() - thumbHeight;
			auto top = boundsTop + (slider->GetRawFromValue() * boundsHeight);
			SetTop(round(top)); SetBottom(round(top + thumbHeight));
		}
	}

	void Slider::Thumb::NotifyMouseEnter()
	{
		m_isOver = true;

		if (IsCapturing())
		{
			m_isPressed = true;
		}
		else
		{
			m_isHot = true;
		}
	}

	void Slider::Thumb::NotifyMouseLeave()
	{
		m_isOver = false;

		if (IsCapturing())
		{
			m_isPressed = false;
		}
		else
		{
			m_isHot = false;
		}
	}

	void Slider::Thumb::NotifyMouseDown(Location location)
	{
		m_isHot = true;
		m_isPressed = true;

		m_anchorOffset = location.y - GetTop();

		GetElementManager()->RequestCapture(dynamic_pointer_cast<Control>(shared_from_this()));
	}

	void Slider::Thumb::NotifyMouseUp(Location location)
	{
		m_isPressed = false;
		if (IsCapturing())
		{
			if (!m_isOver)
			{
				m_isHot = false;
			}

			GetElementManager()->ReleaseCapture();
		}
	}

	void Slider::Thumb::NotifyMouseMove(Location location, bool& updateScreen)
	{
		if (IsCapturing())
		{
			auto slider = m_slider.lock();
			auto track = m_track.lock();
			if (slider && track)
			{
				auto offsetPercent = ((location.y - m_anchorOffset) - track->GetTop()) /
					(track->GetHeight() - slider->GetThumbHeight());
				offsetPercent = max(0.0, offsetPercent);
				offsetPercent = min(1.0, offsetPercent);
				if (slider->GetRawFromValue() != offsetPercent)
				{
					slider->SetValueFromRaw(offsetPercent);
					updateScreen = true;
					return;
				}
			}
		}
		updateScreen = false;
		return;
	}

	const bool& Slider::Thumb::GetIsPressed() const
	{
		return m_isPressed;
	}

	const bool& Slider::Thumb::GetIsHot() const
	{
		return m_isHot;
	}

	const weak_ptr<Slider>& Slider::Thumb::GetSlider() const
	{
		return m_slider;
	}

	void Slider::OnValueChanged()
	{
		if (m_valueChangedCallback)
		{
			m_valueChangedCallback(dynamic_pointer_cast<Slider>(shared_from_this()));
		}
	}

	double Slider::GetRawFromValue()
	{
		return 1.0 - GetValue();
	}

	void Slider::SetValueFromRaw(double raw)
	{
		SetValue(1.0 - raw);
	}
}<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 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.
//
// The DomAutocompleteTests in this file are responsible for ensuring the
// abstract dom autocomplete framework is correctly responding to events and
// delegating to appropriate places. This means concrete implementations should
// focus only on testing the code actually written for that implementation and
// those tests should be completely decoupled from WebCore::Event.

#include <string>

#include "config.h"

#include "base/compiler_specific.h"

MSVC_PUSH_WARNING_LEVEL(0);
#include "HTMLInputElement.h"
#include "HTMLFormElement.h"
#include "Document.h"
#include "Frame.h"
#include "Editor.h"
#include "EventNames.h"
#include "Event.h"
#include "EventListener.h"
#include <wtf/Threading.h>
MSVC_POP_WARNING();

#undef LOG

#include "webkit/glue/autocomplete_input_listener.h"
#include "webkit/glue/webframe.h"
#include "webkit/glue/webframe_impl.h"
#include "webkit/glue/webview.h"
#include "webkit/tools/test_shell/test_shell_test.h"
#include "testing/gtest/include/gtest/gtest.h"

using WebCore::Event;

namespace webkit_glue {

class TestAutocompleteBodyListener : public AutocompleteBodyListener {
 public:
  TestAutocompleteBodyListener() {
  }

  void SetCaretAtEnd(WebCore::HTMLInputElement* element, bool value) {
    std::vector<WebCore::HTMLInputElement*>::iterator iter =
        std::find(caret_at_end_elements_.begin(), caret_at_end_elements_.end(),
                  element);
    if (value) {
      if (iter == caret_at_end_elements_.end())
        caret_at_end_elements_.push_back(element);
    } else {
      if (iter != caret_at_end_elements_.end())
        caret_at_end_elements_.erase(iter);
    }
  }

  void ResetTestState() {
    caret_at_end_elements_.clear();
  }

 protected:
  // AutocompleteBodyListener override.
  virtual bool IsCaretAtEndOfText(WebCore::HTMLInputElement* element,
                                  size_t input_length,
                                  size_t previous_length) const {
    return std::find(caret_at_end_elements_.begin(),
                     caret_at_end_elements_.end(),
                     element) != caret_at_end_elements_.end();
  }

 private:
   // List of elements for which the caret is at the end of the text.
   std::vector<WebCore::HTMLInputElement*> caret_at_end_elements_;
};

class TestAutocompleteInputListener : public AutocompleteInputListener {
 public:
  TestAutocompleteInputListener()
      : blurred_(false),
        did_request_inline_autocomplete_(false) {
  }

  void ResetTestState() {
    blurred_ = false;
    did_request_inline_autocomplete_ = false;
  }

  bool blurred() const { return blurred_; }
  bool did_request_inline_autocomplete() const {
    return did_request_inline_autocomplete_;
  }

  virtual void OnBlur(WebCore::HTMLInputElement* element,
                      const std::wstring& user_input) {
    blurred_ = true;
  }
  virtual void OnInlineAutocompleteNeeded(WebCore::HTMLInputElement* element,
                                          const std::wstring& user_input) {
    did_request_inline_autocomplete_ = true;
  }

 private:
  bool blurred_;
  bool did_request_inline_autocomplete_;
};

namespace {

class DomAutocompleteTests : public TestShellTest {
 public:
  virtual void SetUp() {
    TestShellTest::SetUp();
    // We need a document in order to create HTMLInputElements.
    WebView* view = test_shell_->webView();
    WebFrameImpl* frame = static_cast<WebFrameImpl*>(view->GetMainFrame());
    document_ = frame->frame()->document();
  }

  void FireAndHandleInputEvent(AutocompleteBodyListener* listener,
                               WebCore::HTMLInputElement* element) {
    RefPtr<Event> event(Event::create(WebCore::eventNames().inputEvent,
                                      false, false));
    event->setTarget(element);
    listener->handleEvent(event.get(), false);
  }

  void SimulateTypedInput(TestAutocompleteBodyListener* listener,
                          WebCore::HTMLInputElement* element,
                          const std::wstring& new_input,
                          bool caret_at_end) {
      element->setValue(StdWStringToString(new_input));
      listener->SetCaretAtEnd(element, caret_at_end);
      FireAndHandleInputEvent(listener, element);
  }

  WebCore::Document* document_;
};
}  // namespace

TEST_F(DomAutocompleteTests, OnBlur) {
  RefPtr<WebCore::HTMLInputElement> ignored_element =
      new WebCore::HTMLInputElement(document_);
  RefPtr<WebCore::HTMLInputElement> listened_element =
      new WebCore::HTMLInputElement(document_);
  RefPtr<TestAutocompleteBodyListener> body_listener =
      new TestAutocompleteBodyListener;
  TestAutocompleteInputListener* listener = new TestAutocompleteInputListener();
  // body_listener takes ownership of the listener.
  body_listener->AddInputListener(listened_element.get(), listener);

  // Simulate a blur event to the element we are not listening to.
  // Our listener should not be notified.
  RefPtr<Event> event(Event::create(WebCore::eventNames().DOMFocusOutEvent,
                                    false, false));
  event->setTarget(ignored_element.get());
  body_listener->handleEvent(event.get(), false);
  EXPECT_FALSE(listener->blurred());

  // Now simulate the event on the input element we are listening to.
  event->setTarget(listened_element.get());
  body_listener->handleEvent(event.get(), false);
  EXPECT_TRUE(listener->blurred());
}

TEST_F(DomAutocompleteTests, InlineAutocompleteTriggeredByInputEvent) {
  RefPtr<WebCore::HTMLInputElement> ignored_element =
      new WebCore::HTMLInputElement(document_);
  RefPtr<WebCore::HTMLInputElement> listened_element =
      new WebCore::HTMLInputElement(document_);
  RefPtr<TestAutocompleteBodyListener> body_listener =
      new TestAutocompleteBodyListener;

  TestAutocompleteInputListener* listener = new TestAutocompleteInputListener();
  body_listener->AddInputListener(listened_element.get(), listener);

  // Simulate an inputEvent by setting the value and artificially firing evt.
  // The user typed 'g'.
  SimulateTypedInput(body_listener.get(), ignored_element.get(), L"g", true);
  EXPECT_FALSE(listener->did_request_inline_autocomplete());
  SimulateTypedInput(body_listener.get(), listened_element.get(), L"g", true);
  EXPECT_TRUE(listener->did_request_inline_autocomplete());
}

TEST_F(DomAutocompleteTests, InlineAutocompleteHeuristics) {
  RefPtr<WebCore::HTMLInputElement> input_element =
      new WebCore::HTMLInputElement(document_);
  RefPtr<TestAutocompleteBodyListener> body_listener =
      new TestAutocompleteBodyListener();

  TestAutocompleteInputListener* listener = new TestAutocompleteInputListener();
  body_listener->AddInputListener(input_element.get(), listener);

  // Simulate a user entering some text, and then backspacing to remove
  // a character.
  SimulateTypedInput(body_listener.get(), input_element.get(), L"g", true);
  EXPECT_TRUE(listener->did_request_inline_autocomplete());
  listener->ResetTestState();
  body_listener->ResetTestState();

  SimulateTypedInput(body_listener.get(), input_element.get(), L"go", true);
  EXPECT_TRUE(listener->did_request_inline_autocomplete());
  listener->ResetTestState();
  body_listener->ResetTestState();

  SimulateTypedInput(body_listener.get(), input_element.get(), L"g", true);
  EXPECT_FALSE(listener->did_request_inline_autocomplete());
  listener->ResetTestState();
  body_listener->ResetTestState();

  // Now simulate the user moving the cursor to a position other than the end,
  // and adding text.
  SimulateTypedInput(body_listener.get(), input_element.get(), L"og", false);
  EXPECT_FALSE(listener->did_request_inline_autocomplete());
  listener->ResetTestState();
  body_listener->ResetTestState();

  // Test that same input doesn't trigger autocomplete.
  SimulateTypedInput(body_listener.get(), input_element.get(), L"og", true);
  EXPECT_FALSE(listener->did_request_inline_autocomplete());
  listener->ResetTestState();
  body_listener->ResetTestState();
}

}  // webkit_glue
<commit_msg>AutocompleteBodyListener is created with a ref count of 1, so we need to do an adoptRef before assigning it to a RefPtr, otherwise it is leaked.<commit_after>// Copyright (c) 2006-2008 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.
//
// The DomAutocompleteTests in this file are responsible for ensuring the
// abstract dom autocomplete framework is correctly responding to events and
// delegating to appropriate places. This means concrete implementations should
// focus only on testing the code actually written for that implementation and
// those tests should be completely decoupled from WebCore::Event.

#include <string>

#include "config.h"

#include "base/compiler_specific.h"

MSVC_PUSH_WARNING_LEVEL(0);
#include "HTMLInputElement.h"
#include "HTMLFormElement.h"
#include "Document.h"
#include "Frame.h"
#include "Editor.h"
#include "EventNames.h"
#include "Event.h"
#include "EventListener.h"
#include <wtf/Threading.h>
MSVC_POP_WARNING();

#undef LOG

#include "webkit/glue/autocomplete_input_listener.h"
#include "webkit/glue/webframe.h"
#include "webkit/glue/webframe_impl.h"
#include "webkit/glue/webview.h"
#include "webkit/tools/test_shell/test_shell_test.h"
#include "testing/gtest/include/gtest/gtest.h"

using WebCore::Event;

namespace webkit_glue {

class TestAutocompleteBodyListener : public AutocompleteBodyListener {
 public:
  TestAutocompleteBodyListener() {
  }

  void SetCaretAtEnd(WebCore::HTMLInputElement* element, bool value) {
    std::vector<WebCore::HTMLInputElement*>::iterator iter =
        std::find(caret_at_end_elements_.begin(), caret_at_end_elements_.end(),
                  element);
    if (value) {
      if (iter == caret_at_end_elements_.end())
        caret_at_end_elements_.push_back(element);
    } else {
      if (iter != caret_at_end_elements_.end())
        caret_at_end_elements_.erase(iter);
    }
  }

  void ResetTestState() {
    caret_at_end_elements_.clear();
  }

 protected:
  // AutocompleteBodyListener override.
  virtual bool IsCaretAtEndOfText(WebCore::HTMLInputElement* element,
                                  size_t input_length,
                                  size_t previous_length) const {
    return std::find(caret_at_end_elements_.begin(),
                     caret_at_end_elements_.end(),
                     element) != caret_at_end_elements_.end();
  }

 private:
   // List of elements for which the caret is at the end of the text.
   std::vector<WebCore::HTMLInputElement*> caret_at_end_elements_;
};

class TestAutocompleteInputListener : public AutocompleteInputListener {
 public:
  TestAutocompleteInputListener()
      : blurred_(false),
        did_request_inline_autocomplete_(false) {
  }

  void ResetTestState() {
    blurred_ = false;
    did_request_inline_autocomplete_ = false;
  }

  bool blurred() const { return blurred_; }
  bool did_request_inline_autocomplete() const {
    return did_request_inline_autocomplete_;
  }

  virtual void OnBlur(WebCore::HTMLInputElement* element,
                      const std::wstring& user_input) {
    blurred_ = true;
  }
  virtual void OnInlineAutocompleteNeeded(WebCore::HTMLInputElement* element,
                                          const std::wstring& user_input) {
    did_request_inline_autocomplete_ = true;
  }

 private:
  bool blurred_;
  bool did_request_inline_autocomplete_;
};

namespace {

class DomAutocompleteTests : public TestShellTest {
 public:
  virtual void SetUp() {
    TestShellTest::SetUp();
    // We need a document in order to create HTMLInputElements.
    WebView* view = test_shell_->webView();
    WebFrameImpl* frame = static_cast<WebFrameImpl*>(view->GetMainFrame());
    document_ = frame->frame()->document();
  }

  void FireAndHandleInputEvent(AutocompleteBodyListener* listener,
                               WebCore::HTMLInputElement* element) {
    RefPtr<Event> event(Event::create(WebCore::eventNames().inputEvent,
                                      false, false));
    event->setTarget(element);
    listener->handleEvent(event.get(), false);
  }

  void SimulateTypedInput(TestAutocompleteBodyListener* listener,
                          WebCore::HTMLInputElement* element,
                          const std::wstring& new_input,
                          bool caret_at_end) {
      element->setValue(StdWStringToString(new_input));
      listener->SetCaretAtEnd(element, caret_at_end);
      FireAndHandleInputEvent(listener, element);
  }

  WebCore::Document* document_;
};
}  // namespace

TEST_F(DomAutocompleteTests, OnBlur) {
  RefPtr<WebCore::HTMLInputElement> ignored_element =
      new WebCore::HTMLInputElement(document_);
  RefPtr<WebCore::HTMLInputElement> listened_element =
      new WebCore::HTMLInputElement(document_);
  RefPtr<TestAutocompleteBodyListener> body_listener =
      adoptRef(new TestAutocompleteBodyListener);
  TestAutocompleteInputListener* listener = new TestAutocompleteInputListener();
  // body_listener takes ownership of the listener.
  body_listener->AddInputListener(listened_element.get(), listener);

  // Simulate a blur event to the element we are not listening to.
  // Our listener should not be notified.
  RefPtr<Event> event(Event::create(WebCore::eventNames().DOMFocusOutEvent,
                                    false, false));
  event->setTarget(ignored_element.get());
  body_listener->handleEvent(event.get(), false);
  EXPECT_FALSE(listener->blurred());

  // Now simulate the event on the input element we are listening to.
  event->setTarget(listened_element.get());
  body_listener->handleEvent(event.get(), false);
  EXPECT_TRUE(listener->blurred());
}

TEST_F(DomAutocompleteTests, InlineAutocompleteTriggeredByInputEvent) {
  RefPtr<WebCore::HTMLInputElement> ignored_element =
      new WebCore::HTMLInputElement(document_);
  RefPtr<WebCore::HTMLInputElement> listened_element =
      new WebCore::HTMLInputElement(document_);
  RefPtr<TestAutocompleteBodyListener> body_listener =
      adoptRef(new TestAutocompleteBodyListener());

  TestAutocompleteInputListener* listener = new TestAutocompleteInputListener();
  body_listener->AddInputListener(listened_element.get(), listener);

  // Simulate an inputEvent by setting the value and artificially firing evt.
  // The user typed 'g'.
  SimulateTypedInput(body_listener.get(), ignored_element.get(), L"g", true);
  EXPECT_FALSE(listener->did_request_inline_autocomplete());
  SimulateTypedInput(body_listener.get(), listened_element.get(), L"g", true);
  EXPECT_TRUE(listener->did_request_inline_autocomplete());
}

TEST_F(DomAutocompleteTests, InlineAutocompleteHeuristics) {
  RefPtr<WebCore::HTMLInputElement> input_element =
      new WebCore::HTMLInputElement(document_);
  RefPtr<TestAutocompleteBodyListener> body_listener =
      adoptRef(new TestAutocompleteBodyListener());

  TestAutocompleteInputListener* listener = new TestAutocompleteInputListener();
  body_listener->AddInputListener(input_element.get(), listener);

  // Simulate a user entering some text, and then backspacing to remove
  // a character.
  SimulateTypedInput(body_listener.get(), input_element.get(), L"g", true);
  EXPECT_TRUE(listener->did_request_inline_autocomplete());
  listener->ResetTestState();
  body_listener->ResetTestState();

  SimulateTypedInput(body_listener.get(), input_element.get(), L"go", true);
  EXPECT_TRUE(listener->did_request_inline_autocomplete());
  listener->ResetTestState();
  body_listener->ResetTestState();

  SimulateTypedInput(body_listener.get(), input_element.get(), L"g", true);
  EXPECT_FALSE(listener->did_request_inline_autocomplete());
  listener->ResetTestState();
  body_listener->ResetTestState();

  // Now simulate the user moving the cursor to a position other than the end,
  // and adding text.
  SimulateTypedInput(body_listener.get(), input_element.get(), L"og", false);
  EXPECT_FALSE(listener->did_request_inline_autocomplete());
  listener->ResetTestState();
  body_listener->ResetTestState();

  // Test that same input doesn't trigger autocomplete.
  SimulateTypedInput(body_listener.get(), input_element.get(), L"og", true);
  EXPECT_FALSE(listener->did_request_inline_autocomplete());
  listener->ResetTestState();
  body_listener->ResetTestState();
}

}  // webkit_glue
<|endoftext|>


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

#include "printing/printing_context_cairo.h"

#include "base/logging.h"
#include "base/values.h"
#include "printing/units.h"
#include "printing/print_settings_initializer_gtk.h"

#if defined(OS_CHROMEOS)
#include <unicode/ulocdata.h>
#else
#include <gtk/gtk.h>
#include <gtk/gtkprintunixdialog.h>
#endif  // defined(OS_CHROMEOS)

#if !defined(OS_CHROMEOS)
namespace {
  // Function pointer for creating print dialogs.
  static void* (*create_dialog_func_)(
      printing::PrintingContext::PrintSettingsCallback* callback,
      printing::PrintingContextCairo* context) = NULL;
  // Function pointer for printing documents.
  static void (*print_document_func_)(
      void* print_dialog,
      const printing::NativeMetafile* metafile,
      const string16& document_name) = NULL;
}  // namespace
#endif  // !defined(OS_CHROMEOS)

namespace printing {

// static
  PrintingContext* PrintingContext::Create(const std::string& app_locale) {
  return static_cast<PrintingContext*>(new PrintingContextCairo(app_locale));
}

PrintingContextCairo::PrintingContextCairo(const std::string& app_locale)
#if defined(OS_CHROMEOS)
    : PrintingContext(app_locale) {
#else
    : PrintingContext(app_locale),
      print_dialog_(NULL) {
#endif
}

PrintingContextCairo::~PrintingContextCairo() {
  ReleaseContext();
}

#if !defined(OS_CHROMEOS)
// static
void PrintingContextCairo::SetPrintingFunctions(
    void* (*create_dialog_func)(PrintSettingsCallback* callback,
                                PrintingContextCairo* context),
    void (*print_document_func)(void* print_dialog,
                                const NativeMetafile* metafile,
                                const string16& document_name)) {
  DCHECK(create_dialog_func);
  DCHECK(print_document_func);
  DCHECK(!create_dialog_func_);
  DCHECK(!print_document_func_);
  create_dialog_func_ = create_dialog_func;
  print_document_func_ = print_document_func;
}

void PrintingContextCairo::PrintDocument(const NativeMetafile* metafile) {
  DCHECK(print_dialog_);
  DCHECK(metafile);
  print_document_func_(print_dialog_, metafile, document_name_);
}
#endif  // !defined(OS_CHROMEOS)

void PrintingContextCairo::AskUserForSettings(
    gfx::NativeView parent_view,
    int max_pages,
    bool has_selection,
    PrintSettingsCallback* callback) {
#if defined(OS_CHROMEOS)
  callback->Run(OK);
#else
  print_dialog_ = create_dialog_func_(callback, this);
#endif  // defined(OS_CHROMEOS)
}

PrintingContext::Result PrintingContextCairo::UseDefaultSettings() {
  DCHECK(!in_print_job_);

  ResetSettings();
#if defined(OS_CHROMEOS)
  // For Chrome OS use default values based on the app locale rather than rely
  // on GTK. Note that relying on the app locale does not work well if it is
  // different from the system locale, e.g. a user using Chinese ChromeOS in the
  // US. Eventually we need to get the defaults from the printer.
  // TODO(sanjeevr): We need a better feedback loop between the cloud print
  // dialog and this code.
  int dpi = 300;
  gfx::Size physical_size_device_units;
  gfx::Rect printable_area_device_units;
  int32_t width = 0;
  int32_t height = 0;
  UErrorCode error = U_ZERO_ERROR;
  ulocdata_getPaperSize(app_locale_.c_str(), &height, &width, &error);
  if (error != U_ZERO_ERROR) {
    // If the call failed, assume a paper size of 8.5 x 11 inches.
    LOG(WARNING) << "ulocdata_getPaperSize failed, using 8.5 x 11, error: "
                 << error;
    width = static_cast<int>(8.5 * dpi);
    height = static_cast<int>(11 * dpi);
  } else {
    // ulocdata_getPaperSize returns the width and height in mm.
    // Convert this to pixels based on the dpi.
    width = static_cast<int>(ConvertUnitDouble(width, 25.4, 1.0) * dpi);
    height = static_cast<int>(ConvertUnitDouble(height, 25.4, 1.0) * dpi);
  }

  physical_size_device_units.SetSize(width, height);
  printable_area_device_units.SetRect(
      static_cast<int>(PrintSettingsInitializerGtk::kLeftMarginInInch * dpi),
      static_cast<int>(PrintSettingsInitializerGtk::kTopMarginInInch * dpi),
      width - (PrintSettingsInitializerGtk::kLeftMarginInInch +
          PrintSettingsInitializerGtk::kRightMarginInInch) * dpi,
      height - (PrintSettingsInitializerGtk::kTopMarginInInch +
          PrintSettingsInitializerGtk::kBottomMarginInInch) * dpi);

  settings_.set_dpi(dpi);
  settings_.SetPrinterPrintableArea(physical_size_device_units,
                                    printable_area_device_units,
                                    dpi);
#else  // defined(OS_CHROMEOS)
  GtkWidget* dialog = gtk_print_unix_dialog_new(NULL, NULL);
  GtkPrintSettings* settings =
      gtk_print_unix_dialog_get_settings(GTK_PRINT_UNIX_DIALOG(dialog));
  GtkPageSetup* page_setup =
      gtk_print_unix_dialog_get_page_setup(GTK_PRINT_UNIX_DIALOG(dialog));

  PageRanges ranges_vector;  // Nothing to initialize for default settings.
  PrintSettingsInitializerGtk::InitPrintSettings(
          settings, page_setup, ranges_vector, false, &settings_);

  g_object_unref(settings);
  // |page_setup| is owned by dialog, so it does not need to be unref'ed.
  gtk_widget_destroy(dialog);
#endif  // defined(OS_CHROMEOS)

  return OK;
}

PrintingContext::Result PrintingContextCairo::UpdatePrintSettings(
    const DictionaryValue& job_settings, const PageRanges& ranges) {
  DCHECK(!in_print_job_);

  settings_.ranges = ranges;

  NOTIMPLEMENTED();

  return FAILED;
}

PrintingContext::Result PrintingContextCairo::InitWithSettings(
    const PrintSettings& settings) {
  DCHECK(!in_print_job_);

  settings_ = settings;

  return OK;
}

PrintingContext::Result PrintingContextCairo::NewDocument(
    const string16& document_name) {
  DCHECK(!in_print_job_);
  in_print_job_ = true;

#if !defined(OS_CHROMEOS)
  document_name_ = document_name;
#endif  // !defined(OS_CHROMEOS)

  return OK;
}

PrintingContext::Result PrintingContextCairo::NewPage() {
  if (abort_printing_)
    return CANCEL;
  DCHECK(in_print_job_);

  // Intentional No-op.

  return OK;
}

PrintingContext::Result PrintingContextCairo::PageDone() {
  if (abort_printing_)
    return CANCEL;
  DCHECK(in_print_job_);

  // Intentional No-op.

  return OK;
}

PrintingContext::Result PrintingContextCairo::DocumentDone() {
  if (abort_printing_)
    return CANCEL;
  DCHECK(in_print_job_);

  ResetSettings();
  return OK;
}

void PrintingContextCairo::Cancel() {
  abort_printing_ = true;
  in_print_job_ = false;
}

void PrintingContextCairo::ReleaseContext() {
  // Intentional No-op.
}

gfx::NativeDrawingContext PrintingContextCairo::context() const {
  // Intentional No-op.
  return NULL;
}

}  // namespace printing
<commit_msg>PrintPreview: Remove NOTIMPLEMENTED() function call in PrintingContextCairo::UpdatePrintSettings().<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "printing/printing_context_cairo.h"

#include "base/logging.h"
#include "base/values.h"
#include "printing/units.h"
#include "printing/print_settings_initializer_gtk.h"

#if defined(OS_CHROMEOS)
#include <unicode/ulocdata.h>
#else
#include <gtk/gtk.h>
#include <gtk/gtkprintunixdialog.h>
#endif  // defined(OS_CHROMEOS)

#if !defined(OS_CHROMEOS)
namespace {
  // Function pointer for creating print dialogs.
  static void* (*create_dialog_func_)(
      printing::PrintingContext::PrintSettingsCallback* callback,
      printing::PrintingContextCairo* context) = NULL;
  // Function pointer for printing documents.
  static void (*print_document_func_)(
      void* print_dialog,
      const printing::NativeMetafile* metafile,
      const string16& document_name) = NULL;
}  // namespace
#endif  // !defined(OS_CHROMEOS)

namespace printing {

// static
  PrintingContext* PrintingContext::Create(const std::string& app_locale) {
  return static_cast<PrintingContext*>(new PrintingContextCairo(app_locale));
}

PrintingContextCairo::PrintingContextCairo(const std::string& app_locale)
#if defined(OS_CHROMEOS)
    : PrintingContext(app_locale) {
#else
    : PrintingContext(app_locale),
      print_dialog_(NULL) {
#endif
}

PrintingContextCairo::~PrintingContextCairo() {
  ReleaseContext();
}

#if !defined(OS_CHROMEOS)
// static
void PrintingContextCairo::SetPrintingFunctions(
    void* (*create_dialog_func)(PrintSettingsCallback* callback,
                                PrintingContextCairo* context),
    void (*print_document_func)(void* print_dialog,
                                const NativeMetafile* metafile,
                                const string16& document_name)) {
  DCHECK(create_dialog_func);
  DCHECK(print_document_func);
  DCHECK(!create_dialog_func_);
  DCHECK(!print_document_func_);
  create_dialog_func_ = create_dialog_func;
  print_document_func_ = print_document_func;
}

void PrintingContextCairo::PrintDocument(const NativeMetafile* metafile) {
  DCHECK(print_dialog_);
  DCHECK(metafile);
  print_document_func_(print_dialog_, metafile, document_name_);
}
#endif  // !defined(OS_CHROMEOS)

void PrintingContextCairo::AskUserForSettings(
    gfx::NativeView parent_view,
    int max_pages,
    bool has_selection,
    PrintSettingsCallback* callback) {
#if defined(OS_CHROMEOS)
  callback->Run(OK);
#else
  print_dialog_ = create_dialog_func_(callback, this);
#endif  // defined(OS_CHROMEOS)
}

PrintingContext::Result PrintingContextCairo::UseDefaultSettings() {
  DCHECK(!in_print_job_);

  ResetSettings();
#if defined(OS_CHROMEOS)
  // For Chrome OS use default values based on the app locale rather than rely
  // on GTK. Note that relying on the app locale does not work well if it is
  // different from the system locale, e.g. a user using Chinese ChromeOS in the
  // US. Eventually we need to get the defaults from the printer.
  // TODO(sanjeevr): We need a better feedback loop between the cloud print
  // dialog and this code.
  int dpi = 300;
  gfx::Size physical_size_device_units;
  gfx::Rect printable_area_device_units;
  int32_t width = 0;
  int32_t height = 0;
  UErrorCode error = U_ZERO_ERROR;
  ulocdata_getPaperSize(app_locale_.c_str(), &height, &width, &error);
  if (error != U_ZERO_ERROR) {
    // If the call failed, assume a paper size of 8.5 x 11 inches.
    LOG(WARNING) << "ulocdata_getPaperSize failed, using 8.5 x 11, error: "
                 << error;
    width = static_cast<int>(8.5 * dpi);
    height = static_cast<int>(11 * dpi);
  } else {
    // ulocdata_getPaperSize returns the width and height in mm.
    // Convert this to pixels based on the dpi.
    width = static_cast<int>(ConvertUnitDouble(width, 25.4, 1.0) * dpi);
    height = static_cast<int>(ConvertUnitDouble(height, 25.4, 1.0) * dpi);
  }

  physical_size_device_units.SetSize(width, height);
  printable_area_device_units.SetRect(
      static_cast<int>(PrintSettingsInitializerGtk::kLeftMarginInInch * dpi),
      static_cast<int>(PrintSettingsInitializerGtk::kTopMarginInInch * dpi),
      width - (PrintSettingsInitializerGtk::kLeftMarginInInch +
          PrintSettingsInitializerGtk::kRightMarginInInch) * dpi,
      height - (PrintSettingsInitializerGtk::kTopMarginInInch +
          PrintSettingsInitializerGtk::kBottomMarginInInch) * dpi);

  settings_.set_dpi(dpi);
  settings_.SetPrinterPrintableArea(physical_size_device_units,
                                    printable_area_device_units,
                                    dpi);
#else  // defined(OS_CHROMEOS)
  GtkWidget* dialog = gtk_print_unix_dialog_new(NULL, NULL);
  GtkPrintSettings* settings =
      gtk_print_unix_dialog_get_settings(GTK_PRINT_UNIX_DIALOG(dialog));
  GtkPageSetup* page_setup =
      gtk_print_unix_dialog_get_page_setup(GTK_PRINT_UNIX_DIALOG(dialog));

  PageRanges ranges_vector;  // Nothing to initialize for default settings.
  PrintSettingsInitializerGtk::InitPrintSettings(
          settings, page_setup, ranges_vector, false, &settings_);

  g_object_unref(settings);
  // |page_setup| is owned by dialog, so it does not need to be unref'ed.
  gtk_widget_destroy(dialog);
#endif  // defined(OS_CHROMEOS)

  return OK;
}

PrintingContext::Result PrintingContextCairo::UpdatePrintSettings(
    const DictionaryValue& job_settings, const PageRanges& ranges) {
  DCHECK(!in_print_job_);

  settings_.ranges = ranges;

  // TODO(kmadhusu): Update other print settings such as number of copies,
  // collate, duplex printing, etc.,

  return OK;
}

PrintingContext::Result PrintingContextCairo::InitWithSettings(
    const PrintSettings& settings) {
  DCHECK(!in_print_job_);

  settings_ = settings;

  return OK;
}

PrintingContext::Result PrintingContextCairo::NewDocument(
    const string16& document_name) {
  DCHECK(!in_print_job_);
  in_print_job_ = true;

#if !defined(OS_CHROMEOS)
  document_name_ = document_name;
#endif  // !defined(OS_CHROMEOS)

  return OK;
}

PrintingContext::Result PrintingContextCairo::NewPage() {
  if (abort_printing_)
    return CANCEL;
  DCHECK(in_print_job_);

  // Intentional No-op.

  return OK;
}

PrintingContext::Result PrintingContextCairo::PageDone() {
  if (abort_printing_)
    return CANCEL;
  DCHECK(in_print_job_);

  // Intentional No-op.

  return OK;
}

PrintingContext::Result PrintingContextCairo::DocumentDone() {
  if (abort_printing_)
    return CANCEL;
  DCHECK(in_print_job_);

  ResetSettings();
  return OK;
}

void PrintingContextCairo::Cancel() {
  abort_printing_ = true;
  in_print_job_ = false;
}

void PrintingContextCairo::ReleaseContext() {
  // Intentional No-op.
}

gfx::NativeDrawingContext PrintingContextCairo::context() const {
  // Intentional No-op.
  return NULL;
}

}  // namespace printing
<|endoftext|>