<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <sal/config.h>
#include <unotest/filters-test.hxx>
#include <test/bootstrapfixture.hxx>
#include <rtl/strbuf.hxx>
#include <osl/file.hxx>

#include <sfx2/app.hxx>
#include <sfx2/docfilt.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/frame.hxx>
#include <sfx2/sfxmodelfactory.hxx>
#include <svl/stritem.hxx>

#include <unotools/tempfile.hxx>
#include <comphelper/storagehelper.hxx>

#define CALC_DEBUG_OUTPUT 0
#define TEST_BUG_FILES 0

#include "helper/qahelper.hxx"

#include "docsh.hxx"
#include "postit.hxx"
#include "patattr.hxx"
#include "scitems.hxx"
#include "document.hxx"
#include "cellform.hxx"

#define ODS_FORMAT_TYPE 50331943
#define XLS_FORMAT_TYPE 318767171
#define XLSX_FORMAT_TYPE 268959811
#define LOTUS123_FORMAT_TYPE 268435649

#define ODS     0
#define XLS     1
#define XLSX    2
#define LOTUS123 3

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;

namespace {

struct FileFormat {
    const char* pName; const char* pFilterName; const char* pTypeName; unsigned int nFormatType;
};

FileFormat aFileFormats[] = {
    { "ods" , "calc8", "", ODS_FORMAT_TYPE },
    { "xls" , "MS Excel 97", "calc_MS_EXCEL_97", XLS_FORMAT_TYPE },
    { "xlsx", "Calc MS Excel 2007 XML" , "MS Excel 2007 XML", XLSX_FORMAT_TYPE },
    { "123" , "Lotus", "calc_Lotus", LOTUS123_FORMAT_TYPE }
};

}

class ScExportTest : public test::BootstrapFixture
{
public:
    ScExportTest();

    virtual void setUp();
    virtual void tearDown();

    ScDocShellRef saveAndReload( ScDocShell*, const rtl::OUString&, const rtl::OUString&, const rtl::OUString&, sal_uLong );
    ScDocShellRef saveAndReloadPassword( ScDocShell*, const rtl::OUString&, const rtl::OUString&, const rtl::OUString&, sal_uLong );

    void test();
    void testPasswordExport();

    CPPUNIT_TEST_SUITE(ScExportTest);
    CPPUNIT_TEST(test);
#if !defined(MACOSX) && !defined(DRAGONFLY) && !defined(WNT)
    CPPUNIT_TEST(testPasswordExport);
#endif
    CPPUNIT_TEST_SUITE_END();

private:
    rtl::OUString m_aBaseString;
    uno::Reference<uno::XInterface> m_xCalcComponent;

    ScDocShellRef saveAndReload( ScDocShell* pShell, sal_Int32 nFormat );
};

/*
void ScFiltersTest::createFileURL(const rtl::OUString& aFileBase, const rtl::OUString& aFileExtension, rtl::OUString& rFilePath)
{
    rtl::OUString aSep(RTL_CONSTASCII_USTRINGPARAM("/"));
    rtl::OUStringBuffer aBuffer( getSrcRootURL() );
    aBuffer.append(m_aBaseString).append(aSep).append(aFileExtension);
    aBuffer.append(aSep).append(aFileBase).append(aFileExtension);
    rFilePath = aBuffer.makeStringAndClear();
}
*/

ScDocShellRef ScExportTest::saveAndReloadPassword(ScDocShell* pShell, const rtl::OUString &rFilter,
    const rtl::OUString &rUserData, const rtl::OUString& rTypeName, sal_uLong nFormatType)
{

    utl::TempFile aTempFile;
    aTempFile.EnableKillingFile();
    SfxMedium aStoreMedium( aTempFile.GetURL(), STREAM_STD_WRITE );
    sal_uInt32 nExportFormat = 0;
    if (nFormatType)
        nExportFormat = SFX_FILTER_EXPORT | SFX_FILTER_USESOPTIONS;
    SfxFilter* pExportFilter = new SfxFilter(
        rFilter,
        rtl::OUString(), nFormatType, nExportFormat, rTypeName, 0, rtl::OUString(),
        rUserData, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("private:factory/scalc*")) );
    pExportFilter->SetVersion(SOFFICE_FILEFORMAT_CURRENT);
    aStoreMedium.SetFilter(pExportFilter);
    SfxItemSet* pExportSet = aStoreMedium.GetItemSet();
    uno::Sequence< beans::NamedValue > aEncryptionData = comphelper::OStorageHelper::CreatePackageEncryptionData( rtl::OUString("test") );
    uno::Any xEncryptionData;
    xEncryptionData <<= aEncryptionData;
    pExportSet->Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, xEncryptionData));

    uno::Reference< embed::XStorage > xMedStorage = aStoreMedium.GetStorage();
    ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( xMedStorage, aEncryptionData );

    pShell->DoSaveAs( aStoreMedium );
    pShell->DoClose();

    //std::cout << "File: " << aTempFile.GetURL() << std::endl;

    sal_uInt32 nFormat = 0;
    if (nFormatType)
        nFormat = SFX_FILTER_IMPORT | SFX_FILTER_USESOPTIONS;
    SfxFilter* pFilter = new SfxFilter(
        rFilter,
        rtl::OUString(), nFormatType, nFormat, rTypeName, 0, rtl::OUString(),
        rUserData, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("private:factory/scalc*")) );
    pFilter->SetVersion(SOFFICE_FILEFORMAT_CURRENT);

    ScDocShellRef xDocShRef = new ScDocShell;
    SfxMedium* pSrcMed = new SfxMedium(aTempFile.GetURL(), STREAM_STD_READ);
    SfxItemSet* pSet = pSrcMed->GetItemSet();
    pSet->Put(SfxStringItem(SID_PASSWORD, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("test"))));
    pSrcMed->SetFilter(pFilter);
    if (!xDocShRef->DoLoad(pSrcMed))
    {
        xDocShRef->DoClose();
        // load failed.
        xDocShRef.Clear();
    }

    return xDocShRef;
}

ScDocShellRef ScExportTest::saveAndReload(ScDocShell* pShell, const rtl::OUString &rFilter,
    const rtl::OUString &rUserData, const rtl::OUString& rTypeName, sal_uLong nFormatType)
{

    utl::TempFile aTempFile;
    aTempFile.EnableKillingFile();
    SfxMedium aStoreMedium( aTempFile.GetURL(), STREAM_STD_WRITE );
    sal_uInt32 nExportFormat = 0;
    if (nFormatType)
        nExportFormat = SFX_FILTER_EXPORT | SFX_FILTER_USESOPTIONS;
    SfxFilter* pExportFilter = new SfxFilter(
        rFilter,
        rtl::OUString(), nFormatType, nExportFormat, rTypeName, 0, rtl::OUString(),
        rUserData, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("private:factory/scalc*")) );
    pExportFilter->SetVersion(SOFFICE_FILEFORMAT_CURRENT);
    aStoreMedium.SetFilter(pExportFilter);
    pShell->DoSaveAs( aStoreMedium );
    pShell->DoClose();

    //std::cout << "File: " << aTempFile.GetURL() << std::endl;

    sal_uInt32 nFormat = 0;
    if (nFormatType)
        nFormat = SFX_FILTER_IMPORT | SFX_FILTER_USESOPTIONS;
    SfxFilter* pFilter = new SfxFilter(
        rFilter,
        rtl::OUString(), nFormatType, nFormat, rTypeName, 0, rtl::OUString(),
        rUserData, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("private:factory/scalc*")) );
    pFilter->SetVersion(SOFFICE_FILEFORMAT_CURRENT);

    ScDocShellRef xDocShRef = new ScDocShell;
    SfxMedium* pSrcMed = new SfxMedium(aTempFile.GetURL(), STREAM_STD_READ);
    pSrcMed->SetFilter(pFilter);
    if (!xDocShRef->DoLoad(pSrcMed))
    {
        xDocShRef->DoClose();
        // load failed.
        xDocShRef.Clear();
    }

    return xDocShRef;
}

ScDocShellRef ScExportTest::saveAndReload( ScDocShell* pShell, sal_Int32 nFormat )
{
    rtl::OUString aFileExtension(aFileFormats[nFormat].pName, strlen(aFileFormats[nFormat].pName), RTL_TEXTENCODING_UTF8 );
    rtl::OUString aFilterName(aFileFormats[nFormat].pFilterName, strlen(aFileFormats[nFormat].pFilterName), RTL_TEXTENCODING_UTF8) ;
    rtl::OUString aFilterType(aFileFormats[nFormat].pTypeName, strlen(aFileFormats[nFormat].pTypeName), RTL_TEXTENCODING_UTF8);
    ScDocShellRef xDocSh = saveAndReload(pShell, aFilterName, rtl::OUString(), aFilterType, aFileFormats[nFormat].nFormatType);

    CPPUNIT_ASSERT(xDocSh.Is());
    return xDocSh;
}

void ScExportTest::test()
{
    ScDocShell* pShell = new ScDocShell(
        SFXMODEL_STANDARD |
        SFXMODEL_DISABLE_EMBEDDED_SCRIPTS |
        SFXMODEL_DISABLE_DOCUMENT_RECOVERY);
    pShell->DoInitNew();

    ScDocument* pDoc = pShell->GetDocument();

    pDoc->SetValue(0,0,0, 1.0);
    CPPUNIT_ASSERT(pDoc);

    ScDocShellRef xDocSh = saveAndReload( pShell, ODS );

    CPPUNIT_ASSERT(xDocSh.Is());
    ScDocument* pLoadedDoc = xDocSh->GetDocument();
    double aVal = pLoadedDoc->GetValue(0,0,0);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(aVal, 1.0, 1e-8);
}

void ScExportTest::testPasswordExport()
{
    ScDocShell* pShell = new ScDocShell(
        SFXMODEL_STANDARD |
        SFXMODEL_DISABLE_EMBEDDED_SCRIPTS |
        SFXMODEL_DISABLE_DOCUMENT_RECOVERY);
    pShell->DoInitNew();

    ScDocument* pDoc = pShell->GetDocument();

    pDoc->SetValue(0,0,0, 1.0);
    CPPUNIT_ASSERT(pDoc);

    sal_Int32 nFormat = ODS;
    rtl::OUString aFileExtension(aFileFormats[nFormat].pName, strlen(aFileFormats[nFormat].pName), RTL_TEXTENCODING_UTF8 );
    rtl::OUString aFilterName(aFileFormats[nFormat].pFilterName, strlen(aFileFormats[nFormat].pFilterName), RTL_TEXTENCODING_UTF8) ;
    rtl::OUString aFilterType(aFileFormats[nFormat].pTypeName, strlen(aFileFormats[nFormat].pTypeName), RTL_TEXTENCODING_UTF8);
    ScDocShellRef xDocSh = saveAndReloadPassword(pShell, aFilterName, rtl::OUString(), aFilterType, aFileFormats[nFormat].nFormatType);

    CPPUNIT_ASSERT(xDocSh.Is());
    ScDocument* pLoadedDoc = xDocSh->GetDocument();
    double aVal = pLoadedDoc->GetValue(0,0,0);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(aVal, 1.0, 1e-8);
}

ScExportTest::ScExportTest()
      : m_aBaseString(RTL_CONSTASCII_USTRINGPARAM("/sc/qa/unit/data"))
{
}

void ScExportTest::setUp()
{
    test::BootstrapFixture::setUp();

    // This is a bit of a fudge, we do this to ensure that ScGlobals::ensure,
    // which is a private symbol to us, gets called
    m_xCalcComponent =
        getMultiServiceFactory()->createInstance(rtl::OUString(
        RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.Calc.SpreadsheetDocument")));
    CPPUNIT_ASSERT_MESSAGE("no calc component!", m_xCalcComponent.is());
}

void ScExportTest::tearDown()
{
    uno::Reference< lang::XComponent >( m_xCalcComponent, UNO_QUERY_THROW )->dispose();
    test::BootstrapFixture::tearDown();
}

CPPUNIT_TEST_SUITE_REGISTRATION(ScExportTest);

CPPUNIT_PLUGIN_IMPLEMENT();

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>add initial xlsx cond format export test<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <sal/config.h>
#include <unotest/filters-test.hxx>
#include <test/bootstrapfixture.hxx>
#include <rtl/strbuf.hxx>
#include <osl/file.hxx>

#include <sfx2/app.hxx>
#include <sfx2/docfilt.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/frame.hxx>
#include <sfx2/sfxmodelfactory.hxx>
#include <svl/stritem.hxx>

#include <unotools/tempfile.hxx>
#include <comphelper/storagehelper.hxx>

#define CALC_DEBUG_OUTPUT 0
#define TEST_BUG_FILES 0

#include "helper/qahelper.hxx"

#include "docsh.hxx"
#include "postit.hxx"
#include "patattr.hxx"
#include "scitems.hxx"
#include "document.hxx"
#include "cellform.hxx"

#define ODS_FORMAT_TYPE 50331943
#define XLS_FORMAT_TYPE 318767171
#define XLSX_FORMAT_TYPE 268959811
#define LOTUS123_FORMAT_TYPE 268435649

#define ODS     0
#define XLS     1
#define XLSX    2
#define LOTUS123 3

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;

namespace {

struct FileFormat {
    const char* pName; const char* pFilterName; const char* pTypeName; unsigned int nFormatType;
};

FileFormat aFileFormats[] = {
    { "ods" , "calc8", "", ODS_FORMAT_TYPE },
    { "xls" , "MS Excel 97", "calc_MS_EXCEL_97", XLS_FORMAT_TYPE },
    { "xlsx", "Calc MS Excel 2007 XML" , "MS Excel 2007 XML", XLSX_FORMAT_TYPE },
    { "123" , "Lotus", "calc_Lotus", LOTUS123_FORMAT_TYPE }
};

}

class ScExportTest : public test::BootstrapFixture
{
public:
    ScExportTest();

    virtual void setUp();
    virtual void tearDown();

    ScDocShellRef saveAndReload( ScDocShell*, const rtl::OUString&, const rtl::OUString&, const rtl::OUString&, sal_uLong );
    ScDocShellRef saveAndReloadPassword( ScDocShell*, const rtl::OUString&, const rtl::OUString&, const rtl::OUString&, sal_uLong );

    void test();
    void testPasswordExport();
    void testConditionalFormatExportXLSX();

    CPPUNIT_TEST_SUITE(ScExportTest);
    CPPUNIT_TEST(test);
#if !defined(MACOSX) && !defined(DRAGONFLY) && !defined(WNT)
    CPPUNIT_TEST(testPasswordExport);
#endif
    CPPUNIT_TEST(testConditionalFormatExportXLSX);
    CPPUNIT_TEST_SUITE_END();

private:
    rtl::OUString m_aBaseString;
    uno::Reference<uno::XInterface> m_xCalcComponent;

    ScDocShellRef saveAndReload( ScDocShell* pShell, sal_Int32 nFormat );
    ScDocShellRef loadDocument( const rtl::OUString& rFileNameBase, sal_Int32 nFormat );
    void createFileURL( const rtl::OUString& aFileBase, const rtl::OUString& rFileExtension, rtl::OUString& rFilePath);
    void createCSVPath(const rtl::OUString& rFileBase, rtl::OUString& rCSVPath);
};

void ScExportTest::createFileURL(const rtl::OUString& aFileBase, const rtl::OUString& aFileExtension, rtl::OUString& rFilePath)
{
    rtl::OUString aSep("/");
    rtl::OUStringBuffer aBuffer( getSrcRootURL() );
    aBuffer.append(m_aBaseString).append(aSep).append(aFileExtension);
    aBuffer.append(aSep).append(aFileBase).append(aFileExtension);
    rFilePath = aBuffer.makeStringAndClear();
}

void ScExportTest::createCSVPath(const rtl::OUString& aFileBase, rtl::OUString& rCSVPath)
{
    rtl::OUStringBuffer aBuffer(getSrcRootPath());
    aBuffer.append(m_aBaseString).append(rtl::OUString("/contentCSV/"));
    aBuffer.append(aFileBase).append(rtl::OUString("csv"));
    rCSVPath = aBuffer.makeStringAndClear();
}

ScDocShellRef ScExportTest::saveAndReloadPassword(ScDocShell* pShell, const rtl::OUString &rFilter,
    const rtl::OUString &rUserData, const rtl::OUString& rTypeName, sal_uLong nFormatType)
{

    utl::TempFile aTempFile;
    aTempFile.EnableKillingFile();
    SfxMedium aStoreMedium( aTempFile.GetURL(), STREAM_STD_WRITE );
    sal_uInt32 nExportFormat = 0;
    if (nFormatType)
        nExportFormat = SFX_FILTER_EXPORT | SFX_FILTER_USESOPTIONS;
    SfxFilter* pExportFilter = new SfxFilter(
        rFilter,
        rtl::OUString(), nFormatType, nExportFormat, rTypeName, 0, rtl::OUString(),
        rUserData, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("private:factory/scalc*")) );
    pExportFilter->SetVersion(SOFFICE_FILEFORMAT_CURRENT);
    aStoreMedium.SetFilter(pExportFilter);
    SfxItemSet* pExportSet = aStoreMedium.GetItemSet();
    uno::Sequence< beans::NamedValue > aEncryptionData = comphelper::OStorageHelper::CreatePackageEncryptionData( rtl::OUString("test") );
    uno::Any xEncryptionData;
    xEncryptionData <<= aEncryptionData;
    pExportSet->Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, xEncryptionData));

    uno::Reference< embed::XStorage > xMedStorage = aStoreMedium.GetStorage();
    ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( xMedStorage, aEncryptionData );

    pShell->DoSaveAs( aStoreMedium );
    pShell->DoClose();

    //std::cout << "File: " << aTempFile.GetURL() << std::endl;

    sal_uInt32 nFormat = 0;
    if (nFormatType)
        nFormat = SFX_FILTER_IMPORT | SFX_FILTER_USESOPTIONS;
    SfxFilter* pFilter = new SfxFilter(
        rFilter,
        rtl::OUString(), nFormatType, nFormat, rTypeName, 0, rtl::OUString(),
        rUserData, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("private:factory/scalc*")) );
    pFilter->SetVersion(SOFFICE_FILEFORMAT_CURRENT);

    ScDocShellRef xDocShRef = new ScDocShell;
    SfxMedium* pSrcMed = new SfxMedium(aTempFile.GetURL(), STREAM_STD_READ);
    SfxItemSet* pSet = pSrcMed->GetItemSet();
    pSet->Put(SfxStringItem(SID_PASSWORD, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("test"))));
    pSrcMed->SetFilter(pFilter);
    if (!xDocShRef->DoLoad(pSrcMed))
    {
        xDocShRef->DoClose();
        // load failed.
        xDocShRef.Clear();
    }

    return xDocShRef;
}

ScDocShellRef ScExportTest::saveAndReload(ScDocShell* pShell, const rtl::OUString &rFilter,
    const rtl::OUString &rUserData, const rtl::OUString& rTypeName, sal_uLong nFormatType)
{

    utl::TempFile aTempFile;
    aTempFile.EnableKillingFile();
    SfxMedium aStoreMedium( aTempFile.GetURL(), STREAM_STD_WRITE );
    sal_uInt32 nExportFormat = 0;
    if (nFormatType)
        nExportFormat = SFX_FILTER_EXPORT | SFX_FILTER_USESOPTIONS;
    SfxFilter* pExportFilter = new SfxFilter(
        rFilter,
        rtl::OUString(), nFormatType, nExportFormat, rTypeName, 0, rtl::OUString(),
        rUserData, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("private:factory/scalc*")) );
    pExportFilter->SetVersion(SOFFICE_FILEFORMAT_CURRENT);
    aStoreMedium.SetFilter(pExportFilter);
    pShell->DoSaveAs( aStoreMedium );
    pShell->DoClose();

    //std::cout << "File: " << aTempFile.GetURL() << std::endl;

    sal_uInt32 nFormat = 0;
    if (nFormatType)
        nFormat = SFX_FILTER_IMPORT | SFX_FILTER_USESOPTIONS;
    SfxFilter* pFilter = new SfxFilter(
        rFilter,
        rtl::OUString(), nFormatType, nFormat, rTypeName, 0, rtl::OUString(),
        rUserData, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("private:factory/scalc*")) );
    pFilter->SetVersion(SOFFICE_FILEFORMAT_CURRENT);

    ScDocShellRef xDocShRef = new ScDocShell;
    SfxMedium* pSrcMed = new SfxMedium(aTempFile.GetURL(), STREAM_STD_READ);
    pSrcMed->SetFilter(pFilter);
    if (!xDocShRef->DoLoad(pSrcMed))
    {
        xDocShRef->DoClose();
        // load failed.
        xDocShRef.Clear();
    }

    return xDocShRef;
}

ScDocShellRef ScExportTest::saveAndReload( ScDocShell* pShell, sal_Int32 nFormat )
{
    rtl::OUString aFileExtension(aFileFormats[nFormat].pName, strlen(aFileFormats[nFormat].pName), RTL_TEXTENCODING_UTF8 );
    rtl::OUString aFilterName(aFileFormats[nFormat].pFilterName, strlen(aFileFormats[nFormat].pFilterName), RTL_TEXTENCODING_UTF8) ;
    rtl::OUString aFilterType(aFileFormats[nFormat].pTypeName, strlen(aFileFormats[nFormat].pTypeName), RTL_TEXTENCODING_UTF8);
    ScDocShellRef xDocSh = saveAndReload(pShell, aFilterName, rtl::OUString(), aFilterType, aFileFormats[nFormat].nFormatType);

    CPPUNIT_ASSERT(xDocSh.Is());
    return xDocSh;
}

ScDocShellRef ScExportTest::loadDocument(const rtl::OUString& rFileName, sal_Int32 nFormat)
{
    rtl::OUString aFileExtension(aFileFormats[nFormat].pName, strlen(aFileFormats[nFormat].pName), RTL_TEXTENCODING_UTF8 );
    rtl::OUString aFilterName(aFileFormats[nFormat].pFilterName, strlen(aFileFormats[nFormat].pFilterName), RTL_TEXTENCODING_UTF8) ;
    rtl::OUString aFileName;
    createFileURL( rFileName, aFileExtension, aFileName );
    rtl::OUString aFilterType(aFileFormats[nFormat].pTypeName, strlen(aFileFormats[nFormat].pTypeName), RTL_TEXTENCODING_UTF8);
    unsigned int nFormatType = aFileFormats[nFormat].nFormatType;
    unsigned int nClipboardId = nFormatType ? SFX_FILTER_IMPORT | SFX_FILTER_USESOPTIONS : 0;

    SfxFilter* pFilter = new SfxFilter(
        aFilterName,
        rtl::OUString(), nFormatType, nClipboardId, aFilterType, 0, rtl::OUString(),
        rtl::OUString(), rtl::OUString("private:factory/scalc*") );
    pFilter->SetVersion(SOFFICE_FILEFORMAT_CURRENT);

    ScDocShellRef xDocShRef = new ScDocShell;
    SfxMedium* pSrcMed = new SfxMedium(aFileName, STREAM_STD_READ);
    pSrcMed->SetFilter(pFilter);
    if (!xDocShRef->DoLoad(pSrcMed))
    {
        xDocShRef->DoClose();
        // load failed.
        xDocShRef.Clear();
    }

    return xDocShRef;
}

void ScExportTest::test()
{
    ScDocShell* pShell = new ScDocShell(
        SFXMODEL_STANDARD |
        SFXMODEL_DISABLE_EMBEDDED_SCRIPTS |
        SFXMODEL_DISABLE_DOCUMENT_RECOVERY);
    pShell->DoInitNew();

    ScDocument* pDoc = pShell->GetDocument();

    pDoc->SetValue(0,0,0, 1.0);
    CPPUNIT_ASSERT(pDoc);

    ScDocShellRef xDocSh = saveAndReload( pShell, ODS );

    CPPUNIT_ASSERT(xDocSh.Is());
    ScDocument* pLoadedDoc = xDocSh->GetDocument();
    double aVal = pLoadedDoc->GetValue(0,0,0);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(aVal, 1.0, 1e-8);
}

void ScExportTest::testPasswordExport()
{
    ScDocShell* pShell = new ScDocShell(
        SFXMODEL_STANDARD |
        SFXMODEL_DISABLE_EMBEDDED_SCRIPTS |
        SFXMODEL_DISABLE_DOCUMENT_RECOVERY);
    pShell->DoInitNew();

    ScDocument* pDoc = pShell->GetDocument();

    pDoc->SetValue(0,0,0, 1.0);
    CPPUNIT_ASSERT(pDoc);

    sal_Int32 nFormat = ODS;
    rtl::OUString aFileExtension(aFileFormats[nFormat].pName, strlen(aFileFormats[nFormat].pName), RTL_TEXTENCODING_UTF8 );
    rtl::OUString aFilterName(aFileFormats[nFormat].pFilterName, strlen(aFileFormats[nFormat].pFilterName), RTL_TEXTENCODING_UTF8) ;
    rtl::OUString aFilterType(aFileFormats[nFormat].pTypeName, strlen(aFileFormats[nFormat].pTypeName), RTL_TEXTENCODING_UTF8);
    ScDocShellRef xDocSh = saveAndReloadPassword(pShell, aFilterName, rtl::OUString(), aFilterType, aFileFormats[nFormat].nFormatType);

    CPPUNIT_ASSERT(xDocSh.Is());
    ScDocument* pLoadedDoc = xDocSh->GetDocument();
    double aVal = pLoadedDoc->GetValue(0,0,0);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(aVal, 1.0, 1e-8);
}

void ScExportTest::testConditionalFormatExportXLSX()
{
    ScDocShellRef xShell = loadDocument("new_cond_format_test.", XLSX);
    CPPUNIT_ASSERT(xShell.Is());

    ScDocShellRef xDocSh = saveAndReload(&(*xShell), XLSX);
    CPPUNIT_ASSERT(xDocSh.Is());
    ScDocument* pDoc = xDocSh->GetDocument();
    rtl::OUString aCSVFile("new_cond_format_test.");
    rtl::OUString aCSVPath;
    createCSVPath( aCSVFile, aCSVPath );
    testCondFile(aCSVPath, pDoc, 0);
}

ScExportTest::ScExportTest()
      : m_aBaseString(RTL_CONSTASCII_USTRINGPARAM("/sc/qa/unit/data"))
{
}

void ScExportTest::setUp()
{
    test::BootstrapFixture::setUp();

    // This is a bit of a fudge, we do this to ensure that ScGlobals::ensure,
    // which is a private symbol to us, gets called
    m_xCalcComponent =
        getMultiServiceFactory()->createInstance(rtl::OUString(
        RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.Calc.SpreadsheetDocument")));
    CPPUNIT_ASSERT_MESSAGE("no calc component!", m_xCalcComponent.is());
}

void ScExportTest::tearDown()
{
    uno::Reference< lang::XComponent >( m_xCalcComponent, UNO_QUERY_THROW )->dispose();
    test::BootstrapFixture::tearDown();
}

CPPUNIT_TEST_SUITE_REGISTRATION(ScExportTest);

CPPUNIT_PLUGIN_IMPLEMENT();

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>#include "gtest/gtest.h"
#include "gmock/gmock.h"

#include <Arduino.h>
#include <Wire.h>
#include <functional>

#define MOCK_GLOBAL_METHOD(RETURN_TYPE, FUNCTION_NAME) \
template< typename... Args>\
RETURN_TYPE FUNCTION_NAME (Args... args)\
{\
  return mockPointer->FUNCTION_NAME(args...);\
}\

#define REAL_MOCK_IMPLEMENTATION(MockName)\
struct MockName##_Implementation\

REAL_MOCK_IMPLEMENTATION(Serial)
{
  MOCK_METHOD0(foo, int() );
  MOCK_METHOD1(foo, int(int) );
  MOCK_METHOD2(foo, int(int, int) );
  MOCK_METHOD0(boo, int() );
  MOCK_METHOD1(boo, int(int) );
  MOCK_METHOD2(boo, int(int, int) );
};

#define GLOBAL_MOCK_IMPLEMENTATION(MockName)\
struct MockName##_PointerClass\
{\
  MockName##_Implementation* mockPointer = nullptr;\
};\
struct MockName##_GlobalObjectMethodImplementation : public MockName##_PointerClass\

GLOBAL_MOCK_IMPLEMENTATION(Serial)
{
  MOCK_GLOBAL_METHOD(int, foo)
  MOCK_GLOBAL_METHOD(int, boo)
};

//#define GLOBAL_MOCK_DEFINISIONS(MockName)
class MockGlobalObject : public Serial_GlobalObjectMethodImplementation
{
  MockGlobalObject& operator=(const MockGlobalObject& other) = delete;
  MockGlobalObject(const MockGlobalObject& other) = delete;

public:
  MockGlobalObject(){}

  static MockGlobalObject& getGlobalInstanceReference()
  {
    static MockGlobalObject globalInstance;
    return globalInstance;
  }

};

class MockLocalObject : public Serial_Implementation
{

  MockGlobalObject& globalObj = MockGlobalObject::getGlobalInstanceReference();

public:
  MockLocalObject()
  {
    EXPECT_EQ(nullptr, globalObj.mockPointer)
      << "Are you trying to instantiate multiple local mock objects in one scope?";
    globalObj.mockPointer = this;
  }

  ~MockLocalObject()
  {
    EXPECT_NE(nullptr, globalObj.mockPointer);
    globalObj.mockPointer = nullptr;
  }
};

using ::testing::Return;
using ::testing::_;

auto& globalObj = MockGlobalObject::getGlobalInstanceReference();

TEST(SomeMock, FirstTest)
{
  MockLocalObject obj;

  EXPECT_CALL(obj, foo()).WillOnce(Return(1) );
  EXPECT_CALL(obj, foo(2)).WillOnce(Return(2) );
  EXPECT_CALL(obj, foo(3, 3)).WillOnce(Return(3) );
  EXPECT_CALL(obj, boo(3, 3)).WillOnce(Return(4) );

  EXPECT_EQ(1, globalObj.foo());
  EXPECT_EQ(2, globalObj.foo(2));
  EXPECT_EQ(3, globalObj.foo(3, 3));
  EXPECT_EQ(4, globalObj.boo(3, 3));
}
TEST(SomeMock, SecondTest)
{
  MockLocalObject obj;

  EXPECT_CALL(obj, foo()).WillOnce(Return(1) );
  EXPECT_CALL(obj, foo(2)).WillOnce(Return(2) );
  EXPECT_CALL(obj, foo(3, 3)).WillOnce(Return(3) );

  EXPECT_EQ(1, globalObj.foo());
  EXPECT_EQ(2, globalObj.foo(2));
  EXPECT_EQ(3, globalObj.foo(3, 3));
}

<commit_msg>Refactoring, got working with some defines<commit_after>#include "gtest/gtest.h"
#include "gmock/gmock.h"

#include <Arduino.h>
#include <Wire.h>
#include <functional>

#define MOCK_GLOBAL_METHOD(RETURN_TYPE, FUNCTION_NAME) \
template< typename... Args>\
RETURN_TYPE FUNCTION_NAME (Args... args)\
{\
  return mockPointer->FUNCTION_NAME(args...);\
}\

#define REAL_MOCK_IMPLEMENTATION(MockName)\
struct MockName##_Implementation\

REAL_MOCK_IMPLEMENTATION(Serial)
{
  MOCK_METHOD0(foo, int() );
  MOCK_METHOD1(foo, int(int) );
  MOCK_METHOD2(foo, int(int, int) );
  MOCK_METHOD0(boo, int() );
  MOCK_METHOD1(boo, int(int) );
  MOCK_METHOD2(boo, int(int, int) );
};

#define GLOBAL_MOCK_IMPLEMENTATION(MockName)\
struct MockName##_PointerClass\
{\
  MockName##_Implementation* mockPointer = nullptr;\
};\
struct MockName##_GlobalObjectMethodImplementation : public MockName##_PointerClass\

GLOBAL_MOCK_IMPLEMENTATION(Serial)
{
  MOCK_GLOBAL_METHOD(int, foo)
  MOCK_GLOBAL_METHOD(int, boo)
};

#define GLOBAL_MOCK_DEFINISIONS(MockName)\
class MockName##_GlobalObject : public Serial_GlobalObjectMethodImplementation\
{\
  MockName##_GlobalObject& operator=(const MockName##_GlobalObject& other) = delete;\
  MockName##_GlobalObject(const MockName##_GlobalObject& other) = delete;\
\
public:\
  MockName##_GlobalObject(){}\
\
  static MockName##_GlobalObject& getGlobalInstanceReference()\
  {\
    static MockName##_GlobalObject globalInstance;\
    return globalInstance;\
  }\
};\
class MockName##_LocalObject : public Serial_Implementation\
{\
  MockName##_GlobalObject& globalObj = MockName##_GlobalObject::getGlobalInstanceReference();\
\
public:\
  MockName##_LocalObject()\
  {\
    EXPECT_EQ(nullptr, globalObj.mockPointer)\
      << "Are you trying to instantiate multiple local mock objects in one scope?";\
    globalObj.mockPointer = this;\
  }\
\
  ~MockName##_LocalObject()\
  {\
    EXPECT_NE(nullptr, globalObj.mockPointer);\
    globalObj.mockPointer = nullptr;\
  }\
};\
auto& MockName = MockName##_GlobalObject::getGlobalInstanceReference()\

GLOBAL_MOCK_DEFINISIONS(Serial);

using ::testing::Return;
using ::testing::_;

TEST(SomeMock, FirstTest)
{
  Serial_LocalObject obj;

  EXPECT_CALL(obj, foo()).WillOnce(Return(1) );
  EXPECT_CALL(obj, foo(2)).WillOnce(Return(2) );
  EXPECT_CALL(obj, foo(3, 3)).WillOnce(Return(3) );
  EXPECT_CALL(obj, boo(3, 3)).WillOnce(Return(4) );

  EXPECT_EQ(1, Serial.foo());
  EXPECT_EQ(2, Serial.foo(2));
  EXPECT_EQ(3, Serial.foo(3, 3));
  EXPECT_EQ(4, Serial.boo(3, 3));
}

TEST(SomeMock, SecondTest)
{
  Serial_LocalObject obj;

  EXPECT_CALL(obj, foo()).WillOnce(Return(1) );
  EXPECT_CALL(obj, foo(2)).WillOnce(Return(2) );
  EXPECT_CALL(obj, foo(3, 3)).WillOnce(Return(3) );

  EXPECT_EQ(1, Serial.foo());
  EXPECT_EQ(2, Serial.foo(2));
  EXPECT_EQ(3, Serial.foo(3, 3));
}

<|endoftext|>


<commit_before>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2014 Torus Knot Software Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/

#include "OgreGLES2FrameBufferObject.h"
#include "OgreGLES2HardwarePixelBuffer.h"
#include "OgreGLES2FBORenderTexture.h"
#include "OgreGLES2DepthBuffer.h"
#include "OgreGLUtil.h"
#include "OgreRoot.h"
#include "OgreGLES2RenderSystem.h"
#include "OgreGLES2Support.h"

namespace Ogre {

//-----------------------------------------------------------------------------
    GLES2FrameBufferObject::GLES2FrameBufferObject(GLES2FBOManager *manager, uint fsaa):
        mManager(manager), mNumSamples(fsaa)
    {
        // Generate framebuffer object
        OGRE_CHECK_GL_ERROR(glGenFramebuffers(1, &mFB));

	   if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_DEBUG))
       {
           OGRE_CHECK_GL_ERROR(glLabelObjectEXT(GL_FRAMEBUFFER, mFB, 0, ("FBO #" + StringConverter::toString(mFB)).c_str()));
       }

        mNumSamples = std::min(mNumSamples, manager->getMaxFSAASamples());

        // Will we need a second FBO to do multisampling?
        if (mNumSamples)
        {
            OGRE_CHECK_GL_ERROR(glGenFramebuffers(1, &mMultisampleFB));
            if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_DEBUG))
            {
                OGRE_CHECK_GL_ERROR(glLabelObjectEXT(GL_FRAMEBUFFER, mMultisampleFB, 0, ("MSAA FBO #" + StringConverter::toString(mMultisampleFB)).c_str()));
            }
        }
        else
        {
            mMultisampleFB = 0;
        }

        // Initialise state
        mDepth.buffer = 0;
        mStencil.buffer = 0;
        for(size_t x = 0; x < OGRE_MAX_MULTIPLE_RENDER_TARGETS; ++x)
        {
            mColour[x].buffer=0;
        }
    }

    GLES2FrameBufferObject::~GLES2FrameBufferObject()
    {
        mManager->releaseRenderBuffer(mDepth);
        mManager->releaseRenderBuffer(mStencil);
        mManager->releaseRenderBuffer(mMultisampleColourBuffer);
        // Delete framebuffer object
        OGRE_CHECK_GL_ERROR(glDeleteFramebuffers(1, &mFB));

        if (mMultisampleFB)
            OGRE_CHECK_GL_ERROR(glDeleteFramebuffers(1, &mMultisampleFB));
    }

#if OGRE_PLATFORM == OGRE_PLATFORM_ANDROID || OGRE_PLATFORM == OGRE_PLATFORM_EMSCRIPTEN
    void GLES2FrameBufferObject::notifyOnContextLost()
    {
        mManager->releaseRenderBuffer(mDepth);
        mManager->releaseRenderBuffer(mStencil);
        mManager->releaseRenderBuffer(mMultisampleColourBuffer);

        OGRE_CHECK_GL_ERROR(glDeleteFramebuffers(1, &mFB));

        if (mMultisampleFB)
            OGRE_CHECK_GL_ERROR(glDeleteFramebuffers(1, &mMultisampleFB));
    }

    void GLES2FrameBufferObject::notifyOnContextReset(const GLES2SurfaceDesc &target)
    {
        // Generate framebuffer object
        OGRE_CHECK_GL_ERROR(glGenFramebuffers(1, &mFB));
        OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mFB));

        // Bind target to surface 0 and initialise
        bindSurface(0, target);
    }
#endif

    void GLES2FrameBufferObject::bindSurface(size_t attachment, const GLES2SurfaceDesc &target)
    {
        assert(attachment < OGRE_MAX_MULTIPLE_RENDER_TARGETS);
        mColour[attachment] = target;
        // Re-initialise
        if(mColour[0].buffer)
            initialise();
    }

    void GLES2FrameBufferObject::unbindSurface(size_t attachment)
    {
        assert(attachment < OGRE_MAX_MULTIPLE_RENDER_TARGETS);
        mColour[attachment].buffer = 0;
        // Re-initialise if buffer 0 still bound
        if(mColour[0].buffer)
            initialise();
    }

    void GLES2FrameBufferObject::initialise()
    {
        // Release depth and stencil, if they were bound
        mManager->releaseRenderBuffer(mDepth);
        mManager->releaseRenderBuffer(mStencil);
        mManager->releaseRenderBuffer(mMultisampleColourBuffer);
        // First buffer must be bound
        if(!mColour[0].buffer)
        {
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
                "Attachment 0 must have surface attached",
                "GLES2FrameBufferObject::initialise");
        }

        // If we're doing multisampling, then we need another FBO which contains a
        // renderbuffer which is set up to multisample, and we'll blit it to the final
        // FBO afterwards to perform the multisample resolve. In that case, the
        // mMultisampleFB is bound during rendering and is the one with a depth/stencil

        // Store basic stats
        uint32 width = mColour[0].buffer->getWidth();
        uint32 height = mColour[0].buffer->getHeight();
        GLuint format = mColour[0].buffer->getGLFormat();
        ushort maxSupportedMRTs = Root::getSingleton().getRenderSystem()->getCapabilities()->getNumMultiRenderTargets();

        // Bind simple buffer to add colour attachments
        OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mFB));

        // Bind all attachment points to frame buffer
        for(unsigned int x = 0; x < maxSupportedMRTs; ++x)
        {
            if(mColour[x].buffer)
            {
                if(mColour[x].buffer->getWidth() != width || mColour[x].buffer->getHeight() != height)
                {
                    StringStream ss;
                    ss << "Attachment " << x << " has incompatible size ";
                    ss << mColour[x].buffer->getWidth() << "x" << mColour[x].buffer->getHeight();
                    ss << ". It must be of the same as the size of surface 0, ";
                    ss << width << "x" << height;
                    ss << ".";
                    OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, ss.str(), "GLES2FrameBufferObject::initialise");
                }
                if(mColour[x].buffer->getGLFormat() != format)
                {
                    StringStream ss;
                    ss << "Attachment " << x << " has incompatible format.";
                    OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, ss.str(), "GLES2FrameBufferObject::initialise");
                }
                if(getFormat() == PF_DEPTH)
                    mColour[x].buffer->bindToFramebuffer(GL_DEPTH_ATTACHMENT, mColour[x].zoffset);
                else
                    mColour[x].buffer->bindToFramebuffer(GL_COLOR_ATTACHMENT0+x, mColour[x].zoffset);
            }
            else
            {
                // Detach
                OGRE_CHECK_GL_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, static_cast<GLenum>(GL_COLOR_ATTACHMENT0+x), GL_RENDERBUFFER, 0));
            }
        }

        // Now deal with depth / stencil
        if (mMultisampleFB)
        {
            // Bind multisample buffer
            OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mMultisampleFB));

            // Create AA render buffer (colour)
            // note, this can be shared too because we blit it to the final FBO
            // right after the render is finished
            mMultisampleColourBuffer = mManager->requestRenderBuffer(format, width, height, mNumSamples);

            // Attach it, because we won't be attaching below and non-multisample has
            // actually been attached to other FBO
            mMultisampleColourBuffer.buffer->bindToFramebuffer(GL_COLOR_ATTACHMENT0,
                mMultisampleColourBuffer.zoffset);

            // depth & stencil will be dealt with below
        }

        // Depth buffer is not handled here anymore.
        // See GLES2FrameBufferObject::attachDepthBuffer() & RenderSystem::setDepthBufferFor()

#if OGRE_NO_GLES3_SUPPORT == 0
        GLenum bufs[OGRE_MAX_MULTIPLE_RENDER_TARGETS];
        GLsizei n=0;
        for(unsigned int x=0; x<maxSupportedMRTs; ++x)
        {
            // Fill attached colour buffers
            if(mColour[x].buffer)
            {
                if(getFormat() == PF_DEPTH)
                    bufs[x] = GL_DEPTH_ATTACHMENT;
                else
                    bufs[x] = GL_COLOR_ATTACHMENT0 + x;
                // Keep highest used buffer + 1
                n = x+1;
            }
            else
            {
                bufs[x] = GL_NONE;
            }
        }

        // Drawbuffer extension supported, use it
        if(getFormat() != PF_DEPTH)
            OGRE_CHECK_GL_ERROR(glDrawBuffers(n, bufs));

        if (mMultisampleFB)
        {
            // we need a read buffer because we'll be blitting to mFB
            OGRE_CHECK_GL_ERROR(glReadBuffer(bufs[0]));
        }
        else
        {
            // No read buffer, by default, if we want to read anyway we must not forget to set this.
            OGRE_CHECK_GL_ERROR(glReadBuffer(GL_NONE));
        }
#endif
        // Check status
        GLuint status;
        OGRE_CHECK_GL_ERROR(status = glCheckFramebufferStatus(GL_FRAMEBUFFER));

        // Bind main buffer
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS
        // The screen buffer is 1 on iOS
        OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 1));
#else
        OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0));
#endif

        switch(status)
        {
        case GL_FRAMEBUFFER_COMPLETE:
            // All is good
            break;
        case GL_FRAMEBUFFER_UNSUPPORTED:
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
                "All framebuffer formats with this texture internal format unsupported",
                "GLES2FrameBufferObject::initialise");
        default:
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
                "Framebuffer incomplete or other FBO status error",
                "GLES2FrameBufferObject::initialise");
        }

    }

    void GLES2FrameBufferObject::bind()
    {
        // Bind it to FBO
        const GLuint fb = mMultisampleFB ? mMultisampleFB : mFB;
        OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, fb));
    }

    void GLES2FrameBufferObject::swapBuffers()
    {
        if (mMultisampleFB)
        {
#if OGRE_NO_GLES3_SUPPORT == 0
            GLint oldfb = 0;
            OGRE_CHECK_GL_ERROR(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb));

            // Blit from multisample buffer to final buffer, triggers resolve
            uint32 width = mColour[0].buffer->getWidth();
            uint32 height = mColour[0].buffer->getHeight();
            OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, mMultisampleFB));
            OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFB));
            OGRE_CHECK_GL_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST));
            // Unbind
            OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, oldfb));
#endif
        }
    }

    void GLES2FrameBufferObject::attachDepthBuffer( DepthBuffer *depthBuffer )
    {
        GLES2DepthBuffer *glDepthBuffer = static_cast<GLES2DepthBuffer*>(depthBuffer);
        OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mMultisampleFB ? mMultisampleFB : mFB ));

        if( glDepthBuffer )
        {
            GLES2RenderBuffer *depthBuf   = glDepthBuffer->getDepthBuffer();
            GLES2RenderBuffer *stencilBuf = glDepthBuffer->getStencilBuffer();

            //Attach depth buffer, if it has one.
            if( depthBuf )
                depthBuf->bindToFramebuffer( GL_DEPTH_ATTACHMENT, 0 );

            //Attach stencil buffer, if it has one.
            if( stencilBuf )
                stencilBuf->bindToFramebuffer( GL_STENCIL_ATTACHMENT, 0 );
        }
        else
        {
            OGRE_CHECK_GL_ERROR(glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                                                          GL_RENDERBUFFER, 0));
            OGRE_CHECK_GL_ERROR(glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
                                                          GL_RENDERBUFFER, 0));
        }
    }
    //-----------------------------------------------------------------------------
    void GLES2FrameBufferObject::detachDepthBuffer()
    {
        OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mMultisampleFB ? mMultisampleFB : mFB ));
        OGRE_CHECK_GL_ERROR(glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0 ));
        OGRE_CHECK_GL_ERROR(glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
                                                      GL_RENDERBUFFER, 0 ));
    }

    uint32 GLES2FrameBufferObject::getWidth()
    {
        assert(mColour[0].buffer);
        return mColour[0].buffer->getWidth();
    }
    uint32 GLES2FrameBufferObject::getHeight()
    {
        assert(mColour[0].buffer);
        return mColour[0].buffer->getHeight();
    }
    PixelFormat GLES2FrameBufferObject::getFormat()
    {
        assert(mColour[0].buffer);
        return mColour[0].buffer->getFormat();
    }
    GLsizei GLES2FrameBufferObject::getFSAA()
    {
        return mNumSamples;
    }
//-----------------------------------------------------------------------------
}
<commit_msg>[GLES2] Call glLabelObjectEXT after binding to avoid GL_INVALID_OPERATION on iOS<commit_after>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2014 Torus Knot Software Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/

#include "OgreGLES2FrameBufferObject.h"
#include "OgreGLES2HardwarePixelBuffer.h"
#include "OgreGLES2FBORenderTexture.h"
#include "OgreGLES2DepthBuffer.h"
#include "OgreGLUtil.h"
#include "OgreRoot.h"
#include "OgreGLES2RenderSystem.h"
#include "OgreGLES2Support.h"

namespace Ogre {

//-----------------------------------------------------------------------------
    GLES2FrameBufferObject::GLES2FrameBufferObject(GLES2FBOManager *manager, uint fsaa):
        mManager(manager), mNumSamples(fsaa)
    {
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS
        GLint oldfb = 0;
        OGRE_CHECK_GL_ERROR(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb));
#endif

        // Generate framebuffer object
        OGRE_CHECK_GL_ERROR(glGenFramebuffers(1, &mFB));

	   if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_DEBUG))
       {
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS
           OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mFB)); // to avoid GL_INVALID_OPERATION in glLabelObjectEXT(GL_FRAMEBUFFER,...) on iOS
#endif
           OGRE_CHECK_GL_ERROR(glLabelObjectEXT(GL_FRAMEBUFFER, mFB, 0, ("FBO #" + StringConverter::toString(mFB)).c_str()));
       }

        mNumSamples = std::min(mNumSamples, manager->getMaxFSAASamples());

        // Will we need a second FBO to do multisampling?
        if (mNumSamples)
        {
            OGRE_CHECK_GL_ERROR(glGenFramebuffers(1, &mMultisampleFB));
            if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_DEBUG))
            {
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS
                OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mMultisampleFB)); // to avoid GL_INVALID_OPERATION in glLabelObjectEXT(GL_FRAMEBUFFER,...) on iOS
#endif
                OGRE_CHECK_GL_ERROR(glLabelObjectEXT(GL_FRAMEBUFFER, mMultisampleFB, 0, ("MSAA FBO #" + StringConverter::toString(mMultisampleFB)).c_str()));
            }
        }
        else
        {
            mMultisampleFB = 0;
        }

#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS
        OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, oldfb));
#endif

        // Initialise state
        mDepth.buffer = 0;
        mStencil.buffer = 0;
        for(size_t x = 0; x < OGRE_MAX_MULTIPLE_RENDER_TARGETS; ++x)
        {
            mColour[x].buffer=0;
        }
    }

    GLES2FrameBufferObject::~GLES2FrameBufferObject()
    {
        mManager->releaseRenderBuffer(mDepth);
        mManager->releaseRenderBuffer(mStencil);
        mManager->releaseRenderBuffer(mMultisampleColourBuffer);
        // Delete framebuffer object
        OGRE_CHECK_GL_ERROR(glDeleteFramebuffers(1, &mFB));

        if (mMultisampleFB)
            OGRE_CHECK_GL_ERROR(glDeleteFramebuffers(1, &mMultisampleFB));
    }

#if OGRE_PLATFORM == OGRE_PLATFORM_ANDROID || OGRE_PLATFORM == OGRE_PLATFORM_EMSCRIPTEN
    void GLES2FrameBufferObject::notifyOnContextLost()
    {
        mManager->releaseRenderBuffer(mDepth);
        mManager->releaseRenderBuffer(mStencil);
        mManager->releaseRenderBuffer(mMultisampleColourBuffer);

        OGRE_CHECK_GL_ERROR(glDeleteFramebuffers(1, &mFB));

        if (mMultisampleFB)
            OGRE_CHECK_GL_ERROR(glDeleteFramebuffers(1, &mMultisampleFB));
    }

    void GLES2FrameBufferObject::notifyOnContextReset(const GLES2SurfaceDesc &target)
    {
        // Generate framebuffer object
        OGRE_CHECK_GL_ERROR(glGenFramebuffers(1, &mFB));
        OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mFB));

        // Bind target to surface 0 and initialise
        bindSurface(0, target);
    }
#endif

    void GLES2FrameBufferObject::bindSurface(size_t attachment, const GLES2SurfaceDesc &target)
    {
        assert(attachment < OGRE_MAX_MULTIPLE_RENDER_TARGETS);
        mColour[attachment] = target;
        // Re-initialise
        if(mColour[0].buffer)
            initialise();
    }

    void GLES2FrameBufferObject::unbindSurface(size_t attachment)
    {
        assert(attachment < OGRE_MAX_MULTIPLE_RENDER_TARGETS);
        mColour[attachment].buffer = 0;
        // Re-initialise if buffer 0 still bound
        if(mColour[0].buffer)
            initialise();
    }

    void GLES2FrameBufferObject::initialise()
    {
        // Release depth and stencil, if they were bound
        mManager->releaseRenderBuffer(mDepth);
        mManager->releaseRenderBuffer(mStencil);
        mManager->releaseRenderBuffer(mMultisampleColourBuffer);
        // First buffer must be bound
        if(!mColour[0].buffer)
        {
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
                "Attachment 0 must have surface attached",
                "GLES2FrameBufferObject::initialise");
        }

        // If we're doing multisampling, then we need another FBO which contains a
        // renderbuffer which is set up to multisample, and we'll blit it to the final
        // FBO afterwards to perform the multisample resolve. In that case, the
        // mMultisampleFB is bound during rendering and is the one with a depth/stencil

        // Store basic stats
        uint32 width = mColour[0].buffer->getWidth();
        uint32 height = mColour[0].buffer->getHeight();
        GLuint format = mColour[0].buffer->getGLFormat();
        ushort maxSupportedMRTs = Root::getSingleton().getRenderSystem()->getCapabilities()->getNumMultiRenderTargets();

        // Bind simple buffer to add colour attachments
        OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mFB));

        // Bind all attachment points to frame buffer
        for(unsigned int x = 0; x < maxSupportedMRTs; ++x)
        {
            if(mColour[x].buffer)
            {
                if(mColour[x].buffer->getWidth() != width || mColour[x].buffer->getHeight() != height)
                {
                    StringStream ss;
                    ss << "Attachment " << x << " has incompatible size ";
                    ss << mColour[x].buffer->getWidth() << "x" << mColour[x].buffer->getHeight();
                    ss << ". It must be of the same as the size of surface 0, ";
                    ss << width << "x" << height;
                    ss << ".";
                    OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, ss.str(), "GLES2FrameBufferObject::initialise");
                }
                if(mColour[x].buffer->getGLFormat() != format)
                {
                    StringStream ss;
                    ss << "Attachment " << x << " has incompatible format.";
                    OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, ss.str(), "GLES2FrameBufferObject::initialise");
                }
                if(getFormat() == PF_DEPTH)
                    mColour[x].buffer->bindToFramebuffer(GL_DEPTH_ATTACHMENT, mColour[x].zoffset);
                else
                    mColour[x].buffer->bindToFramebuffer(GL_COLOR_ATTACHMENT0+x, mColour[x].zoffset);
            }
            else
            {
                // Detach
                OGRE_CHECK_GL_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, static_cast<GLenum>(GL_COLOR_ATTACHMENT0+x), GL_RENDERBUFFER, 0));
            }
        }

        // Now deal with depth / stencil
        if (mMultisampleFB)
        {
            // Bind multisample buffer
            OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mMultisampleFB));

            // Create AA render buffer (colour)
            // note, this can be shared too because we blit it to the final FBO
            // right after the render is finished
            mMultisampleColourBuffer = mManager->requestRenderBuffer(format, width, height, mNumSamples);

            // Attach it, because we won't be attaching below and non-multisample has
            // actually been attached to other FBO
            mMultisampleColourBuffer.buffer->bindToFramebuffer(GL_COLOR_ATTACHMENT0,
                mMultisampleColourBuffer.zoffset);

            // depth & stencil will be dealt with below
        }

        // Depth buffer is not handled here anymore.
        // See GLES2FrameBufferObject::attachDepthBuffer() & RenderSystem::setDepthBufferFor()

#if OGRE_NO_GLES3_SUPPORT == 0
        GLenum bufs[OGRE_MAX_MULTIPLE_RENDER_TARGETS];
        GLsizei n=0;
        for(unsigned int x=0; x<maxSupportedMRTs; ++x)
        {
            // Fill attached colour buffers
            if(mColour[x].buffer)
            {
                if(getFormat() == PF_DEPTH)
                    bufs[x] = GL_DEPTH_ATTACHMENT;
                else
                    bufs[x] = GL_COLOR_ATTACHMENT0 + x;
                // Keep highest used buffer + 1
                n = x+1;
            }
            else
            {
                bufs[x] = GL_NONE;
            }
        }

        // Drawbuffer extension supported, use it
        if(getFormat() != PF_DEPTH)
            OGRE_CHECK_GL_ERROR(glDrawBuffers(n, bufs));

        if (mMultisampleFB)
        {
            // we need a read buffer because we'll be blitting to mFB
            OGRE_CHECK_GL_ERROR(glReadBuffer(bufs[0]));
        }
        else
        {
            // No read buffer, by default, if we want to read anyway we must not forget to set this.
            OGRE_CHECK_GL_ERROR(glReadBuffer(GL_NONE));
        }
#endif
        // Check status
        GLuint status;
        OGRE_CHECK_GL_ERROR(status = glCheckFramebufferStatus(GL_FRAMEBUFFER));

        // Bind main buffer
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS
        // The screen buffer is 1 on iOS
        OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 1));
#else
        OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0));
#endif

        switch(status)
        {
        case GL_FRAMEBUFFER_COMPLETE:
            // All is good
            break;
        case GL_FRAMEBUFFER_UNSUPPORTED:
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
                "All framebuffer formats with this texture internal format unsupported",
                "GLES2FrameBufferObject::initialise");
        default:
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
                "Framebuffer incomplete or other FBO status error",
                "GLES2FrameBufferObject::initialise");
        }

    }

    void GLES2FrameBufferObject::bind()
    {
        // Bind it to FBO
        const GLuint fb = mMultisampleFB ? mMultisampleFB : mFB;
        OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, fb));
    }

    void GLES2FrameBufferObject::swapBuffers()
    {
        if (mMultisampleFB)
        {
#if OGRE_NO_GLES3_SUPPORT == 0
            GLint oldfb = 0;
            OGRE_CHECK_GL_ERROR(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb));

            // Blit from multisample buffer to final buffer, triggers resolve
            uint32 width = mColour[0].buffer->getWidth();
            uint32 height = mColour[0].buffer->getHeight();
            OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, mMultisampleFB));
            OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFB));
            OGRE_CHECK_GL_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST));
            // Unbind
            OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, oldfb));
#endif
        }
    }

    void GLES2FrameBufferObject::attachDepthBuffer( DepthBuffer *depthBuffer )
    {
        GLES2DepthBuffer *glDepthBuffer = static_cast<GLES2DepthBuffer*>(depthBuffer);
        OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mMultisampleFB ? mMultisampleFB : mFB ));

        if( glDepthBuffer )
        {
            GLES2RenderBuffer *depthBuf   = glDepthBuffer->getDepthBuffer();
            GLES2RenderBuffer *stencilBuf = glDepthBuffer->getStencilBuffer();

            //Attach depth buffer, if it has one.
            if( depthBuf )
                depthBuf->bindToFramebuffer( GL_DEPTH_ATTACHMENT, 0 );

            //Attach stencil buffer, if it has one.
            if( stencilBuf )
                stencilBuf->bindToFramebuffer( GL_STENCIL_ATTACHMENT, 0 );
        }
        else
        {
            OGRE_CHECK_GL_ERROR(glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                                                          GL_RENDERBUFFER, 0));
            OGRE_CHECK_GL_ERROR(glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
                                                          GL_RENDERBUFFER, 0));
        }
    }
    //-----------------------------------------------------------------------------
    void GLES2FrameBufferObject::detachDepthBuffer()
    {
        OGRE_CHECK_GL_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, mMultisampleFB ? mMultisampleFB : mFB ));
        OGRE_CHECK_GL_ERROR(glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0 ));
        OGRE_CHECK_GL_ERROR(glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
                                                      GL_RENDERBUFFER, 0 ));
    }

    uint32 GLES2FrameBufferObject::getWidth()
    {
        assert(mColour[0].buffer);
        return mColour[0].buffer->getWidth();
    }
    uint32 GLES2FrameBufferObject::getHeight()
    {
        assert(mColour[0].buffer);
        return mColour[0].buffer->getHeight();
    }
    PixelFormat GLES2FrameBufferObject::getFormat()
    {
        assert(mColour[0].buffer);
        return mColour[0].buffer->getFormat();
    }
    GLsizei GLES2FrameBufferObject::getFSAA()
    {
        return mNumSamples;
    }
//-----------------------------------------------------------------------------
}
<|endoftext|>


<commit_before><?hh // strict
/**
 * @copyright   2010-2015, The Titon Project
 * @license     http://opensource.org/licenses/bsd-license.php
 * @link        http://titon.io
 */

namespace Titon\Kernel;

use Titon\Event\EmitsEvents;
use Titon\Event\Subject;
use Titon\Kernel\Event\ShutdownEvent;
use Titon\Kernel\Event\StartupEvent;
use Titon\Kernel\Event\TerminateEvent;
use Titon\Kernel\Middleware\Pipeline;

/**
 * The AbstractKernel is the base kernel implementation that handles the middleware pipeline process.
 * All child kernels must implement the `handle()` method to customize the input and output handling process.
 *
 * @package Titon\Kernel
 */
abstract class AbstractKernel<Ta as Application, Ti as Input, To as Output> implements Kernel<Ta, Ti, To>, Subject {
    use EmitsEvents;

    /**
     * The current application.
     *
     * @var \Titon\Kernel\Application
     */
    protected Ta $app;

    /**
     * The CLI exit code to terminate with.
     *
     * @var int
     */
    protected int $exitCode = 0;

    /**
     * The contextual input object.
     *
     * @var \Titon\Kernel\Input
     */
    protected ?Ti $input;

    /**
     * The contextual output object.
     *
     * @var \Titon\Kernel\Output
     */
    protected ?To $output;

    /**
     * Pipeline to manage middleware.
     *
     * @var \Titon\Kernel\Middleware\Pipeline
     */
    protected Pipeline<Ti, To> $pipeline;

    /**
     * The time the execution started.
     *
     * @var float
     */
    protected float $startTime;

    /**
     * Instantiate a new application and pipeline.
     *
     * @param \Titon\Kernel\Application $app
     * @param \Titon\Kernel\Middleware\Pipeline $pipeline
     */
    public function __construct(Ta $app, Pipeline<Ti, To> $pipeline) {
        $this->app = $app;
        $this->pipeline = $pipeline;
        $this->startTime = microtime(true);
    }

    /**
     * {@inheritdoc}
     */
    public function getApplication(): Ta {
        return $this->app;
    }

    /**
     * Return the total time it took to execute up to this point.
     *
     * @return float
     */
    public function getExecutionTime(): float {
        return microtime(true) - $this->getStartTime();
    }

    /**
     * {@inheritdoc}
     */
    public function getInput(): ?Ti {
        return $this->input;
    }

    /**
     * {@inheritdoc}
     */
    public function getOutput(): ?To {
        return $this->output;
    }

    /**
     * Return the execution start time.
     *
     * @return float
     */
    public function getStartTime(): float {
        return $this->startTime;
    }

    /**
     * {@inheritdoc}
     */
    public function pipe(Middleware<Ti, To> $middleware): this {
        $this->pipeline->through($middleware);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    final public function run(Ti $input, To $output): To {
        $this->input = $input;
        $this->output = $output;

        // Emit startup events
        $this->startup();

        // Since the kernel itself is middleware, add the kernel as the last item in the pipeline.
        // This allows the `handle()` method to be ran after all other middleware.
        $this->pipe($this);

        // Handle the middleware pipeline stack
        $this->output = $output = $this->pipeline->handle($input, $output);

        // Emit shutdown events
        $this->shutdown();

        return $output;
    }

    /**
     * {@inheritdoc}
     */
    public function terminate(): void {
        $input = $this->getInput();
        $output = $this->getOutput();

        invariant($input !== null && $output !== null, 'Input and Output must not be null.');

        $this->emit(new TerminateEvent($this, $input, $output));

        exit($this->exitCode);
    }

    /**
     * Triggered after the pipeline is handled but before the output is sent.
     */
    protected function shutdown(): void {
        $input = $this->getInput();
        $output = $this->getOutput();

        invariant($input !== null && $output !== null, 'Input and Output must not be null.');

        $this->emit(new ShutdownEvent($this, $input, $output));
    }

    /**
     * Triggered before the pipeline is handled.
     */
    protected function startup(): void {
        $input = $this->getInput();
        $output = $this->getOutput();

        invariant($input !== null && $output !== null, 'Input and Output must not be null.');

        $this->emit(new StartupEvent($this, $input, $output));
    }

}
<commit_msg>Made the kernel pipeline optional.<commit_after><?hh // strict
/**
 * @copyright   2010-2015, The Titon Project
 * @license     http://opensource.org/licenses/bsd-license.php
 * @link        http://titon.io
 */

namespace Titon\Kernel;

use Titon\Event\EmitsEvents;
use Titon\Event\Subject;
use Titon\Kernel\Event\ShutdownEvent;
use Titon\Kernel\Event\StartupEvent;
use Titon\Kernel\Event\TerminateEvent;
use Titon\Kernel\Middleware\Pipeline;

/**
 * The AbstractKernel is the base kernel implementation that handles the middleware pipeline process.
 * All child kernels must implement the `handle()` method to customize the input and output handling process.
 *
 * @package Titon\Kernel
 */
abstract class AbstractKernel<Ta as Application, Ti as Input, To as Output> implements Kernel<Ta, Ti, To>, Subject {
    use EmitsEvents;

    /**
     * The current application.
     *
     * @var \Titon\Kernel\Application
     */
    protected Ta $app;

    /**
     * The CLI exit code to terminate with.
     *
     * @var int
     */
    protected int $exitCode = 0;

    /**
     * The contextual input object.
     *
     * @var \Titon\Kernel\Input
     */
    protected ?Ti $input;

    /**
     * The contextual output object.
     *
     * @var \Titon\Kernel\Output
     */
    protected ?To $output;

    /**
     * Pipeline to manage middleware.
     *
     * @var \Titon\Kernel\Middleware\Pipeline
     */
    protected Pipeline<Ti, To> $pipeline;

    /**
     * The time the execution started.
     *
     * @var float
     */
    protected float $startTime;

    /**
     * Instantiate a new application and pipeline.
     *
     * @param \Titon\Kernel\Application $app
     * @param \Titon\Kernel\Middleware\Pipeline $pipeline
     */
    public function __construct(Ta $app, Pipeline<Ti, To> $pipeline = null) {
        if ($pipeline === null) {
            $pipeline = new Pipeline();
        }

        $this->app = $app;
        $this->pipeline = $pipeline;
        $this->startTime = microtime(true);
    }

    /**
     * {@inheritdoc}
     */
    public function getApplication(): Ta {
        return $this->app;
    }

    /**
     * Return the total time it took to execute up to this point.
     *
     * @return float
     */
    public function getExecutionTime(): float {
        return microtime(true) - $this->getStartTime();
    }

    /**
     * {@inheritdoc}
     */
    public function getInput(): ?Ti {
        return $this->input;
    }

    /**
     * {@inheritdoc}
     */
    public function getOutput(): ?To {
        return $this->output;
    }

    /**
     * Return the execution start time.
     *
     * @return float
     */
    public function getStartTime(): float {
        return $this->startTime;
    }

    /**
     * {@inheritdoc}
     */
    public function pipe(Middleware<Ti, To> $middleware): this {
        $this->pipeline->through($middleware);

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    final public function run(Ti $input, To $output): To {
        $this->input = $input;
        $this->output = $output;

        // Emit startup events
        $this->startup();

        // Since the kernel itself is middleware, add the kernel as the last item in the pipeline.
        // This allows the `handle()` method to be ran after all other middleware.
        $this->pipe($this);

        // Handle the middleware pipeline stack
        $this->output = $output = $this->pipeline->handle($input, $output);

        // Emit shutdown events
        $this->shutdown();

        return $output;
    }

    /**
     * {@inheritdoc}
     */
    public function terminate(): void {
        $input = $this->getInput();
        $output = $this->getOutput();

        invariant($input !== null && $output !== null, 'Input and Output must not be null.');

        $this->emit(new TerminateEvent($this, $input, $output));

        exit($this->exitCode);
    }

    /**
     * Triggered after the pipeline is handled but before the output is sent.
     */
    protected function shutdown(): void {
        $input = $this->getInput();
        $output = $this->getOutput();

        invariant($input !== null && $output !== null, 'Input and Output must not be null.');

        $this->emit(new ShutdownEvent($this, $input, $output));
    }

    /**
     * Triggered before the pipeline is handled.
     */
    protected function startup(): void {
        $input = $this->getInput();
        $output = $this->getOutput();

        invariant($input !== null && $output !== null, 'Input and Output must not be null.');

        $this->emit(new StartupEvent($this, $input, $output));
    }

}
<|endoftext|>


<commit_before>//
//  CACheckbox.cpp
//  CrossApp
//
//  Created by Li Yuanfeng on 14-3-23.
//  Copyright (c) 2014 http://9miao.com All rights reserved.
//

#include "CACheckbox.h"
#include "view/CAScale9ImageView.h"
#include "view/CAView.h"
#include "view/CAScrollView.h"
#include "dispatcher/CATouch.h"
#include "basics/CAPointExtension.h"
#include "cocoa/CCSet.h"
#include "view/CALabel.h"
#include "basics/CAApplication.h"
#include "basics/CAScheduler.h"
#include "animation/CAViewAnimation.h"
#include "support/CAThemeManager.h"
#include "support/ccUtils.h"
NS_CC_BEGIN

CACheckbox::CACheckbox()
:m_pImageView(nullptr)
,m_pLabel(nullptr)
,m_sTitleFontName("")
,m_fTitleFontSize(0)
,m_bTitleBold(false)
,m_pTitleLabelSize(DSizeZero)
,m_bDefineTitleLabelSize(false)
,m_pImageSize(DSizeZero)
,m_bDefineImageSize(false)
,m_pTitleOffset(DSizeZero)
,m_bDefineTitleOffset(false)
,m_pImageOffset(DSizeZero)
,m_bDefineImageOffset(false)
,m_pImageNormal(nullptr)
,m_pImageSelected(nullptr)
,m_cImageColorNormal(CAColor_white)
,m_cImageColorSelected(CAColor_white)
,m_cTitleColorNormal(CAColor_white)
,m_cTitleColorSelected(CAColor_white)
,m_bIsOn(false)
{
    m_pImageView = new CAImageView();
    m_pImageView->init();
    this->insertSubview(m_pImageView, 1);

    m_pLabel = new CALabel();
    m_pLabel->init();
    m_pLabel->setTextAlignment(CATextAlignment::Left);
    m_pLabel->setVerticalTextAlignmet(CAVerticalTextAlignment::Center);
    m_pLabel->setNumberOfLine(1);
    this->insertSubview(m_pLabel, 1);
}

CACheckbox::~CACheckbox(void)
{
    CC_SAFE_RELEASE_NULL(m_pImageView);
    CC_SAFE_RELEASE_NULL(m_pLabel);
    CC_SAFE_RELEASE_NULL(m_pImageNormal);
    CC_SAFE_RELEASE_NULL(m_pImageSelected);
}

CACheckbox* CACheckbox::create()
{

    CACheckbox* btn = new CACheckbox();

    if (btn && btn->init())
    {
        btn->autorelease();
        return btn;
    }

    CC_SAFE_DELETE(btn);
    return NULL;
}

CACheckbox* CACheckbox::createWithFrame(const DRect& rect)
{
    CACheckbox* btn = new CACheckbox();

    if (btn && btn->initWithFrame(rect))
    {
        btn->autorelease();
        return btn;
    }

    CC_SAFE_DELETE(btn);
    return NULL;
}

CACheckbox* CACheckbox::createWithCenter(const DRect& rect)
{
    CACheckbox* btn = new CACheckbox();

    if (btn && btn->initWithCenter(rect))
    {
        btn->autorelease();
        return btn;
    }

    CC_SAFE_DELETE(btn);
    return NULL;
}

CACheckbox* CACheckbox::createWithLayout(const CrossApp::DLayout &layout)
{
    CACheckbox* btn = new CACheckbox();

    if (btn && btn->initWithLayout(layout))
    {
        btn->autorelease();
        return btn;
    }

    CC_SAFE_DELETE(btn);
    return NULL;
}

bool CACheckbox::init()
{
    if (!CAControl::init())
    {
        return false;
    }

    return true;
}

void CACheckbox::onExitTransitionDidStart()
{
    CAControl::onExitTransitionDidStart();
}

void CACheckbox::onEnterTransitionDidFinish()
{
    CAControl::onEnterTransitionDidFinish();

    this->updateCheckboxState();
}

void CACheckbox::setImageStateNormal(CAImage* var)
{
    CC_SAFE_RETAIN(var);
    CC_SAFE_RETAIN(m_pImageNormal);
    m_pImageNormal = var;

    this->updateCheckboxState();
}

CAImage* CACheckbox::getImageStateNormal()
{
    return m_pImageNormal;
}

void CACheckbox::setImageStateSelected(CAImage* var)
{
    CC_SAFE_RETAIN(var);
    CC_SAFE_RETAIN(m_pImageSelected);
    m_pImageSelected = var;

    this->updateCheckboxState();
}

CAImage* CACheckbox::getImageStateSelected()
{
    return m_pImageSelected;
}

void CACheckbox::setTitleStateNormal(const std::string& var)
{
    m_sTitleNormal = var;

    this->updateCheckboxState();
}

const std::string& CACheckbox::getTitleStateNormal()
{
    return m_sTitleNormal;
}

void CACheckbox::setTitleStateSelected(const std::string& var)
{
    m_sTitleSelected = var;

    this->updateCheckboxState();
}

const std::string& CACheckbox::getTitleStateSelected()
{
    return m_sTitleSelected;
}

void CACheckbox::setImageColorStateNormal(const CAColor4B& var)
{
    m_cImageColorNormal = var;

    this->updateCheckboxState();
}

void CACheckbox::setImageColorStateSelected(const CAColor4B& var)
{
    m_cImageColorSelected = var;

    this->updateCheckboxState();
}

void CACheckbox::setTitleColorStateNormal(const CAColor4B& var)
{
    m_cTitleColorNormal = var;

    this->updateCheckboxState();
}

void CACheckbox::setTitleColorStateSelected(const CAColor4B& var)
{
    m_cTitleColorSelected = var;

    this->updateCheckboxState();
}

void CACheckbox::setTitleFontName(const std::string& var)
{
    if (m_sTitleFontName.compare(var))
    {
        m_sTitleFontName = var;
        m_pLabel->setFontName(m_sTitleFontName.c_str());

        this->updateCheckboxState();
    }
}

void CACheckbox::setImageOffset(const DSize& offset)
{
    m_bDefineImageOffset = true;
    m_pImageOffset = offset;
    DRect rect = m_pImageView->getCenter();
    rect.origin = m_obContentSize/2;
    rect.origin.x += offset.width;
    rect.origin.y += offset.height;
    m_pImageView->setCenter(rect);
}

void CACheckbox::setImageSize(const DSize& size)
{
    m_bDefineImageSize = true;
    m_pImageSize = size;
    DRect rect = m_pImageView->getCenter();
    rect.size = m_pImageSize;
    m_pImageView->setCenter(rect);
}

void CACheckbox::setTitleOffset(const DSize& offset)
{
    m_bDefineTitleOffset = true;
    m_pTitleOffset = offset;
    DRect rect = m_pLabel->getCenter();
    rect.origin = m_obContentSize/2;
    rect.origin.x += offset.width;
    rect.origin.y += offset.height;
    m_pLabel->setCenter(rect);
}

void CACheckbox::setTitleLabelSize(const DSize& size)
{
    m_bDefineTitleLabelSize= true;
    m_pTitleLabelSize = size;
    DRect rect = m_pLabel->getCenter();
    rect.size = m_pTitleLabelSize;
    m_pLabel->setCenter(rect);
}

void CACheckbox::setTitleFontSize(float fontSize)
{
    m_fTitleFontSize = fontSize;
    m_pLabel->setFontSize(m_fTitleFontSize);
}

void CACheckbox::setTitleBold(bool bold)
{
    m_bTitleBold = bold;
    m_pLabel->setBold(bold);
}

void CACheckbox::setTitleTextAlignment(const CATextAlignment& var)
{
    m_pLabel->setTextAlignment(var);
}

void CACheckbox::setTarget(const std::function<void(bool on)>& function)
{
    m_function = function;
}

void CACheckbox::setIsOn(bool on)
{
    if (m_bIsOn != on)
    {
        m_bIsOn = on;
        this->updateCheckboxState();
    }
}

bool CACheckbox::isOn()
{
    return m_bIsOn;
}

void CACheckbox::updateWithPreferredSize()
{
    if (m_fTitleFontSize < FLT_EPSILON)
    {
        m_pLabel->setFontSize(this->getBounds().size.height * 0.667f);
    }
}

bool CACheckbox::ccTouchBegan(CrossApp::CATouch *pTouch, CrossApp::CAEvent *pEvent)
{
    DPoint point = pTouch->getLocation();
    point = this->convertToNodeSpace(point);

    this->setHighlightedState(true);

    return true;
}

void CACheckbox::ccTouchMoved(CrossApp::CATouch *pTouch, CrossApp::CAEvent *pEvent)
{
    DPoint point = pTouch->getLocation();
    point = this->convertToNodeSpace(point);

    if (getBounds().containsPoint(point))
    {
        this->setHighlightedState(true);
    }
    else
    {
        this->setHighlightedState(false);
    }
}

void CACheckbox::ccTouchEnded(CrossApp::CATouch *pTouch, CrossApp::CAEvent *pEvent)
{
    DPoint point = pTouch->getLocation();
    point = this->convertToNodeSpace(point);

    this->setHighlightedState(false);

    bool on = !m_bIsOn;

    if (getBounds().containsPoint(point))
    {
        on = !m_bIsOn;
    }
    else
    {
        on = m_bIsOn;
    }

    if (on != m_bIsOn)
    {
        this->setIsOn(on);
        if (m_function)
        {
            m_function(m_bIsOn);
        }
    }
}

void CACheckbox::ccTouchCancelled(CrossApp::CATouch *pTouch, CrossApp::CAEvent *pEvent)
{
    this->setHighlightedState(false);
}

void CACheckbox::setHighlightedState(bool var)
{
    CAColor4B imageColor = m_bIsOn ? m_cImageColorSelected : m_cImageColorNormal;
    CAColor4B titleColor = m_bIsOn ? m_cTitleColorSelected : m_cTitleColorNormal;

    if (var)
    {
        imageColor = ccc4Mult(imageColor, 0.9f);
        titleColor = ccc4Mult(titleColor, 0.9f);
    }

    m_pImageView->setColor(imageColor);
    m_pLabel->setColor(titleColor);
}

void CACheckbox::updateCheckboxState()
{
    CC_RETURN_IF(!m_bRunning);

    DRect imageViewCenter = DRectZero;
    DRect rect = DRectZero;
    DRect labelCenter = this->getBounds();
    float labelSize = 0;

    CAImage* image = (m_bIsOn && m_pImageSelected) ? m_pImageSelected : m_pImageNormal;
    const std::string& title = (m_bIsOn && !m_sTitleSelected.empty()) ? m_sTitleSelected : m_sTitleNormal;

    if (image && title.length() == 0)
    {
        DSize size = this->getBounds().size;
        DSize iSize = image->getContentSize();
        float scaleX = size.width / iSize.width * 0.8f;
        float scaleY = size.height / iSize.height * 0.8f;
        float scale = MIN(scaleX, scaleY);
        iSize = ccpMult(iSize, scale);

        imageViewCenter.origin = size / 2;
        imageViewCenter.size = iSize;
    }
    else if (!image && title.length() > 0)
    {
        labelSize = m_obContentSize.height * 0.8f;
        labelCenter.size = m_obContentSize;
        labelCenter.origin.x = m_obContentSize.width * 0.5f;
        labelCenter.origin.y = m_obContentSize.height * 0.425f;
    }
    else if (image && title.length() > 0)
    {
        DSize size = m_obContentSize;
        DSize iSize = image->getContentSize();
        float scaleX = size.width / iSize.width * 0.8f;
        float scaleY = size.height / iSize.height * 0.8f;
        float scale = MIN(scaleX, scaleY);
        iSize = ccpMult(iSize, scale);

        imageViewCenter.size = iSize;
        imageViewCenter.origin.x = iSize.width * 0.5f + 10;
        imageViewCenter.origin.y = size.height * 0.5f;

        labelSize = size.height * 0.8f;
        labelCenter.size.width = m_obContentSize.width - iSize.width - 30;
        labelCenter.size.height = m_obContentSize.height;
        labelCenter.origin.x = iSize.width + 20 + labelCenter.size.width /2;
        labelCenter.origin.y = size.height * 0.425f;
    }

    if (!title.empty())
    {
        if (m_bDefineTitleLabelSize)
        {
            labelCenter.size = m_pTitleLabelSize;
        }

        if(m_bDefineTitleOffset)
        {
            labelCenter.origin = ccpMult(m_obContentSize, 0.5f);
            labelCenter.origin = ccpAdd(labelCenter.origin, m_pTitleOffset);
        }
        m_pLabel->setCenter(labelCenter);

        if(m_fTitleFontSize == 0)
        {
            m_fTitleFontSize = labelSize;
        }
        m_pLabel->setFontSize(m_fTitleFontSize);

        m_pLabel->setColor(m_bIsOn ? m_cTitleColorSelected : m_cTitleColorNormal);
    }

    if (strcmp(title.c_str(), m_pLabel->getText().c_str()))
    {
        m_pLabel->setText(title.c_str());
    }

    if (image)
    {
        m_pImageView->setColor(m_bIsOn ? m_cImageColorSelected : m_cImageColorNormal);

        if (m_bDefineImageSize)
        {
            imageViewCenter.size = m_pImageSize;
        }
        if (m_bDefineImageOffset)
        {
            imageViewCenter.origin = ccpMult(m_obContentSize, 0.5f);
            imageViewCenter.origin = ccpAdd(imageViewCenter.origin, m_pImageOffset);
        }
        m_pImageView->setCenter(imageViewCenter);
    }

    if (image != m_pImageView->getImage())
    {
        m_pImageView->setImage(image);
    }
}

void CACheckbox::setContentSize(const DSize & var)
{
    DSize size = var;
//    if (m_bRecSpe)
//    {
//        const CAThemeManager::stringMap& map = CAApplication::getApplication()->getThemeManager()->getThemeMap("");
//        int h = atoi(map.at("height").c_str());
//        size.height = (h == 0) ? size.height : h;
//    }
    CAView::setContentSize(size);

    this->updateWithPreferredSize();
    this->updateCheckboxState();
}

NS_CC_END
<commit_msg>no message<commit_after>//
//  CACheckbox.cpp
//  CrossApp
//
//  Created by Li Yuanfeng on 14-3-23.
//  Copyright (c) 2014 http://9miao.com All rights reserved.
//

#include "CACheckbox.h"
#include "view/CAScale9ImageView.h"
#include "view/CAView.h"
#include "view/CAScrollView.h"
#include "dispatcher/CATouch.h"
#include "basics/CAPointExtension.h"
#include "cocoa/CCSet.h"
#include "view/CALabel.h"
#include "basics/CAApplication.h"
#include "basics/CAScheduler.h"
#include "animation/CAViewAnimation.h"
#include "support/CAThemeManager.h"
#include "support/ccUtils.h"
NS_CC_BEGIN

CACheckbox::CACheckbox()
:m_pImageView(nullptr)
,m_pLabel(nullptr)
,m_sTitleFontName("")
,m_fTitleFontSize(0)
,m_bTitleBold(false)
,m_pTitleLabelSize(DSizeZero)
,m_bDefineTitleLabelSize(false)
,m_pImageSize(DSizeZero)
,m_bDefineImageSize(false)
,m_pTitleOffset(DSizeZero)
,m_bDefineTitleOffset(false)
,m_pImageOffset(DSizeZero)
,m_bDefineImageOffset(false)
,m_pImageNormal(nullptr)
,m_pImageSelected(nullptr)
,m_cImageColorNormal(CAColor_white)
,m_cImageColorSelected(CAColor_white)
,m_cTitleColorNormal(CAColor_white)
,m_cTitleColorSelected(CAColor_white)
,m_bIsOn(false)
{
    m_pImageView = new CAImageView();
    m_pImageView->init();
    this->insertSubview(m_pImageView, 1);

    m_pLabel = new CALabel();
    m_pLabel->init();
    m_pLabel->setTextAlignment(CATextAlignment::Left);
    m_pLabel->setVerticalTextAlignmet(CAVerticalTextAlignment::Center);
    m_pLabel->setNumberOfLine(1);
    this->insertSubview(m_pLabel, 1);

    const CAThemeManager::stringMap& map = CAApplication::getApplication()->getThemeManager()->getThemeMap("CACheckbox");
    this->setImageStateNormal(CAImage::create(map.at("checkbox_image_normal")));
    this->setImageStateSelected(CAImage::create(map.at("checkbox_image_selected")));
}

CACheckbox::~CACheckbox(void)
{
    CC_SAFE_RELEASE_NULL(m_pImageView);
    CC_SAFE_RELEASE_NULL(m_pLabel);
    CC_SAFE_RELEASE_NULL(m_pImageNormal);
    CC_SAFE_RELEASE_NULL(m_pImageSelected);
}

CACheckbox* CACheckbox::create()
{

    CACheckbox* btn = new CACheckbox();

    if (btn && btn->init())
    {
        btn->autorelease();
        return btn;
    }

    CC_SAFE_DELETE(btn);
    return NULL;
}

CACheckbox* CACheckbox::createWithFrame(const DRect& rect)
{
    CACheckbox* btn = new CACheckbox();

    if (btn && btn->initWithFrame(rect))
    {
        btn->autorelease();
        return btn;
    }

    CC_SAFE_DELETE(btn);
    return NULL;
}

CACheckbox* CACheckbox::createWithCenter(const DRect& rect)
{
    CACheckbox* btn = new CACheckbox();

    if (btn && btn->initWithCenter(rect))
    {
        btn->autorelease();
        return btn;
    }

    CC_SAFE_DELETE(btn);
    return NULL;
}

CACheckbox* CACheckbox::createWithLayout(const CrossApp::DLayout &layout)
{
    CACheckbox* btn = new CACheckbox();

    if (btn && btn->initWithLayout(layout))
    {
        btn->autorelease();
        return btn;
    }

    CC_SAFE_DELETE(btn);
    return NULL;
}

bool CACheckbox::init()
{
    if (!CAControl::init())
    {
        return false;
    }

    return true;
}

void CACheckbox::onExitTransitionDidStart()
{
    CAControl::onExitTransitionDidStart();
}

void CACheckbox::onEnterTransitionDidFinish()
{
    CAControl::onEnterTransitionDidFinish();

    this->updateCheckboxState();
}

void CACheckbox::setImageStateNormal(CAImage* var)
{
    CC_SAFE_RETAIN(var);
    CC_SAFE_RETAIN(m_pImageNormal);
    m_pImageNormal = var;

    this->updateCheckboxState();
}

CAImage* CACheckbox::getImageStateNormal()
{
    return m_pImageNormal;
}

void CACheckbox::setImageStateSelected(CAImage* var)
{
    CC_SAFE_RETAIN(var);
    CC_SAFE_RETAIN(m_pImageSelected);
    m_pImageSelected = var;

    this->updateCheckboxState();
}

CAImage* CACheckbox::getImageStateSelected()
{
    return m_pImageSelected;
}

void CACheckbox::setTitleStateNormal(const std::string& var)
{
    m_sTitleNormal = var;

    this->updateCheckboxState();
}

const std::string& CACheckbox::getTitleStateNormal()
{
    return m_sTitleNormal;
}

void CACheckbox::setTitleStateSelected(const std::string& var)
{
    m_sTitleSelected = var;

    this->updateCheckboxState();
}

const std::string& CACheckbox::getTitleStateSelected()
{
    return m_sTitleSelected;
}

void CACheckbox::setImageColorStateNormal(const CAColor4B& var)
{
    m_cImageColorNormal = var;

    this->updateCheckboxState();
}

void CACheckbox::setImageColorStateSelected(const CAColor4B& var)
{
    m_cImageColorSelected = var;

    this->updateCheckboxState();
}

void CACheckbox::setTitleColorStateNormal(const CAColor4B& var)
{
    m_cTitleColorNormal = var;

    this->updateCheckboxState();
}

void CACheckbox::setTitleColorStateSelected(const CAColor4B& var)
{
    m_cTitleColorSelected = var;

    this->updateCheckboxState();
}

void CACheckbox::setTitleFontName(const std::string& var)
{
    if (m_sTitleFontName.compare(var))
    {
        m_sTitleFontName = var;
        m_pLabel->setFontName(m_sTitleFontName.c_str());

        this->updateCheckboxState();
    }
}

void CACheckbox::setImageOffset(const DSize& offset)
{
    m_bDefineImageOffset = true;
    m_pImageOffset = offset;
    DRect rect = m_pImageView->getCenter();
    rect.origin = m_obContentSize/2;
    rect.origin.x += offset.width;
    rect.origin.y += offset.height;
    m_pImageView->setCenter(rect);
}

void CACheckbox::setImageSize(const DSize& size)
{
    m_bDefineImageSize = true;
    m_pImageSize = size;
    DRect rect = m_pImageView->getCenter();
    rect.size = m_pImageSize;
    m_pImageView->setCenter(rect);
}

void CACheckbox::setTitleOffset(const DSize& offset)
{
    m_bDefineTitleOffset = true;
    m_pTitleOffset = offset;
    DRect rect = m_pLabel->getCenter();
    rect.origin = m_obContentSize/2;
    rect.origin.x += offset.width;
    rect.origin.y += offset.height;
    m_pLabel->setCenter(rect);
}

void CACheckbox::setTitleLabelSize(const DSize& size)
{
    m_bDefineTitleLabelSize= true;
    m_pTitleLabelSize = size;
    DRect rect = m_pLabel->getCenter();
    rect.size = m_pTitleLabelSize;
    m_pLabel->setCenter(rect);
}

void CACheckbox::setTitleFontSize(float fontSize)
{
    m_fTitleFontSize = fontSize;
    m_pLabel->setFontSize(m_fTitleFontSize);
}

void CACheckbox::setTitleBold(bool bold)
{
    m_bTitleBold = bold;
    m_pLabel->setBold(bold);
}

void CACheckbox::setTitleTextAlignment(const CATextAlignment& var)
{
    m_pLabel->setTextAlignment(var);
}

void CACheckbox::setTarget(const std::function<void(bool on)>& function)
{
    m_function = function;
}

void CACheckbox::setIsOn(bool on)
{
    if (m_bIsOn != on)
    {
        m_bIsOn = on;
        this->updateCheckboxState();
    }
}

bool CACheckbox::isOn()
{
    return m_bIsOn;
}

void CACheckbox::updateWithPreferredSize()
{
    if (m_fTitleFontSize < FLT_EPSILON)
    {
        m_pLabel->setFontSize(this->getBounds().size.height * 0.667f);
    }
}

bool CACheckbox::ccTouchBegan(CrossApp::CATouch *pTouch, CrossApp::CAEvent *pEvent)
{
    DPoint point = pTouch->getLocation();
    point = this->convertToNodeSpace(point);

    this->setHighlightedState(true);

    return true;
}

void CACheckbox::ccTouchMoved(CrossApp::CATouch *pTouch, CrossApp::CAEvent *pEvent)
{
    DPoint point = pTouch->getLocation();
    point = this->convertToNodeSpace(point);

    if (getBounds().containsPoint(point))
    {
        this->setHighlightedState(true);
    }
    else
    {
        this->setHighlightedState(false);
    }
}

void CACheckbox::ccTouchEnded(CrossApp::CATouch *pTouch, CrossApp::CAEvent *pEvent)
{
    DPoint point = pTouch->getLocation();
    point = this->convertToNodeSpace(point);

    this->setHighlightedState(false);

    bool on = !m_bIsOn;

    if (getBounds().containsPoint(point))
    {
        on = !m_bIsOn;
    }
    else
    {
        on = m_bIsOn;
    }

    if (on != m_bIsOn)
    {
        this->setIsOn(on);
        if (m_function)
        {
            m_function(m_bIsOn);
        }
    }
}

void CACheckbox::ccTouchCancelled(CrossApp::CATouch *pTouch, CrossApp::CAEvent *pEvent)
{
    this->setHighlightedState(false);
}

void CACheckbox::setHighlightedState(bool var)
{
    CAColor4B imageColor = m_bIsOn ? m_cImageColorSelected : m_cImageColorNormal;
    CAColor4B titleColor = m_bIsOn ? m_cTitleColorSelected : m_cTitleColorNormal;

    if (var)
    {
        imageColor = ccc4Mult(imageColor, 0.9f);
        titleColor = ccc4Mult(titleColor, 0.9f);
    }

    m_pImageView->setColor(imageColor);
    m_pLabel->setColor(titleColor);
}

void CACheckbox::updateCheckboxState()
{
    CC_RETURN_IF(!m_bRunning);

    DRect imageViewCenter = DRectZero;
    DRect rect = DRectZero;
    DRect labelCenter = this->getBounds();
    float labelSize = 0;

    CAImage* image = (m_bIsOn && m_pImageSelected) ? m_pImageSelected : m_pImageNormal;
    const std::string& title = (m_bIsOn && !m_sTitleSelected.empty()) ? m_sTitleSelected : m_sTitleNormal;

    if (image && title.length() == 0)
    {
        DSize size = this->getBounds().size;
        DSize iSize = image->getContentSize();
        float scaleX = size.width / iSize.width * 0.8f;
        float scaleY = size.height / iSize.height * 0.8f;
        float scale = MIN(scaleX, scaleY);
        iSize = ccpMult(iSize, scale);

        imageViewCenter.origin = size / 2;
        imageViewCenter.size = iSize;
    }
    else if (!image && title.length() > 0)
    {
        labelSize = m_obContentSize.height * 0.8f;
        labelCenter.size = m_obContentSize;
        labelCenter.origin.x = m_obContentSize.width * 0.5f;
        labelCenter.origin.y = m_obContentSize.height * 0.425f;
    }
    else if (image && title.length() > 0)
    {
        DSize size = m_obContentSize;
        DSize iSize = image->getContentSize();
        float scaleX = size.width / iSize.width * 0.8f;
        float scaleY = size.height / iSize.height * 0.8f;
        float scale = MIN(scaleX, scaleY);
        iSize = ccpMult(iSize, scale);

        imageViewCenter.size = iSize;
        imageViewCenter.origin.x = iSize.width * 0.5f + 10;
        imageViewCenter.origin.y = size.height * 0.5f;

        labelSize = size.height * 0.8f;
        labelCenter.size.width = m_obContentSize.width - iSize.width - 30;
        labelCenter.size.height = m_obContentSize.height;
        labelCenter.origin.x = iSize.width + 20 + labelCenter.size.width /2;
        labelCenter.origin.y = size.height * 0.425f;
    }

    if (!title.empty())
    {
        if (m_bDefineTitleLabelSize)
        {
            labelCenter.size = m_pTitleLabelSize;
        }

        if(m_bDefineTitleOffset)
        {
            labelCenter.origin = ccpMult(m_obContentSize, 0.5f);
            labelCenter.origin = ccpAdd(labelCenter.origin, m_pTitleOffset);
        }
        m_pLabel->setCenter(labelCenter);

        if(m_fTitleFontSize == 0)
        {
            m_fTitleFontSize = labelSize;
        }
        m_pLabel->setFontSize(m_fTitleFontSize);

        m_pLabel->setColor(m_bIsOn ? m_cTitleColorSelected : m_cTitleColorNormal);
    }

    if (strcmp(title.c_str(), m_pLabel->getText().c_str()))
    {
        m_pLabel->setText(title.c_str());
    }

    if (image)
    {
        m_pImageView->setColor(m_bIsOn ? m_cImageColorSelected : m_cImageColorNormal);

        if (m_bDefineImageSize)
        {
            imageViewCenter.size = m_pImageSize;
        }
        if (m_bDefineImageOffset)
        {
            imageViewCenter.origin = ccpMult(m_obContentSize, 0.5f);
            imageViewCenter.origin = ccpAdd(imageViewCenter.origin, m_pImageOffset);
        }
        m_pImageView->setCenter(imageViewCenter);
    }

    if (image != m_pImageView->getImage())
    {
        m_pImageView->setImage(image);
    }
}

void CACheckbox::setContentSize(const DSize & var)
{
    DSize size = var;
//    if (m_bRecSpe)
//    {
//        const CAThemeManager::stringMap& map = CAApplication::getApplication()->getThemeManager()->getThemeMap("");
//        int h = atoi(map.at("height").c_str());
//        size.height = (h == 0) ? size.height : h;
//    }
    CAView::setContentSize(size);

    this->updateWithPreferredSize();
    this->updateCheckboxState();
}

NS_CC_END
<|endoftext|>