<commit_before>/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

/// This files contains a pipeline which converts HLO operations to GPU kernels
/// written in a combination of LLVM and NVVM dialects.

#include "mlir-hlo/Dialect/gml_st/transforms/passes.h"
#include "mlir-hlo/Dialect/mhlo/transforms/passes.h"
#include "mlir-hlo/Transforms/gpu_passes.h"
#include "mlir-hlo/Transforms/passes.h"
#include "mlir/Conversion/AffineToStandard/AffineToStandard.h"
#include "mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h"
#include "mlir/Conversion/ShapeToStandard/ShapeToStandard.h"
#include "mlir/Dialect/Arith/Transforms/Passes.h"
#include "mlir/Dialect/Bufferization/Transforms/Passes.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/GPU/Transforms/Passes.h"
#include "mlir/Dialect/Linalg/Passes.h"
#include "mlir/Dialect/SCF/Transforms/Passes.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Pass/PassManager.h"
#include "mlir/Transforms/Passes.h"

using namespace mlir;
using ::mlir::func::FuncOp;
using ::mlir::gpu::GPUModuleOp;

// TODO(b/233761238): We only want to have this pipeline temporarily, as it is
// not yet clear how exactly it will look like. The goal is to merge this with
// the unified kernel generator + autofusion + XLA Next pipeline once we have
// it, and once this code stabilizes.
void mlir::createHloToGpuPipeline(OpPassManager& pm,
                                  const HloToGpuPipelineOptions& options) {
  pm.addNestedPass<FuncOp>(hlo::createUnbufferizePass());

  // HLO -> Linalg
  pm.addNestedPass<FuncOp>(mhlo::createChloLegalizeToHloPass());
  pm.addPass(createCanonicalizerPass());  // Clean up shape.assuming ops.
  pm.addNestedPass<FuncOp>(mhlo::createLegalizeHloToLinalgPass());

  // Perform tiling either for softmax or for element-wise.
  if (options.experimentalSoftmax) {
    // Simplify unit dimension.
    pm.addPass(mlir::createLinalgFoldUnitExtentDimsPass());

    // Tile parallel dimensions of the softmax-like patterns and distribute them
    // across warps. Warps remain independant of each other.
    pm.addNestedPass<FuncOp>(gml_st::createTilingSoftmaxPass(
        /*distribute=*/true, options.blockTileDim, "block"));
    pm.addNestedPass<FuncOp>(gml_st::createTilingSoftmaxPass(
        /*distribute=*/true, options.warpTileDim, "warp"));

    // GPU-specific tiling for ops on the warp level.
    pm.addNestedPass<FuncOp>(gml_st::createTilingGPUWarpPass());
    pm.addNestedPass<FuncOp>(createScalarizationPass());

    // Clean unit dims.
    pm.addPass(mlir::createLinalgFoldUnitExtentDimsPass());

    // GPU-specific tiling for cwise ops on the warp level.
    // TODO(frgossen): This should be merged with the above tiling-reduction
    // pass.
    pm.addNestedPass<FuncOp>(gml_st::createTilingGPUWarpPass());
    pm.addNestedPass<FuncOp>(createScalarizationPass());

    pm.addNestedPass<FuncOp>(gml_st::createVectorizeGmlStLoopsPass(
        /*vectorizeGmlStOps=*/true, /*distributionLabels=*/{"warp", "thread"}));
  } else {
    // TODO(b/244313563): This is a workaround to avoid temporary allocs within
    // threads. It works for as long as all of our operations are cwise.
    // Vectorize the inner loops instead.
    // TODO(frgossen): We should not have to skip this pass for softmax.
    pm.addNestedPass<FuncOp>(createLinalgElementwiseOpFusionPass());

    // Tiling
    pm.addNestedPass<FuncOp>(gml_st::createTilingCwisePass(
        /*distribute=*/true, options.blockTileDim, "block"));
    pm.addNestedPass<FuncOp>(gml_st::createTilingCwisePass(
        /*distribute=*/true, options.warpTileDim, "warp"));
    pm.addNestedPass<FuncOp>(gml_st::createTilingCwisePass(
        /*distribute=*/true, options.threadTileDim, "thread"));
    pm.addNestedPass<FuncOp>(createScalarizationPass());
  }

  pm.addPass(createCanonicalizerPass());
  pm.addPass(createCSEPass());

  // Bufferization-related passes.
  pm.addNestedPass<FuncOp>(bufferization::createEmptyTensorToAllocTensorPass());
  pm.addPass(hlo::createOneShotBufferizePass());
  // We do not deallocate buffers, since grid-level buffers get converted into
  // functions arguments, while block- (and lower-)level buffers become shared
  // memory. None of which have to be deallocated.
  pm.addPass(createCanonicalizerPass());
  pm.addPass(createCSEPass());
  // Canonicalize away memory copies into itself
  pm.addPass(createCanonicalizerPass());

  // Linalg + GmlSt -> GPU
  pm.addNestedPass<FuncOp>(createGmlStToGpuPass());
  pm.addNestedPass<FuncOp>(gml_st::createGmlStToScfPass());
  pm.addNestedPass<FuncOp>(arith::createArithExpandOpsPass());
  pm.addNestedPass<FuncOp>(createConvertLinalgToLoopsPass());
  pm.addNestedPass<FuncOp>(createCanonicalizerPass());
  pm.addPass(createGpuLauchSinkIndexComputationsPass());
  constexpr llvm::StringRef kGpuDataLayoutSpec =
      "#dlti.dl_spec<#dlti.dl_entry<index,32:i32>>";
  pm.addPass(createGpuKernelOutliningPass(kGpuDataLayoutSpec));
  pm.addNestedPass<GPUModuleOp>(createForLoopSpecializationPass());
  pm.addNestedPass<GPUModuleOp>(hlo::createUnrollLoopsPass());
  pm.addNestedPass<GPUModuleOp>(createLowerAffinePass());
  pm.addNestedPass<GPUModuleOp>(createCanonicalizerPass());
  pm.addNestedPass<GPUModuleOp>(createConvertSCFToCFPass());

  // GPU -> low-level IR
#if TENSORFLOW_USE_ROCM
  pm.addNestedPass<GPUModuleOp>(createGpuKernelToRocdlPass());
#else
  pm.addNestedPass<GPUModuleOp>(createGpuKernelToNvvmPass());
#endif
  pm.addPass(createPropagateStaticShapesToKernelPass());
  pm.addNestedPass<GPUModuleOp>(createCSEPass());
  // Some instructions crash ptxas down the line if they have debug info
  // attached.
  pm.addNestedPass<GPUModuleOp>(createStripDebugInfoPass());
  pm.addNestedPass<FuncOp>(hlo::createAllocToArgPass());
}
<commit_msg>[GML][Softmax] Clean up warp-level tilng in the GPU pipeline<commit_after>/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

/// This files contains a pipeline which converts HLO operations to GPU kernels
/// written in a combination of LLVM and NVVM dialects.

#include "mlir-hlo/Dialect/gml_st/transforms/passes.h"
#include "mlir-hlo/Dialect/mhlo/transforms/passes.h"
#include "mlir-hlo/Transforms/gpu_passes.h"
#include "mlir-hlo/Transforms/passes.h"
#include "mlir/Conversion/AffineToStandard/AffineToStandard.h"
#include "mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h"
#include "mlir/Conversion/ShapeToStandard/ShapeToStandard.h"
#include "mlir/Dialect/Arith/Transforms/Passes.h"
#include "mlir/Dialect/Bufferization/Transforms/Passes.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/GPU/Transforms/Passes.h"
#include "mlir/Dialect/Linalg/Passes.h"
#include "mlir/Dialect/SCF/Transforms/Passes.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Pass/PassManager.h"
#include "mlir/Transforms/Passes.h"

using namespace mlir;
using ::mlir::func::FuncOp;
using ::mlir::gpu::GPUModuleOp;

// TODO(b/233761238): We only want to have this pipeline temporarily, as it is
// not yet clear how exactly it will look like. The goal is to merge this with
// the unified kernel generator + autofusion + XLA Next pipeline once we have
// it, and once this code stabilizes.
void mlir::createHloToGpuPipeline(OpPassManager& pm,
                                  const HloToGpuPipelineOptions& options) {
  pm.addNestedPass<FuncOp>(hlo::createUnbufferizePass());

  // HLO -> Linalg
  pm.addNestedPass<FuncOp>(mhlo::createChloLegalizeToHloPass());
  pm.addPass(createCanonicalizerPass());  // Clean up shape.assuming ops.
  pm.addNestedPass<FuncOp>(mhlo::createLegalizeHloToLinalgPass());

  // Perform tiling either for softmax or for element-wise.
  if (options.experimentalSoftmax) {
    // Simplify unit dimension.
    pm.addPass(mlir::createLinalgFoldUnitExtentDimsPass());

    // Tile parallel dimensions of the softmax-like patterns and distribute them
    // across warps. Warps remain independant of each other.
    pm.addNestedPass<FuncOp>(gml_st::createTilingSoftmaxPass(
        /*distribute=*/true, options.blockTileDim, "block"));
    pm.addNestedPass<FuncOp>(gml_st::createTilingSoftmaxPass(
        /*distribute=*/true, options.warpTileDim, "warp"));

    // GPU-specific tiling for ops on the warp level.
    pm.addNestedPass<FuncOp>(gml_st::createTilingGPUWarpPass());
    pm.addNestedPass<FuncOp>(createScalarizationPass());

    pm.addNestedPass<FuncOp>(gml_st::createVectorizeGmlStLoopsPass(
        /*vectorizeGmlStOps=*/true, /*distributionLabels=*/{"warp", "thread"}));
  } else {
    // TODO(b/244313563): This is a workaround to avoid temporary allocs within
    // threads. It works for as long as all of our operations are cwise.
    // Vectorize the inner loops instead.
    // TODO(frgossen): We should not have to skip this pass for softmax.
    pm.addNestedPass<FuncOp>(createLinalgElementwiseOpFusionPass());

    // Tiling
    pm.addNestedPass<FuncOp>(gml_st::createTilingCwisePass(
        /*distribute=*/true, options.blockTileDim, "block"));
    pm.addNestedPass<FuncOp>(gml_st::createTilingCwisePass(
        /*distribute=*/true, options.warpTileDim, "warp"));
    pm.addNestedPass<FuncOp>(gml_st::createTilingCwisePass(
        /*distribute=*/true, options.threadTileDim, "thread"));
    pm.addNestedPass<FuncOp>(createScalarizationPass());
  }

  pm.addPass(createCanonicalizerPass());
  pm.addPass(createCSEPass());

  // Bufferization-related passes.
  pm.addNestedPass<FuncOp>(bufferization::createEmptyTensorToAllocTensorPass());
  pm.addPass(hlo::createOneShotBufferizePass());
  // We do not deallocate buffers, since grid-level buffers get converted into
  // functions arguments, while block- (and lower-)level buffers become shared
  // memory. None of which have to be deallocated.
  pm.addPass(createCanonicalizerPass());
  pm.addPass(createCSEPass());
  // Canonicalize away memory copies into itself
  pm.addPass(createCanonicalizerPass());

  // Linalg + GmlSt -> GPU
  pm.addNestedPass<FuncOp>(createGmlStToGpuPass());
  pm.addNestedPass<FuncOp>(gml_st::createGmlStToScfPass());
  pm.addNestedPass<FuncOp>(arith::createArithExpandOpsPass());
  pm.addNestedPass<FuncOp>(createConvertLinalgToLoopsPass());
  pm.addNestedPass<FuncOp>(createCanonicalizerPass());
  pm.addPass(createGpuLauchSinkIndexComputationsPass());
  constexpr llvm::StringRef kGpuDataLayoutSpec =
      "#dlti.dl_spec<#dlti.dl_entry<index,32:i32>>";
  pm.addPass(createGpuKernelOutliningPass(kGpuDataLayoutSpec));
  pm.addNestedPass<GPUModuleOp>(createForLoopSpecializationPass());
  pm.addNestedPass<GPUModuleOp>(hlo::createUnrollLoopsPass());
  pm.addNestedPass<GPUModuleOp>(createLowerAffinePass());
  pm.addNestedPass<GPUModuleOp>(createCanonicalizerPass());
  pm.addNestedPass<GPUModuleOp>(createConvertSCFToCFPass());

  // GPU -> low-level IR
#if TENSORFLOW_USE_ROCM
  pm.addNestedPass<GPUModuleOp>(createGpuKernelToRocdlPass());
#else
  pm.addNestedPass<GPUModuleOp>(createGpuKernelToNvvmPass());
#endif
  pm.addPass(createPropagateStaticShapesToKernelPass());
  pm.addNestedPass<GPUModuleOp>(createCSEPass());
  // Some instructions crash ptxas down the line if they have debug info
  // attached.
  pm.addNestedPass<GPUModuleOp>(createStripDebugInfoPass());
  pm.addNestedPass<FuncOp>(hlo::createAllocToArgPass());
}
<|endoftext|>


<commit_before>/*
 * qlibxmlnodemodel - A QAbstractXmlNodeModel that uses libxml2 library
 * Copyright (C) 2012 Alexey Torkhov
 * Copyright (C) 2011 Jonas Gehring
 * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved.
 *
 * Based on qhtmlnodemodel by Jonas Gehring and QT examples
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the copyright holders nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <QDebug>

#include <libxml/HTMLparser.h>
#include <libxml/tree.h>

#include "qlibxmlnodemodel.h"

// Internal private data
class QLibXmlNodeModelPrivate
{
public:
    QLibXmlNodeModel *model;
    QUrl uri;

    xmlDoc *doc;
    xmlNode *root_element;

    QLibXmlNodeModelPrivate(QLibXmlNodeModel *model)
        : model(model), doc(NULL), root_element(NULL)
    {
    }

    // Parses the given source tree
    void parse(const QByteArray &source)
    {
        doc = htmlReadMemory(source.data(), source.size(), uri.toString().toUtf8(), "utf-8", 0);

        if (doc == NULL) {
            qDebug() << "could not parse source" << source;
        }

        root_element = xmlDocGetRootElement(doc);
    }

    // Converts a model index to a HTML node
    xmlNode *toNode(const QXmlNodeModelIndex &index) const
    {
        return (xmlNode *)index.internalPointer();
    }

    // Converts a HTML node to a model index
    QXmlNodeModelIndex toNodeIndex(xmlNode *node) const
    {
        return model->createIndex((void *)node);
    }
    QXmlNodeModelIndex toNodeIndex(xmlDoc *node) const
    {
        return model->createIndex((void *)node);
    }
    QXmlNodeModelIndex toNodeIndex(xmlAttr *node) const
    {
        return model->createIndex((void *)node);
    }
};

void libXmlErrorFunc(void *ctx, const char *msg, ...)
{
   char buf[1024];
   va_list args;
   va_start(args, msg);
   vsnprintf(buf, 1024, msg, args);
   va_end(args);

   qDebug() << buf;
   return;
}

/*!
 * Constructor passes \a pool to the base class
 */
QLibXmlNodeModel::QLibXmlNodeModel(const QXmlNamePool& namePool, const QByteArray &source, const QUrl &uri)
    : QSimpleXmlNodeModel(namePool), d(new QLibXmlNodeModelPrivate(this))
{
    xmlSetGenericErrorFunc(NULL, libXmlErrorFunc);

    d->uri = uri;
    d->parse(source);
}

/*!
 * Destructor
 */
QLibXmlNodeModel::~QLibXmlNodeModel()
{
    delete d;
}

/*!
 * This function is called by the QtXmlPatterns query engine when it
 * wants to move to the next node in the model. It moves along an \a
 * axis, \e from the node specified by \a nodeIndex.
 */
QXmlNodeModelIndex
QLibXmlNodeModel::nextFromSimpleAxis(SimpleAxis axis, const QXmlNodeModelIndex &nodeIndex) const
{
    xmlNode *node = d->toNode(nodeIndex);
    qDebug() << "nextFromSimpleAxis()" << node << axis;
    if (!node) {
        qDebug() << "Invalid node";
        return QXmlNodeModelIndex();
    }

    switch (axis) {
        case Parent:
            qDebug() << "Parent " << node->parent;
            if (!node->parent) {
                return QXmlNodeModelIndex();
            }
            return d->toNodeIndex(node->parent);

        case FirstChild:
            qDebug() << "Child " << node->children;
            if (!node->children) {
                return QXmlNodeModelIndex();
            }
            return d->toNodeIndex(node->children);

        case PreviousSibling:
            qDebug() << "Prev " << node->prev;
            if (!node->prev) {
                return QXmlNodeModelIndex();
            }
            return d->toNodeIndex(node->prev);

        case NextSibling:
            qDebug() << "Next " << node->next;
            if (!node->next) {
                return QXmlNodeModelIndex();
            }
            return d->toNodeIndex(node->next);
    }

    Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid axis!");
    return QXmlNodeModelIndex();
}

/*!
 * Returns the document URI of \a n. The document URI identifies the
 * resource which is the document. For example, the document could be a
 * regular file, e.g., \c{file:/}, or it could be the \c{http://} URL of
 * the location of a file. The document URI is used for resolving URIs
 * and to simply know where the document is.
 */
QUrl QLibXmlNodeModel::documentUri(const QXmlNodeModelIndex &node) const
{
    Q_UNUSED(node);
    return d->uri;
}

/*!
 * Returns a value indicating the kind of node identified by \a ni.
 * The caller guarantees that \a ni is not null and that it identifies
 * a node in this node model. This function maps to the \c
 * dm:node-kind() accessor.
 */
QXmlNodeModelIndex::NodeKind
QLibXmlNodeModel::kind(const QXmlNodeModelIndex &nodeIndex) const
{
    xmlNode *node = d->toNode(nodeIndex);
    qDebug() << "kind()" << node;
    if (!node) {
        qDebug() << "Invalid node";
        return QXmlNodeModelIndex::Element;
    }

    qDebug() << "Node type" << node->type;

    switch (node->type) {
        case XML_ELEMENT_NODE:
            return QXmlNodeModelIndex::Element;
        case XML_ATTRIBUTE_NODE:
            return QXmlNodeModelIndex::Attribute;
        case XML_TEXT_NODE:
            return QXmlNodeModelIndex::Text;
        case XML_COMMENT_NODE:
            return QXmlNodeModelIndex::Comment;
        case XML_DOCUMENT_NODE:
        case XML_HTML_DOCUMENT_NODE:
            return QXmlNodeModelIndex::Document;
        case XML_NAMESPACE_DECL:
            return QXmlNodeModelIndex::Namespace;
        case XML_PI_NODE:
            return QXmlNodeModelIndex::ProcessingInstruction;
    }

    qDebug() << "Invalid node type" << node->type;
    return QXmlNodeModelIndex::Element;
}

/*!
 * This function returns the relative document order for the
 * nodes indexed by \a ni1 and \a ni2. It is used for the \c Is
 * operator and for sorting nodes in document order.
 */
QXmlNodeModelIndex::DocumentOrder QLibXmlNodeModel::compareOrder(const QXmlNodeModelIndex &nodeIndex1, const QXmlNodeModelIndex &nodeIndex2) const
{
    xmlNode *node1 = d->toNode(nodeIndex1);
    xmlNode *node2 = d->toNode(nodeIndex2);
    qDebug() << "compareOrder()" << node1 << node2;
    return QXmlNodeModelIndex::Precedes;
}

/*!
 * Returns the name of \a node. The caller guarantees that \a node is
 * not null and that it is contained in this node model.
 */
QXmlName QLibXmlNodeModel::name(const QXmlNodeModelIndex &nodeIndex) const
{
    xmlNode *node = d->toNode(nodeIndex);
    qDebug() << "name()" << node;
    if (!node) {
        qDebug() << "Invalid node";
        return QXmlName(namePool(), QString());
    }

    qDebug() << "Node name" << (const char *)node->name;

    return QXmlName(namePool(), QString((const char *)node->name));
}

/*!
 * Returns the root node of the tree that contains the node whose index
 * is \a n. The caller guarantees that \a n is not \c null and that it
 * identifies a node in this node model.
 */
QXmlNodeModelIndex QLibXmlNodeModel::root(const QXmlNodeModelIndex &nodeIndex) const
{
    Q_UNUSED(nodeIndex);
    return d->toNodeIndex(d->doc);
}

/*!
 * Returns the typed value for \a node, which must be either an
 * attribute or an element. The QVariant returned represents the atomic
 * value of an attribute or the atomic value contained in an element.
 *
 * If the QVariant is returned as a default constructed variant,
 * it means that \a node has no typed value.
 */
QVariant QLibXmlNodeModel::typedValue(const QXmlNodeModelIndex &nodeIndex) const
{
    xmlNode *node = d->toNode(nodeIndex);
    qDebug() << "typedValue()" << node;
    if (!node) {
        qDebug() << "Invalid node";
        return QVariant();
    }

    if (node->type == XML_ATTRIBUTE_NODE) {
        xmlChar *buf = xmlNodeListGetString(node->doc, node->children, 1);
        QString str = (const char *)buf;
        xmlFree(buf);

        qDebug() << "Attribute typed value" << str;
        return str;
    }

    qDebug() << "Node typed value" << (const char *)node->name;
    return QString((const char *)node->name);
}

/*!
 * Returns the attributes of \a element. The caller guarantees
 * that \a element is an element in this node model.
 */
QVector<QXmlNodeModelIndex> QLibXmlNodeModel::attributes(const QXmlNodeModelIndex &nodeIndex) const
{
    xmlNode *node = d->toNode(nodeIndex);
    qDebug() << "attributes()" << node;

    QVector<QXmlNodeModelIndex> result;
    for (xmlAttr *cur = node->properties; cur != NULL; cur = cur->next) {
        result += d->toNodeIndex(cur);
    }

    return result;
}

/*!
 * Returns the string value for node \a n.
 *
 * The caller guarantees that \a n is not \c null and that it belong to
 * this QAbstractXmlNodeModel instance.
 *
 * This function maps to the \c dm:string-value() accessor, which the
 * specification completely specifies. Here's a summary:
 *
 * \list
 * \o For processing instructions, the string value is the data
 * section(excluding any whitespace appearing between the name and the
 * data).
 * \o For text nodes, the string value equals the text node.
 * \o For comments, the content of the comment
 * \o For elements, the concatenation of all text nodes that are
 * descendants. Note, this is not only the children, but the
 * childrens' childrens' text nodes, and so forth.
 * \o For document nodes, the concatenation of all text nodes in the
 * document.
 * \endlist
 */
QString QLibXmlNodeModel::stringValue (const QXmlNodeModelIndex &nodeIndex) const
{
    xmlNode *node = d->toNode(nodeIndex);
    qDebug() << "stringValue()" << node;

    if (node->type == XML_TEXT_NODE) {
        xmlChar *buf = xmlNodeGetContent(node);
        QString str((const char *)buf);
        xmlFree(buf);
        return str;
    }

    return QSimpleXmlNodeModel::stringValue(nodeIndex);
}
<commit_msg>Various fixes.<commit_after>/*
 * qlibxmlnodemodel - A QAbstractXmlNodeModel that uses libxml2 library
 * Copyright (C) 2012 Alexey Torkhov
 * Copyright (C) 2011 Jonas Gehring
 * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved.
 *
 * Based on qhtmlnodemodel by Jonas Gehring and QT examples
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the copyright holders nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <QDebug>

#include <libxml/HTMLparser.h>
#include <libxml/tree.h>

#include "qlibxmlnodemodel.h"

// Internal private data
class QLibXmlNodeModelPrivate
{
public:
    QLibXmlNodeModel *model;
    QUrl uri;

    xmlDoc *doc;

    QLibXmlNodeModelPrivate(QLibXmlNodeModel *model)
        : model(model), doc(NULL)
    {
    }

    ~QLibXmlNodeModelPrivate()
    {
        xmlFreeDoc(doc);
        doc = NULL;
    }

    // Parses the given source tree
    void parse(const QByteArray &source)
    {
        doc = htmlReadMemory(source.data(), source.size(), uri.toString().toUtf8(), "utf-8", 0);

        if (doc == NULL) {
            qDebug() << "could not parse source" << source;
        }
    }

    // Converts a model index to a HTML node
    xmlNode *toNode(const QXmlNodeModelIndex &index) const
    {
        return (xmlNode *)index.internalPointer();
    }

    // Converts a HTML node to a model index
    QXmlNodeModelIndex toNodeIndex(xmlNode *node) const
    {
        return model->createIndex((void *)node);
    }
    QXmlNodeModelIndex toNodeIndex(xmlDoc *node) const
    {
        return model->createIndex((void *)node);
    }
    QXmlNodeModelIndex toNodeIndex(xmlAttr *node) const
    {
        return model->createIndex((void *)node);
    }
};

void libXmlErrorFunc(void *ctx, const char *msg, ...)
{
   char buf[1024];
   va_list args;
   va_start(args, msg);
   vsnprintf(buf, 1024, msg, args);
   va_end(args);

   qDebug() << buf;
   return;
}

/*!
 * Constructor passes \a pool to the base class
 */
QLibXmlNodeModel::QLibXmlNodeModel(const QXmlNamePool& namePool, const QByteArray &source, const QUrl &uri)
    : QSimpleXmlNodeModel(namePool), d(new QLibXmlNodeModelPrivate(this))
{
    xmlSetGenericErrorFunc(NULL, libXmlErrorFunc);

    d->uri = uri;
    d->parse(source);
}

/*!
 * Destructor
 */
QLibXmlNodeModel::~QLibXmlNodeModel()
{
    delete d;
}

/*!
 * This function is called by the QtXmlPatterns query engine when it
 * wants to move to the next node in the model. It moves along an \a
 * axis, \e from the node specified by \a nodeIndex.
 */
QXmlNodeModelIndex
QLibXmlNodeModel::nextFromSimpleAxis(SimpleAxis axis, const QXmlNodeModelIndex &nodeIndex) const
{
    xmlNode *node = d->toNode(nodeIndex);
    qDebug() << "nextFromSimpleAxis()" << node << axis;
    if (!node) {
        qDebug() << "Invalid node";
        return QXmlNodeModelIndex();
    }

    switch (axis) {
        case Parent:
            qDebug() << "Parent " << node->parent;
            if (!node->parent) {
                return QXmlNodeModelIndex();
            }
            return d->toNodeIndex(node->parent);

        case FirstChild:
            qDebug() << "Child " << node->children;
            if (!node->children) {
                return QXmlNodeModelIndex();
            }
            return d->toNodeIndex(node->children);

        case PreviousSibling:
            qDebug() << "Prev " << node->prev;
            if (!node->prev) {
                return QXmlNodeModelIndex();
            }
            return d->toNodeIndex(node->prev);

        case NextSibling:
            qDebug() << "Next " << node->next;
            if (!node->next) {
                return QXmlNodeModelIndex();
            }
            return d->toNodeIndex(node->next);
    }

    Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid axis!");
    return QXmlNodeModelIndex();
}

/*!
 * Returns the document URI of \a n. The document URI identifies the
 * resource which is the document. For example, the document could be a
 * regular file, e.g., \c{file:/}, or it could be the \c{http://} URL of
 * the location of a file. The document URI is used for resolving URIs
 * and to simply know where the document is.
 */
QUrl QLibXmlNodeModel::documentUri(const QXmlNodeModelIndex &node) const
{
    Q_UNUSED(node);
    return d->uri;
}

/*!
 * Returns a value indicating the kind of node identified by \a ni.
 * The caller guarantees that \a ni is not null and that it identifies
 * a node in this node model. This function maps to the \c
 * dm:node-kind() accessor.
 */
QXmlNodeModelIndex::NodeKind
QLibXmlNodeModel::kind(const QXmlNodeModelIndex &nodeIndex) const
{
    xmlNode *node = d->toNode(nodeIndex);
    qDebug() << "kind()" << node;
    if (!node) {
        qDebug() << "Invalid node";
        return QXmlNodeModelIndex::Element;
    }

    qDebug() << "Node type" << node->type;

    switch (node->type) {
        case XML_ELEMENT_NODE:
            return QXmlNodeModelIndex::Element;
        case XML_ATTRIBUTE_NODE:
            return QXmlNodeModelIndex::Attribute;
        case XML_TEXT_NODE:
        case XML_CDATA_SECTION_NODE:
            return QXmlNodeModelIndex::Text;
        case XML_COMMENT_NODE:
            return QXmlNodeModelIndex::Comment;
        case XML_DOCUMENT_NODE:
        case XML_HTML_DOCUMENT_NODE:
            return QXmlNodeModelIndex::Document;
        case XML_NAMESPACE_DECL:
            return QXmlNodeModelIndex::Namespace;
        case XML_PI_NODE:
            return QXmlNodeModelIndex::ProcessingInstruction;
    }

    qDebug() << "Invalid node type" << node->type;
    return QXmlNodeModelIndex::Element;
}

/*!
 * This function returns the relative document order for the
 * nodes indexed by \a ni1 and \a ni2. It is used for the \c Is
 * operator and for sorting nodes in document order.
 */
QXmlNodeModelIndex::DocumentOrder QLibXmlNodeModel::compareOrder(const QXmlNodeModelIndex &nodeIndex1, const QXmlNodeModelIndex &nodeIndex2) const
{
    xmlNode *node1 = d->toNode(nodeIndex1);
    xmlNode *node2 = d->toNode(nodeIndex2);
    qDebug() << "compareOrder()" << node1 << node2;
    return QXmlNodeModelIndex::Precedes;
}

/*!
 * Returns the name of \a node. The caller guarantees that \a node is
 * not null and that it is contained in this node model.
 */
QXmlName QLibXmlNodeModel::name(const QXmlNodeModelIndex &nodeIndex) const
{
    xmlNode *node = d->toNode(nodeIndex);
    qDebug() << "name()" << node;
    if (!node) {
        qDebug() << "Invalid node";
        return QXmlName(namePool(), QString());
    }

    qDebug() << "Node name" << (const char *)node->name;
    return QXmlName(namePool(), QString((const char *)node->name));
}

/*!
 * Returns the root node of the tree that contains the node whose index
 * is \a n. The caller guarantees that \a n is not \c null and that it
 * identifies a node in this node model.
 */
QXmlNodeModelIndex QLibXmlNodeModel::root(const QXmlNodeModelIndex &nodeIndex) const
{
    Q_UNUSED(nodeIndex);
    return d->toNodeIndex(d->doc);
}

/*!
 * Returns the typed value for \a node, which must be either an
 * attribute or an element. The QVariant returned represents the atomic
 * value of an attribute or the atomic value contained in an element.
 *
 * If the QVariant is returned as a default constructed variant,
 * it means that \a node has no typed value.
 */
QVariant QLibXmlNodeModel::typedValue(const QXmlNodeModelIndex &nodeIndex) const
{
    xmlNode *node = d->toNode(nodeIndex);
    qDebug() << "typedValue()" << node;
    if (!node) {
        qDebug() << "Invalid node";
        return QVariant();
    }

    if (node->type == XML_ATTRIBUTE_NODE) {
        xmlChar *buf = xmlNodeListGetString(node->doc, node->children, 1);
        QString str = (const char *)buf;
        xmlFree(buf);

        qDebug() << "Attribute typed value" << str;
        return str;
    }

    qDebug() << "Node typed value" << (const char *)node->name;
    return QString((const char *)node->name);
}

/*!
 * Returns the attributes of \a element. The caller guarantees
 * that \a element is an element in this node model.
 */
QVector<QXmlNodeModelIndex> QLibXmlNodeModel::attributes(const QXmlNodeModelIndex &nodeIndex) const
{
    xmlNode *node = d->toNode(nodeIndex);
    qDebug() << "attributes()" << node;

    QVector<QXmlNodeModelIndex> result;
    for (xmlAttr *cur = node->properties; cur != NULL; cur = cur->next) {
        result += d->toNodeIndex(cur);
    }

    return result;
}

/*!
 * Returns the string value for node \a n.
 *
 * The caller guarantees that \a n is not \c null and that it belong to
 * this QAbstractXmlNodeModel instance.
 *
 * This function maps to the \c dm:string-value() accessor, which the
 * specification completely specifies. Here's a summary:
 *
 * \list
 * \o For processing instructions, the string value is the data
 * section(excluding any whitespace appearing between the name and the
 * data).
 * \o For text nodes, the string value equals the text node.
 * \o For comments, the content of the comment
 * \o For elements, the concatenation of all text nodes that are
 * descendants. Note, this is not only the children, but the
 * childrens' childrens' text nodes, and so forth.
 * \o For document nodes, the concatenation of all text nodes in the
 * document.
 * \endlist
 */
QString QLibXmlNodeModel::stringValue (const QXmlNodeModelIndex &nodeIndex) const
{
    xmlNode *node = d->toNode(nodeIndex);
    qDebug() << "stringValue()" << node;

    if (node->type == XML_TEXT_NODE ||
        node->type == XML_CDATA_SECTION_NODE ||
        node->type == XML_COMMENT_NODE) {
        xmlChar *buf = xmlNodeGetContent(node);
        QString str((const char *)buf);
        xmlFree(buf);
        return str;
    }

    return QSimpleXmlNodeModel::stringValue(nodeIndex);
}
<|endoftext|>


<commit_before>// Copyright 2014 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/enhanced_bookmarks/android/enhanced_bookmarks_bridge.h"

#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/prefs/pref_service.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
#include "chrome/browser/enhanced_bookmarks/android/bookmark_image_service_android.h"
#include "chrome/browser/enhanced_bookmarks/android/bookmark_image_service_factory.h"
#include "chrome/browser/enhanced_bookmarks/chrome_bookmark_server_cluster_service.h"
#include "chrome/browser/enhanced_bookmarks/chrome_bookmark_server_cluster_service_factory.h"
#include "chrome/browser/enhanced_bookmarks/enhanced_bookmark_model_factory.h"
#include "chrome/browser/profiles/profile_android.h"
#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/pref_names.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/browser/bookmark_utils.h"
#include "components/bookmarks/common/android/bookmark_id.h"
#include "components/bookmarks/common/android/bookmark_type.h"
#include "components/enhanced_bookmarks/enhanced_bookmark_model.h"
#include "components/enhanced_bookmarks/image_record.h"
#include "components/signin/core/browser/signin_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "jni/EnhancedBookmarksBridge_jni.h"
#include "ui/gfx/android/java_bitmap.h"
#include "ui/gfx/image/image.h"

using base::android::AttachCurrentThread;
using base::android::ScopedJavaGlobalRef;
using bookmarks::android::JavaBookmarkIdCreateBookmarkId;
using bookmarks::android::JavaBookmarkIdGetId;
using bookmarks::android::JavaBookmarkIdGetType;
using bookmarks::BookmarkNode;
using bookmarks::BookmarkType;
using content::WebContents;
using content::BrowserThread;
using enhanced_bookmarks::ImageRecord;

namespace {

void Callback(ScopedJavaGlobalRef<jobject>* j_callback,
              const ImageRecord& image_record) {
  JNIEnv* env = base::android::AttachCurrentThread();

  scoped_ptr<ScopedJavaGlobalRef<jobject> > j_callback_ptr(j_callback);
  ScopedJavaLocalRef<jstring> j_url =
      base::android::ConvertUTF8ToJavaString(env, image_record.url.spec());

  SkBitmap bitmap = image_record.image.AsBitmap();
  ScopedJavaLocalRef<jobject> j_bitmap;
  if (!bitmap.isNull()) {
    j_bitmap = gfx::ConvertToJavaBitmap(&bitmap);
  }

  enhanced_bookmarks::android::Java_SalientImageCallback_onSalientImageReady(
      env, j_callback_ptr->obj(), j_bitmap.Release(), j_url.Release());
}

}  // namespace

namespace enhanced_bookmarks {
namespace android {

EnhancedBookmarksBridge::EnhancedBookmarksBridge(JNIEnv* env,
    jobject obj,
    Profile* profile) : weak_java_ref_(env, obj) {
  profile_ = profile;
  enhanced_bookmark_model_ =
      EnhancedBookmarkModelFactory::GetForBrowserContext(profile_);
  enhanced_bookmark_model_->SetVersionSuffix(chrome::VersionInfo().OSType());
  cluster_service_ =
      ChromeBookmarkServerClusterServiceFactory::GetForBrowserContext(profile_);
  cluster_service_->AddObserver(this);
  bookmark_image_service_ = static_cast<BookmarkImageServiceAndroid*>(
      BookmarkImageServiceFactory::GetForBrowserContext(profile_));
  search_service_.reset(new BookmarkServerSearchService(
      profile_->GetRequestContext(),
      ProfileOAuth2TokenServiceFactory::GetForProfile(profile_),
      SigninManagerFactory::GetForProfile(profile_),
      EnhancedBookmarkModelFactory::GetForBrowserContext(profile_)));
  search_service_->AddObserver(this);
}

EnhancedBookmarksBridge::~EnhancedBookmarksBridge() {
  cluster_service_->RemoveObserver(this);
  search_service_->RemoveObserver(this);
}

void EnhancedBookmarksBridge::Destroy(JNIEnv*, jobject) {
  delete this;
}

void EnhancedBookmarksBridge::SalientImageForUrl(JNIEnv* env,
                                                 jobject obj,
                                                 jstring j_url,
                                                 jobject j_callback) {
  DCHECK(j_callback);

  GURL url(base::android::ConvertJavaStringToUTF16(env, j_url));
  scoped_ptr<ScopedJavaGlobalRef<jobject>> j_callback_ptr(
      new ScopedJavaGlobalRef<jobject>());
  j_callback_ptr->Reset(env, j_callback);
  bookmark_image_service_->SalientImageForUrl(
      url, base::Bind(&Callback, j_callback_ptr.release()));
}

void EnhancedBookmarksBridge::FetchImageForTab(JNIEnv* env,
                                               jobject obj,
                                               jobject j_web_contents) {
  WebContents* web_contents = WebContents::FromJavaWebContents(j_web_contents);
  bookmark_image_service_->RetrieveSalientImageFromContext(
      web_contents->GetMainFrame(), web_contents->GetURL(), true);
}

ScopedJavaLocalRef<jstring> EnhancedBookmarksBridge::GetBookmarkDescription(
    JNIEnv* env, jobject obj, jlong id, jint type) {
  DCHECK(enhanced_bookmark_model_->loaded());
  if (type != BookmarkType::BOOKMARK_TYPE_NORMAL) {
    return base::android::ConvertUTF8ToJavaString(env, std::string());
  }

  const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(
      enhanced_bookmark_model_->bookmark_model(), static_cast<int64>(id));

  return node ? base::android::ConvertUTF8ToJavaString(
                    env, enhanced_bookmark_model_->GetDescription(node))
              : ScopedJavaLocalRef<jstring>();
}

void EnhancedBookmarksBridge::SetBookmarkDescription(JNIEnv* env,
                                                     jobject obj,
                                                     jlong id,
                                                     jint type,
                                                     jstring description) {
  DCHECK(enhanced_bookmark_model_->loaded());
  DCHECK_EQ(type, BookmarkType::BOOKMARK_TYPE_NORMAL);

  const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(
      enhanced_bookmark_model_->bookmark_model(), static_cast<int64>(id));

  enhanced_bookmark_model_->SetDescription(
      node, base::android::ConvertJavaStringToUTF8(env, description));
}

ScopedJavaLocalRef<jobjectArray> EnhancedBookmarksBridge::GetFiltersForBookmark(
    JNIEnv* env,
    jobject obj,
    jlong id,
    jint type) {
  DCHECK(enhanced_bookmark_model_->loaded());
  if (type != BookmarkType::BOOKMARK_TYPE_NORMAL) {
    return base::android::ToJavaArrayOfStrings(env, std::vector<std::string>());
  }

  const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(
        enhanced_bookmark_model_->bookmark_model(), static_cast<int64>(id));
  std::vector<std::string> filters =
      cluster_service_->ClustersForBookmark(node);
  return base::android::ToJavaArrayOfStrings(env, filters);
}

void EnhancedBookmarksBridge::GetBookmarksForFilter(JNIEnv* env,
                                                    jobject obj,
                                                    jstring j_filter,
                                                    jobject j_result_obj) {
  DCHECK(enhanced_bookmark_model_->loaded());
  const std::string title =
      base::android::ConvertJavaStringToUTF8(env, j_filter);
  const std::vector<const BookmarkNode*> bookmarks =
      cluster_service_->BookmarksForClusterNamed(title);
  for (const BookmarkNode* node : bookmarks) {
    Java_EnhancedBookmarksBridge_addToBookmarkIdList(
        env, j_result_obj, node->id(), node->type());
  }
}

ScopedJavaLocalRef<jobjectArray> EnhancedBookmarksBridge::GetFilters(
    JNIEnv* env,
    jobject obj) {
  DCHECK(enhanced_bookmark_model_->loaded());
  const std::vector<std::string> filters = cluster_service_->GetClusters();
  return base::android::ToJavaArrayOfStrings(env, filters);
}

ScopedJavaLocalRef<jobject> EnhancedBookmarksBridge::AddFolder(JNIEnv* env,
    jobject obj,
    jobject j_parent_id_obj,
    jint index,
    jstring j_title) {
  DCHECK(enhanced_bookmark_model_->loaded());
  long bookmark_id = JavaBookmarkIdGetId(env, j_parent_id_obj);
  const BookmarkNode* parent = bookmarks::GetBookmarkNodeByID(
      enhanced_bookmark_model_->bookmark_model(),
      static_cast<int64>(bookmark_id));
  const BookmarkNode* new_node = enhanced_bookmark_model_->AddFolder(
      parent, index, base::android::ConvertJavaStringToUTF16(env, j_title));
  if (!new_node) {
    NOTREACHED();
    return ScopedJavaLocalRef<jobject>();
  }
  ScopedJavaLocalRef<jobject> new_java_obj =
      JavaBookmarkIdCreateBookmarkId(env, new_node->id(), new_node->type());
  return new_java_obj;
}

void EnhancedBookmarksBridge::MoveBookmark(JNIEnv* env,
                                           jobject obj,
                                           jobject j_bookmark_id_obj,
                                           jobject j_parent_id_obj) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(enhanced_bookmark_model_->loaded());

  long bookmark_id = JavaBookmarkIdGetId(env, j_bookmark_id_obj);
  const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(
      enhanced_bookmark_model_->bookmark_model(),
      static_cast<int64>(bookmark_id));
  if (!IsEditable(node)) {
    NOTREACHED();
    return;
  }
  bookmark_id = JavaBookmarkIdGetId(env, j_parent_id_obj);
  const BookmarkNode* new_parent_node = bookmarks::GetBookmarkNodeByID(
      enhanced_bookmark_model_->bookmark_model(),
      static_cast<int64>(bookmark_id));
  enhanced_bookmark_model_->Move(node, new_parent_node,
                                 new_parent_node->child_count());
}

ScopedJavaLocalRef<jobject> EnhancedBookmarksBridge::AddBookmark(
    JNIEnv* env,
    jobject obj,
    jobject j_parent_id_obj,
    jint index,
    jstring j_title,
    jstring j_url) {
  DCHECK(enhanced_bookmark_model_->loaded());
  long bookmark_id = JavaBookmarkIdGetId(env, j_parent_id_obj);
  const BookmarkNode* parent = bookmarks::GetBookmarkNodeByID(
      enhanced_bookmark_model_->bookmark_model(),
      static_cast<int64>(bookmark_id));

  const BookmarkNode* new_node = enhanced_bookmark_model_->AddURL(
      parent,
      index,
      base::android::ConvertJavaStringToUTF16(env, j_title),
      GURL(base::android::ConvertJavaStringToUTF16(env, j_url)),
      base::Time::Now());
  if (!new_node) {
    NOTREACHED();
    return ScopedJavaLocalRef<jobject>();
  }
  ScopedJavaLocalRef<jobject> new_java_obj =
      JavaBookmarkIdCreateBookmarkId(env, new_node->id(), new_node->type());
  return new_java_obj;
}

void EnhancedBookmarksBridge::SendSearchRequest(JNIEnv* env,
                                                jobject obj,
                                                jstring j_query) {
  search_service_->Search(base::android::ConvertJavaStringToUTF8(env, j_query));
}

ScopedJavaLocalRef<jobject> EnhancedBookmarksBridge::GetSearchResults(
    JNIEnv* env,
    jobject obj,
    jstring j_query) {
  DCHECK(enhanced_bookmark_model_->loaded());

  ScopedJavaLocalRef<jobject> j_list =
          Java_EnhancedBookmarksBridge_createBookmarkIdList(env);
  scoped_ptr<std::vector<const BookmarkNode*>> results =
      search_service_->ResultForQuery(
          base::android::ConvertJavaStringToUTF8(env, j_query));

  // If result is null, return a null java reference.
  if (!results.get())
    return ScopedJavaLocalRef<jobject>();

  for (const BookmarkNode* node : *results) {
    Java_EnhancedBookmarksBridge_addToBookmarkIdList(env, j_list.obj(),
                                                     node->id(), node->type());
  }
  return j_list;
}

void EnhancedBookmarksBridge::OnChange(BookmarkServerService* service) {
  DCHECK(enhanced_bookmark_model_->loaded());
  JNIEnv* env = AttachCurrentThread();

  ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
  if (obj.is_null())
    return;

  if (service == cluster_service_) {
    Java_EnhancedBookmarksBridge_onFiltersChanged(env, obj.obj());
  } else if (service == search_service_.get()) {
    Java_EnhancedBookmarksBridge_onSearchResultReturned(env, obj.obj());
  }
}

bool EnhancedBookmarksBridge::IsEditable(const BookmarkNode* node) const {
  if (!node || (node->type() != BookmarkNode::FOLDER &&
      node->type() != BookmarkNode::URL)) {
    return false;
  }
  return profile_->GetPrefs()->GetBoolean(
        bookmarks::prefs::kEditBookmarksEnabled);
}

static jlong Init(JNIEnv* env, jobject obj, jobject j_profile) {
  return reinterpret_cast<jlong>(new EnhancedBookmarksBridge(
        env, obj, ProfileAndroid::FromProfileAndroid(j_profile)));
}

bool RegisterEnhancedBookmarksBridge(JNIEnv* env) {
  return RegisterNativesImpl(env);
}

}  // namespace android
}  // namespace enhanced_bookmarks
<commit_msg>Fix bug that AddBookmarks returns partner bookmark node<commit_after>// Copyright 2014 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/enhanced_bookmarks/android/enhanced_bookmarks_bridge.h"

#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/prefs/pref_service.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
#include "chrome/browser/enhanced_bookmarks/android/bookmark_image_service_android.h"
#include "chrome/browser/enhanced_bookmarks/android/bookmark_image_service_factory.h"
#include "chrome/browser/enhanced_bookmarks/chrome_bookmark_server_cluster_service.h"
#include "chrome/browser/enhanced_bookmarks/chrome_bookmark_server_cluster_service_factory.h"
#include "chrome/browser/enhanced_bookmarks/enhanced_bookmark_model_factory.h"
#include "chrome/browser/profiles/profile_android.h"
#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/pref_names.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/browser/bookmark_utils.h"
#include "components/bookmarks/common/android/bookmark_id.h"
#include "components/bookmarks/common/android/bookmark_type.h"
#include "components/enhanced_bookmarks/enhanced_bookmark_model.h"
#include "components/enhanced_bookmarks/image_record.h"
#include "components/signin/core/browser/signin_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "jni/EnhancedBookmarksBridge_jni.h"
#include "ui/gfx/android/java_bitmap.h"
#include "ui/gfx/image/image.h"

using base::android::AttachCurrentThread;
using base::android::ScopedJavaGlobalRef;
using bookmarks::android::JavaBookmarkIdCreateBookmarkId;
using bookmarks::android::JavaBookmarkIdGetId;
using bookmarks::android::JavaBookmarkIdGetType;
using bookmarks::BookmarkNode;
using bookmarks::BookmarkType;
using content::WebContents;
using content::BrowserThread;
using enhanced_bookmarks::ImageRecord;

namespace {

void Callback(ScopedJavaGlobalRef<jobject>* j_callback,
              const ImageRecord& image_record) {
  JNIEnv* env = base::android::AttachCurrentThread();

  scoped_ptr<ScopedJavaGlobalRef<jobject> > j_callback_ptr(j_callback);
  ScopedJavaLocalRef<jstring> j_url =
      base::android::ConvertUTF8ToJavaString(env, image_record.url.spec());

  SkBitmap bitmap = image_record.image.AsBitmap();
  ScopedJavaLocalRef<jobject> j_bitmap;
  if (!bitmap.isNull()) {
    j_bitmap = gfx::ConvertToJavaBitmap(&bitmap);
  }

  enhanced_bookmarks::android::Java_SalientImageCallback_onSalientImageReady(
      env, j_callback_ptr->obj(), j_bitmap.Release(), j_url.Release());
}

}  // namespace

namespace enhanced_bookmarks {
namespace android {

EnhancedBookmarksBridge::EnhancedBookmarksBridge(JNIEnv* env,
    jobject obj,
    Profile* profile) : weak_java_ref_(env, obj) {
  profile_ = profile;
  enhanced_bookmark_model_ =
      EnhancedBookmarkModelFactory::GetForBrowserContext(profile_);
  enhanced_bookmark_model_->SetVersionSuffix(chrome::VersionInfo().OSType());
  cluster_service_ =
      ChromeBookmarkServerClusterServiceFactory::GetForBrowserContext(profile_);
  cluster_service_->AddObserver(this);
  bookmark_image_service_ = static_cast<BookmarkImageServiceAndroid*>(
      BookmarkImageServiceFactory::GetForBrowserContext(profile_));
  search_service_.reset(new BookmarkServerSearchService(
      profile_->GetRequestContext(),
      ProfileOAuth2TokenServiceFactory::GetForProfile(profile_),
      SigninManagerFactory::GetForProfile(profile_),
      EnhancedBookmarkModelFactory::GetForBrowserContext(profile_)));
  search_service_->AddObserver(this);
}

EnhancedBookmarksBridge::~EnhancedBookmarksBridge() {
  cluster_service_->RemoveObserver(this);
  search_service_->RemoveObserver(this);
}

void EnhancedBookmarksBridge::Destroy(JNIEnv*, jobject) {
  delete this;
}

void EnhancedBookmarksBridge::SalientImageForUrl(JNIEnv* env,
                                                 jobject obj,
                                                 jstring j_url,
                                                 jobject j_callback) {
  DCHECK(j_callback);

  GURL url(base::android::ConvertJavaStringToUTF16(env, j_url));
  scoped_ptr<ScopedJavaGlobalRef<jobject>> j_callback_ptr(
      new ScopedJavaGlobalRef<jobject>());
  j_callback_ptr->Reset(env, j_callback);
  bookmark_image_service_->SalientImageForUrl(
      url, base::Bind(&Callback, j_callback_ptr.release()));
}

void EnhancedBookmarksBridge::FetchImageForTab(JNIEnv* env,
                                               jobject obj,
                                               jobject j_web_contents) {
  WebContents* web_contents = WebContents::FromJavaWebContents(j_web_contents);
  bookmark_image_service_->RetrieveSalientImageFromContext(
      web_contents->GetMainFrame(), web_contents->GetURL(), true);
}

ScopedJavaLocalRef<jstring> EnhancedBookmarksBridge::GetBookmarkDescription(
    JNIEnv* env, jobject obj, jlong id, jint type) {
  DCHECK(enhanced_bookmark_model_->loaded());
  if (type != BookmarkType::BOOKMARK_TYPE_NORMAL) {
    return base::android::ConvertUTF8ToJavaString(env, std::string());
  }

  const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(
      enhanced_bookmark_model_->bookmark_model(), static_cast<int64>(id));

  return node ? base::android::ConvertUTF8ToJavaString(
                    env, enhanced_bookmark_model_->GetDescription(node))
              : ScopedJavaLocalRef<jstring>();
}

void EnhancedBookmarksBridge::SetBookmarkDescription(JNIEnv* env,
                                                     jobject obj,
                                                     jlong id,
                                                     jint type,
                                                     jstring description) {
  DCHECK(enhanced_bookmark_model_->loaded());
  DCHECK_EQ(type, BookmarkType::BOOKMARK_TYPE_NORMAL);

  const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(
      enhanced_bookmark_model_->bookmark_model(), static_cast<int64>(id));

  enhanced_bookmark_model_->SetDescription(
      node, base::android::ConvertJavaStringToUTF8(env, description));
}

ScopedJavaLocalRef<jobjectArray> EnhancedBookmarksBridge::GetFiltersForBookmark(
    JNIEnv* env,
    jobject obj,
    jlong id,
    jint type) {
  DCHECK(enhanced_bookmark_model_->loaded());
  if (type != BookmarkType::BOOKMARK_TYPE_NORMAL) {
    return base::android::ToJavaArrayOfStrings(env, std::vector<std::string>());
  }

  const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(
        enhanced_bookmark_model_->bookmark_model(), static_cast<int64>(id));
  std::vector<std::string> filters =
      cluster_service_->ClustersForBookmark(node);
  return base::android::ToJavaArrayOfStrings(env, filters);
}

void EnhancedBookmarksBridge::GetBookmarksForFilter(JNIEnv* env,
                                                    jobject obj,
                                                    jstring j_filter,
                                                    jobject j_result_obj) {
  DCHECK(enhanced_bookmark_model_->loaded());
  const std::string title =
      base::android::ConvertJavaStringToUTF8(env, j_filter);
  const std::vector<const BookmarkNode*> bookmarks =
      cluster_service_->BookmarksForClusterNamed(title);
  for (const BookmarkNode* node : bookmarks) {
    Java_EnhancedBookmarksBridge_addToBookmarkIdList(
        env, j_result_obj, node->id(), node->type());
  }
}

ScopedJavaLocalRef<jobjectArray> EnhancedBookmarksBridge::GetFilters(
    JNIEnv* env,
    jobject obj) {
  DCHECK(enhanced_bookmark_model_->loaded());
  const std::vector<std::string> filters = cluster_service_->GetClusters();
  return base::android::ToJavaArrayOfStrings(env, filters);
}

ScopedJavaLocalRef<jobject> EnhancedBookmarksBridge::AddFolder(JNIEnv* env,
    jobject obj,
    jobject j_parent_id_obj,
    jint index,
    jstring j_title) {
  DCHECK(enhanced_bookmark_model_->loaded());
  long bookmark_id = JavaBookmarkIdGetId(env, j_parent_id_obj);
  const BookmarkNode* parent = bookmarks::GetBookmarkNodeByID(
      enhanced_bookmark_model_->bookmark_model(),
      static_cast<int64>(bookmark_id));
  const BookmarkNode* new_node = enhanced_bookmark_model_->AddFolder(
      parent, index, base::android::ConvertJavaStringToUTF16(env, j_title));
  if (!new_node) {
    NOTREACHED();
    return ScopedJavaLocalRef<jobject>();
  }
  ScopedJavaLocalRef<jobject> new_java_obj = JavaBookmarkIdCreateBookmarkId(
      env, new_node->id(), BookmarkType::BOOKMARK_TYPE_NORMAL);
  return new_java_obj;
}

void EnhancedBookmarksBridge::MoveBookmark(JNIEnv* env,
                                           jobject obj,
                                           jobject j_bookmark_id_obj,
                                           jobject j_parent_id_obj) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(enhanced_bookmark_model_->loaded());

  long bookmark_id = JavaBookmarkIdGetId(env, j_bookmark_id_obj);
  const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(
      enhanced_bookmark_model_->bookmark_model(),
      static_cast<int64>(bookmark_id));
  if (!IsEditable(node)) {
    NOTREACHED();
    return;
  }
  bookmark_id = JavaBookmarkIdGetId(env, j_parent_id_obj);
  const BookmarkNode* new_parent_node = bookmarks::GetBookmarkNodeByID(
      enhanced_bookmark_model_->bookmark_model(),
      static_cast<int64>(bookmark_id));
  enhanced_bookmark_model_->Move(node, new_parent_node,
                                 new_parent_node->child_count());
}

ScopedJavaLocalRef<jobject> EnhancedBookmarksBridge::AddBookmark(
    JNIEnv* env,
    jobject obj,
    jobject j_parent_id_obj,
    jint index,
    jstring j_title,
    jstring j_url) {
  DCHECK(enhanced_bookmark_model_->loaded());
  long bookmark_id = JavaBookmarkIdGetId(env, j_parent_id_obj);
  const BookmarkNode* parent = bookmarks::GetBookmarkNodeByID(
      enhanced_bookmark_model_->bookmark_model(),
      static_cast<int64>(bookmark_id));

  const BookmarkNode* new_node = enhanced_bookmark_model_->AddURL(
      parent,
      index,
      base::android::ConvertJavaStringToUTF16(env, j_title),
      GURL(base::android::ConvertJavaStringToUTF16(env, j_url)),
      base::Time::Now());
  if (!new_node) {
    NOTREACHED();
    return ScopedJavaLocalRef<jobject>();
  }
  ScopedJavaLocalRef<jobject> new_java_obj = JavaBookmarkIdCreateBookmarkId(
      env, new_node->id(), BookmarkType::BOOKMARK_TYPE_NORMAL);
  return new_java_obj;
}

void EnhancedBookmarksBridge::SendSearchRequest(JNIEnv* env,
                                                jobject obj,
                                                jstring j_query) {
  search_service_->Search(base::android::ConvertJavaStringToUTF8(env, j_query));
}

ScopedJavaLocalRef<jobject> EnhancedBookmarksBridge::GetSearchResults(
    JNIEnv* env,
    jobject obj,
    jstring j_query) {
  DCHECK(enhanced_bookmark_model_->loaded());

  ScopedJavaLocalRef<jobject> j_list =
          Java_EnhancedBookmarksBridge_createBookmarkIdList(env);
  scoped_ptr<std::vector<const BookmarkNode*>> results =
      search_service_->ResultForQuery(
          base::android::ConvertJavaStringToUTF8(env, j_query));

  // If result is null, return a null java reference.
  if (!results.get())
    return ScopedJavaLocalRef<jobject>();

  for (const BookmarkNode* node : *results) {
    Java_EnhancedBookmarksBridge_addToBookmarkIdList(env, j_list.obj(),
                                                     node->id(), node->type());
  }
  return j_list;
}

void EnhancedBookmarksBridge::OnChange(BookmarkServerService* service) {
  DCHECK(enhanced_bookmark_model_->loaded());
  JNIEnv* env = AttachCurrentThread();

  ScopedJavaLocalRef<jobject> obj = weak_java_ref_.get(env);
  if (obj.is_null())
    return;

  if (service == cluster_service_) {
    Java_EnhancedBookmarksBridge_onFiltersChanged(env, obj.obj());
  } else if (service == search_service_.get()) {
    Java_EnhancedBookmarksBridge_onSearchResultReturned(env, obj.obj());
  }
}

bool EnhancedBookmarksBridge::IsEditable(const BookmarkNode* node) const {
  if (!node || (node->type() != BookmarkNode::FOLDER &&
      node->type() != BookmarkNode::URL)) {
    return false;
  }
  return profile_->GetPrefs()->GetBoolean(
        bookmarks::prefs::kEditBookmarksEnabled);
}

static jlong Init(JNIEnv* env, jobject obj, jobject j_profile) {
  return reinterpret_cast<jlong>(new EnhancedBookmarksBridge(
        env, obj, ProfileAndroid::FromProfileAndroid(j_profile)));
}

bool RegisterEnhancedBookmarksBridge(JNIEnv* env) {
  return RegisterNativesImpl(env);
}

}  // namespace android
}  // namespace enhanced_bookmarks
<|endoftext|>


<commit_before>#include "optionsdialog.h"
#include "optionsmodel.h"
#include "bitcoinamountfield.h"
#include "monitoreddatamapper.h"
#include "guiutil.h"
#include "bitcoinunits.h"
#include "qvaluecombobox.h"

#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPushButton>
#include <QListWidget>
#include <QStackedWidget>

#include <QCheckBox>
#include <QLabel>
#include <QLineEdit>
#include <QIntValidator>
#include <QDoubleValidator>
#include <QRegExpValidator>
#include <QDialogButtonBox>

/* First page of options */
class MainOptionsPage : public QWidget
{
    Q_OBJECT
public:
    explicit MainOptionsPage(QWidget *parent=0);

    void setMapper(MonitoredDataMapper *mapper);
private:
    QCheckBox *bitcoin_at_startup;
#ifndef Q_WS_MAC
    QCheckBox *minimize_to_tray;
#endif
    QCheckBox *map_port_upnp;
#ifndef Q_WS_MAC
    QCheckBox *minimize_on_close;
#endif
    QCheckBox *connect_socks4;
    QLineEdit *proxy_ip;
    QLineEdit *proxy_port;
    BitcoinAmountField *fee_edit;

signals:

public slots:

};

class DisplayOptionsPage : public QWidget
{
    Q_OBJECT
public:
    explicit DisplayOptionsPage(QWidget *parent=0);

    void setMapper(MonitoredDataMapper *mapper);
private:
    QValueComboBox *unit;
    QCheckBox *display_addresses;
signals:

public slots:

};

#include "optionsdialog.moc"

OptionsDialog::OptionsDialog(QWidget *parent):
    QDialog(parent), contents_widget(0), pages_widget(0),
    model(0), main_page(0), display_page(0)
{
    contents_widget = new QListWidget();
    contents_widget->setMaximumWidth(128);

    pages_widget = new QStackedWidget();
    pages_widget->setMinimumWidth(300);

    QListWidgetItem *item_main = new QListWidgetItem(tr("Main"));
    contents_widget->addItem(item_main);
    main_page = new MainOptionsPage(this);
    pages_widget->addWidget(main_page);

    QListWidgetItem *item_display = new QListWidgetItem(tr("Display"));
    contents_widget->addItem(item_display);
    display_page = new DisplayOptionsPage(this);
    pages_widget->addWidget(display_page);

    contents_widget->setCurrentRow(0);

    QHBoxLayout *main_layout = new QHBoxLayout();
    main_layout->addWidget(contents_widget);
    main_layout->addWidget(pages_widget, 1);

    QVBoxLayout *layout = new QVBoxLayout();
    layout->addLayout(main_layout);

    QDialogButtonBox *buttonbox = new QDialogButtonBox();
    buttonbox->setStandardButtons(QDialogButtonBox::Apply|QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
    apply_button = buttonbox->button(QDialogButtonBox::Apply);
    layout->addWidget(buttonbox);

    setLayout(layout);
    setWindowTitle(tr("Options"));

    /* Widget-to-option mapper */
    mapper = new MonitoredDataMapper(this);
    mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
    mapper->setOrientation(Qt::Vertical);
    /* enable apply button when data modified */
    connect(mapper, SIGNAL(viewModified()), this, SLOT(enableApply()));
    /* disable apply button when new data loaded */
    connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(disableApply()));

    /* Event bindings */
    connect(contents_widget, SIGNAL(currentRowChanged(int)), this, SLOT(changePage(int)));
    connect(buttonbox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(okClicked()));
    connect(buttonbox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(cancelClicked()));
    connect(buttonbox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(applyClicked()));
}

void OptionsDialog::setModel(OptionsModel *model)
{
    this->model = model;

    mapper->setModel(model);
    main_page->setMapper(mapper);
    display_page->setMapper(mapper);

    mapper->toFirst();
}

void OptionsDialog::changePage(int index)
{
    pages_widget->setCurrentIndex(index);
}

void OptionsDialog::okClicked()
{
    mapper->submit();
    accept();
}

void OptionsDialog::cancelClicked()
{
    reject();
}

void OptionsDialog::applyClicked()
{
    mapper->submit();
    apply_button->setEnabled(false);
}

void OptionsDialog::enableApply()
{
    apply_button->setEnabled(true);
}

void OptionsDialog::disableApply()
{
    apply_button->setEnabled(false);
}

MainOptionsPage::MainOptionsPage(QWidget *parent):
        QWidget(parent)
{
    QVBoxLayout *layout = new QVBoxLayout();

    bitcoin_at_startup = new QCheckBox(tr("&Start Bitcoin on window system startup"));
    bitcoin_at_startup->setToolTip(tr("Automatically start Bitcoin after the computer is turned on"));
    layout->addWidget(bitcoin_at_startup);

#ifndef Q_WS_MAC
    minimize_to_tray = new QCheckBox(tr("&Minimize to the tray instead of the taskbar"));
    minimize_to_tray->setToolTip(tr("Show only a tray icon after minimizing the window"));
    layout->addWidget(minimize_to_tray);
#endif

    map_port_upnp = new QCheckBox(tr("Map port using &UPnP"));
    map_port_upnp->setToolTip(tr("Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled."));
    layout->addWidget(map_port_upnp);

#ifndef Q_WS_MAC
    minimize_on_close = new QCheckBox(tr("M&inimize on close"));
    minimize_on_close->setToolTip(tr("Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu."));
    layout->addWidget(minimize_on_close);
#endif

    connect_socks4 = new QCheckBox(tr("&Connect through SOCKS4 proxy:"));
    connect_socks4->setToolTip(tr("Connect to the Bitcon network through a SOCKS4 proxy (e.g. when connecting through Tor)"));
    layout->addWidget(connect_socks4);

    QHBoxLayout *proxy_hbox = new QHBoxLayout();
    proxy_hbox->addSpacing(18);
    QLabel *proxy_ip_label = new QLabel(tr("Proxy &IP: "));
    proxy_hbox->addWidget(proxy_ip_label);
    proxy_ip = new QLineEdit();
    proxy_ip->setMaximumWidth(140);
    proxy_ip->setEnabled(false);
    proxy_ip->setValidator(new QRegExpValidator(QRegExp("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"), this));
    proxy_ip->setToolTip(tr("IP address of the proxy (e.g. 127.0.0.1)"));
    proxy_ip_label->setBuddy(proxy_ip);
    proxy_hbox->addWidget(proxy_ip);
    QLabel *proxy_port_label = new QLabel(tr("&Port: "));
    proxy_hbox->addWidget(proxy_port_label);
    proxy_port = new QLineEdit();
    proxy_port->setMaximumWidth(55);
    proxy_port->setValidator(new QIntValidator(0, 65535, this));
    proxy_port->setEnabled(false);
    proxy_port->setToolTip(tr("Port of the proxy (e.g. 1234)"));
    proxy_port_label->setBuddy(proxy_port);
    proxy_hbox->addWidget(proxy_port);
    proxy_hbox->addStretch(1);

    layout->addLayout(proxy_hbox);
    QLabel *fee_help = new QLabel(tr("Optional transaction fee per kB that helps make sure your transactions are processed quickly.  Most transactions are 1 kB.  Fee 0.01 recommended."));
    fee_help->setWordWrap(true);
    layout->addWidget(fee_help);

    QHBoxLayout *fee_hbox = new QHBoxLayout();
    fee_hbox->addSpacing(18);
    QLabel *fee_label = new QLabel(tr("Pay transaction &fee"));
    fee_hbox->addWidget(fee_label);
    fee_edit = new BitcoinAmountField();
    fee_edit->setToolTip(tr("Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB. Fee 0.01 recommended."));

    fee_label->setBuddy(fee_edit);
    fee_hbox->addWidget(fee_edit);
    fee_hbox->addStretch(1);

    layout->addLayout(fee_hbox);

    layout->addStretch(1); // Extra space at bottom

    setLayout(layout);

    connect(connect_socks4, SIGNAL(toggled(bool)), proxy_ip, SLOT(setEnabled(bool)));
    connect(connect_socks4, SIGNAL(toggled(bool)), proxy_port, SLOT(setEnabled(bool)));

#ifndef USE_UPNP
    map_port_upnp->setDisabled(true);
#endif
}

void MainOptionsPage::setMapper(MonitoredDataMapper *mapper)
{
    // Map model to widgets
    mapper->addMapping(bitcoin_at_startup, OptionsModel::StartAtStartup);
#ifndef Q_WS_MAC
    mapper->addMapping(minimize_to_tray, OptionsModel::MinimizeToTray);
#endif
    mapper->addMapping(map_port_upnp, OptionsModel::MapPortUPnP);
#ifndef Q_WS_MAC
    mapper->addMapping(minimize_on_close, OptionsModel::MinimizeOnClose);
#endif
    mapper->addMapping(connect_socks4, OptionsModel::ConnectSOCKS4);
    mapper->addMapping(proxy_ip, OptionsModel::ProxyIP);
    mapper->addMapping(proxy_port, OptionsModel::ProxyPort);
    mapper->addMapping(fee_edit, OptionsModel::Fee);
}

DisplayOptionsPage::DisplayOptionsPage(QWidget *parent):
        QWidget(parent)
{
    QVBoxLayout *layout = new QVBoxLayout();

    QHBoxLayout *unit_hbox = new QHBoxLayout();
    unit_hbox->addSpacing(18);
    QLabel *unit_label = new QLabel(tr("&Unit to show amounts in: "));
    unit_hbox->addWidget(unit_label);
    unit = new QValueComboBox(this);
    unit->setModel(new BitcoinUnits(this));
    unit->setToolTip(tr("Choose the default subdivision unit to show in the interface, and when sending coins"));

    unit_label->setBuddy(unit);
    unit_hbox->addWidget(unit);

    layout->addLayout(unit_hbox);

    display_addresses = new QCheckBox(tr("Display addresses in transaction list"), this);
    layout->addWidget(display_addresses);

    layout->addStretch();

    setLayout(layout);
}

void DisplayOptionsPage::setMapper(MonitoredDataMapper *mapper)
{
    mapper->addMapping(unit, OptionsModel::DisplayUnit);
    mapper->addMapping(display_addresses, OptionsModel::DisplayAddresses);
}
<commit_msg>Bitcoin-Qt: Remove redundant tooltip on optional transaction fee. Fixes #1218<commit_after>#include "optionsdialog.h"
#include "optionsmodel.h"
#include "bitcoinamountfield.h"
#include "monitoreddatamapper.h"
#include "guiutil.h"
#include "bitcoinunits.h"
#include "qvaluecombobox.h"

#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPushButton>
#include <QListWidget>
#include <QStackedWidget>

#include <QCheckBox>
#include <QLabel>
#include <QLineEdit>
#include <QIntValidator>
#include <QDoubleValidator>
#include <QRegExpValidator>
#include <QDialogButtonBox>

/* First page of options */
class MainOptionsPage : public QWidget
{
    Q_OBJECT
public:
    explicit MainOptionsPage(QWidget *parent=0);

    void setMapper(MonitoredDataMapper *mapper);
private:
    QCheckBox *bitcoin_at_startup;
#ifndef Q_WS_MAC
    QCheckBox *minimize_to_tray;
#endif
    QCheckBox *map_port_upnp;
#ifndef Q_WS_MAC
    QCheckBox *minimize_on_close;
#endif
    QCheckBox *connect_socks4;
    QLineEdit *proxy_ip;
    QLineEdit *proxy_port;
    BitcoinAmountField *fee_edit;

signals:

public slots:

};

class DisplayOptionsPage : public QWidget
{
    Q_OBJECT
public:
    explicit DisplayOptionsPage(QWidget *parent=0);

    void setMapper(MonitoredDataMapper *mapper);
private:
    QValueComboBox *unit;
    QCheckBox *display_addresses;
signals:

public slots:

};

#include "optionsdialog.moc"

OptionsDialog::OptionsDialog(QWidget *parent):
    QDialog(parent), contents_widget(0), pages_widget(0),
    model(0), main_page(0), display_page(0)
{
    contents_widget = new QListWidget();
    contents_widget->setMaximumWidth(128);

    pages_widget = new QStackedWidget();
    pages_widget->setMinimumWidth(300);

    QListWidgetItem *item_main = new QListWidgetItem(tr("Main"));
    contents_widget->addItem(item_main);
    main_page = new MainOptionsPage(this);
    pages_widget->addWidget(main_page);

    QListWidgetItem *item_display = new QListWidgetItem(tr("Display"));
    contents_widget->addItem(item_display);
    display_page = new DisplayOptionsPage(this);
    pages_widget->addWidget(display_page);

    contents_widget->setCurrentRow(0);

    QHBoxLayout *main_layout = new QHBoxLayout();
    main_layout->addWidget(contents_widget);
    main_layout->addWidget(pages_widget, 1);

    QVBoxLayout *layout = new QVBoxLayout();
    layout->addLayout(main_layout);

    QDialogButtonBox *buttonbox = new QDialogButtonBox();
    buttonbox->setStandardButtons(QDialogButtonBox::Apply|QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
    apply_button = buttonbox->button(QDialogButtonBox::Apply);
    layout->addWidget(buttonbox);

    setLayout(layout);
    setWindowTitle(tr("Options"));

    /* Widget-to-option mapper */
    mapper = new MonitoredDataMapper(this);
    mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
    mapper->setOrientation(Qt::Vertical);
    /* enable apply button when data modified */
    connect(mapper, SIGNAL(viewModified()), this, SLOT(enableApply()));
    /* disable apply button when new data loaded */
    connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(disableApply()));

    /* Event bindings */
    connect(contents_widget, SIGNAL(currentRowChanged(int)), this, SLOT(changePage(int)));
    connect(buttonbox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(okClicked()));
    connect(buttonbox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(cancelClicked()));
    connect(buttonbox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(applyClicked()));
}

void OptionsDialog::setModel(OptionsModel *model)
{
    this->model = model;

    mapper->setModel(model);
    main_page->setMapper(mapper);
    display_page->setMapper(mapper);

    mapper->toFirst();
}

void OptionsDialog::changePage(int index)
{
    pages_widget->setCurrentIndex(index);
}

void OptionsDialog::okClicked()
{
    mapper->submit();
    accept();
}

void OptionsDialog::cancelClicked()
{
    reject();
}

void OptionsDialog::applyClicked()
{
    mapper->submit();
    apply_button->setEnabled(false);
}

void OptionsDialog::enableApply()
{
    apply_button->setEnabled(true);
}

void OptionsDialog::disableApply()
{
    apply_button->setEnabled(false);
}

MainOptionsPage::MainOptionsPage(QWidget *parent):
        QWidget(parent)
{
    QVBoxLayout *layout = new QVBoxLayout();

    bitcoin_at_startup = new QCheckBox(tr("&Start Bitcoin on window system startup"));
    bitcoin_at_startup->setToolTip(tr("Automatically start Bitcoin after the computer is turned on"));
    layout->addWidget(bitcoin_at_startup);

#ifndef Q_WS_MAC
    minimize_to_tray = new QCheckBox(tr("&Minimize to the tray instead of the taskbar"));
    minimize_to_tray->setToolTip(tr("Show only a tray icon after minimizing the window"));
    layout->addWidget(minimize_to_tray);
#endif

    map_port_upnp = new QCheckBox(tr("Map port using &UPnP"));
    map_port_upnp->setToolTip(tr("Automatically open the Bitcoin client port on the router. This only works when your router supports UPnP and it is enabled."));
    layout->addWidget(map_port_upnp);

#ifndef Q_WS_MAC
    minimize_on_close = new QCheckBox(tr("M&inimize on close"));
    minimize_on_close->setToolTip(tr("Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu."));
    layout->addWidget(minimize_on_close);
#endif

    connect_socks4 = new QCheckBox(tr("&Connect through SOCKS4 proxy:"));
    connect_socks4->setToolTip(tr("Connect to the Bitcon network through a SOCKS4 proxy (e.g. when connecting through Tor)"));
    layout->addWidget(connect_socks4);

    QHBoxLayout *proxy_hbox = new QHBoxLayout();
    proxy_hbox->addSpacing(18);
    QLabel *proxy_ip_label = new QLabel(tr("Proxy &IP: "));
    proxy_hbox->addWidget(proxy_ip_label);
    proxy_ip = new QLineEdit();
    proxy_ip->setMaximumWidth(140);
    proxy_ip->setEnabled(false);
    proxy_ip->setValidator(new QRegExpValidator(QRegExp("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"), this));
    proxy_ip->setToolTip(tr("IP address of the proxy (e.g. 127.0.0.1)"));
    proxy_ip_label->setBuddy(proxy_ip);
    proxy_hbox->addWidget(proxy_ip);
    QLabel *proxy_port_label = new QLabel(tr("&Port: "));
    proxy_hbox->addWidget(proxy_port_label);
    proxy_port = new QLineEdit();
    proxy_port->setMaximumWidth(55);
    proxy_port->setValidator(new QIntValidator(0, 65535, this));
    proxy_port->setEnabled(false);
    proxy_port->setToolTip(tr("Port of the proxy (e.g. 1234)"));
    proxy_port_label->setBuddy(proxy_port);
    proxy_hbox->addWidget(proxy_port);
    proxy_hbox->addStretch(1);

    layout->addLayout(proxy_hbox);
    QLabel *fee_help = new QLabel(tr("Optional transaction fee per kB that helps make sure your transactions are processed quickly.  Most transactions are 1 kB.  Fee 0.01 recommended."));
    fee_help->setWordWrap(true);
    layout->addWidget(fee_help);

    QHBoxLayout *fee_hbox = new QHBoxLayout();
    fee_hbox->addSpacing(18);
    QLabel *fee_label = new QLabel(tr("Pay transaction &fee"));
    fee_hbox->addWidget(fee_label);
    fee_edit = new BitcoinAmountField();

    fee_label->setBuddy(fee_edit);
    fee_hbox->addWidget(fee_edit);
    fee_hbox->addStretch(1);

    layout->addLayout(fee_hbox);

    layout->addStretch(1); // Extra space at bottom

    setLayout(layout);

    connect(connect_socks4, SIGNAL(toggled(bool)), proxy_ip, SLOT(setEnabled(bool)));
    connect(connect_socks4, SIGNAL(toggled(bool)), proxy_port, SLOT(setEnabled(bool)));

#ifndef USE_UPNP
    map_port_upnp->setDisabled(true);
#endif
}

void MainOptionsPage::setMapper(MonitoredDataMapper *mapper)
{
    // Map model to widgets
    mapper->addMapping(bitcoin_at_startup, OptionsModel::StartAtStartup);
#ifndef Q_WS_MAC
    mapper->addMapping(minimize_to_tray, OptionsModel::MinimizeToTray);
#endif
    mapper->addMapping(map_port_upnp, OptionsModel::MapPortUPnP);
#ifndef Q_WS_MAC
    mapper->addMapping(minimize_on_close, OptionsModel::MinimizeOnClose);
#endif
    mapper->addMapping(connect_socks4, OptionsModel::ConnectSOCKS4);
    mapper->addMapping(proxy_ip, OptionsModel::ProxyIP);
    mapper->addMapping(proxy_port, OptionsModel::ProxyPort);
    mapper->addMapping(fee_edit, OptionsModel::Fee);
}

DisplayOptionsPage::DisplayOptionsPage(QWidget *parent):
        QWidget(parent)
{
    QVBoxLayout *layout = new QVBoxLayout();

    QHBoxLayout *unit_hbox = new QHBoxLayout();
    unit_hbox->addSpacing(18);
    QLabel *unit_label = new QLabel(tr("&Unit to show amounts in: "));
    unit_hbox->addWidget(unit_label);
    unit = new QValueComboBox(this);
    unit->setModel(new BitcoinUnits(this));
    unit->setToolTip(tr("Choose the default subdivision unit to show in the interface, and when sending coins"));

    unit_label->setBuddy(unit);
    unit_hbox->addWidget(unit);

    layout->addLayout(unit_hbox);

    display_addresses = new QCheckBox(tr("Display addresses in transaction list"), this);
    layout->addWidget(display_addresses);

    layout->addStretch();

    setLayout(layout);
}

void DisplayOptionsPage::setMapper(MonitoredDataMapper *mapper)
{
    mapper->addMapping(unit, OptionsModel::DisplayUnit);
    mapper->addMapping(display_addresses, OptionsModel::DisplayAddresses);
}
<|endoftext|>


<commit_before>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, version 1.0 RC 1        *
*                (c) 2006-2011 INRIA, USTL, UJF, CNRS, MGH                    *
*                                                                             *
* This program is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU General Public License as published by the Free  *
* Software Foundation; either version 2 of the License, or (at your option)   *
* any later version.                                                          *
*                                                                             *
* This program is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    *
* more details.                                                               *
*                                                                             *
* You should have received a copy of the GNU General Public License along     *
* with this program; if not, write to the Free Software Foundation, Inc., 51  *
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.                   *
*******************************************************************************
*                            SOFA :: Applications                             *
*                                                                             *
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/
#include <plugins/SofaTest/Sofa_test.h>
#include <sofa/simulation/graph/DAGSimulation.h>
#include <plugins/SceneCreator/SceneCreator.h>

namespace sofa {
using namespace modeling;

/** Test the Simulation class
*/
struct Simulation_test: public Sofa_test<double>
{
    // root
   simulation::Node::SPtr root;

   /// Test Simulation::computeBBox
   void computeBBox()
   {
       // Init Sofa
       simulation::Simulation* simulation;
       sofa::simulation::setSimulation(simulation = new sofa::simulation::graph::DAGSimulation());

       root = simulation::getSimulation()->createNewGraph("root");

       // create DOFs and its expected bounding box
       MechanicalObject3::SPtr DOF = core::objectmodel::New<MechanicalObject3>();
       root->addObject(DOF);
       DOF->resize(4);
       MechanicalObject3::WriteVecCoord x = DOF->writePositions();
       x[0] = defaulttype::Vector3(0,0,0);
       x[1] = defaulttype::Vector3(1,0,0);
       x[2] = defaulttype::Vector3(0,1,0);
       x[3] = defaulttype::Vector3(0,0,1);
       defaulttype::Vector3 expectedMin(0,0,0), expectedMax(1,1,1);
       DOF->showObject.setValue(true); // bbox is updated only for drawn MO

       // end create scene
       //*********
       initScene();
       //*********

       defaulttype::Vector3 sceneMinBBox, sceneMaxBBox;
       simulation->computeBBox(root.get(), sceneMinBBox.ptr(), sceneMaxBBox.ptr());

       if( vectorMaxDiff(sceneMinBBox,expectedMin)>this->epsilon() || vectorMaxDiff(sceneMaxBBox,expectedMax)>this->epsilon() )
       {
           ADD_FAILURE() << "Wrong bounding box, expected (" << expectedMin <<", "<<expectedMax<<") , got ("<< sceneMinBBox <<", "<<sceneMaxBBox << ")" << endl;
       }

   }

};

TEST_F( Simulation_test,SimulationTest)
{
     this->computeBBox();
}

}// namespace sofa

<commit_msg>Add: object suppression unit test<commit_after>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, version 1.0 RC 1        *
*                (c) 2006-2011 INRIA, USTL, UJF, CNRS, MGH                    *
*                                                                             *
* This program is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU General Public License as published by the Free  *
* Software Foundation; either version 2 of the License, or (at your option)   *
* any later version.                                                          *
*                                                                             *
* This program is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    *
* more details.                                                               *
*                                                                             *
* You should have received a copy of the GNU General Public License along     *
* with this program; if not, write to the Free Software Foundation, Inc., 51  *
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.                   *
*******************************************************************************
*                            SOFA :: Applications                             *
*                                                                             *
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/
#include <plugins/SofaTest/Sofa_test.h>
#include <sofa/simulation/graph/DAGSimulation.h>
#include <plugins/SceneCreator/SceneCreator.h>
#include <SofaBaseMechanics/UniformMass.h>

namespace sofa {
using namespace modeling;

static int objectCounter = 0;

template <class DataType>
class InstrumentedObject : public DataType
{
public:
    InstrumentedObject()
    {
        objectCounter++;
    }

    ~InstrumentedObject()
    {
        objectCounter--;
    }
};

/** Test the Simulation class
*/
struct Simulation_test: public Sofa_test<double>
{
    // root
   simulation::Node::SPtr root;
   typedef component::mass::UniformMass<defaulttype::Vec3Types, SReal> UniformMass3;

   /// Test Simulation::computeBBox
   void computeBBox()
   {
       // Init Sofa
       simulation::Simulation* simulation;
       sofa::simulation::setSimulation(simulation = new sofa::simulation::graph::DAGSimulation());

       root = simulation::getSimulation()->createNewGraph("root");

       // create DOFs and its expected bounding box
       MechanicalObject3::SPtr DOF = core::objectmodel::New<MechanicalObject3>();
       root->addObject(DOF);
       DOF->resize(4);
       MechanicalObject3::WriteVecCoord x = DOF->writePositions();
       x[0] = defaulttype::Vector3(0,0,0);
       x[1] = defaulttype::Vector3(1,0,0);
       x[2] = defaulttype::Vector3(0,1,0);
       x[3] = defaulttype::Vector3(0,0,1);
       defaulttype::Vector3 expectedMin(0,0,0), expectedMax(1,1,1);
       DOF->showObject.setValue(true); // bbox is updated only for drawn MO

       // end create scene
       //*********
       initScene();
       //*********

       defaulttype::Vector3 sceneMinBBox, sceneMaxBBox;
       simulation->computeBBox(root.get(), sceneMinBBox.ptr(), sceneMaxBBox.ptr());

       if( vectorMaxDiff(sceneMinBBox,expectedMin)>this->epsilon() || vectorMaxDiff(sceneMaxBBox,expectedMax)>this->epsilon() )
       {
           ADD_FAILURE() << "Wrong bounding box, expected (" << expectedMin <<", "<<expectedMax<<") , got ("<< sceneMinBBox <<", "<<sceneMaxBBox << ")" << endl;
       }

   }

   void objectSuppression()
   {
       // Init Sofa
       simulation::Simulation* simulation;
       sofa::simulation::setSimulation(simulation = new sofa::simulation::graph::DAGSimulation());
       root = simulation::getSimulation()->createNewGraph("root");
       root->addObject(core::objectmodel::New<InstrumentedObject<MechanicalObject3>>());
       root->addObject(core::objectmodel::New<InstrumentedObject<UniformMass3>>());
       simulation::Node::SPtr child  = simulation::getSimulation()->createNewNode("child");
       root->addChild(child);
       child->addObject(core::objectmodel::New<InstrumentedObject<MechanicalObject3>>());
       child->addObject(core::objectmodel::New<InstrumentedObject<UniformMass3>>());

       //
       root = simulation::getSimulation()->createNewGraph("root2");
   }
};

TEST_F( Simulation_test,SimulationTest)
{
     this->computeBBox();

}

TEST_F( Simulation_test,objectSuppression)
{
    this->objectSuppression();
    if(objectCounter>0)
        ADD_FAILURE() << "missing delete: "<<objectCounter<<endl;

}
}// namespace sofa

<|endoftext|>


<commit_before>#include <osg/Camera>
#include <osg/Texture2D>
#include <osgDB/ReadFile>
#include <osgGA/TrackballManipulator>
#include <osgViewer/Viewer>

class FindTextureVisitor : public osg::NodeVisitor
{
public:
    FindTextureVisitor(osg::Texture* tex)
        : texture_(tex)
    {
        setTraversalMode(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN);
    }

    virtual void apply(osg::Node& node)
    {
        replaceTexure(node.getStateSet());
        traverse(node);
    }

    virtual void apply(osg::Geode& geode)
    {
        replaceTexure(geode.getStateSet());
        for (unsigned int i = 0; i < geode.getNumDrawables(); ++i)
        {
            replaceTexure(geode.getDrawable(i)->getStateSet());
        }
        traverse(geode);
    }

    void replaceTexure(osg::StateSet* ss)
    {
        if (ss)
        {
            osg::Texture* oldTexture = dynamic_cast<osg::Texture*>(
                ss->getTextureAttribute(0, osg::StateAttribute::TEXTURE));
            if (oldTexture)
            {
                ss->setTextureAttribute(0, texture_.get());
            }
        }
    }

protected:
    osg::ref_ptr<osg::Texture> texture_;
};

int main(
    int /*argc*/,
    char** /*argv*/)
{
    osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("lz.osg");
    osg::ref_ptr<osg::Node> sub_model = osgDB::readNodeFile("glider.osg");

    int tex_width = 1024;
    int tex_height = 1024;
    osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
    texture->setTextureSize(tex_width, tex_height);
    texture->setInternalFormat(GL_RGBA);
    texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
    texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);

    FindTextureVisitor ftv(texture.get());
    if (model.valid())
    {
        model->accept(ftv);
    }

    osg::ref_ptr<osg::Camera> camera = new osg::Camera;
    camera->setViewport(0, 0, tex_width, tex_height);
    camera->setClearColor(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
    camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    camera->setRenderOrder(osg::Camera::PRE_RENDER);
    camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
    camera->attach(osg::Camera::COLOR_BUFFER, texture.get());
    camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
    camera->addChild(sub_model);

    osg::ref_ptr<osg::Group> root = new osg::Group;
    root->addChild(camera.get());
    root->addChild(model.get());

    osgViewer::Viewer viewer;
    viewer.setSceneData(root.get());
    viewer.setCameraManipulator(new osgGA::TrackballManipulator);

    float range = 0.25f;
    float speed = range / 50;
    float delta = speed;
    float bias = 0.0f;
    osg::Vec3 eye(0.0f, -5.0f, 5.0f);

    while (!viewer.done())
    {
        if (bias < -range)
        {
            delta = speed;
        }
        else if (bias > range)
        {
            delta = -speed;
        }
        bias += delta;

        camera->setViewMatrixAsLookAt(
            eye,
            osg::Vec3(),
            osg::Vec3(bias, 1.0f, 1.0f));

        viewer.frame();
    }

    return 0;
}
<commit_msg>Finish chapter 7<commit_after>#include <osg/Camera>
#include <osg/Texture2D>
#include <osgDB/ReadFile>
#include <osgGA/TrackballManipulator>
#include <osgViewer/Viewer>

class FindTextureVisitor : public osg::NodeVisitor
{
public:
    FindTextureVisitor(osg::Texture* tex)
        : texture_(tex)
    {
        setTraversalMode(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN);
    }

    virtual void apply(osg::Node& node)
    {
        replaceTexure(node.getStateSet());
        traverse(node);
    }

    virtual void apply(osg::Geode& geode)
    {
        replaceTexure(geode.getStateSet());
        for (unsigned int i = 0; i < geode.getNumDrawables(); ++i)
        {
            replaceTexure(geode.getDrawable(i)->getStateSet());
        }
        traverse(geode);
    }

    void replaceTexure(osg::StateSet* ss)
    {
        if (ss)
        {
            osg::Texture* oldTexture = dynamic_cast<osg::Texture*>(
                ss->getTextureAttribute(0, osg::StateAttribute::TEXTURE));
            if (oldTexture)
            {
                ss->setTextureAttribute(0, texture_.get());
            }
        }
    }

protected:
    osg::ref_ptr<osg::Texture> texture_;
};

int main(
    int /*argc*/,
    char** /*argv*/)
{
    osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("lz.osg");
    osg::ref_ptr<osg::Node> sub_model = osgDB::readNodeFile("glider.osg");

    int tex_width = 1024;
    int tex_height = 1024;
    osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
    texture->setTextureSize(tex_width, tex_height);
    texture->setInternalFormat(GL_RGBA);
    texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
    texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);

    FindTextureVisitor ftv(texture.get());
    if (model.valid())
    {
        model->accept(ftv);
    }

    osg::ref_ptr<osg::Camera> camera = new osg::Camera;
    camera->setViewport(0, 0, tex_width, tex_height);
    camera->setClearColor(osg::Vec4(1.0f, 1.0f, 1.0f, 0.0f));
    camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    camera->setRenderOrder(osg::Camera::PRE_RENDER);
    camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
    camera->attach(osg::Camera::COLOR_BUFFER, texture.get());
    camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
    camera->addChild(sub_model);

    osg::ref_ptr<osg::Group> root = new osg::Group;
    root->addChild(camera.get());
    root->addChild(model.get());

    osgViewer::Viewer viewer;
    viewer.setSceneData(root.get());
    viewer.setCameraManipulator(new osgGA::TrackballManipulator);

    float range = 0.25f;
    float speed = range / 50;
    float delta = speed;
    float bias = 0.0f;
    osg::Vec3 eye(0.0f, -5.0f, 5.0f);

    while (!viewer.done())
    {
        if (bias < -range)
        {
            delta = speed;
        }
        else if (bias > range)
        {
            delta = -speed;
        }
        bias += delta;

        camera->setViewMatrixAsLookAt(
            eye,
            osg::Vec3(),
            osg::Vec3(bias, 1.0f, 1.0f));

        viewer.frame();
    }

    return 0;
}
<|endoftext|>