<commit_before>/*****************************************************************************************
 *                                                                                *
 * OpenSpace                                                                             *
 *                                                                                *
 * Copyright (c) 2014-2019                                                               *
 *                                                                                *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this  *
 * software and associated documentation files (the "Software"), to deal in the Software *
 * without restriction, including without limitation the rights to use, copy, modify,    *
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to    *
 * permit persons to whom the Software is furnished to do so, subject to the following   *
 * conditions:                                                                           *
 *                                                                                *
 * The above copyright notice and this permission notice shall be included in all copies *
 * or substantial portions of the Software.                                              *
 *                                                                                *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,   *
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A         *
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT    *
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF  *
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE  *
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                                         *
 ****************************************************************************************/

#include <modules/base/rendering/renderablesphere.h>

#include <modules/base/basemodule.h>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <openspace/engine/globals.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/util/powerscaledsphere.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/glm.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/io/texture/texturereader.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureunit.h>
#include <ghoul/opengl/programobject.h>

namespace {
    constexpr const char* ProgramName = "Sphere";

    constexpr const std::array<const char*, 5> UniformNames = {
        "opacity", "modelViewProjection", "modelViewRotation", "colorTexture",
        "mirrorTexture"
    };

    enum class Orientation : int {
        Outside = 0,
        Inside,
        Both
    };

    constexpr openspace::properties::Property::PropertyInfo TextureInfo = {
        "Texture",
        "Texture",
        "This value specifies an image that is loaded from disk and is used as a texture "
        "that is applied to this sphere. This image is expected to be an equirectangular "
        "projection."
    };

    constexpr openspace::properties::Property::PropertyInfo MirrorTextureInfo = {
        "MirrorTexture",
        "Mirror Texture",
        "Mirror the texture along the x-axis."
    };

    constexpr openspace::properties::Property::PropertyInfo OrientationInfo = {
        "Orientation",
        "Orientation",
        "Specifies whether the texture is applied to the inside of the sphere, the "
        "outside of the sphere, or both."
    };

    constexpr openspace::properties::Property::PropertyInfo UseAdditiveBlendingInfo = {
        "UseAdditiveBlending",
        "Use Additive Blending",
        "Render the object using additive blending."
    };

    constexpr openspace::properties::Property::PropertyInfo SegmentsInfo = {
        "Segments",
        "Number of Segments",
        "This value specifies the number of segments that the sphere is separated in."
    };

    constexpr openspace::properties::Property::PropertyInfo SizeInfo = {
        "Size",
        "Size (in meters)",
        "This value specifies the radius of the sphere in meters."
    };

    constexpr openspace::properties::Property::PropertyInfo FadeOutThresholdInfo = {
        "FadeOutThreshold",
        "Fade-Out Threshold",
        "This value determines percentage of the sphere is visible before starting "
        "fading-out it."
    };

    constexpr openspace::properties::Property::PropertyInfo FadeInThresholdInfo = {
        "FadeInThreshold",
        "Fade-In Threshold",
        "Distance from center of MilkyWay from where the astronomical object starts to "
        "fade in."
    };

    constexpr openspace::properties::Property::PropertyInfo DisableFadeInOutInfo = {
        "DisableFadeInOut",
        "Disable Fade-In/Fade-Out effects",
        "Enables/Disables the Fade-In/Out effects."
    };

    constexpr openspace::properties::Property::PropertyInfo BackgroundInfo = {
        "Background",
        "Sets the current sphere rendering as a background rendering type",
        "Enables/Disables background rendering."
    };
} // namespace

namespace openspace {

documentation::Documentation RenderableSphere::Documentation() {
    using namespace documentation;
    return {
        "RenderableSphere",
        "base_renderable_sphere",
        {
            {
                SizeInfo.identifier,
                new DoubleVerifier,
                Optional::No,
                SizeInfo.description
            },
            {
                SegmentsInfo.identifier,
                new IntVerifier,
                Optional::No,
                SegmentsInfo.description
            },
            {
                TextureInfo.identifier,
                new StringVerifier,
                Optional::No,
                TextureInfo.description
            },
            {
                OrientationInfo.identifier,
                new StringInListVerifier({ "Inside", "Outside", "Both" }),
                Optional::Yes,
                OrientationInfo.description
            },
            {
                UseAdditiveBlendingInfo.identifier,
                new BoolVerifier,
                Optional::Yes,
                UseAdditiveBlendingInfo.description
            },
            {
                MirrorTextureInfo.identifier,
                new BoolVerifier,
                Optional::Yes,
                MirrorTextureInfo.description
            },
            {
                FadeOutThresholdInfo.identifier,
                new DoubleInRangeVerifier(0.0, 1.0),
                Optional::Yes,
                FadeOutThresholdInfo.description
            },
            {
                FadeInThresholdInfo.identifier,
                new DoubleVerifier,
                Optional::Yes,
                FadeInThresholdInfo.description
            },
            {
                DisableFadeInOutInfo.identifier,
                new BoolVerifier,
                Optional::Yes,
                DisableFadeInOutInfo.description
            },
            {
                BackgroundInfo.identifier,
                new BoolVerifier,
                Optional::Yes,
                BackgroundInfo.description
            },
        }
    };
}

RenderableSphere::RenderableSphere(const ghoul::Dictionary& dictionary)
    : Renderable(dictionary)
    , _texturePath(TextureInfo)
    , _orientation(OrientationInfo, properties::OptionProperty::DisplayType::Dropdown)
    , _size(SizeInfo, 1.f, 0.f, 1e35f)
    , _segments(SegmentsInfo, 8, 4, 1000)
    , _mirrorTexture(MirrorTextureInfo, false)
    , _useAdditiveBlending(UseAdditiveBlendingInfo, false)
    , _disableFadeInDistance(DisableFadeInOutInfo, true)
    , _backgroundRendering(BackgroundInfo, false)
    , _fadeInThreshold(FadeInThresholdInfo, -1.f, 0.f, 1.f)
    , _fadeOutThreshold(FadeOutThresholdInfo, -1.f, 0.f, 1.f)
{
    documentation::testSpecificationAndThrow(
        Documentation(),
        dictionary,
        "RenderableSphere"
    );

    addProperty(_opacity);
    registerUpdateRenderBinFromOpacity();

    _size = static_cast<float>(dictionary.value<double>(SizeInfo.identifier));
    _segments = static_cast<int>(dictionary.value<double>(SegmentsInfo.identifier));
    _texturePath = absPath(dictionary.value<std::string>(TextureInfo.identifier));

    _orientation.addOptions({
        { static_cast<int>(Orientation::Outside), "Outside" },
        { static_cast<int>(Orientation::Inside), "Inside" },
        { static_cast<int>(Orientation::Both), "Both" }
    });

    if (dictionary.hasKey(OrientationInfo.identifier)) {
        const std::string& v = dictionary.value<std::string>(OrientationInfo.identifier);
        if (v == "Inside") {
            _orientation = static_cast<int>(Orientation::Inside);
        }
        else if (v == "Outside") {
            _orientation = static_cast<int>(Orientation::Outside);
        }
        else if (v == "Both") {
            _orientation = static_cast<int>(Orientation::Both);
        }
        else {
            throw ghoul::MissingCaseException();
        }
    }
    else {
        _orientation = static_cast<int>(Orientation::Outside);
    }
    addProperty(_orientation);

    addProperty(_size);
    _size.onChange([this]() { _sphereIsDirty = true; });

    addProperty(_segments);
    _segments.onChange([this]() { _sphereIsDirty = true; });

    addProperty(_texturePath);
    _texturePath.onChange([this]() { loadTexture(); });

    addProperty(_mirrorTexture);
    addProperty(_useAdditiveBlending);

    if (dictionary.hasKey(MirrorTextureInfo.identifier)) {
       _mirrorTexture = dictionary.value<bool>(MirrorTextureInfo.identifier);
    }
    if (dictionary.hasKey(UseAdditiveBlendingInfo.identifier)) {
        _useAdditiveBlending = dictionary.value<bool>(UseAdditiveBlendingInfo.identifier);

        if (_useAdditiveBlending) {
            setRenderBin(Renderable::RenderBin::Transparent);
        }
    }

    if (dictionary.hasKey(FadeOutThresholdInfo.identifier)) {
        _fadeOutThreshold = static_cast<float>(
            dictionary.value<double>(FadeOutThresholdInfo.identifier)
        );
        addProperty(_fadeOutThreshold);
    }

    if (dictionary.hasKey(FadeInThresholdInfo.identifier)) {
        _fadeInThreshold = static_cast<float>(
            dictionary.value<double>(FadeInThresholdInfo.identifier)
        );
        addProperty(_fadeInThreshold);
    }

    if (dictionary.hasKey(FadeOutThresholdInfo.identifier) ||
        dictionary.hasKey(FadeInThresholdInfo.identifier)) {
        _disableFadeInDistance.set(false);
        addProperty(_disableFadeInDistance);
    }

    if (dictionary.hasKey(BackgroundInfo.identifier)) {
        _backgroundRendering = dictionary.value<bool>(BackgroundInfo.identifier);

        if (_backgroundRendering) {
            setRenderBin(Renderable::RenderBin::Background);
        }
    }
}

bool RenderableSphere::isReady() const {
    return _shader && _texture;
}

void RenderableSphere::initializeGL() {
    _sphere = std::make_unique<PowerScaledSphere>(_size, _segments);
    _sphere->initialize();

    _shader = BaseModule::ProgramObjectManager.request(
        ProgramName,
        []() -> std::unique_ptr<ghoul::opengl::ProgramObject> {
            return global::renderEngine.buildRenderProgram(
                ProgramName,
                absPath("${MODULE_BASE}/shaders/sphere_vs.glsl"),
                absPath("${MODULE_BASE}/shaders/sphere_fs.glsl")
            );
        }
    );

    ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames);

    loadTexture();
}

void RenderableSphere::deinitializeGL() {
    _texture = nullptr;

    BaseModule::ProgramObjectManager.release(
        ProgramName,
        [](ghoul::opengl::ProgramObject* p) {
            global::renderEngine.removeRenderProgram(p);
        }
    );
    _shader = nullptr;
}

void RenderableSphere::render(const RenderData& data, RendererTasks&) {
    Orientation orientation = static_cast<Orientation>(_orientation.value());

    glm::dmat4 modelTransform =
        glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
        glm::dmat4(data.modelTransform.rotation) *
        glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale));

    glm::dmat3 modelRotation =
        glm::dmat3(data.modelTransform.rotation);

    // Activate shader
    using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError;
    _shader->activate();
    _shader->setIgnoreUniformLocationError(IgnoreError::Yes);

    glm::mat4 modelViewProjection = data.camera.projectionMatrix() *
                             glm::mat4(data.camera.combinedViewMatrix() * modelTransform);
    _shader->setUniform(_uniformCache.modelViewProjection, modelViewProjection);

    glm::mat4 modelViewRotation = glm::mat3(
        glm::dmat3(data.camera.viewRotationMatrix()) * modelRotation
    );
    _shader->setUniform(_uniformCache.modelViewRotation, modelViewRotation);

    float adjustedTransparency = _opacity;

    if (_fadeInThreshold > -1.0) {
        const float logDistCamera = glm::log(static_cast<float>(
            glm::distance(data.camera.positionVec3(), data.modelTransform.translation)
        ));
        const float startLogFadeDistance = glm::log(_size * _fadeInThreshold);
        const float stopLogFadeDistance = startLogFadeDistance + 1.f;

        if (logDistCamera > startLogFadeDistance && logDistCamera < stopLogFadeDistance) {
            const float fadeFactor = glm::clamp(
                (logDistCamera - startLogFadeDistance) /
                (stopLogFadeDistance - startLogFadeDistance),
                0.f,
                1.f
            );
            adjustedTransparency *= fadeFactor;
        }
        else if (logDistCamera <= startLogFadeDistance) {
            adjustedTransparency = 0.f;
        }
    }

    if (_fadeOutThreshold > -1.0) {
        const float logDistCamera = glm::log(static_cast<float>(
            glm::distance(data.camera.positionVec3(), data.modelTransform.translation)
        ));
        const float startLogFadeDistance = glm::log(_size * _fadeOutThreshold);
        const float stopLogFadeDistance = startLogFadeDistance + 1.f;

        if (logDistCamera > startLogFadeDistance && logDistCamera < stopLogFadeDistance) {
            const float fadeFactor = glm::clamp(
                (logDistCamera - startLogFadeDistance) /
                (stopLogFadeDistance - startLogFadeDistance),
                0.f,
                1.f
            );
            adjustedTransparency *= (1.f - fadeFactor);
        }
        else if (logDistCamera >= stopLogFadeDistance) {
            adjustedTransparency = 0.f;
        }
    }

    // Performance wise
    if (adjustedTransparency < 0.01f) {
        return;
    }

    _shader->setUniform(_uniformCache.opacity, adjustedTransparency);
    _shader->setUniform(_uniformCache._mirrorTexture, _mirrorTexture.value());

    ghoul::opengl::TextureUnit unit;
    unit.activate();
    _texture->bind();
    _shader->setUniform(_uniformCache.colorTexture, unit);

    // Setting these states should not be necessary,
    // since they are the default state in OpenSpace.
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);

    if (orientation == Orientation::Inside) {
        glCullFace(GL_FRONT);
    } else if (orientation == Orientation::Both) {
        glDisable(GL_CULL_FACE);
    }

    bool usingFramebufferRenderer = global::renderEngine.rendererImplementation() ==
                                    RenderEngine::RendererImplementation::Framebuffer;

    bool usingABufferRenderer = global::renderEngine.rendererImplementation() ==
                                RenderEngine::RendererImplementation::ABuffer;

    if (usingABufferRenderer && _useAdditiveBlending) {
        _shader->setUniform("additiveBlending", true);
    }

    if (usingFramebufferRenderer && _useAdditiveBlending) {
        glBlendFunc(GL_SRC_ALPHA, GL_ONE);
        glDepthMask(false);
    }

    _sphere->render();

    if (usingFramebufferRenderer && _useAdditiveBlending) {
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glDepthMask(true);
    }

    _shader->setIgnoreUniformLocationError(IgnoreError::No);
    _shader->deactivate();

    if (orientation == Orientation::Inside) {
        glCullFace(GL_BACK);
    } else if (orientation == Orientation::Both) {
        glEnable(GL_CULL_FACE);
    }
}

void RenderableSphere::update(const UpdateData&) {
    if (_shader->isDirty()) {
        _shader->rebuildFromFile();
        ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames);
    }

    if (_sphereIsDirty) {
        _sphere = std::make_unique<PowerScaledSphere>(_size, _segments);
        _sphere->initialize();
        _sphereIsDirty = false;
    }
}

void RenderableSphere::loadTexture() {
    if (!_texturePath.value().empty()) {
        std::unique_ptr<ghoul::opengl::Texture> texture =
            ghoul::io::TextureReader::ref().loadTexture(_texturePath);

        if (texture) {
            LDEBUGC(
                "RenderableSphere",
                fmt::format("Loaded texture from '{}'", absPath(_texturePath))
            );
            texture->uploadTexture();
            texture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap);
            _texture = std::move(texture);
        }
    }
}

} // namespace openspace
<commit_msg>Fixed invalid OpenGL state.<commit_after>/*****************************************************************************************
 *                                                                                *
 * OpenSpace                                                                             *
 *                                                                                *
 * Copyright (c) 2014-2019                                                               *
 *                                                                                *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this  *
 * software and associated documentation files (the "Software"), to deal in the Software *
 * without restriction, including without limitation the rights to use, copy, modify,    *
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to    *
 * permit persons to whom the Software is furnished to do so, subject to the following   *
 * conditions:                                                                           *
 *                                                                                *
 * The above copyright notice and this permission notice shall be included in all copies *
 * or substantial portions of the Software.                                              *
 *                                                                                *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,   *
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A         *
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT    *
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF  *
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE  *
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                                         *
 ****************************************************************************************/

#include <modules/base/rendering/renderablesphere.h>

#include <modules/base/basemodule.h>
#include <openspace/documentation/documentation.h>
#include <openspace/documentation/verifier.h>
#include <openspace/engine/globals.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/util/powerscaledsphere.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/glm.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/io/texture/texturereader.h>
#include <ghoul/opengl/texture.h>
#include <ghoul/opengl/textureunit.h>
#include <ghoul/opengl/programobject.h>

namespace {
    constexpr const char* ProgramName = "Sphere";

    constexpr const std::array<const char*, 5> UniformNames = {
        "opacity", "modelViewProjection", "modelViewRotation", "colorTexture",
        "mirrorTexture"
    };

    enum class Orientation : int {
        Outside = 0,
        Inside,
        Both
    };

    constexpr openspace::properties::Property::PropertyInfo TextureInfo = {
        "Texture",
        "Texture",
        "This value specifies an image that is loaded from disk and is used as a texture "
        "that is applied to this sphere. This image is expected to be an equirectangular "
        "projection."
    };

    constexpr openspace::properties::Property::PropertyInfo MirrorTextureInfo = {
        "MirrorTexture",
        "Mirror Texture",
        "Mirror the texture along the x-axis."
    };

    constexpr openspace::properties::Property::PropertyInfo OrientationInfo = {
        "Orientation",
        "Orientation",
        "Specifies whether the texture is applied to the inside of the sphere, the "
        "outside of the sphere, or both."
    };

    constexpr openspace::properties::Property::PropertyInfo UseAdditiveBlendingInfo = {
        "UseAdditiveBlending",
        "Use Additive Blending",
        "Render the object using additive blending."
    };

    constexpr openspace::properties::Property::PropertyInfo SegmentsInfo = {
        "Segments",
        "Number of Segments",
        "This value specifies the number of segments that the sphere is separated in."
    };

    constexpr openspace::properties::Property::PropertyInfo SizeInfo = {
        "Size",
        "Size (in meters)",
        "This value specifies the radius of the sphere in meters."
    };

    constexpr openspace::properties::Property::PropertyInfo FadeOutThresholdInfo = {
        "FadeOutThreshold",
        "Fade-Out Threshold",
        "This value determines percentage of the sphere is visible before starting "
        "fading-out it."
    };

    constexpr openspace::properties::Property::PropertyInfo FadeInThresholdInfo = {
        "FadeInThreshold",
        "Fade-In Threshold",
        "Distance from center of MilkyWay from where the astronomical object starts to "
        "fade in."
    };

    constexpr openspace::properties::Property::PropertyInfo DisableFadeInOutInfo = {
        "DisableFadeInOut",
        "Disable Fade-In/Fade-Out effects",
        "Enables/Disables the Fade-In/Out effects."
    };

    constexpr openspace::properties::Property::PropertyInfo BackgroundInfo = {
        "Background",
        "Sets the current sphere rendering as a background rendering type",
        "Enables/Disables background rendering."
    };
} // namespace

namespace openspace {

documentation::Documentation RenderableSphere::Documentation() {
    using namespace documentation;
    return {
        "RenderableSphere",
        "base_renderable_sphere",
        {
            {
                SizeInfo.identifier,
                new DoubleVerifier,
                Optional::No,
                SizeInfo.description
            },
            {
                SegmentsInfo.identifier,
                new IntVerifier,
                Optional::No,
                SegmentsInfo.description
            },
            {
                TextureInfo.identifier,
                new StringVerifier,
                Optional::No,
                TextureInfo.description
            },
            {
                OrientationInfo.identifier,
                new StringInListVerifier({ "Inside", "Outside", "Both" }),
                Optional::Yes,
                OrientationInfo.description
            },
            {
                UseAdditiveBlendingInfo.identifier,
                new BoolVerifier,
                Optional::Yes,
                UseAdditiveBlendingInfo.description
            },
            {
                MirrorTextureInfo.identifier,
                new BoolVerifier,
                Optional::Yes,
                MirrorTextureInfo.description
            },
            {
                FadeOutThresholdInfo.identifier,
                new DoubleInRangeVerifier(0.0, 1.0),
                Optional::Yes,
                FadeOutThresholdInfo.description
            },
            {
                FadeInThresholdInfo.identifier,
                new DoubleVerifier,
                Optional::Yes,
                FadeInThresholdInfo.description
            },
            {
                DisableFadeInOutInfo.identifier,
                new BoolVerifier,
                Optional::Yes,
                DisableFadeInOutInfo.description
            },
            {
                BackgroundInfo.identifier,
                new BoolVerifier,
                Optional::Yes,
                BackgroundInfo.description
            },
        }
    };
}

RenderableSphere::RenderableSphere(const ghoul::Dictionary& dictionary)
    : Renderable(dictionary)
    , _texturePath(TextureInfo)
    , _orientation(OrientationInfo, properties::OptionProperty::DisplayType::Dropdown)
    , _size(SizeInfo, 1.f, 0.f, 1e35f)
    , _segments(SegmentsInfo, 8, 4, 1000)
    , _mirrorTexture(MirrorTextureInfo, false)
    , _useAdditiveBlending(UseAdditiveBlendingInfo, false)
    , _disableFadeInDistance(DisableFadeInOutInfo, true)
    , _backgroundRendering(BackgroundInfo, false)
    , _fadeInThreshold(FadeInThresholdInfo, -1.f, 0.f, 1.f)
    , _fadeOutThreshold(FadeOutThresholdInfo, -1.f, 0.f, 1.f)
{
    documentation::testSpecificationAndThrow(
        Documentation(),
        dictionary,
        "RenderableSphere"
    );

    addProperty(_opacity);
    registerUpdateRenderBinFromOpacity();

    _size = static_cast<float>(dictionary.value<double>(SizeInfo.identifier));
    _segments = static_cast<int>(dictionary.value<double>(SegmentsInfo.identifier));
    _texturePath = absPath(dictionary.value<std::string>(TextureInfo.identifier));

    _orientation.addOptions({
        { static_cast<int>(Orientation::Outside), "Outside" },
        { static_cast<int>(Orientation::Inside), "Inside" },
        { static_cast<int>(Orientation::Both), "Both" }
    });

    if (dictionary.hasKey(OrientationInfo.identifier)) {
        const std::string& v = dictionary.value<std::string>(OrientationInfo.identifier);
        if (v == "Inside") {
            _orientation = static_cast<int>(Orientation::Inside);
        }
        else if (v == "Outside") {
            _orientation = static_cast<int>(Orientation::Outside);
        }
        else if (v == "Both") {
            _orientation = static_cast<int>(Orientation::Both);
        }
        else {
            throw ghoul::MissingCaseException();
        }
    }
    else {
        _orientation = static_cast<int>(Orientation::Outside);
    }
    addProperty(_orientation);

    addProperty(_size);
    _size.onChange([this]() { _sphereIsDirty = true; });

    addProperty(_segments);
    _segments.onChange([this]() { _sphereIsDirty = true; });

    addProperty(_texturePath);
    _texturePath.onChange([this]() { loadTexture(); });

    addProperty(_mirrorTexture);
    addProperty(_useAdditiveBlending);

    if (dictionary.hasKey(MirrorTextureInfo.identifier)) {
       _mirrorTexture = dictionary.value<bool>(MirrorTextureInfo.identifier);
    }
    if (dictionary.hasKey(UseAdditiveBlendingInfo.identifier)) {
        _useAdditiveBlending = dictionary.value<bool>(UseAdditiveBlendingInfo.identifier);

        if (_useAdditiveBlending) {
            setRenderBin(Renderable::RenderBin::Transparent);
        }
    }

    if (dictionary.hasKey(FadeOutThresholdInfo.identifier)) {
        _fadeOutThreshold = static_cast<float>(
            dictionary.value<double>(FadeOutThresholdInfo.identifier)
        );
        addProperty(_fadeOutThreshold);
    }

    if (dictionary.hasKey(FadeInThresholdInfo.identifier)) {
        _fadeInThreshold = static_cast<float>(
            dictionary.value<double>(FadeInThresholdInfo.identifier)
        );
        addProperty(_fadeInThreshold);
    }

    if (dictionary.hasKey(FadeOutThresholdInfo.identifier) ||
        dictionary.hasKey(FadeInThresholdInfo.identifier)) {
        _disableFadeInDistance.set(false);
        addProperty(_disableFadeInDistance);
    }

    if (dictionary.hasKey(BackgroundInfo.identifier)) {
        _backgroundRendering = dictionary.value<bool>(BackgroundInfo.identifier);

        if (_backgroundRendering) {
            setRenderBin(Renderable::RenderBin::Background);
        }
    }
}

bool RenderableSphere::isReady() const {
    return _shader && _texture;
}

void RenderableSphere::initializeGL() {
    _sphere = std::make_unique<PowerScaledSphere>(_size, _segments);
    _sphere->initialize();

    _shader = BaseModule::ProgramObjectManager.request(
        ProgramName,
        []() -> std::unique_ptr<ghoul::opengl::ProgramObject> {
            return global::renderEngine.buildRenderProgram(
                ProgramName,
                absPath("${MODULE_BASE}/shaders/sphere_vs.glsl"),
                absPath("${MODULE_BASE}/shaders/sphere_fs.glsl")
            );
        }
    );

    ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames);

    loadTexture();
}

void RenderableSphere::deinitializeGL() {
    _texture = nullptr;

    BaseModule::ProgramObjectManager.release(
        ProgramName,
        [](ghoul::opengl::ProgramObject* p) {
            global::renderEngine.removeRenderProgram(p);
        }
    );
    _shader = nullptr;
}

void RenderableSphere::render(const RenderData& data, RendererTasks&) {
    Orientation orientation = static_cast<Orientation>(_orientation.value());

    glm::dmat4 modelTransform =
        glm::translate(glm::dmat4(1.0), data.modelTransform.translation) *
        glm::dmat4(data.modelTransform.rotation) *
        glm::scale(glm::dmat4(1.0), glm::dvec3(data.modelTransform.scale));

    glm::dmat3 modelRotation =
        glm::dmat3(data.modelTransform.rotation);

    // Activate shader
    using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError;
    _shader->activate();
    _shader->setIgnoreUniformLocationError(IgnoreError::Yes);

    glm::mat4 modelViewProjection = data.camera.projectionMatrix() *
                             glm::mat4(data.camera.combinedViewMatrix() * modelTransform);
    _shader->setUniform(_uniformCache.modelViewProjection, modelViewProjection);

    glm::mat3 modelViewRotation = glm::mat3(
        glm::dmat3(data.camera.viewRotationMatrix()) * modelRotation
    );
    _shader->setUniform(_uniformCache.modelViewRotation, modelViewRotation);

    float adjustedTransparency = _opacity;

    if (_fadeInThreshold > -1.0) {
        const float logDistCamera = glm::log(static_cast<float>(
            glm::distance(data.camera.positionVec3(), data.modelTransform.translation)
        ));
        const float startLogFadeDistance = glm::log(_size * _fadeInThreshold);
        const float stopLogFadeDistance = startLogFadeDistance + 1.f;

        if (logDistCamera > startLogFadeDistance && logDistCamera < stopLogFadeDistance) {
            const float fadeFactor = glm::clamp(
                (logDistCamera - startLogFadeDistance) /
                (stopLogFadeDistance - startLogFadeDistance),
                0.f,
                1.f
            );
            adjustedTransparency *= fadeFactor;
        }
        else if (logDistCamera <= startLogFadeDistance) {
            adjustedTransparency = 0.f;
        }
    }

    if (_fadeOutThreshold > -1.0) {
        const float logDistCamera = glm::log(static_cast<float>(
            glm::distance(data.camera.positionVec3(), data.modelTransform.translation)
        ));
        const float startLogFadeDistance = glm::log(_size * _fadeOutThreshold);
        const float stopLogFadeDistance = startLogFadeDistance + 1.f;

        if (logDistCamera > startLogFadeDistance && logDistCamera < stopLogFadeDistance) {
            const float fadeFactor = glm::clamp(
                (logDistCamera - startLogFadeDistance) /
                (stopLogFadeDistance - startLogFadeDistance),
                0.f,
                1.f
            );
            adjustedTransparency *= (1.f - fadeFactor);
        }
        else if (logDistCamera >= stopLogFadeDistance) {
            adjustedTransparency = 0.f;
        }
    }

    // Performance wise
    if (adjustedTransparency < 0.01f) {
        return;
    }

    _shader->setUniform(_uniformCache.opacity, adjustedTransparency);
    _shader->setUniform(_uniformCache._mirrorTexture, _mirrorTexture.value());

    ghoul::opengl::TextureUnit unit;
    unit.activate();
    _texture->bind();
    _shader->setUniform(_uniformCache.colorTexture, unit);

    // Setting these states should not be necessary,
    // since they are the default state in OpenSpace.
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);

    if (orientation == Orientation::Inside) {
        glCullFace(GL_FRONT);
    } else if (orientation == Orientation::Both) {
        glDisable(GL_CULL_FACE);
    }

    bool usingFramebufferRenderer = global::renderEngine.rendererImplementation() ==
                                    RenderEngine::RendererImplementation::Framebuffer;

    bool usingABufferRenderer = global::renderEngine.rendererImplementation() ==
                                RenderEngine::RendererImplementation::ABuffer;

    if (usingABufferRenderer && _useAdditiveBlending) {
        _shader->setUniform("additiveBlending", true);
    }

    if (usingFramebufferRenderer && _useAdditiveBlending) {
        glBlendFunc(GL_SRC_ALPHA, GL_ONE);
        glDepthMask(false);
    }

    _sphere->render();

    if (usingFramebufferRenderer && _useAdditiveBlending) {
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glDepthMask(true);
    }

    _shader->setIgnoreUniformLocationError(IgnoreError::No);
    _shader->deactivate();

    if (orientation == Orientation::Inside) {
        glCullFace(GL_BACK);
    } else if (orientation == Orientation::Both) {
        glEnable(GL_CULL_FACE);
    }
}

void RenderableSphere::update(const UpdateData&) {
    if (_shader->isDirty()) {
        _shader->rebuildFromFile();
        ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames);
    }

    if (_sphereIsDirty) {
        _sphere = std::make_unique<PowerScaledSphere>(_size, _segments);
        _sphere->initialize();
        _sphereIsDirty = false;
    }
}

void RenderableSphere::loadTexture() {
    if (!_texturePath.value().empty()) {
        std::unique_ptr<ghoul::opengl::Texture> texture =
            ghoul::io::TextureReader::ref().loadTexture(_texturePath);

        if (texture) {
            LDEBUGC(
                "RenderableSphere",
                fmt::format("Loaded texture from '{}'", absPath(_texturePath))
            );
            texture->uploadTexture();
            texture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap);
            _texture = std::move(texture);
        }
    }
}

} // namespace openspace
<|endoftext|>


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

#include "chrome/browser/profiles/profile_shortcut_manager_win.h"

#include "base/bind.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/app_icon_win.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile_info_cache.h"
#include "chrome/browser/profiles/profile_info_util.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/shell_util.h"
#include "content/public/browser/browser_thread.h"
#include "grit/generated_resources.h"
#include "skia/ext/image_operations.h"
#include "skia/ext/platform_canvas.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/icon_util.h"
#include "ui/gfx/image/image.h"

using content::BrowserThread;

namespace {

const char kProfileIconFileName[] = "Google Profile.ico";
const int kProfileAvatarShortcutBadgeWidth = 28;
const int kProfileAvatarShortcutBadgeHeight = 28;
const int kShortcutIconSize = 48;

// Creates the argument to pass to the Windows executable that launches Chrome
// with the profile in |profile_base_dir|.
// For example: --profile-directory="Profile 2"
string16 CreateProfileShortcutSwitch(string16 profile_base_dir) {
  string16 profile_directory = base::StringPrintf(L"--%ls=\"%ls\"",
      ASCIIToUTF16(switches::kProfileDirectory).c_str(),
      profile_base_dir.c_str());
  return profile_directory;
}

// Wrap a ShellUtil function that returns a bool so it can be posted in a
// task to the FILE thread.
void CallShellUtilBoolFunction(
    const base::Callback<bool(void)>& bool_function) {
  bool_function.Run();
}

// Creates a desktop shortcut icon file (.ico) on the disk for a given profile,
// badging the browser distribution icon with the profile avatar.
// |profile_base_dir| is the base directory (and key) of the profile. Returns
// a path to the shortcut icon file on disk, which is empty if this fails.
// Use index 0 when assigning the resulting file as the icon.
FilePath CreateChromeDesktopShortcutIconForProfile(
    const FilePath& profile_path,
    const gfx::Image& avatar_image) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  const SkBitmap* avatar_bitmap = avatar_image.ToSkBitmap();
  HICON app_icon_handle = GetAppIconForSize(kShortcutIconSize);
  scoped_ptr<SkBitmap> app_icon_bitmap(
      IconUtil::CreateSkBitmapFromHICON(app_icon_handle));
  DestroyIcon(app_icon_handle);
  if (!app_icon_bitmap.get())
    return FilePath();

  // TODO(stevet): Share this chunk of code with
  // avatar_menu_button::DrawTaskBarDecoration.
  const SkBitmap* source_bitmap = NULL;
  SkBitmap squarer_bitmap;
  if ((avatar_bitmap->width() == profiles::kAvatarIconWidth) &&
      (avatar_bitmap->height() == profiles::kAvatarIconHeight)) {
    // Shave a couple of columns so the bitmap is more square. So when
    // resized to a square aspect ratio it looks pretty.
    int x = 2;
    avatar_bitmap->extractSubset(&squarer_bitmap, SkIRect::MakeXYWH(x, 0,
        profiles::kAvatarIconWidth - x * 2, profiles::kAvatarIconHeight));
    source_bitmap = &squarer_bitmap;
  } else {
    source_bitmap = avatar_bitmap;
  }
  SkBitmap sk_icon = skia::ImageOperations::Resize(
      *source_bitmap,
      skia::ImageOperations::RESIZE_LANCZOS3,
      kProfileAvatarShortcutBadgeWidth,
      kProfileAvatarShortcutBadgeHeight);

  // Overlay the avatar on the icon, anchoring it to the bottom-right of the
  // icon.
  scoped_ptr<SkCanvas> offscreen_canvas(
      skia::CreateBitmapCanvas(app_icon_bitmap->width(),
                               app_icon_bitmap->height(),
                               false));
  DCHECK(offscreen_canvas.get());
  offscreen_canvas->drawBitmap(*app_icon_bitmap, 0, 0);
  offscreen_canvas->drawBitmap(
      sk_icon,
      app_icon_bitmap->width() - kProfileAvatarShortcutBadgeWidth,
      app_icon_bitmap->height() - kProfileAvatarShortcutBadgeHeight);
  const SkBitmap& final_bitmap =
      offscreen_canvas->getDevice()->accessBitmap(false);

  // Finally, write the .ico file containing this new bitmap.
  FilePath icon_path = profile_path.AppendASCII(kProfileIconFileName);
  if (!IconUtil::CreateIconFileFromSkBitmap(final_bitmap, icon_path))
    return FilePath();

  return icon_path;
}

// Creates a desktop shortcut to open Chrome with the given profile name and
// base directory. Iff |create|, create shortcut if it doesn't already exist.
// Must be called on the FILE thread.
void CreateChromeDesktopShortcutForProfile(
    const string16& profile_name,
    const string16& profile_base_dir,
    const FilePath& profile_path,
    const gfx::Image* avatar_image,
    bool create) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  FilePath chrome_exe;
  if (!PathService::Get(base::FILE_EXE, &chrome_exe))
    return;
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  string16 description;
  if (!dist)
    return;
  else
    description = WideToUTF16(dist->GetAppDescription());
  const string16& directory = CreateProfileShortcutSwitch(profile_base_dir);
  FilePath icon_path = avatar_image ?
      CreateChromeDesktopShortcutIconForProfile(profile_path, *avatar_image) :
      FilePath();

  ShellUtil::CreateChromeDesktopShortcut(
      dist,
      chrome_exe.value(),
      description,
      profile_name,
      directory,
      icon_path.empty() ? chrome_exe.value() : icon_path.value(),
      icon_path.empty() ? dist->GetIconIndex() : 0,
      ShellUtil::CURRENT_USER,
      false,  // Use alternate text.
      create);  // Create if it doesn't already exist.
}

// Renames an existing Chrome desktop profile shortcut. Must be called on the
// FILE thread.
void RenameChromeDesktopShortcutForProfile(
    const string16& old_shortcut,
    const string16& new_shortcut) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  FilePath shortcut_path;
  if (ShellUtil::GetDesktopPath(false,  // User's directory instead of system.
                                &shortcut_path)) {
    FilePath old_profile = shortcut_path.Append(old_shortcut);
    FilePath new_profile = shortcut_path.Append(new_shortcut);
    file_util::Move(old_profile, new_profile);
  }
}

// Updates the arguments to a Chrome desktop shortcut for a profile. Must be
// called on the FILE thread.
void UpdateChromeDesktopShortcutForProfile(
    const string16& shortcut,
    const string16& arguments,
    const FilePath& profile_path,
    const gfx::Image* avatar_image) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  FilePath shortcut_path;
  if (!ShellUtil::GetDesktopPath(false, &shortcut_path))
    return;

  shortcut_path = shortcut_path.Append(shortcut);
  FilePath chrome_exe;
  if (!PathService::Get(base::FILE_EXE, &chrome_exe))
    return;
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  string16 description;
  if (!dist)
    return;
  else
    description = WideToUTF16(dist->GetAppDescription());
  FilePath icon_path = avatar_image ?
      CreateChromeDesktopShortcutIconForProfile(profile_path, *avatar_image) :
      FilePath();

  ShellUtil::UpdateChromeShortcut(
      dist,
      chrome_exe.value(),
      shortcut_path.value(),
      arguments,
      description,
      icon_path.empty() ? chrome_exe.value() : icon_path.value(),
      icon_path.empty() ? dist->GetIconIndex() : 0,
      false);
}

}  // namespace

ProfileShortcutManagerWin::ProfileShortcutManagerWin() {
}

ProfileShortcutManagerWin::~ProfileShortcutManagerWin() {
}

void ProfileShortcutManagerWin::OnProfileAdded(
    const string16& profile_name,
    const string16& profile_base_dir,
    const FilePath& profile_path,
    const gfx::Image* avatar_image) {
  // Launch task to add shortcut to desktop on Windows. If this is the very
  // first profile created, don't add the user name to the shortcut.
  // TODO(mirandac): respect master_preferences choice to create no shortcuts
  // (see http://crbug.com/104463)
  if (g_browser_process->profile_manager()->GetNumberOfProfiles() > 1) {
    {
      // We make a copy of the Image to ensure that the underlying image data is
      // AddRef'd, in case the original copy gets deleted.
      gfx::Image* avatar_copy = avatar_image ?
          new gfx::Image(*avatar_image) : NULL;
      BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
          base::Bind(&CreateChromeDesktopShortcutForProfile,
                     profile_name, profile_base_dir, profile_path,
                     base::Owned(avatar_copy), true));
    }

    // If this is the very first multi-user account created, change the
    // original shortcut to launch with the First User profile.
    PrefService* local_state = g_browser_process->local_state();
    if (local_state->GetInteger(prefs::kProfilesNumCreated) == 2) {
      string16 default_name = l10n_util::GetStringUTF16(
          IDS_DEFAULT_PROFILE_NAME);
      BrowserDistribution* dist = BrowserDistribution::GetDistribution();

      string16 old_shortcut;
      string16 new_shortcut;
      if (ShellUtil::GetChromeShortcutName(dist, false, L"", &old_shortcut) &&
          ShellUtil::GetChromeShortcutName(dist, false, default_name,
                                           &new_shortcut)) {
        // Update doesn't allow changing the target, so rename first.
        BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
            base::Bind(&RenameChromeDesktopShortcutForProfile,
                       old_shortcut, new_shortcut));
        // TODO(stevet): We actually need to retrieve the newly assigned avatar
        // icon for the original profile here and update it with that.
        BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
            base::Bind(&UpdateChromeDesktopShortcutForProfile,
                       new_shortcut, UTF8ToUTF16(chrome::kInitialProfile),
                       profile_path, static_cast<gfx::Image*>(NULL)));
      }
    }
  } else {  // Only one profile, so create original shortcut, with no avatar.
    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
        base::Bind(&CreateChromeDesktopShortcutForProfile,
                   L"", L"", FilePath(), static_cast<gfx::Image*>(NULL), true));
  }
}

void ProfileShortcutManagerWin::OnProfileRemoved(
    const string16& profile_name) {
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  string16 shortcut;
  if (ShellUtil::GetChromeShortcutName(dist, false, profile_name, &shortcut)) {
    std::vector<string16> shortcuts(1, shortcut);
    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
        base::Bind(&CallShellUtilBoolFunction,
            base::Bind(
                &ShellUtil::RemoveChromeDesktopShortcutsWithAppendedNames,
                shortcuts)));
  }
}

void ProfileShortcutManagerWin::OnProfileNameChanged(
    const string16& old_profile_name,
    const string16& new_profile_name) {
  // Launch task to change name of desktop shortcut on Windows.
  // TODO(mirandac): respect master_preferences choice to create no shortcuts
  // (see http://crbug.com/104463)
  string16 old_shortcut;
  string16 new_shortcut;
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  if (ShellUtil::GetChromeShortcutName(
          dist, false, old_profile_name, &old_shortcut) &&
      ShellUtil::GetChromeShortcutName(
          dist, false, new_profile_name, &new_shortcut)) {
    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
        base::Bind(&RenameChromeDesktopShortcutForProfile,
                   old_shortcut,
                   new_shortcut));
  }
}

void ProfileShortcutManagerWin::OnProfileAvatarChanged(
      const string16& profile_name,
      const string16& profile_base_dir,
      const FilePath& profile_path,
      const gfx::Image* avatar_image) {
  // Launch task to change the icon of the desktop shortcut on windows.
  string16 new_shortcut;
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  if (ShellUtil::GetChromeShortcutName(dist, false, profile_name,
                                       &new_shortcut)) {
    // We make a copy of the Image to ensure that the underlying image data is
    // AddRef'd, in case the original copy gets deleted.
    gfx::Image* avatar_copy = avatar_image ?
        new gfx::Image(*avatar_image) : NULL;
    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
        base::Bind(&UpdateChromeDesktopShortcutForProfile,
                   new_shortcut,
                   CreateProfileShortcutSwitch(profile_base_dir),
                   profile_path,
                   base::Owned(avatar_copy)));
  }
}

// static
std::vector<string16> ProfileShortcutManagerWin::GenerateShortcutsFromProfiles(
    const std::vector<string16>& profile_names) {
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  std::vector<string16> shortcuts;
  shortcuts.reserve(profile_names.size());
  for (std::vector<string16>::const_iterator it = profile_names.begin();
       it != profile_names.end();
       ++it) {
    string16 shortcut;
    if (ShellUtil::GetChromeShortcutName(dist, false, *it, &shortcut))
      shortcuts.push_back(shortcut);
  }
  return shortcuts;
}
<commit_msg>Fix an issue where the first profile desktop shortcut gets the correct arguments.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/profiles/profile_shortcut_manager_win.h"

#include "base/bind.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/app_icon_win.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile_info_cache.h"
#include "chrome/browser/profiles/profile_info_util.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/shell_util.h"
#include "content/public/browser/browser_thread.h"
#include "grit/generated_resources.h"
#include "skia/ext/image_operations.h"
#include "skia/ext/platform_canvas.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/icon_util.h"
#include "ui/gfx/image/image.h"

using content::BrowserThread;

namespace {

const char kProfileIconFileName[] = "Google Profile.ico";
const int kProfileAvatarShortcutBadgeWidth = 28;
const int kProfileAvatarShortcutBadgeHeight = 28;
const int kShortcutIconSize = 48;

// Creates the argument to pass to the Windows executable that launches Chrome
// with the profile in |profile_base_dir|.
// For example: --profile-directory="Profile 2"
string16 CreateProfileShortcutSwitch(string16 profile_base_dir) {
  string16 profile_directory = base::StringPrintf(L"--%ls=\"%ls\"",
      ASCIIToUTF16(switches::kProfileDirectory).c_str(),
      profile_base_dir.c_str());
  return profile_directory;
}

// Wrap a ShellUtil function that returns a bool so it can be posted in a
// task to the FILE thread.
void CallShellUtilBoolFunction(
    const base::Callback<bool(void)>& bool_function) {
  bool_function.Run();
}

// Creates a desktop shortcut icon file (.ico) on the disk for a given profile,
// badging the browser distribution icon with the profile avatar.
// |profile_base_dir| is the base directory (and key) of the profile. Returns
// a path to the shortcut icon file on disk, which is empty if this fails.
// Use index 0 when assigning the resulting file as the icon.
FilePath CreateChromeDesktopShortcutIconForProfile(
    const FilePath& profile_path,
    const gfx::Image& avatar_image) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  const SkBitmap* avatar_bitmap = avatar_image.ToSkBitmap();
  HICON app_icon_handle = GetAppIconForSize(kShortcutIconSize);
  scoped_ptr<SkBitmap> app_icon_bitmap(
      IconUtil::CreateSkBitmapFromHICON(app_icon_handle));
  DestroyIcon(app_icon_handle);
  if (!app_icon_bitmap.get())
    return FilePath();

  // TODO(stevet): Share this chunk of code with
  // avatar_menu_button::DrawTaskBarDecoration.
  const SkBitmap* source_bitmap = NULL;
  SkBitmap squarer_bitmap;
  if ((avatar_bitmap->width() == profiles::kAvatarIconWidth) &&
      (avatar_bitmap->height() == profiles::kAvatarIconHeight)) {
    // Shave a couple of columns so the bitmap is more square. So when
    // resized to a square aspect ratio it looks pretty.
    int x = 2;
    avatar_bitmap->extractSubset(&squarer_bitmap, SkIRect::MakeXYWH(x, 0,
        profiles::kAvatarIconWidth - x * 2, profiles::kAvatarIconHeight));
    source_bitmap = &squarer_bitmap;
  } else {
    source_bitmap = avatar_bitmap;
  }
  SkBitmap sk_icon = skia::ImageOperations::Resize(
      *source_bitmap,
      skia::ImageOperations::RESIZE_LANCZOS3,
      kProfileAvatarShortcutBadgeWidth,
      kProfileAvatarShortcutBadgeHeight);

  // Overlay the avatar on the icon, anchoring it to the bottom-right of the
  // icon.
  scoped_ptr<SkCanvas> offscreen_canvas(
      skia::CreateBitmapCanvas(app_icon_bitmap->width(),
                               app_icon_bitmap->height(),
                               false));
  DCHECK(offscreen_canvas.get());
  offscreen_canvas->drawBitmap(*app_icon_bitmap, 0, 0);
  offscreen_canvas->drawBitmap(
      sk_icon,
      app_icon_bitmap->width() - kProfileAvatarShortcutBadgeWidth,
      app_icon_bitmap->height() - kProfileAvatarShortcutBadgeHeight);
  const SkBitmap& final_bitmap =
      offscreen_canvas->getDevice()->accessBitmap(false);

  // Finally, write the .ico file containing this new bitmap.
  FilePath icon_path = profile_path.AppendASCII(kProfileIconFileName);
  if (!IconUtil::CreateIconFileFromSkBitmap(final_bitmap, icon_path))
    return FilePath();

  return icon_path;
}

// Creates a desktop shortcut to open Chrome with the given profile name and
// base directory. Iff |create|, create shortcut if it doesn't already exist.
// Must be called on the FILE thread.
void CreateChromeDesktopShortcutForProfile(
    const string16& profile_name,
    const string16& profile_base_dir,
    const FilePath& profile_path,
    const gfx::Image* avatar_image,
    bool create) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  FilePath chrome_exe;
  if (!PathService::Get(base::FILE_EXE, &chrome_exe))
    return;
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  string16 description;
  if (!dist)
    return;
  else
    description = WideToUTF16(dist->GetAppDescription());
  const string16& directory = CreateProfileShortcutSwitch(profile_base_dir);
  FilePath icon_path = avatar_image ?
      CreateChromeDesktopShortcutIconForProfile(profile_path, *avatar_image) :
      FilePath();

  ShellUtil::CreateChromeDesktopShortcut(
      dist,
      chrome_exe.value(),
      description,
      profile_name,
      directory,
      icon_path.empty() ? chrome_exe.value() : icon_path.value(),
      icon_path.empty() ? dist->GetIconIndex() : 0,
      ShellUtil::CURRENT_USER,
      false,  // Use alternate text.
      create);  // Create if it doesn't already exist.
}

// Renames an existing Chrome desktop profile shortcut. Must be called on the
// FILE thread.
void RenameChromeDesktopShortcutForProfile(
    const string16& old_shortcut,
    const string16& new_shortcut) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  FilePath shortcut_path;
  if (ShellUtil::GetDesktopPath(false,  // User's directory instead of system.
                                &shortcut_path)) {
    FilePath old_profile = shortcut_path.Append(old_shortcut);
    FilePath new_profile = shortcut_path.Append(new_shortcut);
    file_util::Move(old_profile, new_profile);
  }
}

// Updates the arguments to a Chrome desktop shortcut for a profile. Must be
// called on the FILE thread.
void UpdateChromeDesktopShortcutForProfile(
    const string16& shortcut,
    const string16& arguments,
    const FilePath& profile_path,
    const gfx::Image* avatar_image) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  FilePath shortcut_path;
  if (!ShellUtil::GetDesktopPath(false, &shortcut_path))
    return;

  shortcut_path = shortcut_path.Append(shortcut);
  FilePath chrome_exe;
  if (!PathService::Get(base::FILE_EXE, &chrome_exe))
    return;
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  string16 description;
  if (!dist)
    return;
  else
    description = WideToUTF16(dist->GetAppDescription());
  FilePath icon_path = avatar_image ?
      CreateChromeDesktopShortcutIconForProfile(profile_path, *avatar_image) :
      FilePath();

  ShellUtil::UpdateChromeShortcut(
      dist,
      chrome_exe.value(),
      shortcut_path.value(),
      arguments,
      description,
      icon_path.empty() ? chrome_exe.value() : icon_path.value(),
      icon_path.empty() ? dist->GetIconIndex() : 0,
      false);
}

}  // namespace

ProfileShortcutManagerWin::ProfileShortcutManagerWin() {
}

ProfileShortcutManagerWin::~ProfileShortcutManagerWin() {
}

void ProfileShortcutManagerWin::OnProfileAdded(
    const string16& profile_name,
    const string16& profile_base_dir,
    const FilePath& profile_path,
    const gfx::Image* avatar_image) {
  // Launch task to add shortcut to desktop on Windows. If this is the very
  // first profile created, don't add the user name to the shortcut.
  // TODO(mirandac): respect master_preferences choice to create no shortcuts
  // (see http://crbug.com/104463)
  if (g_browser_process->profile_manager()->GetNumberOfProfiles() > 1) {
    {
      // We make a copy of the Image to ensure that the underlying image data is
      // AddRef'd, in case the original copy gets deleted.
      gfx::Image* avatar_copy = avatar_image ?
          new gfx::Image(*avatar_image) : NULL;
      BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
          base::Bind(&CreateChromeDesktopShortcutForProfile,
                     profile_name, profile_base_dir, profile_path,
                     base::Owned(avatar_copy), true));
    }

    // If this is the very first multi-user account created, change the
    // original shortcut to launch with the First User profile.
    PrefService* local_state = g_browser_process->local_state();
    if (local_state->GetInteger(prefs::kProfilesNumCreated) == 2) {
      string16 default_name = l10n_util::GetStringUTF16(
          IDS_DEFAULT_PROFILE_NAME);
      BrowserDistribution* dist = BrowserDistribution::GetDistribution();

      string16 old_shortcut;
      string16 new_shortcut;
      if (ShellUtil::GetChromeShortcutName(dist, false, L"", &old_shortcut) &&
          ShellUtil::GetChromeShortcutName(dist, false, default_name,
                                           &new_shortcut)) {
        // Update doesn't allow changing the target, so rename first.
        BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
            base::Bind(&RenameChromeDesktopShortcutForProfile,
                       old_shortcut, new_shortcut));
        // TODO(stevet): We actually need to retrieve the newly assigned avatar
        // icon for the original profile here and update it with that.
        BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
            base::Bind(&UpdateChromeDesktopShortcutForProfile,
                       new_shortcut,
                       CreateProfileShortcutSwitch(
                           UTF8ToUTF16(chrome::kInitialProfile)),
                       profile_path, static_cast<gfx::Image*>(NULL)));
      }
    }
  } else {  // Only one profile, so create original shortcut, with no avatar.
    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
        base::Bind(&CreateChromeDesktopShortcutForProfile,
                   L"", L"", FilePath(), static_cast<gfx::Image*>(NULL), true));
  }
}

void ProfileShortcutManagerWin::OnProfileRemoved(
    const string16& profile_name) {
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  string16 shortcut;
  if (ShellUtil::GetChromeShortcutName(dist, false, profile_name, &shortcut)) {
    std::vector<string16> shortcuts(1, shortcut);
    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
        base::Bind(&CallShellUtilBoolFunction,
            base::Bind(
                &ShellUtil::RemoveChromeDesktopShortcutsWithAppendedNames,
                shortcuts)));
  }
}

void ProfileShortcutManagerWin::OnProfileNameChanged(
    const string16& old_profile_name,
    const string16& new_profile_name) {
  // Launch task to change name of desktop shortcut on Windows.
  // TODO(mirandac): respect master_preferences choice to create no shortcuts
  // (see http://crbug.com/104463)
  string16 old_shortcut;
  string16 new_shortcut;
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  if (ShellUtil::GetChromeShortcutName(
          dist, false, old_profile_name, &old_shortcut) &&
      ShellUtil::GetChromeShortcutName(
          dist, false, new_profile_name, &new_shortcut)) {
    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
        base::Bind(&RenameChromeDesktopShortcutForProfile,
                   old_shortcut,
                   new_shortcut));
  }
}

void ProfileShortcutManagerWin::OnProfileAvatarChanged(
      const string16& profile_name,
      const string16& profile_base_dir,
      const FilePath& profile_path,
      const gfx::Image* avatar_image) {
  // Launch task to change the icon of the desktop shortcut on windows.
  string16 new_shortcut;
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  if (ShellUtil::GetChromeShortcutName(dist, false, profile_name,
                                       &new_shortcut)) {
    // We make a copy of the Image to ensure that the underlying image data is
    // AddRef'd, in case the original copy gets deleted.
    gfx::Image* avatar_copy = avatar_image ?
        new gfx::Image(*avatar_image) : NULL;
    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
        base::Bind(&UpdateChromeDesktopShortcutForProfile,
                   new_shortcut,
                   CreateProfileShortcutSwitch(profile_base_dir),
                   profile_path,
                   base::Owned(avatar_copy)));
  }
}

// static
std::vector<string16> ProfileShortcutManagerWin::GenerateShortcutsFromProfiles(
    const std::vector<string16>& profile_names) {
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  std::vector<string16> shortcuts;
  shortcuts.reserve(profile_names.size());
  for (std::vector<string16>::const_iterator it = profile_names.begin();
       it != profile_names.end();
       ++it) {
    string16 shortcut;
    if (ShellUtil::GetChromeShortcutName(dist, false, *it, &shortcut))
      shortcuts.push_back(shortcut);
  }
  return shortcuts;
}
<|endoftext|>


<commit_before>#include "MqttPublisher.h"

MqttPublisher::MqttPublisher(string brokerUrl, string clientID)
        : brokerURL(brokerUrl),
          clientID(clientID)
{
    client = nullptr;
}
MqttPublisher::MqttPublisher(string brokerUrl, string userName, string password, string clientID)
        : brokerURL(brokerUrl),
    userName(userName),
    password(password),
    clientID(clientID)
{
    client = nullptr;
}

MqttPublisher::~MqttPublisher() {
    stop();
}

void MqttPublisher::start(bool asyncMode) {
    MqttPublisher::asyncMode = asyncMode;
    if(asyncMode)
        setupAsyncClient();
    else
        setupClient();
}

void onDisconnect(void* context, MQTTAsync_successData* response)
{
    printf("Successful disconnection\n");
}

void MqttPublisher::stop() {
    // Disconnect
    std::cout << "Disconnecting..." << std::flush;
    if(client != nullptr) {
        MQTTClient_disconnect(client, 10000);
        MQTTClient_destroy(&client);
    } else if(asyncClient != nullptr) {
        MQTTAsync_disconnectOptions opts = MQTTAsync_disconnectOptions_initializer;
        int rc;

        opts.onSuccess = onDisconnect;
        opts.context = client;

        if ((rc = MQTTAsync_disconnect(asyncClient, &opts)) != MQTTASYNC_SUCCESS)
        {
            printf("Failed to start sendMessage, return code %d\n", rc);
        }
        MQTTAsync_destroy(&asyncClient);
    }
    client = nullptr;
    asyncClient = nullptr;
    std::cout << "OK" << std::endl;
}

extern "C" void onSend(void* context, MQTTAsync_successData* response)
{
    MQTTAsync client = (MQTTAsync)context;
#ifdef PRINT_DEBUG
    printf("Message with token value %d delivery confirmed\n", response->token);
#endif
}

void MqttPublisher::queueForPublish(string topicName, MqttQOS qos, byte *payload, size_t len) {
    MQTTAsync_message pubmsg = MQTTAsync_message_initializer;
    MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
    int rc;

    opts.onSuccess = onSend;
    opts.context = asyncClient;

    pubmsg.payload = payload;
    pubmsg.payloadlen = len;
    pubmsg.qos = qos;
    pubmsg.retained = 0;
    if ((rc = MQTTAsync_sendMessage(asyncClient, topicName.c_str(), &pubmsg, &opts)) != MQTTASYNC_SUCCESS) {
        printf("Failed to start sendMessage, return code %d\n", rc);
    } else {
#ifdef PRINT_DEBUG
        printf("MQTTAsync_sendMessage\n");
#endif
    }

}
void MqttPublisher::publish(string topicName, MqttQOS qos, byte *payload, size_t len) {
    if(asyncMode) {
        queueForPublish(topicName, qos, payload, len);
        return;
    }

    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    MQTTClient_deliveryToken token;
    pubmsg.payload = payload;
    pubmsg.payloadlen = len;
    pubmsg.qos = qos;
    pubmsg.retained = 0;
    long timeout = 10000L;
    MQTTClient_publishMessage(client, topicName.c_str(), &pubmsg, &token);
#ifdef DEBUG
    printf("Waiting for up to %ld seconds for publication of %s\n"
                    "on topic %s for client with ClientID: %s\n",
            timeout/1000, "beacon", topicName.c_str(), clientID.c_str());
#endif
    int rc = MQTTClient_waitForCompletion(client, token, timeout);
    if(rc != 0)
        printf("Failed to start sendMessage, return code %d\n", rc);
}

void MqttPublisher::setupClient() {
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    std::cout << "Connecting..." << std::flush;
    MQTTClient_create(&client, brokerURL.c_str(), clientID.c_str(),
            MQTTCLIENT_PERSISTENCE_NONE, NULL);

    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;
    int rc;
    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
        printf("Failed to connect, return code %d\n", rc);
    } else {
        std::cout << "OK" << std::endl;
    }
}

extern "C" void onConnectFailure(void* context, MQTTAsync_failureData* response)
{
    printf("Connect failed, rc %d\n", response ? response->code : 0);
}

extern "C" void onConnect(void* context, MQTTAsync_successData* response)
{
    printf("onConnect\n");
}

extern "C" void onConnectionLost(void *context, char *cause)
{
    MQTTAsync asyncClient = (MQTTAsync)context;
    MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
    int rc;

    printf("Connection lost, cause: %s\n", cause);
    printf("Reconnecting\n");
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;
    if ((rc = MQTTAsync_connect(asyncClient, &conn_opts)) != MQTTASYNC_SUCCESS) {
        printf("Failed to start connect, return code %d\n", rc);
    }
}

void MqttPublisher::setupAsyncClient() {
    MQTTAsync_create(&asyncClient, brokerURL.c_str(), clientID.c_str(), MQTTCLIENT_PERSISTENCE_NONE, NULL);
    MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
    int rc;

    MQTTAsync_setCallbacks(client, NULL, onConnectionLost, NULL, NULL);

    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;
    conn_opts.onSuccess = onConnect;
    conn_opts.onFailure = onConnectFailure;
    conn_opts.context = client;
    if ((rc = MQTTAsync_connect(asyncClient, &conn_opts)) != MQTTASYNC_SUCCESS)
    {
        printf("Failed to start connect, return code %d\n", rc);
    }
}<commit_msg>Only set the onFailure sendMessage callback<commit_after>#include <MQTTAsync.h>
#include "MqttPublisher.h"

MqttPublisher::MqttPublisher(string brokerUrl, string clientID)
        : brokerURL(brokerUrl),
          clientID(clientID)
{
    client = nullptr;
}
MqttPublisher::MqttPublisher(string brokerUrl, string userName, string password, string clientID)
        : brokerURL(brokerUrl),
    userName(userName),
    password(password),
    clientID(clientID)
{
    client = nullptr;
}

MqttPublisher::~MqttPublisher() {
    stop();
}

void MqttPublisher::start(bool asyncMode) {
    MqttPublisher::asyncMode = asyncMode;
    if(asyncMode)
        setupAsyncClient();
    else
        setupClient();
}

void onDisconnect(void* context, MQTTAsync_successData* response)
{
    printf("Successful disconnection\n");
}

void MqttPublisher::stop() {
    // Disconnect
    std::cout << "Disconnecting..." << std::flush;
    if(client != nullptr) {
        MQTTClient_disconnect(client, 10000);
        MQTTClient_destroy(&client);
    } else if(asyncClient != nullptr) {
        MQTTAsync_disconnectOptions opts = MQTTAsync_disconnectOptions_initializer;
        int rc;

        opts.onSuccess = onDisconnect;
        opts.context = client;

        if ((rc = MQTTAsync_disconnect(asyncClient, &opts)) != MQTTASYNC_SUCCESS)
        {
            printf("Failed to start sendMessage, return code %d\n", rc);
        }
        MQTTAsync_destroy(&asyncClient);
    }
    client = nullptr;
    asyncClient = nullptr;
    std::cout << "OK" << std::endl;
}

extern "C" void onSend(void* context, MQTTAsync_successData* response)
{
    MQTTAsync client = (MQTTAsync)context;
#ifdef PRINT_DEBUG
    printf("Message with token value %d delivery confirmed\n", response->token);
#endif
}
extern "C" void onSendFailure(void* context,  MQTTAsync_failureData* response) {
    printf("delivery failed, token: %d \n", response->token);
}

void MqttPublisher::queueForPublish(string topicName, MqttQOS qos, byte *payload, size_t len) {
    MQTTAsync_message pubmsg = MQTTAsync_message_initializer;
    MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
    int rc;

    opts.onSuccess = onSend;
    opts.onFailure = onSendFailure;
    opts.context = asyncClient;

    pubmsg.payload = payload;
    pubmsg.payloadlen = len;
    pubmsg.qos = qos;
    pubmsg.retained = 0;
    if ((rc = MQTTAsync_sendMessage(asyncClient, topicName.c_str(), &pubmsg, &opts)) != MQTTASYNC_SUCCESS) {
        printf("Failed to start sendMessage, return code %d\n", rc);
    } else {
#ifdef PRINT_DEBUG
        printf("MQTTAsync_sendMessage\n");
#endif
    }

}
void MqttPublisher::publish(string topicName, MqttQOS qos, byte *payload, size_t len) {
    if(asyncMode) {
        queueForPublish(topicName, qos, payload, len);
        return;
    }

    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    MQTTClient_deliveryToken token;
    pubmsg.payload = payload;
    pubmsg.payloadlen = len;
    pubmsg.qos = qos;
    pubmsg.retained = 0;
    long timeout = 10000L;
    MQTTClient_publishMessage(client, topicName.c_str(), &pubmsg, &token);
#ifdef DEBUG
    printf("Waiting for up to %ld seconds for publication of %s\n"
                    "on topic %s for client with ClientID: %s\n",
            timeout/1000, "beacon", topicName.c_str(), clientID.c_str());
#endif
    int rc = MQTTClient_waitForCompletion(client, token, timeout);
    if(rc != 0)
        printf("Failed to start sendMessage, return code %d\n", rc);
}

void MqttPublisher::setupClient() {
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    std::cout << "Connecting..." << std::flush;
    MQTTClient_create(&client, brokerURL.c_str(), clientID.c_str(),
            MQTTCLIENT_PERSISTENCE_NONE, NULL);

    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;
    int rc;
    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
        printf("Failed to connect, return code %d\n", rc);
    } else {
        std::cout << "OK" << std::endl;
    }
}

extern "C" void onConnectFailure(void* context, MQTTAsync_failureData* response)
{
    printf("Connect failed, rc %d\n", response ? response->code : 0);
}

extern "C" void onConnect(void* context, MQTTAsync_successData* response)
{
    printf("onConnect\n");
}

extern "C" void onConnectionLost(void *context, char *cause)
{
    MQTTAsync asyncClient = (MQTTAsync)context;
    MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
    int rc;

    printf("Connection lost, cause: %s\n", cause);
    printf("Reconnecting\n");
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;
    if ((rc = MQTTAsync_connect(asyncClient, &conn_opts)) != MQTTASYNC_SUCCESS) {
        printf("Failed to start connect, return code %d\n", rc);
    }
}

void MqttPublisher::setupAsyncClient() {
    MQTTAsync_create(&asyncClient, brokerURL.c_str(), clientID.c_str(), MQTTCLIENT_PERSISTENCE_NONE, NULL);
    MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
    int rc;

    MQTTAsync_setCallbacks(client, NULL, onConnectionLost, NULL, NULL);

    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;
    conn_opts.onSuccess = onConnect;
    conn_opts.onFailure = onConnectFailure;
    conn_opts.context = client;
    if ((rc = MQTTAsync_connect(asyncClient, &conn_opts)) != MQTTASYNC_SUCCESS)
    {
        printf("Failed to start connect, return code %d\n", rc);
    }
}<|endoftext|>


<commit_before>/*
** Copyright 2017 ViVoka
**
** Made by leroy_v
** Mail <leroy_v@vivoka.com>
**
** vivoka.com
*/

#include "HistoryViewer.hpp"

// Project includes ------------------------------------------------------------
#include "Constants.hpp"
#include "DateTimeItem.hpp"

// Qt includes -----------------------------------------------------------------
#include <QKeyEvent>

HistoryViewer::HistoryViewer(QWidget * parent) :
    QWidget(parent)
{
    _requests.reserve(Constants::maxHistorySize);

    _ui.setupUi(this);
    _ui.tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    _ui.tableWidget->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);

    QObject::connect(_ui.tableWidget, &QTableWidget::itemSelectionChanged,
                     this, &HistoryViewer::_itemSelectionChanged);

    // Clear history button
    QObject::connect(_ui.pbClear, &QPushButton::clicked,
                     this, &HistoryViewer::_onPbClearClicked);
    // Delete request button
    QObject::connect(_ui.pbDelete, &QPushButton::clicked,
                     this, &HistoryViewer::_onPbDeleteClicked);
}

bool HistoryViewer::hasRequest(RequestPtr request) const
{
    return _requests.contains(request);
}

void HistoryViewer::updateRequest(RequestPtr request)
{
    const auto row = _getRowForRequest(request);
    _fillTableRow(row, request);

    _ui.tableWidget->viewport()->update();
}

void HistoryViewer::addRequest(RequestPtr request)
{
    if (_requests.size() >= Constants::maxHistorySize)
    {
        QObject::disconnect(_ui.tableWidget, &QTableWidget::itemSelectionChanged,
                            this, &HistoryViewer::_itemSelectionChanged);
        const auto rowToRemove = _ui.tableWidget->rowCount() - 1;
        const auto requestIdx  = _getRequestIdxForItem(_ui.tableWidget->item(rowToRemove, 0));
        _ui.tableWidget->removeRow(rowToRemove);
        _requests.remove(requestIdx);
        QObject::connect(_ui.tableWidget, &QTableWidget::itemSelectionChanged,
                            this, &HistoryViewer::_itemSelectionChanged);
    }

    _addRequestToTable(request);
    _requests.push_back(request);

    _ui.tableWidget->selectRow(0);
}

void HistoryViewer::load(QDataStream & in)
{
    in >> _requests;
    for (const auto & request : _requests)
        _addRequestToTable(request);
}

void HistoryViewer::save(QDataStream & out) const
{
    out << _requests;
}

void HistoryViewer::keyPressEvent(QKeyEvent * event)
{
    if (!event->matches(QKeySequence::Delete))
        return ;

    _ui.pbDelete->click();
}

void HistoryViewer::_addRequestToTable(const RequestPtr request)
{
    const int row = _ui.tableWidget->rowCount();
    _ui.tableWidget->setRowCount(row + 1);
    _fillTableRow(row, request);

    _ui.tableWidget->item(row, 0)->setData(Qt::UserRole, QVariant::fromValue(request.get()));

    _ui.pbClear->setEnabled(true);
    _ui.tableWidget->sortItems(3, Qt::DescendingOrder);
    _ui.tableWidget->viewport()->update();
}

void HistoryViewer::_fillTableRow(int row, const RequestPtr request)
{
    _ui.tableWidget->setItem(row, 0, _createTableItem(request->method.constData()));
    _ui.tableWidget->setItem(row, 1, _createTableItem(request->url().toString()));
    _ui.tableWidget->setItem(row, 2, _createTableItem(QString("%1 %2").arg(request->statusCode)
                                                       .arg(request->reasonPhrase)));
    _ui.tableWidget->setItem(row, 3, _createTableItem(request->date.toString(DateTimeItem::dateFormat), true));
    _ui.tableWidget->setItem(row, 4, _createTableItem(_formatSize(request->responseContent.size())));
    _ui.tableWidget->setItem(row, 5, _createTableItem(QString("%1 ms").arg(request->elapsedTime)));
}

int HistoryViewer::_getRequestIdxForItem(const QTableWidgetItem * item) const
{
    const auto request = _getRequestForItem(item);
    for (int i = 0; i < _requests.size(); ++i)
        if (_requests.at(i).get() == request)
            return i;
    return -1;
}

Request * HistoryViewer::_getRequestForItem(const QTableWidgetItem * item) const
{
    const auto firstItem = item->column() == 0 ? item : _ui.tableWidget->item(item->row(), 0);
    return qvariant_cast<Request *>(firstItem->data(Qt::UserRole));
}

int HistoryViewer::_getRowForRequest(const RequestPtr request) const
{
    for (int row = 0; row < _ui.tableWidget->rowCount(); ++row)
    {
        const auto item = _ui.tableWidget->item(row, 0);
        if (qvariant_cast<Request *>(item->data(Qt::UserRole)) == request.get())
            return row;
    }

    return -1;
}

QTableWidgetItem * HistoryViewer::_createTableItem(const QString & text, bool dateTime)
{
    auto item = dateTime ? new DateTimeItem(text) : new QTableWidgetItem(text);
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    return item;
}

QString HistoryViewer::_formatSize(qint32 size)
{
    static const auto f = [](const int value, const int factor)
    {
        const auto val = (value % factor) / (factor / 10);
        if (val == 0)
            return QString::number(value / factor);
        else
            return QString("%1.%2").arg(value / factor).arg(val);
    };

    if (size < 1024)
        return QString("%1 B").arg(size);
    else if (size < 1024 * 1024)
        return QString("%1 kB").arg(f(size, 1024));
    else if (size < 1024 * 1024 * 1024)
        return QString("%1 MB").arg(f(size , 1024 * 1024));
    else
        return QString("%1 GB").arg(f(size , 1024 * 1024 * 1024));
}

void HistoryViewer::_itemSelectionChanged()
{
    const auto areItemSelected = !_ui.tableWidget->selectedItems().isEmpty();
    _ui.pbDelete->setEnabled(areItemSelected);

    if (!areItemSelected)
        return ;

    const auto idx = _getRequestIdxForItem(_ui.tableWidget->currentItem());
    emit currentChanged(_requests.at(idx));
}

void HistoryViewer::_onPbClearClicked()
{
    _ui.tableWidget->clearContents();
    _ui.tableWidget->setRowCount(0);
    _requests.clear();
    _ui.pbClear->setEnabled(false);
}

void HistoryViewer::_onPbDeleteClicked()
{
    auto selectedItems = _ui.tableWidget->selectedItems();
    std::sort(selectedItems.begin(), selectedItems.end(),
              [](const QTableWidgetItem * v1, const QTableWidgetItem * v2)
    { return v1->row() < v2->row(); });

    selectedItems.erase(std::unique(selectedItems.begin(), selectedItems.end(),
                                    [](const QTableWidgetItem * v1, const QTableWidgetItem * v2)
    { return v1->row() == v2->row(); }), selectedItems.end());

    for (const auto & item : selectedItems)
    {
        const auto idx = _getRequestIdxForItem(item);
        _requests.removeAt(idx);

        QObject::disconnect(_ui.tableWidget, &QTableWidget::itemSelectionChanged,
                            this, &HistoryViewer::_itemSelectionChanged);
        _ui.tableWidget->removeRow(item->row());
        QObject::connect(_ui.tableWidget, &QTableWidget::itemSelectionChanged,
                         this, &HistoryViewer::_itemSelectionChanged);
    }

    if (_ui.tableWidget->rowCount() == 0)
        _ui.pbClear->setEnabled(false);

    _itemSelectionChanged();
}
<commit_msg>Fix UserRole data issue in QTableWidgetItem<commit_after>/*
** Copyright 2017 ViVoka
**
** Made by leroy_v
** Mail <leroy_v@vivoka.com>
**
** vivoka.com
*/

#include "HistoryViewer.hpp"

// Project includes ------------------------------------------------------------
#include "Constants.hpp"
#include "DateTimeItem.hpp"

// Qt includes -----------------------------------------------------------------
#include <QKeyEvent>

HistoryViewer::HistoryViewer(QWidget * parent) :
    QWidget(parent)
{
    _requests.reserve(Constants::maxHistorySize);

    _ui.setupUi(this);
    _ui.tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    _ui.tableWidget->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);

    QObject::connect(_ui.tableWidget, &QTableWidget::itemSelectionChanged,
                     this, &HistoryViewer::_itemSelectionChanged);

    // Clear history button
    QObject::connect(_ui.pbClear, &QPushButton::clicked,
                     this, &HistoryViewer::_onPbClearClicked);
    // Delete request button
    QObject::connect(_ui.pbDelete, &QPushButton::clicked,
                     this, &HistoryViewer::_onPbDeleteClicked);
}

bool HistoryViewer::hasRequest(RequestPtr request) const
{
    return _requests.contains(request);
}

void HistoryViewer::updateRequest(RequestPtr request)
{
    const auto row = _getRowForRequest(request);
    _fillTableRow(row, request);

    _ui.tableWidget->viewport()->update();
}

void HistoryViewer::addRequest(RequestPtr request)
{
    if (_requests.size() >= Constants::maxHistorySize)
    {
        QObject::disconnect(_ui.tableWidget, &QTableWidget::itemSelectionChanged,
                            this, &HistoryViewer::_itemSelectionChanged);
        const auto rowToRemove = _ui.tableWidget->rowCount() - 1;
        const auto requestIdx  = _getRequestIdxForItem(_ui.tableWidget->item(rowToRemove, 0));
        _ui.tableWidget->removeRow(rowToRemove);
        _requests.remove(requestIdx);
        QObject::connect(_ui.tableWidget, &QTableWidget::itemSelectionChanged,
                            this, &HistoryViewer::_itemSelectionChanged);
    }

    _addRequestToTable(request);
    _requests.push_back(request);

    _ui.tableWidget->selectRow(0);
}

void HistoryViewer::load(QDataStream & in)
{
    in >> _requests;
    for (const auto & request : _requests)
        _addRequestToTable(request);
}

void HistoryViewer::save(QDataStream & out) const
{
    out << _requests;
}

void HistoryViewer::keyPressEvent(QKeyEvent * event)
{
    if (!event->matches(QKeySequence::Delete))
        return ;

    _ui.pbDelete->click();
}

void HistoryViewer::_addRequestToTable(const RequestPtr request)
{
    const int row = _ui.tableWidget->rowCount();
    _ui.tableWidget->setRowCount(row + 1);
    _fillTableRow(row, request);

    _ui.pbClear->setEnabled(true);
    _ui.tableWidget->sortItems(3, Qt::DescendingOrder);
    _ui.tableWidget->viewport()->update();
}

void HistoryViewer::_fillTableRow(int row, const RequestPtr request)
{
    _ui.tableWidget->setItem(row, 0, _createTableItem(request->method.constData()));
    _ui.tableWidget->setItem(row, 1, _createTableItem(request->url().toString()));
    _ui.tableWidget->setItem(row, 2, _createTableItem(QString("%1 %2").arg(request->statusCode)
                                                       .arg(request->reasonPhrase)));
    _ui.tableWidget->setItem(row, 3, _createTableItem(request->date.toString(DateTimeItem::dateFormat), true));
    _ui.tableWidget->setItem(row, 4, _createTableItem(_formatSize(request->responseContent.size())));
    _ui.tableWidget->setItem(row, 5, _createTableItem(QString("%1 ms").arg(request->elapsedTime)));

    _ui.tableWidget->item(row, 0)->setData(Qt::UserRole, QVariant::fromValue(request.get()));
}

int HistoryViewer::_getRequestIdxForItem(const QTableWidgetItem * item) const
{
    const auto request = _getRequestForItem(item);
    for (int i = 0; i < _requests.size(); ++i)
        if (_requests.at(i).get() == request)
            return i;
    return -1;
}

Request * HistoryViewer::_getRequestForItem(const QTableWidgetItem * item) const
{
    const auto firstItem = item->column() == 0 ? item : _ui.tableWidget->item(item->row(), 0);
    return qvariant_cast<Request *>(firstItem->data(Qt::UserRole));
}

int HistoryViewer::_getRowForRequest(const RequestPtr request) const
{
    for (int row = 0; row < _ui.tableWidget->rowCount(); ++row)
    {
        const auto item = _ui.tableWidget->item(row, 0);
        if (qvariant_cast<Request *>(item->data(Qt::UserRole)) == request.get())
            return row;
    }

    return -1;
}

QTableWidgetItem * HistoryViewer::_createTableItem(const QString & text, bool dateTime)
{
    auto item = dateTime ? new DateTimeItem(text) : new QTableWidgetItem(text);
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    return item;
}

QString HistoryViewer::_formatSize(qint32 size)
{
    static const auto f = [](const int value, const int factor)
    {
        const auto val = (value % factor) / (factor / 10);
        if (val == 0)
            return QString::number(value / factor);
        else
            return QString("%1.%2").arg(value / factor).arg(val);
    };

    if (size < 1024)
        return QString("%1 B").arg(size);
    else if (size < 1024 * 1024)
        return QString("%1 kB").arg(f(size, 1024));
    else if (size < 1024 * 1024 * 1024)
        return QString("%1 MB").arg(f(size , 1024 * 1024));
    else
        return QString("%1 GB").arg(f(size , 1024 * 1024 * 1024));
}

void HistoryViewer::_itemSelectionChanged()
{
    const auto areItemSelected = !_ui.tableWidget->selectedItems().isEmpty();
    _ui.pbDelete->setEnabled(areItemSelected);

    if (!areItemSelected)
        return ;

    const auto idx = _getRequestIdxForItem(_ui.tableWidget->currentItem());
    emit currentChanged(_requests.at(idx));
}

void HistoryViewer::_onPbClearClicked()
{
    _ui.tableWidget->clearContents();
    _ui.tableWidget->setRowCount(0);
    _requests.clear();
    _ui.pbClear->setEnabled(false);
}

void HistoryViewer::_onPbDeleteClicked()
{
    auto selectedItems = _ui.tableWidget->selectedItems();
    std::sort(selectedItems.begin(), selectedItems.end(),
              [](const QTableWidgetItem * v1, const QTableWidgetItem * v2)
    { return v1->row() < v2->row(); });

    selectedItems.erase(std::unique(selectedItems.begin(), selectedItems.end(),
                                    [](const QTableWidgetItem * v1, const QTableWidgetItem * v2)
    { return v1->row() == v2->row(); }), selectedItems.end());

    for (const auto & item : selectedItems)
    {
        const auto idx = _getRequestIdxForItem(item);
        _requests.removeAt(idx);

        QObject::disconnect(_ui.tableWidget, &QTableWidget::itemSelectionChanged,
                            this, &HistoryViewer::_itemSelectionChanged);
        _ui.tableWidget->removeRow(item->row());
        QObject::connect(_ui.tableWidget, &QTableWidget::itemSelectionChanged,
                         this, &HistoryViewer::_itemSelectionChanged);
    }

    if (_ui.tableWidget->rowCount() == 0)
        _ui.pbClear->setEnabled(false);

    _itemSelectionChanged();
}
<|endoftext|>


<commit_before>/* This file is part of ksirc
   Copyright (c) 2001 Malte Starostik <malte@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

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

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

/*
Color parser code courtesy of ksirc <http://www.kde.org>
Modified by Jason Keirstead <jason@keirstead.org>
*/

// $Id$

#include <kopetenotifyclient.h>
#include <kdebug.h>
#include <qstylesheet.h>
#include "ksparser.h"

KSParser KSParser::m_parser;

const QColor KSParser::IRC_Colors[17]=
{
	Qt::white,
	Qt::black,
	Qt::darkBlue,
	Qt::darkGreen,
	Qt::red,
	Qt::darkRed,
	Qt::darkMagenta,
	Qt::darkYellow,
	Qt::yellow,
	Qt::green,
	Qt::darkCyan,
	Qt::cyan,
	Qt::blue,
	Qt::magenta,
	Qt::darkGray,
	Qt::gray,
	QColor() // default invalid color, must be the last
};

KSParser::KSParser()
{
	kdDebug(14120) << k_funcinfo << endl;
}

KSParser::~KSParser()
{
	kdDebug(14120) << k_funcinfo << endl;
}

/* NOTE: If thread corruption are seen simply ad a qlock here */
QCString KSParser::parse(const QCString &message)
{
	return m_parser._parse( QString::fromLatin1(message) ).latin1();
}

QString KSParser::_parse(const QString &message)
{
	QString res;
	m_tags.clear();
	m_attributes.clear();

	for(uint i=0; i<message.length(); i++)
	{
		switch( message[i].latin1() )
		{
		case 3:		//Color code
			if(message[++i].digitValue()>-1)
			{
				QString tagStyle;
				QString fgColor = QString( message[i] );
				if( message[++i].digitValue() > -1 )
					fgColor += message[i];

				QString bgColor;
				if( message[i] == ',' )
				{
					bgColor = QString( message[i] );
					if( message[++i].digitValue() > -1 )
						bgColor += message[i];
				}

				QColor c = ircColor(fgColor.toInt());
				if ( c.isValid() )
					tagStyle += QString::fromLatin1("color:%1;").arg(c.name());

				if(!bgColor.isEmpty())
				{
					c = ircColor(bgColor.toInt());
					if(c.isValid())
						tagStyle += QString::fromLatin1("background-color:%1;").arg(c.name());
				}

				if(!tagStyle.isEmpty())
					res += pushTag(QString::fromLatin1("span"), QString::fromLatin1("style=\"%1\"").arg( tagStyle ) );

				i--;
			}
			else
				res += popTag( QString::fromLatin1("span") );
			break;
		case 2:		//Bold
			res += toggleTag("b");
			break;
		case 6:		//Invert Colors
			break;
		case 7:		//System bell
			KNotifyClient::beep( QString::fromLatin1("IRC beep event received in a message") );
			break;
		case 15:	//Plain Text, close all tags
			res.append( popAll() );
			break;
		case 31: 	//Underline
			res += toggleTag("u");
			break;
		case '\n':
			res += QString::fromLatin1("
");
			break;
		case '\t':
			res += QString::fromLatin1("    ");
			break;
		default:
			res += QStyleSheet::escape(message[i]);
		}
	}
	res.append( popAll() );
	return res;
}

QString KSParser::pushTag(const QString &tag, const QString &attributes)
{
	QString res;
	m_tags.push(tag);
	if(!m_attributes.contains(tag))
		m_attributes.insert(tag, attributes);
	else if(!attributes.isEmpty())
		m_attributes.replace(tag, attributes);
	res.append("<" + tag);
	if(!m_attributes[tag].isEmpty())
		res.append(" " + m_attributes[tag]);
	return res + ">";
}

QString KSParser::popTag(const QString &tag)
{
	if (!m_tags.contains(tag))
		return QString::null;

	QString res;
	QValueStack<QString> savedTags;
	while(m_tags.top() != tag)
	{
		savedTags.push(m_tags.pop());
		res.append("</" + savedTags.top() + ">");
	}
	res.append("</" + m_tags.pop() + ">");
	m_attributes.remove(tag);
	while(!savedTags.isEmpty())
		res.append(pushTag(savedTags.pop()));
	return res;
}

QString KSParser::toggleTag(const QString &tag)
{
	return m_attributes.contains(tag)?popTag(tag):pushTag(tag);
}

QString KSParser::popAll()
{
	QString res;
	while(!m_tags.isEmpty())
		res.append("</" + m_tags.pop() + ">");
	m_attributes.clear();
	return res;
}

int KSParser::colorForHTML(const QString &html)
{
	QColor color(html);
	for(uint i=0; i<sizeof(IRC_Colors)/sizeof(QColor); i++)
	{
		if(IRC_Colors[i] == color)
			return i;
	}
	return -1;
}
<commit_msg>No response from poster, I am going to assume this fixes the bug.<commit_after>/* This file is part of ksirc
   Copyright (c) 2001 Malte Starostik <malte@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

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

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

/*
Color parser code courtesy of ksirc <http://www.kde.org>
Modified by Jason Keirstead <jason@keirstead.org>
*/

// $Id$

#include <kopetenotifyclient.h>
#include <kdebug.h>
#include <qstylesheet.h>
#include "ksparser.h"

KSParser KSParser::m_parser;

const QColor KSParser::IRC_Colors[17]=
{
	Qt::white,
	Qt::black,
	Qt::darkBlue,
	Qt::darkGreen,
	Qt::red,
	Qt::darkRed,
	Qt::darkMagenta,
	Qt::darkYellow,
	Qt::yellow,
	Qt::green,
	Qt::darkCyan,
	Qt::cyan,
	Qt::blue,
	Qt::magenta,
	Qt::darkGray,
	Qt::gray,
	QColor() // default invalid color, must be the last
};

KSParser::KSParser()
{
	kdDebug(14120) << k_funcinfo << endl;
}

KSParser::~KSParser()
{
	kdDebug(14120) << k_funcinfo << endl;
}

/* NOTE: If thread corruption are seen simply ad a qlock here */
QCString KSParser::parse(const QCString &message)
{
	return m_parser._parse( QString::fromLatin1(message) ).latin1();
}

QString KSParser::_parse(const QString &message)
{
	QString res;
	m_tags.clear();
	m_attributes.clear();

	for(uint i=0; i<message.length(); i++)
	{
		switch( message[i].latin1() )
		{
		case 3:		//Color code
			if(message[++i].digitValue()>-1)
			{
				QString tagStyle;
				QString fgColor = QString( message[i] );
				if( message[++i].digitValue() > -1 )
					fgColor += message[i];

				QString bgColor;
				if( message[i] == ',' )
				{
					bgColor = QString( message[i] );
					if( message[++i].digitValue() > -1 )
						bgColor += message[i];
				}

				QColor c = ircColor(fgColor.toInt());
				if ( c.isValid() )
					tagStyle += QString::fromLatin1("color:%1;").arg(c.name());

				if(!bgColor.isEmpty())
				{
					c = ircColor(bgColor.toInt());
					if(c.isValid())
						tagStyle += QString::fromLatin1("background-color:%1;").arg(c.name());
				}

				if(!tagStyle.isEmpty())
					res += pushTag(QString::fromLatin1("span"), QString::fromLatin1("style=\"%1\"").arg( tagStyle ) );

				i--;
			}
			else
				res += popTag( QString::fromLatin1("span") );
			break;
		case 2:		//Bold
			res += toggleTag("b");
			break;
		case 6:		//Invert Colors
			break;
		case 7:		//System bell
			KNotifyClient::beep( QString::fromLatin1("IRC beep event received in a message") );
			break;
		case '\t':
			res += QString::fromLatin1("    ");
			break;
		case '\n':
			res += QString::fromLatin1("
");
			break;
		case 15:	//Plain Text, close all tags
			res.append( popAll() );
			break;
		case 31: 	//Underline
			res += toggleTag("u");
			break;
		case '<':
			res += QString::fromLatin1("<");
			break;
		case '>':
			res += QString::fromLatin1(">");
			break;
		default:
			res += QStyleSheet::escape(message[i]);
		}
	}
	res.append( popAll() );
	return res;
}

QString KSParser::pushTag(const QString &tag, const QString &attributes)
{
	QString res;
	m_tags.push(tag);
	if(!m_attributes.contains(tag))
		m_attributes.insert(tag, attributes);
	else if(!attributes.isEmpty())
		m_attributes.replace(tag, attributes);
	res.append("<" + tag);
	if(!m_attributes[tag].isEmpty())
		res.append(" " + m_attributes[tag]);
	return res + ">";
}

QString KSParser::popTag(const QString &tag)
{
	if (!m_tags.contains(tag))
		return QString::null;

	QString res;
	QValueStack<QString> savedTags;
	while(m_tags.top() != tag)
	{
		savedTags.push(m_tags.pop());
		res.append("</" + savedTags.top() + ">");
	}
	res.append("</" + m_tags.pop() + ">");
	m_attributes.remove(tag);
	while(!savedTags.isEmpty())
		res.append(pushTag(savedTags.pop()));
	return res;
}

QString KSParser::toggleTag(const QString &tag)
{
	return m_attributes.contains(tag)?popTag(tag):pushTag(tag);
}

QString KSParser::popAll()
{
	QString res;
	while(!m_tags.isEmpty())
		res.append("</" + m_tags.pop() + ">");
	m_attributes.clear();
	return res;
}

int KSParser::colorForHTML(const QString &html)
{
	QColor color(html);
	for(uint i=0; i<sizeof(IRC_Colors)/sizeof(QColor); i++)
	{
		if(IRC_Colors[i] == color)
			return i;
	}
	return -1;
}
<|endoftext|>