<commit_before>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2018-2020 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <modules/plottinggl/processors/colorscalelegend.h>
#include <modules/opengl/texture/textureutils.h>
#include <modules/opengl/shader/shaderutils.h>

namespace inviwo {
namespace plot {

// The Class Identifier has to be globally unique. Use a reverse DNS naming scheme
const ProcessorInfo ColorScaleLegend::processorInfo_{
    "org.inviwo.ColorScaleLegend",  // Class identifier
    "Color Scale Legend",           // Display name
    "Plotting",                     // Category
    CodeState::Stable,              // Code state
    "GL, Plotting, TF"              // Tags
};
const ProcessorInfo ColorScaleLegend::getProcessorInfo() const { return processorInfo_; }

ColorScaleLegend::ColorScaleLegend()
    : Processor()
    , inport_("inport")
    , outport_("outport")
    , volumeInport_("volumeInport")
    , enabled_("enabled", "Enabled", true)
    , isotfComposite_("isotfComposite", "TF & Isovalues")
    , positioning_("positioning", "Positioning & Size")
    , legendPlacement_("legendPlacement", "Legend Placement",
                       {{"top", "Top", 0},
                        {"right", "Right", 1},
                        {"bottom", "Bottom", 2},
                        {"left", "Left", 3},
                        {"custom", "Custom", 4}},
                       1)
    , rotation_("legendRotation", "Legend Rotation",
                {{"degree0", "0 degrees", 0},
                 {"degree90", "90 degrees", 1},
                 {"degree180", "180 degrees", 2},
                 {"degree270", "270 degrees", 3}},
                1)
    , position_("position", "Position", vec2(0.5f), vec2(0.0f), vec2(1.0f))
    , margin_("margin", "Margin (in pixels)", 25, 0, 100)
    , legendSize_("legendSize", "Legend Size", vec2(200, 25), vec2(10, 10), vec2(2000, 500))
    , axisStyle_("style", "Style")
    , title_("title", "Legend Title", "Legend")
    , backgroundStyle_("backgroundStyle", "Background",
                       {{"noBackground", "No background", BackgroundStyle::NoBackground},
                        {"checkerBoard", "Checker board", BackgroundStyle::CheckerBoard},
                        {"solid", "Solid", BackgroundStyle::SolidColor}},
                       0)
    , checkerBoardSize_("checkerBoardSize", "Checker Board Size", 5, 1, 20)
    , bgColor_("backgroundColor", "Background Color", vec4(0.0f, 0.0f, 0.0f, 1.0f), vec4(0.0f),
               vec4(1.0f), Defaultvalues<vec4>::getInc(), InvalidationLevel::InvalidOutput,
               PropertySemantics::Color)
    , borderWidth_("borderWidth", "Border Width", 2, 0, 10)
    , shader_("img_texturequad.vert", "legend.frag")
    , axis_("axis", "Scale Axis")
    , axisRenderer_(axis_) {

    shader_.onReload([this]() { invalidate(InvalidationLevel::InvalidResources); });

    inport_.setOptional(true);
    volumeInport_.setOptional(true);
    addPort(inport_);
    addPort(outport_);
    addPort(volumeInport_);

    // legend position
    positioning_.addProperties(legendPlacement_, rotation_, position_, margin_, legendSize_);
    rotation_.visibilityDependsOn(legendPlacement_, [](auto l) { return l.get() == 4; });
    position_.visibilityDependsOn(legendPlacement_, [](auto l) { return l.get() == 4; });
    axis_.flipped_.visibilityDependsOn(legendPlacement_, [](auto l) { return l.get() == 4; });

    // legend style
    axisStyle_.insertProperty(0, title_);
    axisStyle_.addProperties(backgroundStyle_, checkerBoardSize_, bgColor_, borderWidth_);
    checkerBoardSize_.setVisible(false);
    bgColor_.setVisible(false);

    axisStyle_.registerProperty(axis_);

    addProperties(enabled_, isotfComposite_, positioning_, axisStyle_, axis_);

    // set initial axis parameters
    axis_.width_ = 0;
    axis_.setCaption(title_.get());
    axis_.captionSettings_.setChecked(true);
    axis_.labelSettings_.font_.fontFace_.set(axis_.captionSettings_.font_.fontFace_.get());
    axis_.captionSettings_.offset_.set(20);
    axis_.captionSettings_.font_.anchorPos_.set(vec2{0.0f, 0.0f});
    axis_.setCurrentStateAsDefault();

    title_.onChange([&]() { axis_.setCaption(title_.get()); });

    const auto getAxisOrientation = [](int i) {
        switch (i) {
            default:
            case 0:  // 0 degrees rotation (top)
                return AxisProperty::Orientation::Horizontal;
            case 1:  // 90 degrees rotation (right)
                return AxisProperty::Orientation::Vertical;
            case 2:  // 180 degrees rotation (bottom)
                return AxisProperty::Orientation::Horizontal;
            case 3:  // 270 degrees rotation (left)
                return AxisProperty::Orientation::Vertical;
        }
    };
    const auto getAxisPlacement = [](int i) {
        switch (i) {
            default:
            case 0:  // 0 degrees rotation (top)
                return AxisProperty::Placement::Outside;
            case 1:  // 90 degrees rotation (right)
                return AxisProperty::Placement::Outside;
            case 2:  // 180 degrees rotation (bottom)
                return AxisProperty::Placement::Inside;
            case 3:  // 270 degrees rotation (left)
                return AxisProperty::Placement::Inside;
        }
    };

    legendPlacement_.onChange([this]() {
        if (legendPlacement_ != 4) rotation_.set(legendPlacement_.get());
    });
    if (legendPlacement_ != 4) rotation_.set(legendPlacement_.get());

    auto updatePlacement = [&]() {
        axis_.orientation_.set(getAxisOrientation(rotation_.get()));
        axis_.placement_.set(getAxisPlacement(rotation_.get()));

        if (rotation_ == 0 || rotation_ == 3) {  // Top/Left
            axis_.flipped_ = false;
        } else if (rotation_ == 1 || rotation_ == 2) {  // Right/Bottom
            axis_.flipped_ = true;
        }
    };

    rotation_.onChange(updatePlacement);
    updatePlacement();

    checkerBoardSize_.visibilityDependsOn(
        backgroundStyle_, [&](auto p) { return p.get() == BackgroundStyle::CheckerBoard; });
    bgColor_.visibilityDependsOn(backgroundStyle_,
                                 [&](auto p) { return p.get() == BackgroundStyle::SolidColor; });
}

// this function handles the legend rotation and updates the axis thereafter
std::tuple<ivec2, ivec2, ivec2, ivec2> ColorScaleLegend::getPositions(ivec2 dimensions) const {
    const float ticsWidth = ceil(axis_.majorTicks_.tickWidth_.get());
    const auto borderWidth = borderWidth_.get();

    const auto [position, rotation, legendSize] = [&]() {
        const std::array<vec2, 4> pos = {{{0.5f, 1.0f}, {1.0f, 0.5f}, {0.5f, 0.0f}, {0.0f, 0.5f}}};
        const auto initialPos = legendPlacement_ == 4 ? position_.get() : pos[legendPlacement_];
        const auto rotation = legendPlacement_ == 4 ? rotation_.get() : legendPlacement_.get();
        const auto size =
            rotation % 2 == 0 ? legendSize_.get() : ivec2{legendSize_.get().y, legendSize_.get().x};

        auto lpos = 0.5f * vec2{size + 2 * ivec2{margin_}} +
                    initialPos * vec2{dimensions - size - 2 * ivec2{margin_}};

        return std::make_tuple(lpos, rotation, size);
    }();

    // define the legend corners
    const ivec2 bottomLeft = position - vec2{legendSize} * vec2{0.5};
    const ivec2 bottomRight = vec2(bottomLeft.x + legendSize.x, bottomLeft.y);
    const ivec2 topLeft = vec2(bottomLeft.x, bottomLeft.y + legendSize.y);
    const ivec2 topRight = vec2(bottomRight.x, topLeft.y);

    ivec2 axisStart{0};
    ivec2 axisEnd{0};

    switch (rotation_.get()) {
        default:
        case 0:  // 0 degrees rotation (top)
            axisStart = bottomLeft + ivec2(ticsWidth / 2, 0) - ivec2(borderWidth);
            axisEnd = bottomRight - ivec2(ticsWidth / 2, 0) + ivec2(borderWidth, -borderWidth);
            break;
        case 1:  // 90 degrees rotation (right)
            axisStart = bottomLeft + ivec2(0, ticsWidth / 2) - ivec2(borderWidth);
            axisEnd = topLeft - ivec2(0, ticsWidth / 2) + ivec2(-borderWidth, borderWidth);
            break;
        case 2:  // 180 degrees rotation (bottom)
            axisStart = topLeft + ivec2(ticsWidth / 2, 0) + ivec2(-borderWidth, borderWidth);
            axisEnd = topRight - ivec2(ticsWidth / 2, 0) + ivec2(borderWidth);
            break;
        case 3:  // 270 degrees rotation (left)
            axisStart = bottomRight + ivec2(0, ticsWidth / 2) + ivec2(borderWidth, -borderWidth);
            axisEnd = topRight - ivec2(0, ticsWidth / 2) + ivec2(borderWidth);
            break;
    }
    return {bottomLeft, legendSize, axisStart, axisEnd};
}

void ColorScaleLegend::process() {
    utilgl::activateTargetAndClearOrCopySource(outport_, inport_);
    if (!enabled_) {
        return;
    }

    // update the legend range if a volume is connected to inport
    if (volumeInport_.isChanged() && volumeInport_.hasData()) {
        axis_.setRange(volumeInport_.getData()->dataMap_.valueRange);
    } else if (!volumeInport_.isConnected()) {
        axis_.setRange(vec2(0, 1));
    }

    const ivec2 dimensions = outport_.getDimensions();
    const auto [bottomLeft, legendSize, axisStart, axisEnd] = getPositions(dimensions);
    axisRenderer_.render(dimensions, axisStart, axisEnd);

    utilgl::DepthFuncState depthFunc(GL_ALWAYS);
    utilgl::BlendModeState blending(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

    TextureUnitContainer units;
    utilgl::Activate<Shader> activate(&shader_);
    utilgl::bindAndSetUniforms(shader_, units, isotfComposite_);
    utilgl::setUniforms(shader_, axis_.color_, borderWidth_, backgroundStyle_, checkerBoardSize_,
                        rotation_, isotfComposite_);

    if (backgroundStyle_ == BackgroundStyle::NoBackground) {
        shader_.setUniform("backgroundColor", vec4(0.0f));
    } else {
        shader_.setUniform("backgroundColor", bgColor_);
    }

    const ivec4 view{bottomLeft - ivec2{borderWidth_}, legendSize + ivec2{borderWidth_ * 2}};
    shader_.setUniform("viewport", view);
    utilgl::ViewportState viewport(view);

    utilgl::singleDrawImagePlaneRect();
    utilgl::deactivateCurrentTarget();
}

}  // namespace plot

}  // namespace inviwo
<commit_msg>PlottingGL: requested changes<commit_after>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2018-2020 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <modules/plottinggl/processors/colorscalelegend.h>
#include <modules/opengl/texture/textureutils.h>
#include <modules/opengl/shader/shaderutils.h>

namespace inviwo {
namespace plot {

// The Class Identifier has to be globally unique. Use a reverse DNS naming scheme
const ProcessorInfo ColorScaleLegend::processorInfo_{
    "org.inviwo.ColorScaleLegend",  // Class identifier
    "Color Scale Legend",           // Display name
    "Plotting",                     // Category
    CodeState::Stable,              // Code state
    "GL, Plotting, TF"              // Tags
};
const ProcessorInfo ColorScaleLegend::getProcessorInfo() const { return processorInfo_; }

ColorScaleLegend::ColorScaleLegend()
    : Processor()
    , inport_("inport")
    , outport_("outport")
    , volumeInport_("volumeInport")
    , enabled_("enabled", "Enabled", true)
    , isotfComposite_("isotfComposite", "TF & Isovalues")
    , positioning_("positioning", "Positioning & Size")
    , legendPlacement_("legendPlacement", "Legend Placement",
                       {{"top", "Top", 0},
                        {"right", "Right", 1},
                        {"bottom", "Bottom", 2},
                        {"left", "Left", 3},
                        {"custom", "Custom", 4}},
                       1)
    , rotation_("legendRotation", "Legend Rotation",
                {{"degree0", "0 degrees", 0},
                 {"degree90", "90 degrees", 1},
                 {"degree180", "180 degrees", 2},
                 {"degree270", "270 degrees", 3}},
                1)
    , position_("position", "Position", vec2(0.5f), vec2(0.0f), vec2(1.0f))
    , margin_("margin", "Margin (in pixels)", 25, 0, 100)
    , legendSize_("legendSize", "Legend Size", vec2(200, 25), vec2(10, 10), vec2(2000, 500))
    , axisStyle_("style", "Style")
    , title_("title", "Legend Title", "Legend")
    , backgroundStyle_("backgroundStyle", "Background",
                       {{"noBackground", "No background", BackgroundStyle::NoBackground},
                        {"checkerBoard", "Checker board", BackgroundStyle::CheckerBoard},
                        {"solid", "Solid", BackgroundStyle::SolidColor}},
                       0)
    , checkerBoardSize_("checkerBoardSize", "Checker Board Size", 5, 1, 20)
    , bgColor_("backgroundColor", "Background Color", vec4(0.0f, 0.0f, 0.0f, 1.0f), vec4(0.0f),
               vec4(1.0f), Defaultvalues<vec4>::getInc(), InvalidationLevel::InvalidOutput,
               PropertySemantics::Color)
    , borderWidth_("borderWidth", "Border Width", 2, 0, 10)
    , shader_("img_texturequad.vert", "legend.frag")
    , axis_("axis", "Scale Axis")
    , axisRenderer_(axis_) {

    shader_.onReload([this]() { invalidate(InvalidationLevel::InvalidResources); });

    inport_.setOptional(true);
    volumeInport_.setOptional(true);
    addPort(inport_);
    addPort(outport_);
    addPort(volumeInport_);

    // legend position
    positioning_.addProperties(legendPlacement_, rotation_, position_, margin_, legendSize_);
    rotation_.visibilityDependsOn(legendPlacement_, [](auto l) { return l.get() == 4; });
    position_.visibilityDependsOn(legendPlacement_, [](auto l) { return l.get() == 4; });
    axis_.flipped_.visibilityDependsOn(legendPlacement_, [](auto l) { return l.get() == 4; });

    // legend style
    axisStyle_.insertProperty(0, title_);
    axisStyle_.addProperties(backgroundStyle_, checkerBoardSize_, bgColor_, borderWidth_);
    checkerBoardSize_.setVisible(false);
    bgColor_.setVisible(false);

    axisStyle_.registerProperty(axis_);

    addProperties(enabled_, isotfComposite_, positioning_, axisStyle_, axis_);

    // set initial axis parameters
    axis_.width_ = 0;
    axis_.setCaption(title_.get());
    axis_.captionSettings_.setChecked(true);
    axis_.labelSettings_.font_.fontFace_.set(axis_.captionSettings_.font_.fontFace_.get());
    axis_.captionSettings_.offset_.set(20);
    axis_.captionSettings_.font_.anchorPos_.set(vec2{0.0f, 0.0f});
    axis_.setCurrentStateAsDefault();

    title_.onChange([&]() { axis_.setCaption(title_.get()); });

    const auto getAxisOrientation = [](int i) {
        switch (i) {
            default:
            case 0:  // 0 degrees rotation (top)
                return AxisProperty::Orientation::Horizontal;
            case 1:  // 90 degrees rotation (right)
                return AxisProperty::Orientation::Vertical;
            case 2:  // 180 degrees rotation (bottom)
                return AxisProperty::Orientation::Horizontal;
            case 3:  // 270 degrees rotation (left)
                return AxisProperty::Orientation::Vertical;
        }
    };
    const auto getAxisPlacement = [](int i) {
        switch (i) {
            default:
            case 0:  // 0 degrees rotation (top)
                return AxisProperty::Placement::Outside;
            case 1:  // 90 degrees rotation (right)
                return AxisProperty::Placement::Outside;
            case 2:  // 180 degrees rotation (bottom)
                return AxisProperty::Placement::Inside;
            case 3:  // 270 degrees rotation (left)
                return AxisProperty::Placement::Inside;
        }
    };

    legendPlacement_.onChange([this]() {
        if (legendPlacement_ != 4) rotation_.set(legendPlacement_.get());
    });
    if (legendPlacement_ != 4) rotation_.set(legendPlacement_.get());

    auto updatePlacement = [&]() {
        axis_.orientation_.set(getAxisOrientation(rotation_.get()));
        axis_.placement_.set(getAxisPlacement(rotation_.get()));

        if (rotation_ == 0 || rotation_ == 3) {  // Top/Left
            axis_.flipped_ = false;
        } else if (rotation_ == 1 || rotation_ == 2) {  // Right/Bottom
            axis_.flipped_ = true;
        }
    };

    rotation_.onChange(updatePlacement);
    updatePlacement();

    checkerBoardSize_.visibilityDependsOn(
        backgroundStyle_, [&](auto p) { return p.get() == BackgroundStyle::CheckerBoard; });
    bgColor_.visibilityDependsOn(backgroundStyle_,
                                 [&](auto p) { return p.get() == BackgroundStyle::SolidColor; });
}

// this function handles the legend rotation and updates the axis thereafter
std::tuple<ivec2, ivec2, ivec2, ivec2> ColorScaleLegend::getPositions(ivec2 dimensions) const {
    const float ticsWidth = ceil(axis_.majorTicks_.tickWidth_.get());
    const auto borderWidth = borderWidth_.get();

    const auto [position, rotation, legendSize] = [&]() {
        const std::array<vec2, 4> pos = {{{0.5f, 1.0f}, {1.0f, 0.5f}, {0.5f, 0.0f}, {0.0f, 0.5f}}};
        const auto initialPos = legendPlacement_ == 4 ? position_.get() : pos[legendPlacement_];
        const auto rotation = legendPlacement_ == 4 ? rotation_.get() : legendPlacement_.get();
        const auto size =
            rotation % 2 == 0 ? legendSize_.get() : ivec2{legendSize_.get().y, legendSize_.get().x};

        auto lpos = 0.5f * vec2{size + 2 * ivec2{margin_}} +
                    initialPos * vec2{dimensions - size - 2 * ivec2{margin_}};

        return std::make_tuple(lpos, rotation, size);
    }();

    // define the legend corners
    const ivec2 bottomLeft = position - vec2{legendSize} * vec2{0.5};
    const ivec2 bottomRight = vec2(bottomLeft.x + legendSize.x, bottomLeft.y);
    const ivec2 topLeft = vec2(bottomLeft.x, bottomLeft.y + legendSize.y);
    const ivec2 topRight = vec2(bottomRight.x, topLeft.y);

    ivec2 axisStart{0};
    ivec2 axisEnd{0};

    switch (rotation_.get()) {
        default:
        case 0:  // 0 degrees rotation (top)
            axisStart = bottomLeft + ivec2(ticsWidth / 2, 0) - ivec2(borderWidth);
            axisEnd = bottomRight - ivec2(ticsWidth / 2, 0) + ivec2(borderWidth, -borderWidth);
            break;
        case 1:  // 90 degrees rotation (right)
            axisStart = bottomLeft + ivec2(0, ticsWidth / 2) - ivec2(borderWidth);
            axisEnd = topLeft - ivec2(0, ticsWidth / 2) + ivec2(-borderWidth, borderWidth);
            break;
        case 2:  // 180 degrees rotation (bottom)
            axisStart = topLeft + ivec2(ticsWidth / 2, 0) + ivec2(-borderWidth, borderWidth);
            axisEnd = topRight - ivec2(ticsWidth / 2, 0) + ivec2(borderWidth);
            break;
        case 3:  // 270 degrees rotation (left)
            axisStart = bottomRight + ivec2(0, ticsWidth / 2) + ivec2(borderWidth, -borderWidth);
            axisEnd = topRight - ivec2(0, ticsWidth / 2) + ivec2(borderWidth);
            break;
    }
    return {bottomLeft, legendSize, axisStart, axisEnd};
}

void ColorScaleLegend::process() {
    if (!enabled_) {
        if (inport_.isReady()) {
            outport_.setData(inport_.getData());
        } else {
            utilgl::activateAndClearTarget(outport_);
        }

        return;
    }
    utilgl::activateTargetAndClearOrCopySource(outport_, inport_);

    // update the legend range if a volume is connected to inport
    if (volumeInport_.isChanged() && volumeInport_.hasData()) {
        axis_.setRange(volumeInport_.getData()->dataMap_.valueRange);
    } else if (!volumeInport_.isConnected()) {
        axis_.setRange(vec2(0, 1));
    }

    const ivec2 dimensions = outport_.getDimensions();
    const auto [bottomLeft, legendSize, axisStart, axisEnd] = getPositions(dimensions);
    axisRenderer_.render(dimensions, axisStart, axisEnd);

    utilgl::DepthFuncState depthFunc(GL_ALWAYS);
    utilgl::BlendModeState blending(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

    TextureUnitContainer units;
    utilgl::Activate<Shader> activate(&shader_);
    utilgl::bindAndSetUniforms(shader_, units, isotfComposite_);
    utilgl::setUniforms(shader_, axis_.color_, borderWidth_, backgroundStyle_, checkerBoardSize_,
                        rotation_, isotfComposite_);

    if (backgroundStyle_ == BackgroundStyle::NoBackground) {
        shader_.setUniform("backgroundColor", vec4(0.0f));
    } else {
        shader_.setUniform("backgroundColor", bgColor_);
    }

    const ivec4 view{bottomLeft - ivec2{borderWidth_}, legendSize + ivec2{borderWidth_ * 2}};
    shader_.setUniform("viewport", view);
    utilgl::ViewportState viewport(view);

    utilgl::singleDrawImagePlaneRect();
    utilgl::deactivateCurrentTarget();
}

}  // namespace plot

}  // namespace inviwo
<|endoftext|>


<commit_before>//
// Copyright 2014, 2015 Razer Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include "OSVRPrivatePCH.h"
#include "OSVRHMD.h"

// Must put path from Engine/Source to these includes since we are an out-of-tree module.
#include "Runtime/Renderer/Private/RendererPrivate.h"
#include "Runtime/Renderer/Private/ScenePrivate.h"
#include "Runtime/Renderer/Private/PostProcess/PostProcessHMD.h"
#include "Runtime/Engine/Public/ScreenRendering.h"

void FOSVRHMD::DrawDistortionMesh_RenderThread(FRenderingCompositePassContext& Context, const FIntPoint& TextureSize)
{
    // shouldn't be called with a custom present
    check(0);
}

void FOSVRHMD::RenderTexture_RenderThread(FRHICommandListImmediate& RHICmdList, FTexture2DRHIParamRef BackBuffer, FTexture2DRHIParamRef SrcTexture) const
//{
//    check(IsInRenderingThread());
//
//    if (DstRect.IsEmpty())
//    {
//        DstRect = FIntRect(0, 0, DstTexture->GetSizeX(), DstTexture->GetSizeY());
//    }
//    const uint32 ViewportWidth = DstRect.Width();
//    const uint32 ViewportHeight = DstRect.Height();
//    const FIntPoint TargetSize(ViewportWidth, ViewportHeight);
//
//    const float SrcTextureWidth = SrcTexture->GetSizeX();
//    const float SrcTextureHeight = SrcTexture->GetSizeY();
//    float U = 0.f, V = 0.f, USize = 1.f, VSize = 1.f;
//    if (!SrcRect.IsEmpty())
//    {
//        U = SrcRect.Min.X / SrcTextureWidth;
//        V = SrcRect.Min.Y / SrcTextureHeight;
//        USize = SrcRect.Width() / SrcTextureWidth;
//        VSize = SrcRect.Height() / SrcTextureHeight;
//    }
//
//    SetRenderTarget(RHICmdList, DstTexture, FTextureRHIRef());
//    RHICmdList.SetViewport(DstRect.Min.X, DstRect.Min.Y, 0, DstRect.Max.X, DstRect.Max.Y, 1.0f);
//
//    RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
//    RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI());
//    RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
//
//    const auto FeatureLevel = GMaxRHIFeatureLevel;
//    auto ShaderMap = GetGlobalShaderMap(FeatureLevel);
//
//    TShaderMapRef<FScreenVS> VertexShader(ShaderMap);
//    TShaderMapRef<FScreenPS> PixelShader(ShaderMap);
//
//    static FGlobalBoundShaderState BoundShaderState;
//    SetGlobalBoundShaderState(RHICmdList, FeatureLevel, BoundShaderState, RendererModule->GetFilterVertexDeclaration().VertexDeclarationRHI, *VertexShader, *PixelShader);
//
//    PixelShader->SetParameters(RHICmdList, TStaticSamplerState<SF_Bilinear>::GetRHI(), SrcTexture);
//
//    RendererModule->DrawRectangle(
//        RHICmdList,
//        0, 0,
//        ViewportWidth, ViewportHeight,
//        U, V,
//        USize, VSize,
//        TargetSize,
//        FIntPoint(1, 1),
//        *VertexShader,
//        EDRF_Default);
//}
{
    check(IsInRenderingThread());

    const uint32 ViewportWidth = BackBuffer->GetSizeX();
    const uint32 ViewportHeight = BackBuffer->GetSizeY();

    SetRenderTarget(RHICmdList, BackBuffer, FTextureRHIRef());
    RHICmdList.SetViewport(0, 0, 0, ViewportWidth, ViewportHeight, 1.0f);

    RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
    RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI());
    RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());

    const auto FeatureLevel = GMaxRHIFeatureLevel;
    auto ShaderMap = GetGlobalShaderMap(FeatureLevel);

    TShaderMapRef<FScreenVS> VertexShader(ShaderMap);
    TShaderMapRef<FScreenPS> PixelShader(ShaderMap);

    static FGlobalBoundShaderState BoundShaderState;
    SetGlobalBoundShaderState(RHICmdList, FeatureLevel, BoundShaderState, RendererModule->GetFilterVertexDeclaration().VertexDeclarationRHI, *VertexShader, *PixelShader);

    PixelShader->SetParameters(RHICmdList, TStaticSamplerState<SF_Bilinear>::GetRHI(), SrcTexture);
    //if (WindowMirrorMode == 1)
    //{
    //    // need to clear when rendering only one eye since the borders won't be touched by the DrawRect below
    //    RHICmdList.Clear(true, FLinearColor::Black, false, 0, false, 0, FIntRect());

    //    RendererModule->DrawRectangle(
    //        RHICmdList,
    //        ViewportWidth / 4, 0,
    //        ViewportWidth / 2, ViewportHeight,
    //        0.1f, 0.2f,
    //        0.3f, 0.6f,
    //        FIntPoint(ViewportWidth, ViewportHeight),
    //        FIntPoint(1, 1),
    //        *VertexShader,
    //        EDRF_Default);
    //}
    //else if (WindowMirrorMode == 2)
    //{
        RendererModule->DrawRectangle(
            RHICmdList,
            0, 0, // X, Y
            ViewportWidth, ViewportHeight, // SizeX, SizeY
            0.0f, 0.0f, // U, V
            1.0f, 1.0f, // SizeU, SizeV
            FIntPoint(ViewportWidth, ViewportHeight), // TargetSize
            FIntPoint(1, 1), // TextureSize
            *VertexShader,
            EDRF_Default);
    //}
}

void FOSVRHMD::GetEyeRenderParams_RenderThread(const struct FRenderingCompositePassContext& Context, FVector2D& EyeToSrcUVScaleValue, FVector2D& EyeToSrcUVOffsetValue) const
{
	if (Context.View.StereoPass == eSSP_LEFT_EYE)
	{
		EyeToSrcUVOffsetValue.X = 0.0f;
		EyeToSrcUVOffsetValue.Y = 0.0f;

		EyeToSrcUVScaleValue.X = 0.5f;
		EyeToSrcUVScaleValue.Y = 1.0f;
	}
	else
	{
		EyeToSrcUVOffsetValue.X = 0.5f;
		EyeToSrcUVOffsetValue.Y = 0.0f;

		EyeToSrcUVScaleValue.X = 0.5f;
		EyeToSrcUVScaleValue.Y = 1.0f;
	}
}

void FOSVRHMD::GetTimewarpMatrices_RenderThread(const struct FRenderingCompositePassContext& Context, FMatrix& EyeRotationStart, FMatrix& EyeRotationEnd) const
{
	// intentionally left blank
}

// @todo: Why is this function never called?
void FOSVRHMD::PreRenderViewFamily_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneViewFamily& ViewFamily)
{
    check(IsInRenderingThread());
    if (mCustomPresent) {
        mCustomPresent->Initialize();
    }
    // steamVR updates the current pose here, should we?
}

void FOSVRHMD::PreRenderView_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneView& View)
{
    // @todo

}

void FOSVRHMD::CalculateRenderTargetSize(const FViewport& Viewport, uint32& InOutSizeX, uint32& InOutSizeY)
{
    check(IsInGameThread());

    if (!IsStereoEnabled()) {
        return;
    }

    if (mCustomPresent) {
        mCustomPresent->CalculateRenderTargetSize(InOutSizeX, InOutSizeY);
    }
}

bool FOSVRHMD::NeedReAllocateViewportRenderTarget(const FViewport &viewport) {
    check(IsInGameThread());
    if (IsStereoEnabled()) {
        const uint32 inSizeX = viewport.GetSizeXY().X;
        const uint32 inSizeY = viewport.GetSizeXY().Y;
        FIntPoint renderTargetSize;
        renderTargetSize.X = viewport.GetRenderTargetTexture()->GetSizeX();
        renderTargetSize.Y = viewport.GetRenderTargetTexture()->GetSizeY();

        uint32 newSizeX = inSizeX, newSizeY = inSizeY;
        CalculateRenderTargetSize(viewport, newSizeX, newSizeY);
        if (newSizeX != renderTargetSize.X || newSizeY != renderTargetSize.Y) {
            return true;
        }
    }
    return false;
}

void FOSVRHMD::UpdateViewport(bool bUseSeparateRenderTarget, const FViewport& InViewport, class SViewport*)
{
    check(IsInGameThread());

    auto viewportRHI = InViewport.GetViewportRHI().GetReference();
    if (!IsStereoEnabled()) {
        if (!bUseSeparateRenderTarget) {
            viewportRHI->SetCustomPresent(nullptr);
        }
        return;
    }

    if (mCustomPresent) {
        mCustomPresent->UpdateViewport(InViewport, viewportRHI);
    }
}

bool FOSVRHMD::AllocateRenderTargetTexture(uint32 index, uint32 sizeX, uint32 sizeY, uint8 format, uint32 numMips, uint32 flags, uint32 targetableTextureFlags, FTexture2DRHIRef& outTargetableTexture, FTexture2DRHIRef& outShaderResourceTexture, uint32 numSamples)
{
    check(index == 0);
    if (mCustomPresent) {
        return mCustomPresent->AllocateRenderTargetTexture(index, sizeX, sizeY, format, numMips, flags, targetableTextureFlags, outTargetableTexture, outShaderResourceTexture, numSamples);
    }
    return false;
}
<commit_msg>Format/comment.<commit_after>//
// Copyright 2014, 2015 Razer Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include "OSVRPrivatePCH.h"
#include "OSVRHMD.h"

// Must put path from Engine/Source to these includes since we are an out-of-tree module.
#include "Runtime/Renderer/Private/RendererPrivate.h"
#include "Runtime/Renderer/Private/ScenePrivate.h"
#include "Runtime/Renderer/Private/PostProcess/PostProcessHMD.h"
#include "Runtime/Engine/Public/ScreenRendering.h"

void FOSVRHMD::DrawDistortionMesh_RenderThread(FRenderingCompositePassContext& Context, const FIntPoint& TextureSize)
{
    // shouldn't be called with a custom present
    check(0);
}

// Based off of the SteamVR Unreal Plugin implementation.
void FOSVRHMD::RenderTexture_RenderThread(FRHICommandListImmediate& rhiCmdList, FTexture2DRHIParamRef backBuffer, FTexture2DRHIParamRef srcTexture) const
{
    check(IsInRenderingThread());

    const uint32 viewportWidth = backBuffer->GetSizeX();
    const uint32 viewportHeight = backBuffer->GetSizeY();

    SetRenderTarget(rhiCmdList, backBuffer, FTextureRHIRef());
    rhiCmdList.SetViewport(0, 0, 0, viewportWidth, viewportHeight, 1.0f);

    rhiCmdList.SetBlendState(TStaticBlendState<>::GetRHI());
    rhiCmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI());
    rhiCmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());

    const auto featureLevel = GMaxRHIFeatureLevel;
    auto shaderMap = GetGlobalShaderMap(featureLevel);

    TShaderMapRef<FScreenVS> vertexShader(shaderMap);
    TShaderMapRef<FScreenPS> pixelShader(shaderMap);

    static FGlobalBoundShaderState boundShaderState;
    SetGlobalBoundShaderState(rhiCmdList, featureLevel, boundShaderState, RendererModule->GetFilterVertexDeclaration().VertexDeclarationRHI, *vertexShader, *pixelShader);

    pixelShader->SetParameters(rhiCmdList, TStaticSamplerState<SF_Bilinear>::GetRHI(), srcTexture);
    RendererModule->DrawRectangle(
        rhiCmdList,
        0, 0, // X, Y
        viewportWidth, viewportHeight, // SizeX, SizeY
        0.0f, 0.0f, // U, V
        1.0f, 1.0f, // SizeU, SizeV
        FIntPoint(viewportWidth, viewportHeight), // TargetSize
        FIntPoint(1, 1), // TextureSize
        *vertexShader,
        EDRF_Default);
}

void FOSVRHMD::GetEyeRenderParams_RenderThread(const struct FRenderingCompositePassContext& Context, FVector2D& EyeToSrcUVScaleValue, FVector2D& EyeToSrcUVOffsetValue) const
{
	if (Context.View.StereoPass == eSSP_LEFT_EYE)
	{
		EyeToSrcUVOffsetValue.X = 0.0f;
		EyeToSrcUVOffsetValue.Y = 0.0f;

		EyeToSrcUVScaleValue.X = 0.5f;
		EyeToSrcUVScaleValue.Y = 1.0f;
	}
	else
	{
		EyeToSrcUVOffsetValue.X = 0.5f;
		EyeToSrcUVOffsetValue.Y = 0.0f;

		EyeToSrcUVScaleValue.X = 0.5f;
		EyeToSrcUVScaleValue.Y = 1.0f;
	}
}

void FOSVRHMD::GetTimewarpMatrices_RenderThread(const struct FRenderingCompositePassContext& Context, FMatrix& EyeRotationStart, FMatrix& EyeRotationEnd) const
{
	// intentionally left blank
}

// @todo: Why is this function never called?
void FOSVRHMD::PreRenderViewFamily_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneViewFamily& ViewFamily)
{
    check(IsInRenderingThread());
    if (mCustomPresent) {
        mCustomPresent->Initialize();
    }
    // steamVR updates the current pose here, should we?
}

void FOSVRHMD::PreRenderView_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneView& View)
{
    // @todo

}

void FOSVRHMD::CalculateRenderTargetSize(const FViewport& Viewport, uint32& InOutSizeX, uint32& InOutSizeY)
{
    check(IsInGameThread());

    if (!IsStereoEnabled()) {
        return;
    }

    if (mCustomPresent) {
        mCustomPresent->CalculateRenderTargetSize(InOutSizeX, InOutSizeY);
    }
}

bool FOSVRHMD::NeedReAllocateViewportRenderTarget(const FViewport &viewport) {
    check(IsInGameThread());
    if (IsStereoEnabled()) {
        const uint32 inSizeX = viewport.GetSizeXY().X;
        const uint32 inSizeY = viewport.GetSizeXY().Y;
        FIntPoint renderTargetSize;
        renderTargetSize.X = viewport.GetRenderTargetTexture()->GetSizeX();
        renderTargetSize.Y = viewport.GetRenderTargetTexture()->GetSizeY();

        uint32 newSizeX = inSizeX, newSizeY = inSizeY;
        CalculateRenderTargetSize(viewport, newSizeX, newSizeY);
        if (newSizeX != renderTargetSize.X || newSizeY != renderTargetSize.Y) {
            return true;
        }
    }
    return false;
}

void FOSVRHMD::UpdateViewport(bool bUseSeparateRenderTarget, const FViewport& InViewport, class SViewport*)
{
    check(IsInGameThread());

    auto viewportRHI = InViewport.GetViewportRHI().GetReference();
    if (!IsStereoEnabled()) {
        if (!bUseSeparateRenderTarget) {
            viewportRHI->SetCustomPresent(nullptr);
        }
        return;
    }

    if (mCustomPresent) {
        mCustomPresent->UpdateViewport(InViewport, viewportRHI);
    }
}

bool FOSVRHMD::AllocateRenderTargetTexture(uint32 index, uint32 sizeX, uint32 sizeY, uint8 format, uint32 numMips, uint32 flags, uint32 targetableTextureFlags, FTexture2DRHIRef& outTargetableTexture, FTexture2DRHIRef& outShaderResourceTexture, uint32 numSamples)
{
    check(index == 0);
    if (mCustomPresent) {
        return mCustomPresent->AllocateRenderTargetTexture(index, sizeX, sizeY, format, numMips, flags, targetableTextureFlags, outTargetableTexture, outShaderResourceTexture, numSamples);
    }
    return false;
}
<|endoftext|>


<commit_before>
// LogMainView.cpp : CLogMainView 类的实现
//

#include "stdafx.h"
// SHARED_HANDLERS 可以在实现预览、缩略图和搜索筛选器句柄的
// ATL 项目中进行定义，并允许与该项目共享文档代码。
#ifndef SHARED_HANDLERS
#include "LogCC.h"
#endif

#include "LogCCDoc.h"
#include "LogMainView.h"
#include "ILogQuery.h"
#include "LogQueryResult.h"
#include "LogItem.h"
#include "ILogItemPainter.h"
#include "LogPainterFactory.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

static const unsigned LineHeight = 15;

// CLogMainView

IMPLEMENT_DYNCREATE(CLogMainView, CScrollView)

BEGIN_MESSAGE_MAP(CLogMainView, CScrollView)
	ON_WM_ERASEBKGND()
	ON_WM_VSCROLL()
	ON_WM_MOUSEMOVE()
	ON_WM_MOUSEWHEEL()
	ON_WM_KEYDOWN()
	ON_WM_LBUTTONUP()
END_MESSAGE_MAP()

// CLogMainView 构造/析构

CLogMainView::CLogMainView()
{
}

CLogMainView::~CLogMainView()
{
}

// CLogMainView 绘制

void CLogMainView::OnDraw(CDC* pDC)
{
	CLogCCDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	CRect clientRect;
	GetClientRect(clientRect);
	DEBUG_INFO(_T("客户区区域：") << clientRect.left << ", " << clientRect.top << ", "
		<< clientRect.right << ", " << clientRect.bottom);

	HDC memDC = ::CreateCompatibleDC(pDC->GetSafeHdc());

	HBITMAP memBmp = ::CreateCompatibleBitmap(pDC->GetSafeHdc(), clientRect.Width(), clientRect.Height());
	HGDIOBJ oldBmp = ::SelectObject(memDC, memBmp);

	HBRUSH bkgdBrush = reinterpret_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH));
	::FillRect(memDC, clientRect, bkgdBrush);

	HFONT font = ::CreateFont(LineHeight - 2, 0, 0, 0, FW_NORMAL, FALSE, FALSE, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
		DEFAULT_QUALITY, FIXED_PITCH, _T("新宋体"));
	HGDIOBJ oldFont = ::SelectObject(memDC, font);

	DEBUG_INFO(_T("重绘"));

	CPoint scrollPosition = GetScrollPosition();
	DEBUG_INFO(_T("滚动条位置：") << scrollPosition.x << ", " << scrollPosition.y);

	// 顶部可以显示半行
	int yLogLineStart = scrollPosition.y % LineHeight == 0 ? 0 : scrollPosition.y % LineHeight - LineHeight;
	unsigned beginLine = scrollPosition.y / LineHeight;
	// +1是为了底部能显示半行
	unsigned endLine = (scrollPosition.y + clientRect.Height()) / LineHeight + 1;
	endLine = min(endLine, GetDocument()->getModel()->getCurQueryResult()->getCount());
	DEBUG_INFO(_T("行号区间：") << beginLine << ", " << endLine);

	vector<LogItem*> vecLines = GetDocument()->getModel()->getCurQueryResult()->getRange(beginLine, endLine);
	for (unsigned i = 0; i < vecLines.size(); i++) {
		LogItem* item = vecLines[i];
		CRect rect = clientRect;
		rect.top = yLogLineStart + i * LineHeight;
		rect.bottom = rect.top + LineHeight;
		LogPainterFactory::GetInstance()->GetSingleLinePainter()->Draw(memDC, rect, *item);
	}

	::BitBlt(pDC->GetSafeHdc(), scrollPosition.x, scrollPosition.y, clientRect.Width(), clientRect.Height(),
		memDC, 0, 0, SRCCOPY);

	::SelectObject(memDC, oldFont);
	::DeleteObject(font);
	::SelectObject(memDC, oldBmp);
	::DeleteObject(memBmp);
	::DeleteDC(memDC);
}

void CLogMainView::OnInitialUpdate()
{
	CScrollView::OnInitialUpdate();

	getModel()->registerObserver(this);

	UpdateScroll();
	SetFocus();
}

void CLogMainView::PostNcDestroy()
{
	getModel()->unregisterObserver(this);
	__super::PostNcDestroy();
}

void CLogMainView::UpdateScroll()
{
	CRect clientRect;
	GetClientRect(clientRect);
	totalSize.cx = 0;
	// 加1是为了最后一行一定可见
	totalSize.cy = (getModel()->getCurQueryResult()->getCount() + 1) * LineHeight;
#define LOGCC_WINUI_CUSTOMIZE_PAGE_SIZE_LINE_SIZE
#ifdef LOGCC_WINUI_CUSTOMIZE_PAGE_SIZE_LINE_SIZE
	CSize pageSize(clientRect.Width(), clientRect.Height() / LineHeight * LineHeight);
	CSize lineSize(clientRect.Width(), LineHeight);
	SetScrollSizes(MM_TEXT, totalSize, pageSize, lineSize);
#else
	SetScrollSizes(MM_TEXT, totalSize);
#endif
#define LOGCC_WINUI_SCROLL_TO_END_ON_UPDATE
#ifdef LOGCC_WINUI_SCROLL_TO_END_ON_UPDATE
	int y = totalSize.cy - clientRect.Height();
	ScrollToPosition(CPoint(0, max(y, 0)));
#endif
}

void CLogMainView::onGeneralDataChanged() {
	Invalidate();
}

void CLogMainView::onQueryResultChanged() {
	UpdateScroll();
	Invalidate();
}

void CLogMainView::onScrollPositionChanged(int yPosition) {
	CPoint position = GetScrollPosition();
	position.y = yPosition;
	ScrollToPosition(position);
}

#ifdef _DEBUG
CLogCCDoc* CLogMainView::GetDocument() const // 非调试版本是内联的
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CLogCCDoc)));
	return (CLogCCDoc*)m_pDocument;
}
#endif //_DEBUG

// CLogMainView 消息处理程序

BOOL CLogMainView::OnEraseBkgnd(CDC* pDC)
{
#ifdef LOGCC_WINUI_USE_DEFAULT_ERASE_BACKGROUND
	return CScrollView::OnEraseBkgnd(pDC);
#else
	return TRUE;
#endif
}

void CLogMainView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	if (nSBCode == SB_ENDSCROLL)
	{
		Invalidate();
	}
	CScrollView::OnVScroll(nSBCode, nPos, pScrollBar);
}

void CLogMainView::OnMouseMove(UINT nFlags, CPoint point)
{
	if (GetForegroundWindow() == AfxGetMainWnd()) {
		SetFocus();
	}
	__super::OnMouseMove(nFlags, point);
}

void CLogMainView::onSubmit() {
}

BOOL CLogMainView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) {
	CRect clientRect;
	GetClientRect(clientRect);

	int yScrollPos = GetScrollPosition().y;
	if (clientRect.Height() >= totalSize.cy) return FALSE;

	int delta = 3 * LineHeight;
	if (zDelta < 0) {
		// 向下3行
		yScrollPos += delta;
	}
	else {
		// 向上3行
		yScrollPos -= delta;
		yScrollPos = max(yScrollPos, 0);
	}
	getModel()->scrollTo(yScrollPos);
	DEBUG_INFO(_T("滚动位置：") << yScrollPos);
	return __super::OnMouseWheel(nFlags, zDelta, pt);
}

void CLogMainView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
	CRect clientRect;
	GetClientRect(clientRect);

	// NOTICE:	很奇怪的现象，CScrollView在OnInitUpdate之外函数SetScrollSizes，
	//			如果设置的高度小于ClientRect，虽然没有显示滚动条仍然可以滚动，这里特殊处理一下，禁止滚动
	if (clientRect.Height() >= totalSize.cy) return;

	int yScrollPos = GetScrollPosition().y;
	if (::GetKeyState(VK_CONTROL) & 0x80000000) {
		if (nChar == VK_HOME) {
			// 跳到第一页
			yScrollPos = 0;
		} else if (nChar == VK_END) {
			// 跳到最后一页，多出没事
			yScrollPos = (getModel()->getCurQueryResult()->getCount()) * LineHeight;
		} else if (nChar == VK_UP) {
			// 向上1行
			yScrollPos -= LineHeight;
			yScrollPos = max(yScrollPos, 0);
		} else if (nChar == VK_DOWN) {
			// 向下1行
			yScrollPos += LineHeight;
		}
	}
	if (nChar == VK_PRIOR) {
		// 向上1页
		yScrollPos -= clientRect.Height() / LineHeight * LineHeight;
		yScrollPos = max(yScrollPos, 0);
	} else if (nChar == VK_NEXT) {
		// 向下1页
		yScrollPos += clientRect.Height() / LineHeight * LineHeight;
	}
	if (nChar == VK_UP) {
		// 选中上一行
		LogItem* item = getModel()->getSelected();
		if (item) {
			unsigned i = getModel()->getCurQueryResult()->findIndex(item);
			if (i > 0 && i != 0xFFFFFFFF) {
				i--;
				getModel()->setSelected(getModel()->getCurQueryResult()->getIndex(i));
			}
		}
	} else if (nChar == VK_DOWN) {
		// 选中下一行
		LogItem* item = getModel()->getSelected();
		if (item) {
			unsigned i = getModel()->getCurQueryResult()->findIndex(item);
			if (i != 0xFFFFFFFF && i < getModel()->getCurQueryResult()->getCount() - 1) {
				i++;
				getModel()->setSelected(getModel()->getCurQueryResult()->getIndex(i));
			}
		}
	}
	ScrollToPosition(CPoint(0, yScrollPos));
	DEBUG_INFO(_T("滚动位置：") << yScrollPos);
	__super::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CLogMainView::OnLButtonUp(UINT nFlags, CPoint point)
{
	int yScrollPos = GetScrollPosition().y;
	this->selectedLine = (yScrollPos + point.y) / LineHeight;
	__super::OnLButtonUp(nFlags, point);
}
<commit_msg>解决不足一页时上下键不能切换选中行的bug<commit_after>
// LogMainView.cpp : CLogMainView 类的实现
//

#include "stdafx.h"
// SHARED_HANDLERS 可以在实现预览、缩略图和搜索筛选器句柄的
// ATL 项目中进行定义，并允许与该项目共享文档代码。
#ifndef SHARED_HANDLERS
#include "LogCC.h"
#endif

#include "LogCCDoc.h"
#include "LogMainView.h"
#include "ILogQuery.h"
#include "LogQueryResult.h"
#include "LogItem.h"
#include "ILogItemPainter.h"
#include "LogPainterFactory.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

static const unsigned LineHeight = 15;

// CLogMainView

IMPLEMENT_DYNCREATE(CLogMainView, CScrollView)

BEGIN_MESSAGE_MAP(CLogMainView, CScrollView)
	ON_WM_ERASEBKGND()
	ON_WM_VSCROLL()
	ON_WM_MOUSEMOVE()
	ON_WM_MOUSEWHEEL()
	ON_WM_KEYDOWN()
	ON_WM_LBUTTONUP()
END_MESSAGE_MAP()

// CLogMainView 构造/析构

CLogMainView::CLogMainView()
{
}

CLogMainView::~CLogMainView()
{
}

// CLogMainView 绘制

void CLogMainView::OnDraw(CDC* pDC)
{
	CLogCCDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	CRect clientRect;
	GetClientRect(clientRect);
	DEBUG_INFO(_T("客户区区域：") << clientRect.left << ", " << clientRect.top << ", "
		<< clientRect.right << ", " << clientRect.bottom);

	HDC memDC = ::CreateCompatibleDC(pDC->GetSafeHdc());

	HBITMAP memBmp = ::CreateCompatibleBitmap(pDC->GetSafeHdc(), clientRect.Width(), clientRect.Height());
	HGDIOBJ oldBmp = ::SelectObject(memDC, memBmp);

	HBRUSH bkgdBrush = reinterpret_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH));
	::FillRect(memDC, clientRect, bkgdBrush);

	HFONT font = ::CreateFont(LineHeight - 2, 0, 0, 0, FW_NORMAL, FALSE, FALSE, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
		DEFAULT_QUALITY, FIXED_PITCH, _T("新宋体"));
	HGDIOBJ oldFont = ::SelectObject(memDC, font);

	DEBUG_INFO(_T("重绘"));

	CPoint scrollPosition = GetScrollPosition();
	DEBUG_INFO(_T("滚动条位置：") << scrollPosition.x << ", " << scrollPosition.y);

	// 顶部可以显示半行
	int yLogLineStart = scrollPosition.y % LineHeight == 0 ? 0 : scrollPosition.y % LineHeight - LineHeight;
	unsigned beginLine = scrollPosition.y / LineHeight;
	// +1是为了底部能显示半行
	unsigned endLine = (scrollPosition.y + clientRect.Height()) / LineHeight + 1;
	endLine = min(endLine, GetDocument()->getModel()->getCurQueryResult()->getCount());
	DEBUG_INFO(_T("行号区间：") << beginLine << ", " << endLine);

	vector<LogItem*> vecLines = GetDocument()->getModel()->getCurQueryResult()->getRange(beginLine, endLine);
	for (unsigned i = 0; i < vecLines.size(); i++) {
		LogItem* item = vecLines[i];
		CRect rect = clientRect;
		rect.top = yLogLineStart + i * LineHeight;
		rect.bottom = rect.top + LineHeight;
		LogPainterFactory::GetInstance()->GetSingleLinePainter()->Draw(memDC, rect, *item);
	}

	::BitBlt(pDC->GetSafeHdc(), scrollPosition.x, scrollPosition.y, clientRect.Width(), clientRect.Height(),
		memDC, 0, 0, SRCCOPY);

	::SelectObject(memDC, oldFont);
	::DeleteObject(font);
	::SelectObject(memDC, oldBmp);
	::DeleteObject(memBmp);
	::DeleteDC(memDC);
}

void CLogMainView::OnInitialUpdate()
{
	CScrollView::OnInitialUpdate();

	getModel()->registerObserver(this);

	UpdateScroll();
	SetFocus();
}

void CLogMainView::PostNcDestroy()
{
	getModel()->unregisterObserver(this);
	__super::PostNcDestroy();
}

void CLogMainView::UpdateScroll()
{
	CRect clientRect;
	GetClientRect(clientRect);
	totalSize.cx = 0;
	// 加1是为了最后一行一定可见
	totalSize.cy = (getModel()->getCurQueryResult()->getCount() + 1) * LineHeight;
#define LOGCC_WINUI_CUSTOMIZE_PAGE_SIZE_LINE_SIZE
#ifdef LOGCC_WINUI_CUSTOMIZE_PAGE_SIZE_LINE_SIZE
	CSize pageSize(clientRect.Width(), clientRect.Height() / LineHeight * LineHeight);
	CSize lineSize(clientRect.Width(), LineHeight);
	SetScrollSizes(MM_TEXT, totalSize, pageSize, lineSize);
#else
	SetScrollSizes(MM_TEXT, totalSize);
#endif
#define LOGCC_WINUI_SCROLL_TO_END_ON_UPDATE
#ifdef LOGCC_WINUI_SCROLL_TO_END_ON_UPDATE
	int y = totalSize.cy - clientRect.Height();
	ScrollToPosition(CPoint(0, max(y, 0)));
#endif
}

void CLogMainView::onGeneralDataChanged() {
	Invalidate();
}

void CLogMainView::onQueryResultChanged() {
	UpdateScroll();
	Invalidate();
}

void CLogMainView::onScrollPositionChanged(int yPosition) {
	CPoint position = GetScrollPosition();
	position.y = yPosition;
	ScrollToPosition(position);
}

#ifdef _DEBUG
CLogCCDoc* CLogMainView::GetDocument() const // 非调试版本是内联的
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CLogCCDoc)));
	return (CLogCCDoc*)m_pDocument;
}
#endif //_DEBUG

// CLogMainView 消息处理程序

BOOL CLogMainView::OnEraseBkgnd(CDC* pDC)
{
#ifdef LOGCC_WINUI_USE_DEFAULT_ERASE_BACKGROUND
	return CScrollView::OnEraseBkgnd(pDC);
#else
	return TRUE;
#endif
}

void CLogMainView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	if (nSBCode == SB_ENDSCROLL)
	{
		Invalidate();
	}
	CScrollView::OnVScroll(nSBCode, nPos, pScrollBar);
}

void CLogMainView::OnMouseMove(UINT nFlags, CPoint point)
{
	if (GetForegroundWindow() == AfxGetMainWnd()) {
		SetFocus();
	}
	__super::OnMouseMove(nFlags, point);
}

void CLogMainView::onSubmit() {
}

BOOL CLogMainView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) {
	CRect clientRect;
	GetClientRect(clientRect);

	int yScrollPos = GetScrollPosition().y;
	if (clientRect.Height() >= totalSize.cy) return FALSE;

	int delta = 3 * LineHeight;
	if (zDelta < 0) {
		// 向下3行
		yScrollPos += delta;
	}
	else {
		// 向上3行
		yScrollPos -= delta;
		yScrollPos = max(yScrollPos, 0);
	}
	getModel()->scrollTo(yScrollPos);
	DEBUG_INFO(_T("滚动位置：") << yScrollPos);
	return __super::OnMouseWheel(nFlags, zDelta, pt);
}

void CLogMainView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
	CRect clientRect;
	GetClientRect(clientRect);

	if (nChar == VK_UP) {
		// 选中上一行
		LogItem* item = getModel()->getSelected();
		if (item) {
			unsigned i = getModel()->getCurQueryResult()->findIndex(item);
			if (i > 0 && i != 0xFFFFFFFF) {
				i--;
				getModel()->setSelected(getModel()->getCurQueryResult()->getIndex(i));
			}
		}
	} else if (nChar == VK_DOWN) {
		// 选中下一行
		LogItem* item = getModel()->getSelected();
		if (item) {
			unsigned i = getModel()->getCurQueryResult()->findIndex(item);
			if (i != 0xFFFFFFFF && i < getModel()->getCurQueryResult()->getCount() - 1) {
				i++;
				getModel()->setSelected(getModel()->getCurQueryResult()->getIndex(i));
			}
		}
	} else {
		// 以下是滚动的情况
		// NOTICE:	很奇怪的现象，CScrollView在OnInitUpdate之外函数SetScrollSizes，
		//			如果设置的高度小于ClientRect，虽然没有显示滚动条仍然可以滚动，这里特殊处理一下，禁止滚动
		if (clientRect.Height() >= totalSize.cy) return;

		int yScrollPos = GetScrollPosition().y;
		if (::GetKeyState(VK_CONTROL) & 0x80000000) {
			if (nChar == VK_HOME) {
				// 跳到第一页
				yScrollPos = 0;
			} else if (nChar == VK_END) {
				// 跳到最后一页，多出没事
				yScrollPos = (getModel()->getCurQueryResult()->getCount()) * LineHeight;
			} else if (nChar == VK_UP) {
				// 向上1行
				yScrollPos -= LineHeight;
				yScrollPos = max(yScrollPos, 0);
			} else if (nChar == VK_DOWN) {
				// 向下1行
				yScrollPos += LineHeight;
			}
		}
		if (nChar == VK_PRIOR) {
			// 向上1页
			yScrollPos -= clientRect.Height() / LineHeight * LineHeight;
			yScrollPos = max(yScrollPos, 0);
		} else if (nChar == VK_NEXT) {
			// 向下1页
			yScrollPos += clientRect.Height() / LineHeight * LineHeight;
		}
		ScrollToPosition(CPoint(0, yScrollPos));
		DEBUG_INFO(_T("滚动位置：") << yScrollPos);
	}
	__super::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CLogMainView::OnLButtonUp(UINT nFlags, CPoint point)
{
	int yScrollPos = GetScrollPosition().y;
	this->selectedLine = (yScrollPos + point.y) / LineHeight;
	__super::OnLButtonUp(nFlags, point);
}
<|endoftext|>


<commit_before>//@author A0097630B
#include "stdafx.h"
#include "query_executor.h"
#include "../exceptions/context_index_out_of_range_exception.h"
#include "../result.h"
#include "query_executor_builder_visitor.h"

namespace You {
namespace Controller {
namespace Internal {

using You::NLP::TaskField;
using You::NLP::TaskPriority;
using You::NLP::QUERY;
using You::NLP::ADD_QUERY;
using You::NLP::SHOW_QUERY;
using You::NLP::EDIT_QUERY;
using You::NLP::DELETE_QUERY;
using You::NLP::UNDO_QUERY;

QueryExecutorBuilderVisitor::QueryExecutorBuilderVisitor(
	const Controller::Context& context)
	: context(context) {
}

std::unique_ptr<QueryEngine::Query> buildQuery(const ADD_QUERY& query) {
	std::vector<std::unique_ptr<QueryEngine::Query>> addSubtasks;
	std::vector<std::unique_ptr<QueryEngine::Query>> addDependencies;
	for (const auto& subtask : query.subtasks) {
		addSubtasks.emplace_back(buildQuery(subtask));
	}
	auto ptrDep = query.dependent;
	while (query.dependent != nullptr) {
		addDependencies.emplace_back(buildQuery(*ptrDep));
		ptrDep = ptrDep->dependent;
	}
	return std::unique_ptr<QueryEngine::Query>(
		QueryEngine::AddTask(
			query.description,
			query.deadline ? query.deadline.get() : Task::DEFAULT_DEADLINE,
			query.priority == TaskPriority::HIGH ?
			Task::Priority::HIGH : Task::Priority::NORMAL,
			std::move(addDependencies),
			std::move(addSubtasks)));
}

std::unique_ptr<QueryExecutor>
QueryExecutorBuilderVisitor::build(const ADD_QUERY& query) {
	class AddTaskQueryExecutor : public QueryExecutor {
	public:
		explicit AddTaskQueryExecutor(
			std::unique_ptr<QueryEngine::Query>&& query)
			: QueryExecutor(std::move(query)) {
		}

		virtual ~AddTaskQueryExecutor() = default;

	protected:
		Result processResponse(
			const You::QueryEngine::Response& response) override {
			return ADD_RESULT {
				boost::get<Task>(response)
			};
		}
	};

	return std::unique_ptr<QueryExecutor>(
		new AddTaskQueryExecutor(buildAddQuery(query)));
}

std::unique_ptr<QueryEngine::Query>
QueryExecutorBuilderVisitor::buildAddQuery(const ADD_QUERY& query) {
	std::vector<std::unique_ptr<QueryEngine::Query>> subtaskQueries;
	//subtaskQueries.reserve(query.subtasks.size());

	std::transform(begin(query.subtasks), end(query.subtasks),
		std::back_inserter(subtaskQueries), [](const ADD_QUERY& q) {
		return QueryExecutorBuilderVisitor::buildAddQuery(q);
	});

	return QueryEngine::AddTask(
		query.description,
		query.deadline ? query.deadline.get() : Task::DEFAULT_DEADLINE,
		query.priority == TaskPriority::HIGH ?
		Task::Priority::HIGH : Task::Priority::NORMAL,
		{},
		std::move(subtaskQueries)
	);
}

std::unique_ptr<QueryExecutor>
QueryExecutorBuilderVisitor::build(const SHOW_QUERY& query) {
	class ShowTaskQueryExecutor : public QueryExecutor {
	public:
		explicit ShowTaskQueryExecutor(
			std::unique_ptr<QueryEngine::Query>&& query)
			: QueryExecutor(std::move(query)) {
		}

		virtual ~ShowTaskQueryExecutor() = default;

	protected:
		Result processResponse(
			const You::QueryEngine::Response& response) override {
			return SHOW_RESULT {
				boost::get<TaskList>(response)
			};
		}
	};

	using You::QueryEngine::Filter;
	using You::QueryEngine::Comparator;
	Filter filter = Filter::anyTask();
	Comparator comparator(Comparator::notSorted());

	std::for_each(begin(query.predicates), end(query.predicates),
		[&filter](const SHOW_QUERY::FIELD_FILTER& field) {
			std::function<bool(const Task&)> currentFilter;
			switch (field.field) {
			case TaskField::DESCRIPTION:
				assert(boost::get<std::wstring>(&field.value));
				currentFilter = buildComparator(&Task::getDescription,
					field.predicate,
					boost::get<std::wstring>(field.value));
				break;
			case TaskField::DEADLINE:
				assert(boost::get<boost::posix_time::ptime>(&field.value));
				currentFilter = buildComparator(&Task::getDeadline,
					field.predicate,
					boost::get<boost::posix_time::ptime>(field.value));
				break;
			case TaskField::COMPLETE:
				assert(boost::get<bool>(&field.value));
				currentFilter = buildComparator(&Task::isCompleted,
					field.predicate,
					boost::get<bool>(field.value));
				break;
			case TaskField::PRIORITY:
				assert(boost::get<TaskPriority>(&field.value));
				currentFilter = buildComparator(&Task::getPriority,
					field.predicate,
					Controller::nlpToQueryEnginePriority(
						boost::get<TaskPriority>(field.value)));
				break;
			default:
				assert(false); abort();
			}

			filter = filter && Filter(currentFilter);
		});

	std::for_each(begin(query.order), end(query.order),
		[&comparator](const SHOW_QUERY::FIELD_ORDER& field) {
			Comparator comp(Comparator::notSorted());
			switch (field.field) {
			case TaskField::DESCRIPTION:
				comp = Comparator::byDescription();
				break;
			case TaskField::DEADLINE:
				comp = Comparator::byDeadline();
				break;
			case TaskField::PRIORITY:
				comp = Comparator::byPriority();
				break;
			case TaskField::COMPLETE:
			default:
				assert(false); abort();
			}

			if (field.order == SHOW_QUERY::Order::ASCENDING) {
				comp = comp.ascending();
			} else {
				comp = comp.descending();
			}

			comparator = comparator && comp;
		});

	return std::unique_ptr<QueryExecutor>(
		new ShowTaskQueryExecutor(
			QueryEngine::GetTask(filter, comparator)
		)
	);
}

template<typename TValue>
std::function<bool(const Task&)>
QueryExecutorBuilderVisitor::buildComparator(
	TValue(QueryEngine::Task::*selector)() const,
	SHOW_QUERY::Predicate predicate,
	const TValue& value) {
	switch (predicate) {
	case SHOW_QUERY::Predicate::EQ:
		return std::bind(std::equal_to<TValue>(),
			std::bind(selector, std::placeholders::_1),
			value);
	case SHOW_QUERY::Predicate::NOT_EQ:
		return std::bind(std::not_equal_to<TValue>(),
			std::bind(selector, std::placeholders::_1),
			value);
	case SHOW_QUERY::Predicate::LESS_THAN:
		return std::bind(std::less<TValue>(),
			std::bind(selector, std::placeholders::_1),
			value);
	case SHOW_QUERY::Predicate::LESS_THAN_EQ:
		return std::bind(std::less_equal<TValue>(),
			std::bind(selector, std::placeholders::_1),
			value);
	case SHOW_QUERY::Predicate::GREATER_THAN:
		return std::bind(std::greater<TValue>(),
			std::bind(selector, std::placeholders::_1),
			value);
	case SHOW_QUERY::Predicate::GREATER_THAN_EQ:
		return std::bind(std::greater_equal<TValue>(),
			std::bind(selector, std::placeholders::_1),
			value);
	default:
		assert(false); abort();
	}
}

std::unique_ptr<QueryExecutor>
QueryExecutorBuilderVisitor::build(const EDIT_QUERY& query) const {
	class EditTaskQueryExecutor : public QueryExecutor {
	public:
		explicit EditTaskQueryExecutor(
			std::unique_ptr<QueryEngine::Query>&& query)
			: QueryExecutor(std::move(query)) {
		}

		virtual ~EditTaskQueryExecutor() = default;

	protected:
		Result processResponse(
			const You::QueryEngine::Response& response) override {
			return EDIT_RESULT {
				boost::get<Task>(response)
			};
		}
	};

	try {
		Task::ID task = context.at(query.taskID).getID();
		You::Utils::Option<Task::Priority> priority;
		if (query.priority) {
			priority = Controller::nlpToQueryEnginePriority(
				query.priority.get());
		}

		You::Utils::Option<Task::Subtasks> subtasks;
		if (query.childTask) {
			Task::Subtasks subtasksList;
			subtasksList.insert(context.at(query.childTask).getID());
			subtasks = subtasksList;
		}

		You::Utils::Option<Task::Dependencies> dependencies;
		if (query.dependingTask) {
			assert(!query.description &&
				!query.deadline &&
				!priority &&
				!query.complete &&
				!subtasks && "Cannot change dependencies and other properties");
			Task::Dependencies dependenciesList;
			dependenciesList.insert(task);
			dependencies = dependenciesList;
			task = context.at(query.dependingTask).getID();
		}

		return std::unique_ptr<QueryExecutor>(
			new EditTaskQueryExecutor(
				QueryEngine::UpdateTask(
					task,
					query.description,
					query.deadline,
					priority,
					dependencies,
					query.complete,
					boost::none,
					subtasks)));
	} catch (std::out_of_range& e) {
		throw ContextIndexOutOfRangeException(e);
	}
}

std::unique_ptr<QueryExecutor>
QueryExecutorBuilderVisitor::build(const DELETE_QUERY& query) const {
	class DeleteTaskQueryExecutor : public QueryExecutor {
	public:
		explicit DeleteTaskQueryExecutor(
			std::unique_ptr<QueryEngine::Query>&& query)
			: QueryExecutor(std::move(query)) {
		}

		virtual ~DeleteTaskQueryExecutor() = default;

	protected:
		Result processResponse(
			const You::QueryEngine::Response& response) override {
			return DELETE_RESULT {
				boost::get<Task::ID>(response)
			};
		}
	};

	try {
		const Task& task = context.at(query.taskID);

		return std::unique_ptr<QueryExecutor>(
			new DeleteTaskQueryExecutor(
				QueryEngine::DeleteTask(
					task.getID())));
	} catch (std::out_of_range& e) {
		throw ContextIndexOutOfRangeException(e);
	}
}

std::unique_ptr<QueryExecutor>
QueryExecutorBuilderVisitor::build(const UNDO_QUERY& query) const {
	class UndoTaskQueryExecutor : public QueryExecutor {
	public:
		explicit UndoTaskQueryExecutor(
			std::unique_ptr<QueryEngine::Query>&& query)
			: QueryExecutor(std::move(query)) {
		}

		virtual ~UndoTaskQueryExecutor() = default;

	protected:
		Result processResponse(
			const You::QueryEngine::Response& response) override {
			return UNDO_RESULT {
				boost::get<TaskList>(response)
			};
		}
	};

	return std::unique_ptr<QueryExecutor>(
		new UndoTaskQueryExecutor(
			QueryEngine::Undo()));
}

}  // namespace Internal
}  // namespace Controller
}  // namespace You
<commit_msg>Connect with controller<commit_after>//@author A0097630B
#include "stdafx.h"
#include "query_executor.h"
#include "../exceptions/context_index_out_of_range_exception.h"
#include "../result.h"
#include "query_executor_builder_visitor.h"

namespace You {
namespace Controller {
namespace Internal {

using You::NLP::TaskField;
using You::NLP::TaskPriority;
using You::NLP::QUERY;
using You::NLP::ADD_QUERY;
using You::NLP::SHOW_QUERY;
using You::NLP::EDIT_QUERY;
using You::NLP::DELETE_QUERY;
using You::NLP::UNDO_QUERY;

QueryExecutorBuilderVisitor::QueryExecutorBuilderVisitor(
	const Controller::Context& context)
	: context(context) {
}

std::unique_ptr<QueryExecutor>
QueryExecutorBuilderVisitor::build(const ADD_QUERY& query) {
	class AddTaskQueryExecutor : public QueryExecutor {
	public:
		explicit AddTaskQueryExecutor(
			std::unique_ptr<QueryEngine::Query>&& query)
			: QueryExecutor(std::move(query)) {
		}

		virtual ~AddTaskQueryExecutor() = default;

	protected:
		Result processResponse(
			const You::QueryEngine::Response& response) override {
			return ADD_RESULT {
				boost::get<Task>(response)
			};
		}
	};

	return std::unique_ptr<QueryExecutor>(
		new AddTaskQueryExecutor(buildAddQuery(query)));
}

std::unique_ptr<QueryEngine::Query>
QueryExecutorBuilderVisitor::buildAddQuery(const ADD_QUERY& query) {
	std::vector<std::unique_ptr<QueryEngine::Query>> subtaskQueries;
	std::vector<std::unique_ptr<QueryEngine::Query>> dependencyQueries;
	//subtaskQueries.reserve(query.subtasks.size());

	std::transform(begin(query.subtasks), end(query.subtasks),
		std::back_inserter(subtaskQueries), [](const ADD_QUERY& q) {
		return QueryExecutorBuilderVisitor::buildAddQuery(q);
	});

	std::shared_ptr<ADD_QUERY> dependentQuery = query.dependent;
	while (dependentQuery) {
		dependencyQueries.push_back(buildAddQuery(*dependentQuery));
		dependentQuery = (query.dependent)->dependent;
	}

	return QueryEngine::AddTask(
		query.description,
		query.deadline ? query.deadline.get() : Task::DEFAULT_DEADLINE,
		query.priority == TaskPriority::HIGH ?
		Task::Priority::HIGH : Task::Priority::NORMAL,
		std::move(dependencyQueries),
		std::move(subtaskQueries)
	);
}

std::unique_ptr<QueryExecutor>
QueryExecutorBuilderVisitor::build(const SHOW_QUERY& query) {
	class ShowTaskQueryExecutor : public QueryExecutor {
	public:
		explicit ShowTaskQueryExecutor(
			std::unique_ptr<QueryEngine::Query>&& query)
			: QueryExecutor(std::move(query)) {
		}

		virtual ~ShowTaskQueryExecutor() = default;

	protected:
		Result processResponse(
			const You::QueryEngine::Response& response) override {
			return SHOW_RESULT {
				boost::get<TaskList>(response)
			};
		}
	};

	using You::QueryEngine::Filter;
	using You::QueryEngine::Comparator;
	Filter filter = Filter::anyTask();
	Comparator comparator(Comparator::notSorted());

	std::for_each(begin(query.predicates), end(query.predicates),
		[&filter](const SHOW_QUERY::FIELD_FILTER& field) {
			std::function<bool(const Task&)> currentFilter;
			switch (field.field) {
			case TaskField::DESCRIPTION:
				assert(boost::get<std::wstring>(&field.value));
				currentFilter = buildComparator(&Task::getDescription,
					field.predicate,
					boost::get<std::wstring>(field.value));
				break;
			case TaskField::DEADLINE:
				assert(boost::get<boost::posix_time::ptime>(&field.value));
				currentFilter = buildComparator(&Task::getDeadline,
					field.predicate,
					boost::get<boost::posix_time::ptime>(field.value));
				break;
			case TaskField::COMPLETE:
				assert(boost::get<bool>(&field.value));
				currentFilter = buildComparator(&Task::isCompleted,
					field.predicate,
					boost::get<bool>(field.value));
				break;
			case TaskField::PRIORITY:
				assert(boost::get<TaskPriority>(&field.value));
				currentFilter = buildComparator(&Task::getPriority,
					field.predicate,
					Controller::nlpToQueryEnginePriority(
						boost::get<TaskPriority>(field.value)));
				break;
			default:
				assert(false); abort();
			}

			filter = filter && Filter(currentFilter);
		});

	std::for_each(begin(query.order), end(query.order),
		[&comparator](const SHOW_QUERY::FIELD_ORDER& field) {
			Comparator comp(Comparator::notSorted());
			switch (field.field) {
			case TaskField::DESCRIPTION:
				comp = Comparator::byDescription();
				break;
			case TaskField::DEADLINE:
				comp = Comparator::byDeadline();
				break;
			case TaskField::PRIORITY:
				comp = Comparator::byPriority();
				break;
			case TaskField::COMPLETE:
			default:
				assert(false); abort();
			}

			if (field.order == SHOW_QUERY::Order::ASCENDING) {
				comp = comp.ascending();
			} else {
				comp = comp.descending();
			}

			comparator = comparator && comp;
		});

	return std::unique_ptr<QueryExecutor>(
		new ShowTaskQueryExecutor(
			QueryEngine::GetTask(filter, comparator)
		)
	);
}

template<typename TValue>
std::function<bool(const Task&)>
QueryExecutorBuilderVisitor::buildComparator(
	TValue(QueryEngine::Task::*selector)() const,
	SHOW_QUERY::Predicate predicate,
	const TValue& value) {
	switch (predicate) {
	case SHOW_QUERY::Predicate::EQ:
		return std::bind(std::equal_to<TValue>(),
			std::bind(selector, std::placeholders::_1),
			value);
	case SHOW_QUERY::Predicate::NOT_EQ:
		return std::bind(std::not_equal_to<TValue>(),
			std::bind(selector, std::placeholders::_1),
			value);
	case SHOW_QUERY::Predicate::LESS_THAN:
		return std::bind(std::less<TValue>(),
			std::bind(selector, std::placeholders::_1),
			value);
	case SHOW_QUERY::Predicate::LESS_THAN_EQ:
		return std::bind(std::less_equal<TValue>(),
			std::bind(selector, std::placeholders::_1),
			value);
	case SHOW_QUERY::Predicate::GREATER_THAN:
		return std::bind(std::greater<TValue>(),
			std::bind(selector, std::placeholders::_1),
			value);
	case SHOW_QUERY::Predicate::GREATER_THAN_EQ:
		return std::bind(std::greater_equal<TValue>(),
			std::bind(selector, std::placeholders::_1),
			value);
	default:
		assert(false); abort();
	}
}

std::unique_ptr<QueryExecutor>
QueryExecutorBuilderVisitor::build(const EDIT_QUERY& query) const {
	class EditTaskQueryExecutor : public QueryExecutor {
	public:
		explicit EditTaskQueryExecutor(
			std::unique_ptr<QueryEngine::Query>&& query)
			: QueryExecutor(std::move(query)) {
		}

		virtual ~EditTaskQueryExecutor() = default;

	protected:
		Result processResponse(
			const You::QueryEngine::Response& response) override {
			return EDIT_RESULT {
				boost::get<Task>(response)
			};
		}
	};

	try {
		Task::ID task = context.at(query.taskID).getID();
		You::Utils::Option<Task::Priority> priority;
		if (query.priority) {
			priority = Controller::nlpToQueryEnginePriority(
				query.priority.get());
		}

		You::Utils::Option<Task::Subtasks> subtasks;
		if (query.childTask) {
			Task::Subtasks subtasksList;
			subtasksList.insert(context.at(query.childTask).getID());
			subtasks = subtasksList;
		}

		You::Utils::Option<Task::Dependencies> dependencies;
		if (query.dependingTask) {
			assert(!query.description &&
				!query.deadline &&
				!priority &&
				!query.complete &&
				!subtasks && "Cannot change dependencies and other properties");
			Task::Dependencies dependenciesList;
			dependenciesList.insert(task);
			dependencies = dependenciesList;
			task = context.at(query.dependingTask).getID();
		}

		return std::unique_ptr<QueryExecutor>(
			new EditTaskQueryExecutor(
				QueryEngine::UpdateTask(
					task,
					query.description,
					query.deadline,
					priority,
					dependencies,
					query.complete,
					boost::none,
					subtasks)));
	} catch (std::out_of_range& e) {
		throw ContextIndexOutOfRangeException(e);
	}
}

std::unique_ptr<QueryExecutor>
QueryExecutorBuilderVisitor::build(const DELETE_QUERY& query) const {
	class DeleteTaskQueryExecutor : public QueryExecutor {
	public:
		explicit DeleteTaskQueryExecutor(
			std::unique_ptr<QueryEngine::Query>&& query)
			: QueryExecutor(std::move(query)) {
		}

		virtual ~DeleteTaskQueryExecutor() = default;

	protected:
		Result processResponse(
			const You::QueryEngine::Response& response) override {
			return DELETE_RESULT {
				boost::get<Task::ID>(response)
			};
		}
	};

	try {
		const Task& task = context.at(query.taskID);

		return std::unique_ptr<QueryExecutor>(
			new DeleteTaskQueryExecutor(
				QueryEngine::DeleteTask(
					task.getID())));
	} catch (std::out_of_range& e) {
		throw ContextIndexOutOfRangeException(e);
	}
}

std::unique_ptr<QueryExecutor>
QueryExecutorBuilderVisitor::build(const UNDO_QUERY& query) const {
	class UndoTaskQueryExecutor : public QueryExecutor {
	public:
		explicit UndoTaskQueryExecutor(
			std::unique_ptr<QueryEngine::Query>&& query)
			: QueryExecutor(std::move(query)) {
		}

		virtual ~UndoTaskQueryExecutor() = default;

	protected:
		Result processResponse(
			const You::QueryEngine::Response& response) override {
			return UNDO_RESULT {
				boost::get<TaskList>(response)
			};
		}
	};

	return std::unique_ptr<QueryExecutor>(
		new UndoTaskQueryExecutor(
			QueryEngine::Undo()));
}

}  // namespace Internal
}  // namespace Controller
}  // namespace You
<|endoftext|>


<commit_before>#ifndef VIENNAGRID_STORAGE_ID_HPP
#define VIENNAGRID_STORAGE_ID_HPP
#include <boost/iterator/iterator_concepts.hpp>
#include <boost/type_traits/detail/is_function_ptr_helper.hpp>

namespace viennagrid
{

    namespace storage
    {
        template<typename id_type>
        struct id_tag;

        template<typename base_id_type>
        struct smart_id_tag;

        template<typename value_type_, typename base_id_type>
        class smart_id
        {
        public:
            typedef smart_id self_type;
            typedef value_type_ value_type;

            smart_id() : internal_id(0) {}
            explicit smart_id( base_id_type internal_id_ ) : internal_id(internal_id_) {}

            base_id_type get() const { return internal_id; }
            void set( base_id_type internal_id_ ) { internal_id =internal_id_; }

            bool operator== ( self_type rhs ) const { return internal_id == rhs.internal_id; }
            bool operator< ( self_type rhs ) const { return internal_id < rhs.internal_id; }
            bool operator<= ( self_type rhs ) const { return internal_id <= rhs.internal_id; }
            bool operator> ( self_type rhs ) const { return internal_id > rhs.internal_id; }
            bool operator>= (self_type rhs ) const { return internal_id >= rhs.internal_id; }

            self_type & operator++() { ++internal_id; return *this; }
            self_type operator++(int) { self_type tmp(*this); ++*this; return tmp; }

        private:
            base_id_type internal_id;
        };

        template<typename value_type_, typename base_id_type>
        std::ostream & operator<< (std::ostream & os, smart_id<value_type_, base_id_type> id)
        {
            os << id.get();
            return os;
        }

        namespace result_of
        {
            template<typename value_type, typename id_tag>
            struct id;

            template<typename value_type, typename id_type>
            struct id<value_type, id_tag<id_type> >
            {
                typedef id_type type;
            };

            template<typename value_type, typename base_id_type>
            struct id<value_type, smart_id_tag<base_id_type> >
            {
                typedef smart_id<value_type, base_id_type> type;
            };

        }

//         template<typename value_type_, typename base_id_type>
//         smart_id<value_type_, base_id_type> operator+( const smart_id<value_type_, base_id_type> & lhs, const smart_id<value_type_, base_id_type> & rhs )
//         {
//             return smart_id<value_type_, base_id_type>( lhs.get() + rhs.get() );
//         }

//         template<typename value_type_, typename base_id_type>
//         base_id_type operator-( const smart_id<value_type_, base_id_type> & lhs, const smart_id<value_type_, base_id_type> & rhs )
//         {
//             return smart_id<value_type_, base_id_type>( lhs.get() - rhs.get() );
//         }

        template<typename id_type_>
        class id_handler
        {
        public:
            typedef id_type_ id_type;

            id_handler() {}
            id_handler(const id_handler & rhs) : id_(rhs.id_) {}

            void id(id_type id) { id_ = id; }
            id_type id() const { return id_; }

        private:
            id_type id_;
        };

        template<typename id_type_>
        class id_compare
        {
        public:
            typedeef id_type_ id_type;

            id_compare(id_type id__) : id(id__) {}

            template<typename type>
            bool operator() ( const type & to_compare )
            {
                return to_compare.id() == id;
            }

        private:
            id_type id;
        };

        namespace id
        {

            template<typename element_type, typename id_type>
            void set_id( element_type & element, id_type id )
            {
                element.id(id);
            }

            template<typename id_type> void set_id( bool & element, id_type id ) {}
            template<typename id_type> void set_id( char & element, id_type id ) {}
            template<typename id_type> void set_id( unsigned char & element, id_type id ) {}
            template<typename id_type> void set_id( short & element, id_type id ) {}
            template<typename id_type> void set_id( unsigned short & element, id_type id ) {}
            template<typename id_type> void set_id( int & element, id_type id ) {}
            template<typename id_type> void set_id( unsigned int & element, id_type id ) {}
            template<typename id_type> void set_id( long & element, id_type id ) {}
            template<typename id_type> void set_id( unsigned long & element, id_type id ) {}
            template<typename id_type> void set_id( float & element, id_type id ) {}
            template<typename id_type> void set_id( double & element, id_type id ) {}

        }

    }

}

#endif
template<class _Signature >
class result_of;<commit_msg>fixed typo<commit_after>#ifndef VIENNAGRID_STORAGE_ID_HPP
#define VIENNAGRID_STORAGE_ID_HPP
#include <boost/iterator/iterator_concepts.hpp>
#include <boost/type_traits/detail/is_function_ptr_helper.hpp>

namespace viennagrid
{

    namespace storage
    {
        template<typename id_type>
        struct id_tag;

        template<typename base_id_type>
        struct smart_id_tag;

        template<typename value_type_, typename base_id_type>
        class smart_id
        {
        public:
            typedef smart_id self_type;
            typedef value_type_ value_type;

            smart_id() : internal_id(0) {}
            explicit smart_id( base_id_type internal_id_ ) : internal_id(internal_id_) {}

            base_id_type get() const { return internal_id; }
            void set( base_id_type internal_id_ ) { internal_id =internal_id_; }

            bool operator== ( self_type rhs ) const { return internal_id == rhs.internal_id; }
            bool operator< ( self_type rhs ) const { return internal_id < rhs.internal_id; }
            bool operator<= ( self_type rhs ) const { return internal_id <= rhs.internal_id; }
            bool operator> ( self_type rhs ) const { return internal_id > rhs.internal_id; }
            bool operator>= (self_type rhs ) const { return internal_id >= rhs.internal_id; }

            self_type & operator++() { ++internal_id; return *this; }
            self_type operator++(int) { self_type tmp(*this); ++*this; return tmp; }

        private:
            base_id_type internal_id;
        };

        template<typename value_type_, typename base_id_type>
        std::ostream & operator<< (std::ostream & os, smart_id<value_type_, base_id_type> id)
        {
            os << id.get();
            return os;
        }

        namespace result_of
        {
            template<typename value_type, typename id_tag>
            struct id;

            template<typename value_type, typename id_type>
            struct id<value_type, id_tag<id_type> >
            {
                typedef id_type type;
            };

            template<typename value_type, typename base_id_type>
            struct id<value_type, smart_id_tag<base_id_type> >
            {
                typedef smart_id<value_type, base_id_type> type;
            };

        }

//         template<typename value_type_, typename base_id_type>
//         smart_id<value_type_, base_id_type> operator+( const smart_id<value_type_, base_id_type> & lhs, const smart_id<value_type_, base_id_type> & rhs )
//         {
//             return smart_id<value_type_, base_id_type>( lhs.get() + rhs.get() );
//         }

//         template<typename value_type_, typename base_id_type>
//         base_id_type operator-( const smart_id<value_type_, base_id_type> & lhs, const smart_id<value_type_, base_id_type> & rhs )
//         {
//             return smart_id<value_type_, base_id_type>( lhs.get() - rhs.get() );
//         }

        template<typename id_type_>
        class id_handler
        {
        public:
            typedef id_type_ id_type;

            id_handler() {}
            id_handler(const id_handler & rhs) : id_(rhs.id_) {}

            void id(id_type id) { id_ = id; }
            id_type id() const { return id_; }

        private:
            id_type id_;
        };

        template<typename id_type_>
        class id_compare
        {
        public:
            typedef id_type_ id_type;

            id_compare(id_type id__) : id(id__) {}

            template<typename type>
            bool operator() ( const type & to_compare )
            {
                return to_compare.id() == id;
            }

        private:
            id_type id;
        };

        namespace id
        {

            template<typename element_type, typename id_type>
            void set_id( element_type & element, id_type id )
            {
                element.id(id);
            }

            template<typename id_type> void set_id( bool & element, id_type id ) {}
            template<typename id_type> void set_id( char & element, id_type id ) {}
            template<typename id_type> void set_id( unsigned char & element, id_type id ) {}
            template<typename id_type> void set_id( short & element, id_type id ) {}
            template<typename id_type> void set_id( unsigned short & element, id_type id ) {}
            template<typename id_type> void set_id( int & element, id_type id ) {}
            template<typename id_type> void set_id( unsigned int & element, id_type id ) {}
            template<typename id_type> void set_id( long & element, id_type id ) {}
            template<typename id_type> void set_id( unsigned long & element, id_type id ) {}
            template<typename id_type> void set_id( float & element, id_type id ) {}
            template<typename id_type> void set_id( double & element, id_type id ) {}

        }

    }

}

#endif
template<class _Signature >
class result_of;<|endoftext|>