<commit_before>/*
 * Screenshot_Service.cpp
 *
 * Copyright (C) 2020 by MegaMol Team
 * Alle Rechte vorbehalten.
 */

#include "Screenshot_Service.hpp"

 // to grab GL front buffer
#include <glad/glad.h>
#include "GUIState.h"
#include "mmcore/MegaMolGraph.h"

// to write png files
#include "png.h"
#include "mmcore/utility/graphics/ScreenShotComments.h"
#include "vislib/sys/FastFile.h"

#include "mmcore/utility/log/Log.h"

/// XXX TEMP
#include "imgui.h"
#include "imgui_internal.h"

static const std::string service_name = "Screenshot_Service: ";
static void log(std::string const& text) {
    const std::string msg = service_name + text;
    megamol::core::utility::log::Log::DefaultLog.WriteInfo(msg.c_str());
}

static void log_error(std::string const& text) {
    const std::string msg = service_name + text;
    megamol::core::utility::log::Log::DefaultLog.WriteError(msg.c_str());
}

static void log_warning(std::string const& text) {
    const std::string msg = service_name + text;
    megamol::core::utility::log::Log::DefaultLog.WriteWarn(msg.c_str());
}

// need this to pass GL context to screenshot source. this a hack and needs to be properly designed.
static megamol::core::MegaMolGraph* megamolgraph_ptr = nullptr;
static megamol::frontend_resources::GUIState* guistate_resources_ptr = nullptr;
static bool screenshot_show_privacy_note = true;

static void PNGAPI pngErrorFunc(png_structp pngPtr, png_const_charp msg) {
    log("PNG Error: " + std::string(msg));
}

static void PNGAPI pngWarnFunc(png_structp pngPtr, png_const_charp msg) {
    log("PNG Warning: " + std::string(msg));
}

static void PNGAPI pngWriteFileFunc(png_structp pngPtr, png_bytep buf, png_size_t size) {
    vislib::sys::File* f = static_cast<vislib::sys::File*>(png_get_io_ptr(pngPtr));
    f->Write(buf, size);
}

static void PNGAPI pngFlushFileFunc(png_structp pngPtr) {
    vislib::sys::File* f = static_cast<vislib::sys::File*>(png_get_io_ptr(pngPtr));
    f->Flush();
}

static bool write_png_to_file(megamol::frontend_resources::ImageData const& image, std::string const& filename) {
    vislib::sys::FastFile file;
    try {
        // open final image file
        if (!file.Open(filename.c_str(), vislib::sys::File::WRITE_ONLY, vislib::sys::File::SHARE_EXCLUSIVE, vislib::sys::File::CREATE_OVERWRITE))
        {
            log("Cannot open output file" + filename);
            return false;
        }
    } catch (...) {
        log("Error/Exception opening output file" + filename);
        return false;
    }

    png_structp pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, &pngErrorFunc, &pngWarnFunc);
    if (!pngPtr) {
        log("Cannot create png structure");
        return false;
    }

    png_infop pngInfoPtr = png_create_info_struct(pngPtr);
    if (!pngInfoPtr) {
        log("Cannot create png info");
        return false;
    }

    png_set_write_fn(pngPtr, static_cast<void*>(&file), &pngWriteFileFunc, &pngFlushFileFunc);

    png_set_compression_level(pngPtr, 0);

    // todo: camera settings are not stored without magic knowledge about the view
    std::string project = megamolgraph_ptr->Convenience().SerializeGraph();
    project.append(guistate_resources_ptr->request_gui_state(true));
    megamol::core::utility::graphics::ScreenShotComments ssc(project);
    png_set_text(pngPtr, pngInfoPtr, ssc.GetComments().data(), ssc.GetComments().size());

    png_set_IHDR(pngPtr, pngInfoPtr, image.width, image.height, 8,
        PNG_COLOR_TYPE_RGB_ALPHA /* PNG_COLOR_TYPE_RGB */, PNG_INTERLACE_NONE,
        PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

    png_set_rows(pngPtr, pngInfoPtr, const_cast<png_byte**>(reinterpret_cast<png_byte* const*>(image.flipped_rows.data())));

    png_write_png(pngPtr, pngInfoPtr, PNG_TRANSFORM_IDENTITY, NULL);

    png_destroy_write_struct(&pngPtr, &pngInfoPtr);

    if (screenshot_show_privacy_note) {
        // Push log message to GUI pop-up
        megamol::core::utility::log::Log::DefaultLog.WriteWarn("%sScreenshot%s--- PRIVACY NOTE ---\n"
            "Please note that the complete MegaMol project is stored in the header of the screenshot image file. \n"
            "Before giving away the screenshot, clear privacy relevant information in project file before taking screenshot (e.g. user name in file paths). \n"
            ">>> In [megamol_config.lua] set mmSetCliOption(\"privacynote\", \"off\") to permanently turn off privacy notification for screenshots. \n"
            , LOGMESSAGE_GUI_POPUP_START_TAG, LOGMESSAGE_GUI_POPUP_END_TAG);
    }
    return true;
}

void megamol::frontend_resources::GLScreenshotSource::set_read_buffer(ReadBuffer buffer) {
    m_read_buffer = buffer;
    GLenum read_buffer;

    switch (buffer) {
    default:
        [[fallthrough]];
    case ReadBuffer::FRONT:
            read_buffer = GL_FRONT;
        break;
    case ReadBuffer::BACK:
            read_buffer = GL_BACK;
        break;
    case ReadBuffer::COLOR_ATT0:
            read_buffer = GL_COLOR_ATTACHMENT0;
        break;
    case ReadBuffer::COLOR_ATT1:
            read_buffer = GL_COLOR_ATTACHMENT0+1;
        break;
    case ReadBuffer::COLOR_ATT2:
            read_buffer = GL_COLOR_ATTACHMENT0+2;
        break;
    case ReadBuffer::COLOR_ATT3:
            read_buffer = GL_COLOR_ATTACHMENT0+3;
        break;
    }
}

megamol::frontend_resources::ImageData megamol::frontend_resources::GLScreenshotSource::take_screenshot() const {
    // TODO: in FBO-based rendering the FBO object carries its size and we dont need to look it up
    // simpler and more correct approach would be to observe Framebuffer_Events resource
    // but this is our naive implementation for now
    GLint viewport_dims[4] = {0};
    glGetIntegerv(GL_VIEWPORT, viewport_dims);
    GLint fbWidth = viewport_dims[2];
    GLint fbHeight = viewport_dims[3];

    ImageData result;
    result.resize(static_cast<size_t>(fbWidth), static_cast<size_t>(fbHeight));

    glReadBuffer(m_read_buffer);
    glReadPixels(0, 0, fbWidth, fbHeight, GL_RGBA, GL_UNSIGNED_BYTE, result.image.data());

    for (auto& pixel : result.image)
        pixel.a = 255;

    return std::move(result);
}

bool megamol::frontend_resources::ImageDataToPNGWriter::write_image(ImageData image, std::string const& filename) const {
    return write_png_to_file(std::move(image), filename);
}

namespace megamol {
namespace frontend {

Screenshot_Service::Screenshot_Service() {}

Screenshot_Service::~Screenshot_Service() {}

bool Screenshot_Service::init(void* configPtr) {
    if (configPtr == nullptr) return false;

    return init(*static_cast<Config*>(configPtr));
}

bool Screenshot_Service::init(const Config& config) {

    m_requestedResourcesNames =
    {
        "IOpenGL_Context",
        "MegaMolGraph",
        "GUIResource",
        "RuntimeConfig"
    };

    this->m_frontbufferToPNG_trigger = [&](std::string const& filename) -> bool
    {
        log("write screenshot to " + filename);
        return m_toFileWriter_resource.write_screenshot(m_frontbufferSource_resource, filename);
    };

    /// XXX TEMP
    this->m_guiRenderRequest_resource.window_name = "Screenshot Message";
    this->m_guiRenderRequest_resource.callback = [&](gui::WindowCollection::WindowConfiguration& wc) -> void {
        this->guiRenderCallback(wc);
    };

    screenshot_show_privacy_note = config.show_privacy_note;

    log("initialized successfully");
    return true;
}

void Screenshot_Service::close() {
    // close libraries or APIs you manage
    // wrap up resources your service provides, but don not depend on outside resources to be available here
    // after this, at some point only the destructor of your service gets called
}

std::vector<FrontendResource>& Screenshot_Service::getProvidedResources() {
     this->m_providedResourceReferences =
    {
        {"GLScreenshotSource", m_frontbufferSource_resource},
        {"ImageDataToPNGWriter", m_toFileWriter_resource},
        {"GLFrontbufferToPNG_ScreenshotTrigger", m_frontbufferToPNG_trigger},
        /// XXX TEMP
        {"ScreenshotGUIRenderRequest", m_guiRenderRequest_resource}
    };

    return m_providedResourceReferences;
}

const std::vector<std::string> Screenshot_Service::getRequestedResourceNames() const {
    return m_requestedResourcesNames;
}

void Screenshot_Service::setRequestedResources(std::vector<FrontendResource> resources) {
    megamolgraph_ptr = const_cast<megamol::core::MegaMolGraph*>(&resources[1].getResource<megamol::core::MegaMolGraph>());
    guistate_resources_ptr = const_cast<megamol::frontend_resources::GUIState*>(&resources[2].getResource<megamol::frontend_resources::GUIState>());
}

void Screenshot_Service::updateProvidedResources() {
}

void Screenshot_Service::digestChangedRequestedResources() {
    bool need_to_shutdown = false;
    if (need_to_shutdown)
        this->setShutdown();
}

void Screenshot_Service::resetProvidedResources() {
    // this gets called at the end of the main loop iteration
    // since the current resources state should have been handled in this frame already
    // you may clean up resources whose state is not needed for the next iteration
    // e.g. m_keyboardEvents.clear();
    // network_traffic_buffer.reset_to_empty();
}

void Screenshot_Service::preGraphRender() {
    // this gets called right before the graph is told to render something
    // e.g. you can start a start frame timer here

    // rendering via MegaMol View is called after this function finishes
    // in the end this calls the equivalent of ::mmcRenderView(hView, &renderContext)
    // which leads to view.Render()
}

void Screenshot_Service::postGraphRender() {
    // the graph finished rendering and you may more stuff here
    // e.g. end frame timer
    // update window name
    // swap buffers, glClear
}

/// XXX TEMP
bool Screenshot_Service::guiRenderCallback(WinConfig_t& wc) {
    bool valid_imgui_scope =
            ((ImGui::GetCurrentContext() != nullptr) ? (ImGui::GetCurrentContext()->WithinFrameScope) : (false));
    if (valid_imgui_scope) {
        /// TODO

        wc.win_size = ImVec2(300.0f, 300.0f);
        wc.buf_set_pos_size = true; // Call only once ...
        ImGui::TextUnformatted("Hallo Welt ...")

    }
}

} // namespace frontend
} // namespace megamol
<commit_msg>compile fix ...<commit_after>/*
 * Screenshot_Service.cpp
 *
 * Copyright (C) 2020 by MegaMol Team
 * Alle Rechte vorbehalten.
 */

#include "Screenshot_Service.hpp"

 // to grab GL front buffer
#include <glad/glad.h>
#include "GUIState.h"
#include "mmcore/MegaMolGraph.h"

// to write png files
#include "png.h"
#include "mmcore/utility/graphics/ScreenShotComments.h"
#include "vislib/sys/FastFile.h"

#include "mmcore/utility/log/Log.h"

/// XXX TEMP
#include "imgui.h"
#include "imgui_internal.h"

static const std::string service_name = "Screenshot_Service: ";
static void log(std::string const& text) {
    const std::string msg = service_name + text;
    megamol::core::utility::log::Log::DefaultLog.WriteInfo(msg.c_str());
}

static void log_error(std::string const& text) {
    const std::string msg = service_name + text;
    megamol::core::utility::log::Log::DefaultLog.WriteError(msg.c_str());
}

static void log_warning(std::string const& text) {
    const std::string msg = service_name + text;
    megamol::core::utility::log::Log::DefaultLog.WriteWarn(msg.c_str());
}

// need this to pass GL context to screenshot source. this a hack and needs to be properly designed.
static megamol::core::MegaMolGraph* megamolgraph_ptr = nullptr;
static megamol::frontend_resources::GUIState* guistate_resources_ptr = nullptr;
static bool screenshot_show_privacy_note = true;

static void PNGAPI pngErrorFunc(png_structp pngPtr, png_const_charp msg) {
    log("PNG Error: " + std::string(msg));
}

static void PNGAPI pngWarnFunc(png_structp pngPtr, png_const_charp msg) {
    log("PNG Warning: " + std::string(msg));
}

static void PNGAPI pngWriteFileFunc(png_structp pngPtr, png_bytep buf, png_size_t size) {
    vislib::sys::File* f = static_cast<vislib::sys::File*>(png_get_io_ptr(pngPtr));
    f->Write(buf, size);
}

static void PNGAPI pngFlushFileFunc(png_structp pngPtr) {
    vislib::sys::File* f = static_cast<vislib::sys::File*>(png_get_io_ptr(pngPtr));
    f->Flush();
}

static bool write_png_to_file(megamol::frontend_resources::ImageData const& image, std::string const& filename) {
    vislib::sys::FastFile file;
    try {
        // open final image file
        if (!file.Open(filename.c_str(), vislib::sys::File::WRITE_ONLY, vislib::sys::File::SHARE_EXCLUSIVE, vislib::sys::File::CREATE_OVERWRITE))
        {
            log("Cannot open output file" + filename);
            return false;
        }
    } catch (...) {
        log("Error/Exception opening output file" + filename);
        return false;
    }

    png_structp pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, &pngErrorFunc, &pngWarnFunc);
    if (!pngPtr) {
        log("Cannot create png structure");
        return false;
    }

    png_infop pngInfoPtr = png_create_info_struct(pngPtr);
    if (!pngInfoPtr) {
        log("Cannot create png info");
        return false;
    }

    png_set_write_fn(pngPtr, static_cast<void*>(&file), &pngWriteFileFunc, &pngFlushFileFunc);

    png_set_compression_level(pngPtr, 0);

    // todo: camera settings are not stored without magic knowledge about the view
    std::string project = megamolgraph_ptr->Convenience().SerializeGraph();
    project.append(guistate_resources_ptr->request_gui_state(true));
    megamol::core::utility::graphics::ScreenShotComments ssc(project);
    png_set_text(pngPtr, pngInfoPtr, ssc.GetComments().data(), ssc.GetComments().size());

    png_set_IHDR(pngPtr, pngInfoPtr, image.width, image.height, 8,
        PNG_COLOR_TYPE_RGB_ALPHA /* PNG_COLOR_TYPE_RGB */, PNG_INTERLACE_NONE,
        PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

    png_set_rows(pngPtr, pngInfoPtr, const_cast<png_byte**>(reinterpret_cast<png_byte* const*>(image.flipped_rows.data())));

    png_write_png(pngPtr, pngInfoPtr, PNG_TRANSFORM_IDENTITY, NULL);

    png_destroy_write_struct(&pngPtr, &pngInfoPtr);

    if (screenshot_show_privacy_note) {
        // Push log message to GUI pop-up
        megamol::core::utility::log::Log::DefaultLog.WriteWarn("%sScreenshot%s--- PRIVACY NOTE ---\n"
            "Please note that the complete MegaMol project is stored in the header of the screenshot image file. \n"
            "Before giving away the screenshot, clear privacy relevant information in project file before taking screenshot (e.g. user name in file paths). \n"
            ">>> In [megamol_config.lua] set mmSetCliOption(\"privacynote\", \"off\") to permanently turn off privacy notification for screenshots. \n"
            , LOGMESSAGE_GUI_POPUP_START_TAG, LOGMESSAGE_GUI_POPUP_END_TAG);
    }
    return true;
}

void megamol::frontend_resources::GLScreenshotSource::set_read_buffer(ReadBuffer buffer) {
    m_read_buffer = buffer;
    GLenum read_buffer;

    switch (buffer) {
    default:
        [[fallthrough]];
    case ReadBuffer::FRONT:
            read_buffer = GL_FRONT;
        break;
    case ReadBuffer::BACK:
            read_buffer = GL_BACK;
        break;
    case ReadBuffer::COLOR_ATT0:
            read_buffer = GL_COLOR_ATTACHMENT0;
        break;
    case ReadBuffer::COLOR_ATT1:
            read_buffer = GL_COLOR_ATTACHMENT0+1;
        break;
    case ReadBuffer::COLOR_ATT2:
            read_buffer = GL_COLOR_ATTACHMENT0+2;
        break;
    case ReadBuffer::COLOR_ATT3:
            read_buffer = GL_COLOR_ATTACHMENT0+3;
        break;
    }
}

megamol::frontend_resources::ImageData megamol::frontend_resources::GLScreenshotSource::take_screenshot() const {
    // TODO: in FBO-based rendering the FBO object carries its size and we dont need to look it up
    // simpler and more correct approach would be to observe Framebuffer_Events resource
    // but this is our naive implementation for now
    GLint viewport_dims[4] = {0};
    glGetIntegerv(GL_VIEWPORT, viewport_dims);
    GLint fbWidth = viewport_dims[2];
    GLint fbHeight = viewport_dims[3];

    ImageData result;
    result.resize(static_cast<size_t>(fbWidth), static_cast<size_t>(fbHeight));

    glReadBuffer(m_read_buffer);
    glReadPixels(0, 0, fbWidth, fbHeight, GL_RGBA, GL_UNSIGNED_BYTE, result.image.data());

    for (auto& pixel : result.image)
        pixel.a = 255;

    return std::move(result);
}

bool megamol::frontend_resources::ImageDataToPNGWriter::write_image(ImageData image, std::string const& filename) const {
    return write_png_to_file(std::move(image), filename);
}

namespace megamol {
namespace frontend {

Screenshot_Service::Screenshot_Service() {}

Screenshot_Service::~Screenshot_Service() {}

bool Screenshot_Service::init(void* configPtr) {
    if (configPtr == nullptr) return false;

    return init(*static_cast<Config*>(configPtr));
}

bool Screenshot_Service::init(const Config& config) {

    m_requestedResourcesNames =
    {
        "IOpenGL_Context",
        "MegaMolGraph",
        "GUIResource",
        "RuntimeConfig"
    };

    this->m_frontbufferToPNG_trigger = [&](std::string const& filename) -> bool
    {
        log("write screenshot to " + filename);
        return m_toFileWriter_resource.write_screenshot(m_frontbufferSource_resource, filename);
    };

    /// XXX TEMP
    this->m_guiRenderRequest_resource.window_name = "Screenshot Message";
    this->m_guiRenderRequest_resource.callback = [&](gui::WindowCollection::WindowConfiguration& wc) -> void {
        this->guiRenderCallback(wc);
    };

    screenshot_show_privacy_note = config.show_privacy_note;

    log("initialized successfully");
    return true;
}

void Screenshot_Service::close() {
    // close libraries or APIs you manage
    // wrap up resources your service provides, but don not depend on outside resources to be available here
    // after this, at some point only the destructor of your service gets called
}

std::vector<FrontendResource>& Screenshot_Service::getProvidedResources() {
     this->m_providedResourceReferences =
    {
        {"GLScreenshotSource", m_frontbufferSource_resource},
        {"ImageDataToPNGWriter", m_toFileWriter_resource},
        {"GLFrontbufferToPNG_ScreenshotTrigger", m_frontbufferToPNG_trigger},
        /// XXX TEMP
        {"ScreenshotGUIRenderRequest", m_guiRenderRequest_resource}
    };

    return m_providedResourceReferences;
}

const std::vector<std::string> Screenshot_Service::getRequestedResourceNames() const {
    return m_requestedResourcesNames;
}

void Screenshot_Service::setRequestedResources(std::vector<FrontendResource> resources) {
    megamolgraph_ptr = const_cast<megamol::core::MegaMolGraph*>(&resources[1].getResource<megamol::core::MegaMolGraph>());
    guistate_resources_ptr = const_cast<megamol::frontend_resources::GUIState*>(&resources[2].getResource<megamol::frontend_resources::GUIState>());
}

void Screenshot_Service::updateProvidedResources() {
}

void Screenshot_Service::digestChangedRequestedResources() {
    bool need_to_shutdown = false;
    if (need_to_shutdown)
        this->setShutdown();
}

void Screenshot_Service::resetProvidedResources() {
    // this gets called at the end of the main loop iteration
    // since the current resources state should have been handled in this frame already
    // you may clean up resources whose state is not needed for the next iteration
    // e.g. m_keyboardEvents.clear();
    // network_traffic_buffer.reset_to_empty();
}

void Screenshot_Service::preGraphRender() {
    // this gets called right before the graph is told to render something
    // e.g. you can start a start frame timer here

    // rendering via MegaMol View is called after this function finishes
    // in the end this calls the equivalent of ::mmcRenderView(hView, &renderContext)
    // which leads to view.Render()
}

void Screenshot_Service::postGraphRender() {
    // the graph finished rendering and you may more stuff here
    // e.g. end frame timer
    // update window name
    // swap buffers, glClear
}

/// XXX TEMP
bool Screenshot_Service::guiRenderCallback(WinConfig_t& wc) {
    bool valid_imgui_scope =
            ((ImGui::GetCurrentContext() != nullptr) ? (ImGui::GetCurrentContext()->WithinFrameScope) : (false));
    if (valid_imgui_scope) {
        /// TODO

        wc.win_size = ImVec2(300.0f, 300.0f);
        //wc.buf_set_pos_size = true; // Call only once ...
        ImGui::TextUnformatted("Hallo Welt ...");

    }
}

} // namespace frontend
} // namespace megamol
<|endoftext|>


<commit_before>/*
 * Screenshot_Service.cpp
 *
 * Copyright (C) 2020 by MegaMol Team
 * Alle Rechte vorbehalten.
 */

#include "Screenshot_Service.hpp"

#include "OpenGL_Context.h"
#include "GUIState.h"

#include "ImageWrapper.h"
#include "ImageWrapper_to_ByteArray.hpp"

#include "mmcore/MegaMolGraph.h"

// to write png files
#include "png.h"
#include "zlib.h"
#include "mmcore/utility/graphics/ScreenShotComments.h"
#include "vislib/sys/FastFile.h"

#include "mmcore/utility/log/Log.h"

#include "GUIRegisterWindow.h"

static std::shared_ptr<bool> service_open_popup = std::make_shared<bool>(false);
static const std::string service_name = "Screenshot_Service: ";
static const std::string privacy_note("--- PRIVACY NOTE ---\n"
    "Please note that the complete MegaMol project is stored in the header of the screenshot image file. \n"
    "Before giving away the screenshot, clear privacy relevant information in the project file before taking a screenshot (e.g. user name in file paths). \n"
    ">>> In the file [megamol_config.lua] set mmSetCliOption(\"privacynote\", \"off\") to permanently turn off privacy notifications for screenshots.");

static void log(std::string const& text) {
    const std::string msg = service_name + text;
    megamol::core::utility::log::Log::DefaultLog.WriteInfo(msg.c_str());
}

static void log_error(std::string const& text) {
    const std::string msg = service_name + text;
    megamol::core::utility::log::Log::DefaultLog.WriteError(msg.c_str());
}

static void log_warning(std::string const& text) {
    const std::string msg = service_name + text;
    megamol::core::utility::log::Log::DefaultLog.WriteWarn(msg.c_str());
}

// need this to pass GL context to screenshot source. this a hack and needs to be properly designed.
static megamol::core::MegaMolGraph* megamolgraph_ptr = nullptr;
static megamol::frontend_resources::GUIState* guistate_resources_ptr = nullptr;
static bool screenshot_show_privacy_note = true;

unsigned char megamol::frontend::Screenshot_Service::default_alpha_value = 0;

static void PNGAPI pngErrorFunc(png_structp pngPtr, png_const_charp msg) {
    log("PNG Error: " + std::string(msg));
}

static void PNGAPI pngWarnFunc(png_structp pngPtr, png_const_charp msg) {
    log("PNG Warning: " + std::string(msg));
}

static void PNGAPI pngWriteFileFunc(png_structp pngPtr, png_bytep buf, png_size_t size) {
    vislib::sys::File* f = static_cast<vislib::sys::File*>(png_get_io_ptr(pngPtr));
    f->Write(buf, size);
}

static void PNGAPI pngFlushFileFunc(png_structp pngPtr) {
    vislib::sys::File* f = static_cast<vislib::sys::File*>(png_get_io_ptr(pngPtr));
    f->Flush();
}

static bool write_png_to_file(megamol::frontend_resources::ScreenshotImageData const& image, std::filesystem::path const& filename) {
    vislib::sys::FastFile file;
    try {
        // open final image file
        if (!file.Open(filename.native().c_str(), vislib::sys::File::WRITE_ONLY, vislib::sys::File::SHARE_EXCLUSIVE, vislib::sys::File::CREATE_OVERWRITE))
        {
            log("Cannot open output file" + filename.generic_u8string());
            return false;
        }
    } catch (...) {
        log("Error/Exception opening output file" + filename.generic_u8string());
        return false;
    }

    png_structp pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, &pngErrorFunc, &pngWarnFunc);
    if (!pngPtr) {
        log("Cannot create png structure");
        return false;
    }

    png_infop pngInfoPtr = png_create_info_struct(pngPtr);
    if (!pngInfoPtr) {
        log("Cannot create png info");
        return false;
    }

    png_set_write_fn(pngPtr, static_cast<void*>(&file), &pngWriteFileFunc, &pngFlushFileFunc);

    png_set_compression_level(pngPtr, Z_BEST_SPEED);

    // todo: camera settings are not stored without magic knowledge about the view
    std::string project = megamolgraph_ptr->Convenience().SerializeGraph();
    project.append(guistate_resources_ptr->request_gui_state(true));
    megamol::core::utility::graphics::ScreenShotComments ssc(project);
    png_set_text(pngPtr, pngInfoPtr, ssc.GetComments().data(), ssc.GetComments().size());

    png_set_IHDR(pngPtr, pngInfoPtr, image.width, image.height, 8,
        PNG_COLOR_TYPE_RGB_ALPHA /* PNG_COLOR_TYPE_RGB */, PNG_INTERLACE_NONE,
        PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

    png_set_rows(pngPtr, pngInfoPtr, const_cast<png_byte**>(reinterpret_cast<png_byte* const*>(image.flipped_rows.data())));

    png_write_png(pngPtr, pngInfoPtr, PNG_TRANSFORM_IDENTITY, NULL);

    png_destroy_write_struct(&pngPtr, &pngInfoPtr);

    if (screenshot_show_privacy_note) {
        megamol::core::utility::log::Log::DefaultLog.WriteWarn("Screenshot: %s", privacy_note.c_str());
        if (service_open_popup != nullptr)
            *service_open_popup = true;
    }
    return true;
}

megamol::frontend_resources::ImageWrapperScreenshotSource::ImageWrapperScreenshotSource(ImageWrapper const& image)
    : m_image{& const_cast<ImageWrapper&>(image)}
{}

megamol::frontend_resources::ScreenshotImageData const& megamol::frontend_resources::ImageWrapperScreenshotSource::take_screenshot() const {
    static ScreenshotImageData screenshot_image;

    // keep allocated vector memory around
    // note that this initially holds a nullptr texture - bad!
    static frontend_resources::byte_texture image_bytes({});

    // fill bytes with image data
    image_bytes = *m_image;
    auto& byte_vector = image_bytes.as_byte_vector();

    screenshot_image.resize(m_image->size.width, m_image->size.height);

    for (size_t i = 0, j = 0; i < byte_vector.size();) {
        auto r = [&]() { return byte_vector[i++]; };
        auto g = [&]() { return byte_vector[i++]; };
        auto b = [&]() { return byte_vector[i++]; };
        auto a = [&]() { return (m_image->channels == ImageWrapper::DataChannels::RGBA8) ? byte_vector[i++] : megamol::frontend::Screenshot_Service::default_alpha_value; }; // alpha either from image or 1.0
        ScreenshotImageData::Pixel pixel = { r(), g(), b(), a() };
        screenshot_image.image[j++] = pixel;
    }

    return screenshot_image;
}

bool megamol::frontend_resources::ScreenshotImageDataToPNGWriter::write_image(ScreenshotImageData const& image, std::filesystem::path const& filename) const {
    return write_png_to_file(image, filename);
}

namespace megamol {
namespace frontend {

Screenshot_Service::Screenshot_Service() {}

Screenshot_Service::~Screenshot_Service() {
    service_open_popup.reset();
}

bool Screenshot_Service::init(void* configPtr) {
    if (configPtr == nullptr) return false;

    return init(*static_cast<Config*>(configPtr));
}

bool Screenshot_Service::init(const Config& config) {

    m_requestedResourcesNames =
    {
        "optional<OpenGL_Context>", // TODO: for GLScreenshoSource. how to kill?
        "MegaMolGraph",
        "GUIState",
        "RuntimeConfig",
        "optional<GUIRegisterWindow>"
    };

    this->m_frontbufferToPNG_trigger = [&](std::filesystem::path const& filename) -> bool
    {
        log("write screenshot to " + filename.generic_u8string());
        return m_toFileWriter_resource.write_screenshot(m_frontbufferSource_resource, filename);
    };

    screenshot_show_privacy_note = config.show_privacy_note;

    this->m_imagewrapperToPNG_trigger = [&](megamol::frontend_resources::ImageWrapper const& image, std::filesystem::path const& filename) -> bool
    {
        log("write screenshot to " + filename.generic_u8string());
        return m_toFileWriter_resource.write_screenshot(megamol::frontend_resources::ImageWrapperScreenshotSource(image), filename);
    };

    log("initialized successfully");
    return true;
}

void Screenshot_Service::close() {
}

std::vector<FrontendResource>& Screenshot_Service::getProvidedResources() {
     this->m_providedResourceReferences =
    {
        {"GLScreenshotSource", m_frontbufferSource_resource},
        {"ImageDataToPNGWriter", m_toFileWriter_resource},
        {"GLFrontbufferToPNG_ScreenshotTrigger", m_frontbufferToPNG_trigger},
        {"ImageWrapperToPNG_ScreenshotTrigger", m_imagewrapperToPNG_trigger}
    };

    return m_providedResourceReferences;
}

const std::vector<std::string> Screenshot_Service::getRequestedResourceNames() const {
    return m_requestedResourcesNames;
}

void Screenshot_Service::setRequestedResources(std::vector<FrontendResource> resources) {
    megamolgraph_ptr = const_cast<megamol::core::MegaMolGraph*>(&resources[1].getResource<megamol::core::MegaMolGraph>());
    guistate_resources_ptr = const_cast<megamol::frontend_resources::GUIState*>(&resources[2].getResource<megamol::frontend_resources::GUIState>());

    auto maybe_gui_window_request_resource = resources[4].getOptionalResource<megamol::frontend_resources::GUIRegisterWindow>();
    if (maybe_gui_window_request_resource.has_value()) {
        auto& gui_window_request_resource = maybe_gui_window_request_resource.value().get();
        gui_window_request_resource.register_notification("Screenshot", std::weak_ptr<bool>(service_open_popup), privacy_note);
    }
}

void Screenshot_Service::updateProvidedResources() {
}

void Screenshot_Service::digestChangedRequestedResources() {
    bool need_to_shutdown = false;
    if (need_to_shutdown)
        this->setShutdown();
}

void Screenshot_Service::resetProvidedResources() {
}

void Screenshot_Service::preGraphRender() {
}

void Screenshot_Service::postGraphRender() {
}

} // namespace frontend
} // namespace megamol
<commit_msg>Screenshot Service: when no GUI handle optional<GUIState><commit_after>/*
 * Screenshot_Service.cpp
 *
 * Copyright (C) 2020 by MegaMol Team
 * Alle Rechte vorbehalten.
 */

#include "Screenshot_Service.hpp"

#include "OpenGL_Context.h"
#include "GUIState.h"

#include "ImageWrapper.h"
#include "ImageWrapper_to_ByteArray.hpp"

#include "mmcore/MegaMolGraph.h"

// to write png files
#include "png.h"
#include "zlib.h"
#include "mmcore/utility/graphics/ScreenShotComments.h"
#include "vislib/sys/FastFile.h"

#include "mmcore/utility/log/Log.h"

#include "GUIRegisterWindow.h"

static std::shared_ptr<bool> service_open_popup = std::make_shared<bool>(false);
static const std::string service_name = "Screenshot_Service: ";
static const std::string privacy_note("--- PRIVACY NOTE ---\n"
    "Please note that the complete MegaMol project is stored in the header of the screenshot image file. \n"
    "Before giving away the screenshot, clear privacy relevant information in the project file before taking a screenshot (e.g. user name in file paths). \n"
    ">>> In the file [megamol_config.lua] set mmSetCliOption(\"privacynote\", \"off\") to permanently turn off privacy notifications for screenshots.");

static void log(std::string const& text) {
    const std::string msg = service_name + text;
    megamol::core::utility::log::Log::DefaultLog.WriteInfo(msg.c_str());
}

static void log_error(std::string const& text) {
    const std::string msg = service_name + text;
    megamol::core::utility::log::Log::DefaultLog.WriteError(msg.c_str());
}

static void log_warning(std::string const& text) {
    const std::string msg = service_name + text;
    megamol::core::utility::log::Log::DefaultLog.WriteWarn(msg.c_str());
}

// need this to pass GL context to screenshot source. this a hack and needs to be properly designed.
static megamol::core::MegaMolGraph* megamolgraph_ptr = nullptr;
static megamol::frontend_resources::GUIState* guistate_resources_ptr = nullptr;
static bool screenshot_show_privacy_note = true;

unsigned char megamol::frontend::Screenshot_Service::default_alpha_value = 0;

static void PNGAPI pngErrorFunc(png_structp pngPtr, png_const_charp msg) {
    log("PNG Error: " + std::string(msg));
}

static void PNGAPI pngWarnFunc(png_structp pngPtr, png_const_charp msg) {
    log("PNG Warning: " + std::string(msg));
}

static void PNGAPI pngWriteFileFunc(png_structp pngPtr, png_bytep buf, png_size_t size) {
    vislib::sys::File* f = static_cast<vislib::sys::File*>(png_get_io_ptr(pngPtr));
    f->Write(buf, size);
}

static void PNGAPI pngFlushFileFunc(png_structp pngPtr) {
    vislib::sys::File* f = static_cast<vislib::sys::File*>(png_get_io_ptr(pngPtr));
    f->Flush();
}

static bool write_png_to_file(megamol::frontend_resources::ScreenshotImageData const& image, std::filesystem::path const& filename) {
    vislib::sys::FastFile file;
    try {
        // open final image file
        if (!file.Open(filename.native().c_str(), vislib::sys::File::WRITE_ONLY, vislib::sys::File::SHARE_EXCLUSIVE, vislib::sys::File::CREATE_OVERWRITE))
        {
            log("Cannot open output file" + filename.generic_u8string());
            return false;
        }
    } catch (...) {
        log("Error/Exception opening output file" + filename.generic_u8string());
        return false;
    }

    png_structp pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, &pngErrorFunc, &pngWarnFunc);
    if (!pngPtr) {
        log("Cannot create png structure");
        return false;
    }

    png_infop pngInfoPtr = png_create_info_struct(pngPtr);
    if (!pngInfoPtr) {
        log("Cannot create png info");
        return false;
    }

    png_set_write_fn(pngPtr, static_cast<void*>(&file), &pngWriteFileFunc, &pngFlushFileFunc);

    png_set_compression_level(pngPtr, Z_BEST_SPEED);

    // todo: camera settings are not stored without magic knowledge about the view
    std::string project = megamolgraph_ptr->Convenience().SerializeGraph();
    if (guistate_resources_ptr) {
        project.append(guistate_resources_ptr->request_gui_state(true));
    }
    megamol::core::utility::graphics::ScreenShotComments ssc(project);
    png_set_text(pngPtr, pngInfoPtr, ssc.GetComments().data(), ssc.GetComments().size());

    png_set_IHDR(pngPtr, pngInfoPtr, image.width, image.height, 8,
        PNG_COLOR_TYPE_RGB_ALPHA /* PNG_COLOR_TYPE_RGB */, PNG_INTERLACE_NONE,
        PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

    png_set_rows(pngPtr, pngInfoPtr, const_cast<png_byte**>(reinterpret_cast<png_byte* const*>(image.flipped_rows.data())));

    png_write_png(pngPtr, pngInfoPtr, PNG_TRANSFORM_IDENTITY, NULL);

    png_destroy_write_struct(&pngPtr, &pngInfoPtr);

    if (screenshot_show_privacy_note) {
        megamol::core::utility::log::Log::DefaultLog.WriteWarn("Screenshot: %s", privacy_note.c_str());
        if (service_open_popup != nullptr)
            *service_open_popup = true;
    }
    return true;
}

megamol::frontend_resources::ImageWrapperScreenshotSource::ImageWrapperScreenshotSource(ImageWrapper const& image)
    : m_image{& const_cast<ImageWrapper&>(image)}
{}

megamol::frontend_resources::ScreenshotImageData const& megamol::frontend_resources::ImageWrapperScreenshotSource::take_screenshot() const {
    static ScreenshotImageData screenshot_image;

    // keep allocated vector memory around
    // note that this initially holds a nullptr texture - bad!
    static frontend_resources::byte_texture image_bytes({});

    // fill bytes with image data
    image_bytes = *m_image;
    auto& byte_vector = image_bytes.as_byte_vector();

    screenshot_image.resize(m_image->size.width, m_image->size.height);

    for (size_t i = 0, j = 0; i < byte_vector.size();) {
        auto r = [&]() { return byte_vector[i++]; };
        auto g = [&]() { return byte_vector[i++]; };
        auto b = [&]() { return byte_vector[i++]; };
        auto a = [&]() { return (m_image->channels == ImageWrapper::DataChannels::RGBA8) ? byte_vector[i++] : megamol::frontend::Screenshot_Service::default_alpha_value; }; // alpha either from image or 1.0
        ScreenshotImageData::Pixel pixel = { r(), g(), b(), a() };
        screenshot_image.image[j++] = pixel;
    }

    return screenshot_image;
}

bool megamol::frontend_resources::ScreenshotImageDataToPNGWriter::write_image(ScreenshotImageData const& image, std::filesystem::path const& filename) const {
    return write_png_to_file(image, filename);
}

namespace megamol {
namespace frontend {

Screenshot_Service::Screenshot_Service() {}

Screenshot_Service::~Screenshot_Service() {
    service_open_popup.reset();
}

bool Screenshot_Service::init(void* configPtr) {
    if (configPtr == nullptr) return false;

    return init(*static_cast<Config*>(configPtr));
}

bool Screenshot_Service::init(const Config& config) {

    m_requestedResourcesNames =
    {
        "optional<OpenGL_Context>", // TODO: for GLScreenshoSource. how to kill?
        "MegaMolGraph",
        "optional<GUIState>",
        "RuntimeConfig",
        "optional<GUIRegisterWindow>"
    };

    this->m_frontbufferToPNG_trigger = [&](std::filesystem::path const& filename) -> bool
    {
        log("write screenshot to " + filename.generic_u8string());
        return m_toFileWriter_resource.write_screenshot(m_frontbufferSource_resource, filename);
    };

    screenshot_show_privacy_note = config.show_privacy_note;

    this->m_imagewrapperToPNG_trigger = [&](megamol::frontend_resources::ImageWrapper const& image, std::filesystem::path const& filename) -> bool
    {
        log("write screenshot to " + filename.generic_u8string());
        return m_toFileWriter_resource.write_screenshot(megamol::frontend_resources::ImageWrapperScreenshotSource(image), filename);
    };

    log("initialized successfully");
    return true;
}

void Screenshot_Service::close() {
}

std::vector<FrontendResource>& Screenshot_Service::getProvidedResources() {
     this->m_providedResourceReferences =
    {
        {"GLScreenshotSource", m_frontbufferSource_resource},
        {"ImageDataToPNGWriter", m_toFileWriter_resource},
        {"GLFrontbufferToPNG_ScreenshotTrigger", m_frontbufferToPNG_trigger},
        {"ImageWrapperToPNG_ScreenshotTrigger", m_imagewrapperToPNG_trigger}
    };

    return m_providedResourceReferences;
}

const std::vector<std::string> Screenshot_Service::getRequestedResourceNames() const {
    return m_requestedResourcesNames;
}

void Screenshot_Service::setRequestedResources(std::vector<FrontendResource> resources) {
    megamolgraph_ptr = const_cast<megamol::core::MegaMolGraph*>(&resources[1].getResource<megamol::core::MegaMolGraph>());

    auto maybe_gui_state = resources[2].getOptionalResource<megamol::frontend_resources::GUIState>();
    if (maybe_gui_state.has_value()) {
        guistate_resources_ptr = const_cast<megamol::frontend_resources::GUIState*>(& maybe_gui_state.value().get());
    }

    auto maybe_gui_window_request_resource = resources[4].getOptionalResource<megamol::frontend_resources::GUIRegisterWindow>();
    if (maybe_gui_window_request_resource.has_value()) {
        auto& gui_window_request_resource = maybe_gui_window_request_resource.value().get();
        gui_window_request_resource.register_notification("Screenshot", std::weak_ptr<bool>(service_open_popup), privacy_note);
    }
}

void Screenshot_Service::updateProvidedResources() {
}

void Screenshot_Service::digestChangedRequestedResources() {
    bool need_to_shutdown = false;
    if (need_to_shutdown)
        this->setShutdown();
}

void Screenshot_Service::resetProvidedResources() {
}

void Screenshot_Service::preGraphRender() {
}

void Screenshot_Service::postGraphRender() {
}

} // namespace frontend
} // namespace megamol
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/

#include "refactoringchanges.h"
#include "basetexteditor.h"

#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
#include <extensionsystem/pluginmanager.h>

#include <utils/qtcassert.h>

#include <QtCore/QFile>
#include <QtCore/QSet>
#include <QtGui/QMainWindow>
#include <QtGui/QTextBlock>
#include <QtGui/QTextCursor>
#include <QtGui/QTextDocument>
#include <QtCore/QDebug>

using namespace TextEditor;

RefactoringChanges::RefactoringChanges()
    : m_data(new RefactoringChangesData)
{}

RefactoringChanges::RefactoringChanges(RefactoringChangesData *data)
    : m_data(data)
{}

RefactoringChanges::~RefactoringChanges()
{}

BaseTextEditorWidget *RefactoringChanges::editorForFile(const QString &fileName)
{
    Core::EditorManager *editorManager = Core::EditorManager::instance();

    const QList<Core::IEditor *> editors = editorManager->editorsForFileName(fileName);
    foreach (Core::IEditor *editor, editors) {
        BaseTextEditorWidget *textEditor = qobject_cast<BaseTextEditorWidget *>(editor->widget());
        if (textEditor != 0)
            return textEditor;
    }
    return 0;
}

QList<QPair<QTextCursor, QTextCursor > > RefactoringChanges::rangesToSelections(QTextDocument *document,
                                                                                const QList<Range> &ranges)
{
    QList<QPair<QTextCursor, QTextCursor> > selections;

    foreach (const Range &range, ranges) {
        QTextCursor start(document);
        start.setPosition(range.start);
        start.setKeepPositionOnInsert(true);
        QTextCursor end(document);
        end.setPosition(qMin(range.end, document->characterCount() - 1));

        selections.append(qMakePair(start, end));
    }

    return selections;
}

bool RefactoringChanges::createFile(const QString &fileName, const QString &contents, bool reindent, bool openEditor) const
{
    if (QFile::exists(fileName))
        return false;

    BaseTextEditorWidget *editor = editorForFile(fileName);
    if (!editor && openEditor) {
        editor = this->openEditor(fileName, false, -1, -1);
    }

    QTextDocument *document;
    if (editor)
        document = editor->document();
    else
        document = new QTextDocument;

    {
        QTextCursor cursor(document);
        cursor.beginEditBlock();

        cursor.insertText(contents);

        if (reindent) {
            cursor.select(QTextCursor::Document);
            m_data->indentSelection(cursor, fileName, editor);
        }

        cursor.endEditBlock();
    }

    if (!editor) {
        Utils::TextFileFormat format;
        format.codec = Core::EditorManager::instance()->defaultTextCodec();
        QString error;
        bool saveOk = format.writeFile(fileName, document->toPlainText(), &error);
        delete document;
        if (!saveOk)
            return false;
    }

    m_data->fileChanged(fileName);

    return true;
}

bool RefactoringChanges::removeFile(const QString &fileName) const
{
    if (!QFile::exists(fileName))
        return false;

    // ### implement!
    qWarning() << "RefactoringChanges::removeFile is not implemented";
    return true;
}

BaseTextEditorWidget *RefactoringChanges::openEditor(const QString &fileName, bool activate, int line, int column)
{
    Core::EditorManager::OpenEditorFlags flags = Core::EditorManager::IgnoreNavigationHistory;
    if (!activate)
        flags |= Core::EditorManager::NoActivate;
    if (line != -1) {
        // openEditorAt uses a 1-based line and a 0-based column!
        column -= 1;
    }
    Core::IEditor *editor = BaseTextEditorWidget::openEditorAt(
                fileName, line, column, Core::Id(), flags);
    return qobject_cast<BaseTextEditorWidget *>(editor->widget());
}

RefactoringFilePtr RefactoringChanges::file(BaseTextEditorWidget *editor)
{
    return RefactoringFilePtr(new RefactoringFile(editor));
}

RefactoringFilePtr RefactoringChanges::file(const QString &fileName) const
{
    return RefactoringFilePtr(new RefactoringFile(fileName, m_data));
}

RefactoringFile::RefactoringFile(QTextDocument *document, const QString &fileName)
    : m_fileName(fileName)
    , m_document(document)
    , m_editor(0)
    , m_openEditor(false)
    , m_activateEditor(false)
    , m_editorCursorPosition(-1)
    , m_appliedOnce(false)
{ }

RefactoringFile::RefactoringFile(BaseTextEditorWidget *editor)
    : m_fileName(editor->file()->fileName())
    , m_document(0)
    , m_editor(editor)
    , m_openEditor(false)
    , m_activateEditor(false)
    , m_editorCursorPosition(-1)
    , m_appliedOnce(false)
{ }

RefactoringFile::RefactoringFile(const QString &fileName, const QSharedPointer<RefactoringChangesData> &data)
    : m_fileName(fileName)
    , m_data(data)
    , m_document(0)
    , m_editor(0)
    , m_openEditor(false)
    , m_activateEditor(false)
    , m_editorCursorPosition(-1)
    , m_appliedOnce(false)
{
    m_editor = RefactoringChanges::editorForFile(fileName);
}

RefactoringFile::~RefactoringFile()
{
    delete m_document;
}

bool RefactoringFile::isValid() const
{
    if (m_fileName.isEmpty())
        return false;
    return document();
}

const QTextDocument *RefactoringFile::document() const
{
    return mutableDocument();
}

QTextDocument *RefactoringFile::mutableDocument() const
{
    if (m_editor)
        return m_editor->document();
    else if (!m_document) {
        QString fileContents;
        if (!m_fileName.isEmpty()) {
            QString error;
            QTextCodec *defaultCodec = Core::EditorManager::instance()->defaultTextCodec();
            Utils::TextFileFormat::ReadResult result = Utils::TextFileFormat::readFile(
                        m_fileName, defaultCodec,
                        &fileContents, &m_textFileFormat,
                        &error);
            if (result != Utils::TextFileFormat::ReadSuccess) {
                qWarning() << "Could not read " << m_fileName << ". Error: " << error;
                m_textFileFormat.codec = 0;
            }
        }
        // always make a QTextDocument to avoid excessive null checks
        m_document = new QTextDocument(fileContents);
    }
    return m_document;
}

const QTextCursor RefactoringFile::cursor() const
{
    if (m_editor)
        return m_editor->textCursor();
    else if (!m_fileName.isEmpty()) {
        if (QTextDocument *doc = mutableDocument())
            return QTextCursor(doc);
    }

    return QTextCursor();
}

QString RefactoringFile::fileName() const
{
    return m_fileName;
}

int RefactoringFile::position(unsigned line, unsigned column) const
{
    Q_ASSERT(line != 0);
    Q_ASSERT(column != 0);
    if (const QTextDocument *doc = document())
        return doc->findBlockByNumber(line - 1).position() + column - 1;
    return -1;
}

void RefactoringFile::lineAndColumn(int offset, unsigned *line, unsigned *column) const
{
    Q_ASSERT(line);
    Q_ASSERT(column);
    Q_ASSERT(offset >= 0);
    QTextCursor c(cursor());
    c.setPosition(offset);
    *line = c.blockNumber() + 1;
    *column = c.positionInBlock() + 1;
}

QChar RefactoringFile::charAt(int pos) const
{
    if (const QTextDocument *doc = document())
        return doc->characterAt(pos);
    return QChar();
}

QString RefactoringFile::textOf(int start, int end) const
{
    QTextCursor c = cursor();
    c.setPosition(start);
    c.setPosition(end, QTextCursor::KeepAnchor);
    return c.selectedText();
}

QString RefactoringFile::textOf(const Range &range) const
{
    return textOf(range.start, range.end);
}

void RefactoringFile::setChangeSet(const Utils::ChangeSet &changeSet)
{
    if (m_fileName.isEmpty())
        return;

    m_changes = changeSet;
}

void RefactoringFile::appendIndentRange(const Range &range)
{
    if (m_fileName.isEmpty())
        return;

    m_indentRanges.append(range);
}

void RefactoringFile::appendReindentRange(const Range &range)
{
    if (m_fileName.isEmpty())
        return;

    m_reindentRanges.append(range);
}

void RefactoringFile::setOpenEditor(bool activate, int pos)
{
    m_openEditor = true;
    m_activateEditor = activate;
    m_editorCursorPosition = pos;
}

void RefactoringFile::apply()
{
    // open / activate / goto position
    if (m_openEditor && !m_fileName.isEmpty()) {
        unsigned line = -1, column = -1;
        if (m_editorCursorPosition != -1)
            lineAndColumn(m_editorCursorPosition, &line, &column);
        m_editor = RefactoringChanges::openEditor(m_fileName, m_activateEditor, line, column);
        m_openEditor = false;
        m_activateEditor = false;
        m_editorCursorPosition = -1;
    }

    // apply changes, if any
    if (m_data && !(m_indentRanges.isEmpty() && m_changes.isEmpty())) {
        QTextDocument *doc = mutableDocument();
        if (doc) {
            QTextCursor c = cursor();
            if (m_appliedOnce)
                c.joinPreviousEditBlock();
            else
                c.beginEditBlock();

            // build indent selections now, applying the changeset will change locations
            const QList<QPair<QTextCursor, QTextCursor> > &indentSelections =
                    RefactoringChanges::rangesToSelections(doc, m_indentRanges);
            m_indentRanges.clear();
            const QList<QPair<QTextCursor, QTextCursor> > &reindentSelections =
                    RefactoringChanges::rangesToSelections(doc, m_reindentRanges);
            m_reindentRanges.clear();

            // apply changes and reindent
            m_changes.apply(&c);
            m_changes.clear();

            indentOrReindent(&RefactoringChangesData::indentSelection, indentSelections);
            indentOrReindent(&RefactoringChangesData::reindentSelection, reindentSelections);

            c.endEditBlock();

            // if this document doesn't have an editor, write the result to a file
            if (!m_editor && m_textFileFormat.codec) {
                QTC_ASSERT(!m_fileName.isEmpty(), return);
                QString error;
                if (!m_textFileFormat.writeFile(m_fileName, doc->toPlainText(), &error))
                    qWarning() << "Could not apply changes to" << m_fileName << ". Error: " << error;
            }

            fileChanged();
        }
    }

    m_appliedOnce = true;
}

void RefactoringFile::indentOrReindent(void (RefactoringChangesData::*mf)(const QTextCursor &,
                                                                          const QString &,
                                                                          const BaseTextEditorWidget *) const,
                                       const QList<QPair<QTextCursor, QTextCursor> > &ranges)
{
    QPair<QTextCursor, QTextCursor> p;
    foreach (p, ranges) {
        QTextCursor selection(p.first.document());
        selection.setPosition(p.first.position());
        selection.setPosition(p.second.position(), QTextCursor::KeepAnchor);
        ((*m_data).*(mf))(selection, m_fileName, m_editor);
    }
}

void RefactoringFile::fileChanged()
{
    if (!m_fileName.isEmpty())
        m_data->fileChanged(m_fileName);
}

RefactoringChangesData::~RefactoringChangesData()
{}

void RefactoringChangesData::indentSelection(const QTextCursor &, const QString &, const BaseTextEditorWidget *) const
{
    qWarning() << Q_FUNC_INFO << "not implemented";
}

void RefactoringChangesData::reindentSelection(const QTextCursor &, const QString &, const BaseTextEditorWidget *) const
{
    qWarning() << Q_FUNC_INFO << "not implemented";
}

void RefactoringChangesData::fileChanged(const QString &)
{
}
<commit_msg>Fix crash when doing "Move into file" refacoring action.<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/

#include "refactoringchanges.h"
#include "basetexteditor.h"

#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
#include <extensionsystem/pluginmanager.h>

#include <utils/qtcassert.h>

#include <QtCore/QFile>
#include <QtCore/QSet>
#include <QtGui/QMainWindow>
#include <QtGui/QTextBlock>
#include <QtGui/QTextCursor>
#include <QtGui/QTextDocument>
#include <QtCore/QDebug>

using namespace TextEditor;

RefactoringChanges::RefactoringChanges()
    : m_data(new RefactoringChangesData)
{}

RefactoringChanges::RefactoringChanges(RefactoringChangesData *data)
    : m_data(data)
{}

RefactoringChanges::~RefactoringChanges()
{}

BaseTextEditorWidget *RefactoringChanges::editorForFile(const QString &fileName)
{
    Core::EditorManager *editorManager = Core::EditorManager::instance();

    const QList<Core::IEditor *> editors = editorManager->editorsForFileName(fileName);
    foreach (Core::IEditor *editor, editors) {
        BaseTextEditorWidget *textEditor = qobject_cast<BaseTextEditorWidget *>(editor->widget());
        if (textEditor != 0)
            return textEditor;
    }
    return 0;
}

QList<QPair<QTextCursor, QTextCursor > > RefactoringChanges::rangesToSelections(QTextDocument *document,
                                                                                const QList<Range> &ranges)
{
    QList<QPair<QTextCursor, QTextCursor> > selections;

    foreach (const Range &range, ranges) {
        QTextCursor start(document);
        start.setPosition(range.start);
        start.setKeepPositionOnInsert(true);
        QTextCursor end(document);
        end.setPosition(qMin(range.end, document->characterCount() - 1));

        selections.append(qMakePair(start, end));
    }

    return selections;
}

bool RefactoringChanges::createFile(const QString &fileName, const QString &contents, bool reindent, bool openEditor) const
{
    if (QFile::exists(fileName))
        return false;

    // Create a text document for the new file:
    QTextDocument *document = new QTextDocument;
    QTextCursor cursor(document);
    cursor.beginEditBlock();
    cursor.insertText(contents);

    // Reindent the contents:
    if (reindent) {
        cursor.select(QTextCursor::Document);
        m_data->indentSelection(cursor, fileName, 0);
    }
    cursor.endEditBlock();

    // Write the file to disk:
    Utils::TextFileFormat format;
    format.codec = Core::EditorManager::instance()->defaultTextCodec();
    QString error;
    bool saveOk = format.writeFile(fileName, document->toPlainText(), &error);
    delete document;
    if (!saveOk)
        return false;

    m_data->fileChanged(fileName);

    if (openEditor)
        this->openEditor(fileName, /*bool activate =*/ false, -1, -1);

    return true;
}

bool RefactoringChanges::removeFile(const QString &fileName) const
{
    if (!QFile::exists(fileName))
        return false;

    // ### implement!
    qWarning() << "RefactoringChanges::removeFile is not implemented";
    return true;
}

BaseTextEditorWidget *RefactoringChanges::openEditor(const QString &fileName, bool activate, int line, int column)
{
    Core::EditorManager::OpenEditorFlags flags = Core::EditorManager::IgnoreNavigationHistory;
    if (!activate)
        flags |= Core::EditorManager::NoActivate;
    if (line != -1) {
        // openEditorAt uses a 1-based line and a 0-based column!
        column -= 1;
    }
    Core::IEditor *editor = BaseTextEditorWidget::openEditorAt(
                fileName, line, column, Core::Id(), flags);

    if (editor)
        return qobject_cast<BaseTextEditorWidget *>(editor->widget());
    else
        return 0;
}

RefactoringFilePtr RefactoringChanges::file(BaseTextEditorWidget *editor)
{
    return RefactoringFilePtr(new RefactoringFile(editor));
}

RefactoringFilePtr RefactoringChanges::file(const QString &fileName) const
{
    return RefactoringFilePtr(new RefactoringFile(fileName, m_data));
}

RefactoringFile::RefactoringFile(QTextDocument *document, const QString &fileName)
    : m_fileName(fileName)
    , m_document(document)
    , m_editor(0)
    , m_openEditor(false)
    , m_activateEditor(false)
    , m_editorCursorPosition(-1)
    , m_appliedOnce(false)
{ }

RefactoringFile::RefactoringFile(BaseTextEditorWidget *editor)
    : m_fileName(editor->file()->fileName())
    , m_document(0)
    , m_editor(editor)
    , m_openEditor(false)
    , m_activateEditor(false)
    , m_editorCursorPosition(-1)
    , m_appliedOnce(false)
{ }

RefactoringFile::RefactoringFile(const QString &fileName, const QSharedPointer<RefactoringChangesData> &data)
    : m_fileName(fileName)
    , m_data(data)
    , m_document(0)
    , m_editor(0)
    , m_openEditor(false)
    , m_activateEditor(false)
    , m_editorCursorPosition(-1)
    , m_appliedOnce(false)
{
    m_editor = RefactoringChanges::editorForFile(fileName);
}

RefactoringFile::~RefactoringFile()
{
    delete m_document;
}

bool RefactoringFile::isValid() const
{
    if (m_fileName.isEmpty())
        return false;
    return document();
}

const QTextDocument *RefactoringFile::document() const
{
    return mutableDocument();
}

QTextDocument *RefactoringFile::mutableDocument() const
{
    if (m_editor)
        return m_editor->document();
    else if (!m_document) {
        QString fileContents;
        if (!m_fileName.isEmpty()) {
            QString error;
            QTextCodec *defaultCodec = Core::EditorManager::instance()->defaultTextCodec();
            Utils::TextFileFormat::ReadResult result = Utils::TextFileFormat::readFile(
                        m_fileName, defaultCodec,
                        &fileContents, &m_textFileFormat,
                        &error);
            if (result != Utils::TextFileFormat::ReadSuccess) {
                qWarning() << "Could not read " << m_fileName << ". Error: " << error;
                m_textFileFormat.codec = 0;
            }
        }
        // always make a QTextDocument to avoid excessive null checks
        m_document = new QTextDocument(fileContents);
    }
    return m_document;
}

const QTextCursor RefactoringFile::cursor() const
{
    if (m_editor)
        return m_editor->textCursor();
    else if (!m_fileName.isEmpty()) {
        if (QTextDocument *doc = mutableDocument())
            return QTextCursor(doc);
    }

    return QTextCursor();
}

QString RefactoringFile::fileName() const
{
    return m_fileName;
}

int RefactoringFile::position(unsigned line, unsigned column) const
{
    Q_ASSERT(line != 0);
    Q_ASSERT(column != 0);
    if (const QTextDocument *doc = document())
        return doc->findBlockByNumber(line - 1).position() + column - 1;
    return -1;
}

void RefactoringFile::lineAndColumn(int offset, unsigned *line, unsigned *column) const
{
    Q_ASSERT(line);
    Q_ASSERT(column);
    Q_ASSERT(offset >= 0);
    QTextCursor c(cursor());
    c.setPosition(offset);
    *line = c.blockNumber() + 1;
    *column = c.positionInBlock() + 1;
}

QChar RefactoringFile::charAt(int pos) const
{
    if (const QTextDocument *doc = document())
        return doc->characterAt(pos);
    return QChar();
}

QString RefactoringFile::textOf(int start, int end) const
{
    QTextCursor c = cursor();
    c.setPosition(start);
    c.setPosition(end, QTextCursor::KeepAnchor);
    return c.selectedText();
}

QString RefactoringFile::textOf(const Range &range) const
{
    return textOf(range.start, range.end);
}

void RefactoringFile::setChangeSet(const Utils::ChangeSet &changeSet)
{
    if (m_fileName.isEmpty())
        return;

    m_changes = changeSet;
}

void RefactoringFile::appendIndentRange(const Range &range)
{
    if (m_fileName.isEmpty())
        return;

    m_indentRanges.append(range);
}

void RefactoringFile::appendReindentRange(const Range &range)
{
    if (m_fileName.isEmpty())
        return;

    m_reindentRanges.append(range);
}

void RefactoringFile::setOpenEditor(bool activate, int pos)
{
    m_openEditor = true;
    m_activateEditor = activate;
    m_editorCursorPosition = pos;
}

void RefactoringFile::apply()
{
    // open / activate / goto position
    if (m_openEditor && !m_fileName.isEmpty()) {
        unsigned line = -1, column = -1;
        if (m_editorCursorPosition != -1)
            lineAndColumn(m_editorCursorPosition, &line, &column);
        m_editor = RefactoringChanges::openEditor(m_fileName, m_activateEditor, line, column);
        m_openEditor = false;
        m_activateEditor = false;
        m_editorCursorPosition = -1;
    }

    // apply changes, if any
    if (m_data && !(m_indentRanges.isEmpty() && m_changes.isEmpty())) {
        QTextDocument *doc = mutableDocument();
        if (doc) {
            QTextCursor c = cursor();
            if (m_appliedOnce)
                c.joinPreviousEditBlock();
            else
                c.beginEditBlock();

            // build indent selections now, applying the changeset will change locations
            const QList<QPair<QTextCursor, QTextCursor> > &indentSelections =
                    RefactoringChanges::rangesToSelections(doc, m_indentRanges);
            m_indentRanges.clear();
            const QList<QPair<QTextCursor, QTextCursor> > &reindentSelections =
                    RefactoringChanges::rangesToSelections(doc, m_reindentRanges);
            m_reindentRanges.clear();

            // apply changes and reindent
            m_changes.apply(&c);
            m_changes.clear();

            indentOrReindent(&RefactoringChangesData::indentSelection, indentSelections);
            indentOrReindent(&RefactoringChangesData::reindentSelection, reindentSelections);

            c.endEditBlock();

            // if this document doesn't have an editor, write the result to a file
            if (!m_editor && m_textFileFormat.codec) {
                QTC_ASSERT(!m_fileName.isEmpty(), return);
                QString error;
                if (!m_textFileFormat.writeFile(m_fileName, doc->toPlainText(), &error))
                    qWarning() << "Could not apply changes to" << m_fileName << ". Error: " << error;
            }

            fileChanged();
        }
    }

    m_appliedOnce = true;
}

void RefactoringFile::indentOrReindent(void (RefactoringChangesData::*mf)(const QTextCursor &,
                                                                          const QString &,
                                                                          const BaseTextEditorWidget *) const,
                                       const QList<QPair<QTextCursor, QTextCursor> > &ranges)
{
    QPair<QTextCursor, QTextCursor> p;
    foreach (p, ranges) {
        QTextCursor selection(p.first.document());
        selection.setPosition(p.first.position());
        selection.setPosition(p.second.position(), QTextCursor::KeepAnchor);
        ((*m_data).*(mf))(selection, m_fileName, m_editor);
    }
}

void RefactoringFile::fileChanged()
{
    if (!m_fileName.isEmpty())
        m_data->fileChanged(m_fileName);
}

RefactoringChangesData::~RefactoringChangesData()
{}

void RefactoringChangesData::indentSelection(const QTextCursor &, const QString &, const BaseTextEditorWidget *) const
{
    qWarning() << Q_FUNC_INFO << "not implemented";
}

void RefactoringChangesData::reindentSelection(const QTextCursor &, const QString &, const BaseTextEditorWidget *) const
{
    qWarning() << Q_FUNC_INFO << "not implemented";
}

void RefactoringChangesData::fileChanged(const QString &)
{
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// 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 co
// pies 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 al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER 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 "content/nw/src/renderer/shell_content_renderer_client.h"

#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/utf_string_conversions.h"
#include "chrome/renderer/page_click_tracker.h"
#include "content/nw/src/api/dispatcher.h"
#include "content/nw/src/api/api_messages.h"
#include "content/nw/src/api/window_bindings.h"
#include "content/nw/src/common/shell_switches.h"
#include "content/nw/src/nw_package.h"
#include "content/nw/src/nw_version.h"
#include "content/nw/src/renderer/autofill_agent.h"
#include "content/nw/src/renderer/prerenderer/prerenderer_client.h"
#include "content/nw/src/renderer/shell_render_process_observer.h"
#include "content/public/renderer/render_view.h"
#include "content/renderer/render_view_impl.h"
#include "net/proxy/proxy_bypass_rules.h"
#include "third_party/node/src/node.h"
#include "third_party/node/src/req_wrap.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityPolicy.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"

using content::RenderView;
using content::RenderViewImpl;
using net::ProxyBypassRules;
using WebKit::WebFrame;
using WebKit::WebView;

namespace content {

namespace {

RenderView* GetCurrentRenderView() {
  WebFrame* frame = WebFrame::frameForCurrentContext();
  DCHECK(frame);
  if (!frame)
    return NULL;

  WebView* view = frame->view();
  if (!view)
    return NULL;  // can happen during closing.

  RenderView* render_view = RenderView::FromWebView(view);
  DCHECK(render_view);
  return render_view;
}

}  // namespace

ShellContentRendererClient::ShellContentRendererClient() {
}

ShellContentRendererClient::~ShellContentRendererClient() {
}

void ShellContentRendererClient::RenderThreadStarted() {
  // Change working directory.
  CommandLine* command_line = CommandLine::ForCurrentProcess();
  if (command_line->HasSwitch(switches::kWorkingDirectory)) {
    file_util::SetCurrentDirectory(
        command_line->GetSwitchValuePath(switches::kWorkingDirectory));
  }

  int argc = 1;
  char* argv[] = { const_cast<char*>("node"), NULL, NULL };
  std::string node_main;

  // Check if there is a 'node-main'.
  if (command_line->HasSwitch(switches::kNodeMain)) {
    argc++;
    node_main = command_line->GetSwitchValueASCII(switches::kNodeMain);
    argv[1] = const_cast<char*>(node_main.c_str());
  }

  // Initialize uv.
  node::SetupUv(argc, argv);

  // Initialize node after render thread is started.
  v8::V8::Initialize();
  v8::HandleScope scope;

  // Install window bindings into node. The Window API is implemented in node's
  // context, so when a Shell changes to a new location and destroy previous
  // window context, our Window API can still work.
  window_bindings_.reset(new api::WindowBindings());
  v8::RegisterExtension(window_bindings_.get());
  const char* names[] = { "window_bindings.js" };
  v8::ExtensionConfiguration extension_configuration(1, names);

  node::g_context = v8::Context::New(&extension_configuration);
  node::g_context->Enter();

  // Setup node.js.
  node::SetupContext(argc, argv, node::g_context->Global());

  // Start observers.
  shell_observer_.reset(new ShellRenderProcessObserver());
}

void ShellContentRendererClient::RenderViewCreated(RenderView* render_view) {
  new api::Dispatcher(render_view);
  new prerender::PrerendererClient(render_view);

  PageClickTracker* page_click_tracker = new PageClickTracker(render_view);
  nw::AutofillAgent* autofill_agent = new nw::AutofillAgent(render_view);
  page_click_tracker->AddListener(autofill_agent);
}

void ShellContentRendererClient::DidCreateScriptContext(
    WebKit::WebFrame* frame,
    v8::Handle<v8::Context> context,
    int extension_group,
    int world_id) {
  GURL url(frame->document().url());
  DVLOG(1) << url;
  RenderViewImpl* rv = RenderViewImpl::FromWebView(frame->view());
  ProxyBypassRules rules;
  rules.ParseFromString(rv->renderer_preferences_.nw_remote_page_rules);
  bool force_on = rules.Matches(url);
  InstallNodeSymbols(context, url, force_on);
}

bool ShellContentRendererClient::WillSetSecurityToken(
    WebKit::WebFrame* frame,
    v8::Handle<v8::Context> context) {
  // Override context's security token
  context->SetSecurityToken(node::g_context->GetSecurityToken());

  return true;
}

void ShellContentRendererClient::InstallNodeSymbols(
    v8::Handle<v8::Context> context,
    const GURL& url,
    bool force_on) {
  v8::HandleScope handle_scope;

  static bool installed_once = false;

  bool is_file_protocol = url.SchemeIsFile();

  // Do we integrate node?
  bool use_node =
    CommandLine::ForCurrentProcess()->HasSwitch(switches::kmNodejs) &&
    (force_on || is_file_protocol);

  // Test if protocol is 'nw:'
  // test for 'about:blank' is also here becuase window.open would open 'about:blank' first
  bool is_nw_protocol = url.SchemeIs("nw") || url.SchemeIs("about");

  if (use_node || is_nw_protocol) {
    v8::Local<v8::Array> symbols = v8::Array::New(4);
    symbols->Set(0, v8::String::New("global"));
    symbols->Set(1, v8::String::New("process"));
    symbols->Set(2, v8::String::New("Buffer"));
    symbols->Set(3, v8::String::New("root"));

    v8::Local<v8::Object> nodeGlobal = node::g_context->Global();
    v8::Local<v8::Object> v8Global = context->Global();
    for (unsigned i = 0; i < symbols->Length(); ++i) {
      v8::Local<v8::Value> key = symbols->Get(i);
      v8Global->Set(key, nodeGlobal->Get(key));
    }

    if (!installed_once) {
      installed_once = true;

      // The following listener on process should not be added each
      // time when a document is created, or it will leave the
      // reference to the closure created by the call back and leak
      // memory (see #203)

      nodeGlobal->Set(v8::String::New("window"), v8Global);

      // Listen uncaughtException with ReportException.
      v8::Local<v8::Function> cb = v8::FunctionTemplate::New(ReportException)->
        GetFunction();
      v8::Local<v8::Value> argv[] = { v8::String::New("uncaughtException"), cb };
      node::MakeCallback(node::process, "on", 2, argv);
    }
  }

  if (use_node) {
    v8::Local<v8::Script> script = v8::Script::New(v8::String::New(
        // Make node's relative modules work
#if defined(OS_WIN)
        "process.mainModule.filename = decodeURIComponent(window.location.pathname.substr(1));"
#else
        "process.mainModule.filename = decodeURIComponent(window.location.pathname);"
#endif
        "process.mainModule.paths = global.require('module')._nodeModulePaths(process.cwd());"
    ));
    script->Run();
  }

  if (use_node || is_nw_protocol) {
    v8::Local<v8::Script> script = v8::Script::New(v8::String::New(
        // Use WebKit's console globally
        "global.console = console;"

        // Overload require
        "window.require = function(name) {"
        "  if (name == 'nw.gui')"
        "    return nwDispatcher.requireNwGui();"
        "  return global.require(name);"
        "};"

        // Save node-webkit version
        "process.versions['node-webkit'] = '" NW_VERSION_STRING "';"
    ));
    script->Run();
  }
}

// static
v8::Handle<v8::Value> ShellContentRendererClient::ReportException(
    const v8::Arguments& args) {
  v8::HandleScope handle_scope;

  // Do nothing if user is listening to uncaughtException.
  v8::Local<v8::Value> listeners_v =
      node::process->Get(v8::String::New("listeners"));
  v8::Local<v8::Function> listeners =
      v8::Local<v8::Function>::Cast(listeners_v);

  v8::Local<v8::Value> argv[1] = { v8::String::New("uncaughtException") };
  v8::Local<v8::Value> ret = listeners->Call(node::process, 1, argv);
  v8::Local<v8::Array> listener_array = v8::Local<v8::Array>::Cast(ret);

  uint32_t length = listener_array->Length();
  if (length > 1)
    return v8::Undefined();

  // Print stacktrace.
  v8::Local<v8::String> stack_symbol = v8::String::New("stack");
  std::string error;

  v8::Local<v8::Object> exception = args[0]->ToObject();
  if (exception->Has(stack_symbol))
    error = *v8::String::Utf8Value(exception->Get(stack_symbol));
  else
    error = *v8::String::Utf8Value(exception);

  RenderView* render_view = GetCurrentRenderView();
  if (!render_view)
    return v8::Undefined();

  render_view->Send(new ShellViewHostMsg_UncaughtException(
      render_view->GetRoutingID(),
      error));

  return v8::Undefined();
}

}  // namespace content
<commit_msg>install Node for window.open() Windows<commit_after>// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// 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 co
// pies 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 al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER 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 "content/nw/src/renderer/shell_content_renderer_client.h"

#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/utf_string_conversions.h"
#include "chrome/renderer/page_click_tracker.h"
#include "content/nw/src/api/dispatcher.h"
#include "content/nw/src/api/api_messages.h"
#include "content/nw/src/api/window_bindings.h"
#include "content/nw/src/common/shell_switches.h"
#include "content/nw/src/nw_package.h"
#include "content/nw/src/nw_version.h"
#include "content/nw/src/renderer/autofill_agent.h"
#include "content/nw/src/renderer/prerenderer/prerenderer_client.h"
#include "content/nw/src/renderer/shell_render_process_observer.h"
#include "content/public/renderer/render_view.h"
#include "content/renderer/render_view_impl.h"
#include "net/proxy/proxy_bypass_rules.h"
#include "third_party/node/src/node.h"
#include "third_party/node/src/req_wrap.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityPolicy.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"

using content::RenderView;
using content::RenderViewImpl;
using net::ProxyBypassRules;
using WebKit::WebFrame;
using WebKit::WebView;

namespace content {

namespace {

RenderView* GetCurrentRenderView() {
  WebFrame* frame = WebFrame::frameForCurrentContext();
  DCHECK(frame);
  if (!frame)
    return NULL;

  WebView* view = frame->view();
  if (!view)
    return NULL;  // can happen during closing.

  RenderView* render_view = RenderView::FromWebView(view);
  DCHECK(render_view);
  return render_view;
}

}  // namespace

ShellContentRendererClient::ShellContentRendererClient() {
}

ShellContentRendererClient::~ShellContentRendererClient() {
}

void ShellContentRendererClient::RenderThreadStarted() {
  // Change working directory.
  CommandLine* command_line = CommandLine::ForCurrentProcess();
  if (command_line->HasSwitch(switches::kWorkingDirectory)) {
    file_util::SetCurrentDirectory(
        command_line->GetSwitchValuePath(switches::kWorkingDirectory));
  }

  int argc = 1;
  char* argv[] = { const_cast<char*>("node"), NULL, NULL };
  std::string node_main;

  // Check if there is a 'node-main'.
  if (command_line->HasSwitch(switches::kNodeMain)) {
    argc++;
    node_main = command_line->GetSwitchValueASCII(switches::kNodeMain);
    argv[1] = const_cast<char*>(node_main.c_str());
  }

  // Initialize uv.
  node::SetupUv(argc, argv);

  // Initialize node after render thread is started.
  v8::V8::Initialize();
  v8::HandleScope scope;

  // Install window bindings into node. The Window API is implemented in node's
  // context, so when a Shell changes to a new location and destroy previous
  // window context, our Window API can still work.
  window_bindings_.reset(new api::WindowBindings());
  v8::RegisterExtension(window_bindings_.get());
  const char* names[] = { "window_bindings.js" };
  v8::ExtensionConfiguration extension_configuration(1, names);

  node::g_context = v8::Context::New(&extension_configuration);
  node::g_context->Enter();

  // Setup node.js.
  node::SetupContext(argc, argv, node::g_context->Global());

  // Start observers.
  shell_observer_.reset(new ShellRenderProcessObserver());
}

void ShellContentRendererClient::RenderViewCreated(RenderView* render_view) {
  new api::Dispatcher(render_view);
  new prerender::PrerendererClient(render_view);

  PageClickTracker* page_click_tracker = new PageClickTracker(render_view);
  nw::AutofillAgent* autofill_agent = new nw::AutofillAgent(render_view);
  page_click_tracker->AddListener(autofill_agent);
}

void ShellContentRendererClient::DidCreateScriptContext(
    WebKit::WebFrame* frame,
    v8::Handle<v8::Context> context,
    int extension_group,
    int world_id) {
  GURL url(frame->document().url());
  DVLOG(1) << url;
  RenderViewImpl* rv = RenderViewImpl::FromWebView(frame->view());
  ProxyBypassRules rules;
  rules.ParseFromString(rv->renderer_preferences_.nw_remote_page_rules);
  bool force_on = rules.Matches(url);
  InstallNodeSymbols(context, url, force_on);
}

bool ShellContentRendererClient::WillSetSecurityToken(
    WebKit::WebFrame* frame,
    v8::Handle<v8::Context> context) {
  // Override context's security token
  context->SetSecurityToken(node::g_context->GetSecurityToken());

  return true;
}

void ShellContentRendererClient::InstallNodeSymbols(
    v8::Handle<v8::Context> context,
    const GURL& url,
    bool force_on) {
  v8::HandleScope handle_scope;

  static bool installed_once = false;

  bool is_file_protocol = url.SchemeIsFile();

  // Do we integrate node?
  bool use_node =
    CommandLine::ForCurrentProcess()->HasSwitch(switches::kmNodejs) &&
    (force_on || is_file_protocol);

  // Test if protocol is 'nw:'
  // test for 'about:blank' is also here becuase window.open would
  // open 'about:blank' first // FIXME
  bool is_nw_protocol = url.SchemeIs("nw") || !url.is_valid();

  if (use_node || is_nw_protocol) {
    v8::Local<v8::Array> symbols = v8::Array::New(4);
    symbols->Set(0, v8::String::New("global"));
    symbols->Set(1, v8::String::New("process"));
    symbols->Set(2, v8::String::New("Buffer"));
    symbols->Set(3, v8::String::New("root"));

    v8::Local<v8::Object> nodeGlobal = node::g_context->Global();
    v8::Local<v8::Object> v8Global = context->Global();
    for (unsigned i = 0; i < symbols->Length(); ++i) {
      v8::Local<v8::Value> key = symbols->Get(i);
      v8Global->Set(key, nodeGlobal->Get(key));
    }

    if (!installed_once) {
      installed_once = true;

      // The following listener on process should not be added each
      // time when a document is created, or it will leave the
      // reference to the closure created by the call back and leak
      // memory (see #203)

      nodeGlobal->Set(v8::String::New("window"), v8Global);

      // Listen uncaughtException with ReportException.
      v8::Local<v8::Function> cb = v8::FunctionTemplate::New(ReportException)->
        GetFunction();
      v8::Local<v8::Value> argv[] = { v8::String::New("uncaughtException"), cb };
      node::MakeCallback(node::process, "on", 2, argv);
    }
  }

  if (use_node) {
    v8::Local<v8::Script> script = v8::Script::New(v8::String::New(
        // Make node's relative modules work
#if defined(OS_WIN)
        "process.mainModule.filename = decodeURIComponent(window.location.pathname.substr(1));"
#else
        "process.mainModule.filename = decodeURIComponent(window.location.pathname);"
#endif
        "process.mainModule.paths = global.require('module')._nodeModulePaths(process.cwd());"
    ));
    script->Run();
  }

  if (use_node || is_nw_protocol) {
    v8::Local<v8::Script> script = v8::Script::New(v8::String::New(
        // Use WebKit's console globally
        "global.console = console;"

        // Overload require
        "window.require = function(name) {"
        "  if (name == 'nw.gui')"
        "    return nwDispatcher.requireNwGui();"
        "  return global.require(name);"
        "};"

        // Save node-webkit version
        "process.versions['node-webkit'] = '" NW_VERSION_STRING "';"
    ));
    script->Run();
  }
}

// static
v8::Handle<v8::Value> ShellContentRendererClient::ReportException(
    const v8::Arguments& args) {
  v8::HandleScope handle_scope;

  // Do nothing if user is listening to uncaughtException.
  v8::Local<v8::Value> listeners_v =
      node::process->Get(v8::String::New("listeners"));
  v8::Local<v8::Function> listeners =
      v8::Local<v8::Function>::Cast(listeners_v);

  v8::Local<v8::Value> argv[1] = { v8::String::New("uncaughtException") };
  v8::Local<v8::Value> ret = listeners->Call(node::process, 1, argv);
  v8::Local<v8::Array> listener_array = v8::Local<v8::Array>::Cast(ret);

  uint32_t length = listener_array->Length();
  if (length > 1)
    return v8::Undefined();

  // Print stacktrace.
  v8::Local<v8::String> stack_symbol = v8::String::New("stack");
  std::string error;

  v8::Local<v8::Object> exception = args[0]->ToObject();
  if (exception->Has(stack_symbol))
    error = *v8::String::Utf8Value(exception->Get(stack_symbol));
  else
    error = *v8::String::Utf8Value(exception);

  RenderView* render_view = GetCurrentRenderView();
  if (!render_view)
    return v8::Undefined();

  render_view->Send(new ShellViewHostMsg_UncaughtException(
      render_view->GetRoutingID(),
      error));

  return v8::Undefined();
}

}  // namespace content
<|endoftext|>


<commit_before>#pragma once

#include "depthai-shared/common/CameraBoardSocket.hpp"
#include "depthai-shared/common/EepromData.hpp"
#include "depthai-shared/common/optional.hpp"
#include "depthai-shared/datatype/RawStereoDepthConfig.hpp"
#include "depthai-shared/properties/Properties.hpp"

namespace dai {

/**
 * Specify properties for StereoDepth
 */
struct StereoDepthProperties : PropertiesSerializable<Properties, StereoDepthProperties> {
    static constexpr const std::int32_t AUTO = -1;

    struct RectificationMesh {
        /**
         * Uri which points to the mesh array for 'left' input rectification
         */
        std::string meshLeftUri;
        /**
         * Uri which points to the mesh array for 'right' input rectification
         */
        std::string meshRightUri;
        /**
         * Mesh array size in bytes, for each of 'left' and 'right' (need to match)
         */
        tl::optional<std::uint32_t> meshSize;
        /**
         * Distance between mesh points, in the horizontal direction
         */
        uint16_t stepWidth = 16;
        /**
         * Distance between mesh points, in the vertical direction
         */
        uint16_t stepHeight = 16;

        DEPTHAI_SERIALIZE(RectificationMesh, meshLeftUri, meshRightUri, meshSize, stepWidth, stepHeight);
    };

    /// Initial stereo config
    RawStereoDepthConfig initialConfig;

    using MedianFilter = dai::MedianFilter;

    using DepthAlign = dai::RawStereoDepthConfig::AlgorithmControl::DepthAlign;

    /**
     * Which camera to align disparity/depth to.
     * When configured (not AUTO), takes precedence over 'depthAlign'
     */
    CameraBoardSocket depthAlignCamera = CameraBoardSocket::AUTO;

    bool enableRectification = true;

    /**
     * Fill color for missing data at frame edges - grayscale 0..255, or -1 to replicate pixels
     */
    std::int32_t rectifyEdgeFillColor = -1;
    /**
     * Input frame width. Optional (taken from MonoCamera nodes if they exist)
     */
    tl::optional<std::int32_t> width;
    /**
     * Input frame height. Optional (taken from MonoCamera nodes if they exist)
     */
    tl::optional<std::int32_t> height;
    /**
     * Output disparity/depth width. Currently only used when aligning to RGB
     */
    tl::optional<std::int32_t> outWidth;
    /**
     * Output disparity/depth height. Currently only used when aligning to RGB
     */
    tl::optional<std::int32_t> outHeight;
    /**
     * Whether to keep aspect ratio of the input (rectified) or not
     */
    bool outKeepAspectRatio = true;

    /**
     * Specify a direct warp mesh to be used for rectification,
     * instead of intrinsics + extrinsic matrices
     */
    RectificationMesh mesh;

    /**
     * Whether to enable switching stereo modes at runtime or not.
     * E.g. standard to subpixel, standard+LR-check to subpixel + LR-check.
     * Note: It will allocate resources for worst cases scenario,
     * should be enabled only if dynamic mode switch is required.
     * Default value: false.
     */
    bool enableRuntimeStereoModeSwitch = false;

    /// Num frames in output pool
    int numFramesPool = 3;

    /**
     * Number of shaves reserved for stereo depth post processing.
     * Post processing can use multiple shaves to increase performance.
     * -1 means auto, resources will be allocated based on enabled filters.
     * 0 means that it will reuse the shave assigned for main stereo algorithm.
     * For optimal performance it's recommended to allocate more than 0,
     * so post processing will run in parallel with main stereo algorithm.
     */
    std::int32_t numPostProcessingShaves = AUTO;

    /**
     * Number of memory slices reserved for stereo depth post processing.
     * -1 means auto, memory will be allocated based on initial stereo settings and number of shaves.
     * 0 means that it will reuse the memory slices assigned for main stereo algorithm.
     * For optimal performance it's recommended to allocate more than 0,
     * so post processing will run in parallel with main stereo algorithm.
     */
    std::int32_t numPostProcessingMemorySlices = AUTO;
};

DEPTHAI_SERIALIZE_EXT(StereoDepthProperties,
                      initialConfig,
                      depthAlignCamera,
                      enableRectification,
                      rectifyEdgeFillColor,
                      width,
                      height,
                      outWidth,
                      outHeight,
                      outKeepAspectRatio,
                      mesh,
                      enableRuntimeStereoModeSwitch,
                      numFramesPool,
                      numPostProcessingShaves,
                      numPostProcessingMemorySlices);

}  // namespace dai
<commit_msg>Add option to use focal length from intrinsics<commit_after>#pragma once

#include "depthai-shared/common/CameraBoardSocket.hpp"
#include "depthai-shared/common/EepromData.hpp"
#include "depthai-shared/common/optional.hpp"
#include "depthai-shared/datatype/RawStereoDepthConfig.hpp"
#include "depthai-shared/properties/Properties.hpp"

namespace dai {

/**
 * Specify properties for StereoDepth
 */
struct StereoDepthProperties : PropertiesSerializable<Properties, StereoDepthProperties> {
    static constexpr const std::int32_t AUTO = -1;

    struct RectificationMesh {
        /**
         * Uri which points to the mesh array for 'left' input rectification
         */
        std::string meshLeftUri;
        /**
         * Uri which points to the mesh array for 'right' input rectification
         */
        std::string meshRightUri;
        /**
         * Mesh array size in bytes, for each of 'left' and 'right' (need to match)
         */
        tl::optional<std::uint32_t> meshSize;
        /**
         * Distance between mesh points, in the horizontal direction
         */
        uint16_t stepWidth = 16;
        /**
         * Distance between mesh points, in the vertical direction
         */
        uint16_t stepHeight = 16;

        DEPTHAI_SERIALIZE(RectificationMesh, meshLeftUri, meshRightUri, meshSize, stepWidth, stepHeight);
    };

    /// Initial stereo config
    RawStereoDepthConfig initialConfig;

    using MedianFilter = dai::MedianFilter;

    using DepthAlign = dai::RawStereoDepthConfig::AlgorithmControl::DepthAlign;

    /**
     * Which camera to align disparity/depth to.
     * When configured (not AUTO), takes precedence over 'depthAlign'
     */
    CameraBoardSocket depthAlignCamera = CameraBoardSocket::AUTO;

    bool enableRectification = true;

    /**
     * Fill color for missing data at frame edges - grayscale 0..255, or -1 to replicate pixels
     */
    std::int32_t rectifyEdgeFillColor = -1;
    /**
     * Input frame width. Optional (taken from MonoCamera nodes if they exist)
     */
    tl::optional<std::int32_t> width;
    /**
     * Input frame height. Optional (taken from MonoCamera nodes if they exist)
     */
    tl::optional<std::int32_t> height;
    /**
     * Output disparity/depth width. Currently only used when aligning to RGB
     */
    tl::optional<std::int32_t> outWidth;
    /**
     * Output disparity/depth height. Currently only used when aligning to RGB
     */
    tl::optional<std::int32_t> outHeight;
    /**
     * Whether to keep aspect ratio of the input (rectified) or not
     */
    bool outKeepAspectRatio = true;

    /**
     * Specify a direct warp mesh to be used for rectification,
     * instead of intrinsics + extrinsic matrices
     */
    RectificationMesh mesh;

    /**
     * Whether to enable switching stereo modes at runtime or not.
     * E.g. standard to subpixel, standard+LR-check to subpixel + LR-check.
     * Note: It will allocate resources for worst cases scenario,
     * should be enabled only if dynamic mode switch is required.
     * Default value: false.
     */
    bool enableRuntimeStereoModeSwitch = false;

    /// Num frames in output pool
    int numFramesPool = 3;

    /**
     * Number of shaves reserved for stereo depth post processing.
     * Post processing can use multiple shaves to increase performance.
     * -1 means auto, resources will be allocated based on enabled filters.
     * 0 means that it will reuse the shave assigned for main stereo algorithm.
     * For optimal performance it's recommended to allocate more than 0,
     * so post processing will run in parallel with main stereo algorithm.
     */
    std::int32_t numPostProcessingShaves = AUTO;

    /**
     * Number of memory slices reserved for stereo depth post processing.
     * -1 means auto, memory will be allocated based on initial stereo settings and number of shaves.
     * 0 means that it will reuse the memory slices assigned for main stereo algorithm.
     * For optimal performance it's recommended to allocate more than 0,
     * so post processing will run in parallel with main stereo algorithm.
     */
    std::int32_t numPostProcessingMemorySlices = AUTO;

    /**
     * Whether to use focal length from calibration intrinsics or calculate based on calibration FOV.
     * Default behaviour is AUTO, for fisheye lenses focal length is taken from calibration intrinsics,
     * otherwise calculated from FOV and image resolution: focalLength = calib.width / (2.f * tan(calib.fov / 2 / 180.f * pi));
     */
    tl::optional<bool> focalLengthFromCalibration;
};

DEPTHAI_SERIALIZE_EXT(StereoDepthProperties,
                      initialConfig,
                      depthAlignCamera,
                      enableRectification,
                      rectifyEdgeFillColor,
                      width,
                      height,
                      outWidth,
                      outHeight,
                      outKeepAspectRatio,
                      mesh,
                      enableRuntimeStereoModeSwitch,
                      numFramesPool,
                      numPostProcessingShaves,
                      numPostProcessingMemorySlices,
                      focalLengthFromCalibration);

}  // namespace dai
<|endoftext|>