<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-2009 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 "TestContext.h"
#include "VisualTest.h"
#include "SamplePlugin.h"
#include "TestResultWriter.h"
#include "HTMLWriter.h"
#include "OgreConfigFile.h"

#include "OgrePlatform.h"
#if OGRE_PLATFORM == OGRE_PLATFORM_SYMBIAN
#include <coecntrl.h>
#endif

#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif

TestContext::TestContext(int argc, char** argv) :mTimestep(0.01f), mBatch(0)
{
    Ogre::UnaryOptionList unOpt;
    Ogre::BinaryOptionList binOpt;

    // prepopulate expected options
    unOpt["-r"] = false;        // generate reference set
    unOpt["--no-html"] = false; // whether or not to generate html
    unOpt["-d"] = false;        // force config dialogue
    binOpt["-m"] = "";          // optional comment
    binOpt["-ts"] = "VTests";   // name of the test set to use
    binOpt["-c"] = "Reference"; // name of batch to compare against
    binOpt["-n"] = "AUTO";      // name for this batch
    binOpt["-rs"] = "SAVED";    // rendersystem to use (default: use name from the config file/dialog)

    // parse
    findCommandLineOpts(argc, argv, unOpt, binOpt);

    mReferenceSet = unOpt["-r"];
    mTestSetName = binOpt["-ts"];
    mComment = binOpt["-m"];
    mGenerateHtml = !unOpt["--no-html"];
    mBatchName = binOpt["-n"];
    mCompareWith = binOpt["-c"];
    mForceConfig = unOpt["-d"];
    mRenderSystemName = binOpt["-rs"];
}
//-----------------------------------------------------------------------

TestContext::~TestContext()
{
    if (mBatch)
        delete mBatch;
}
//-----------------------------------------------------------------------

void TestContext::setup()
{
    // standard setup
    SampleContext::setup();

    // get the path and list of test plugins from the config file
    Ogre::ConfigFile testConfig;
    testConfig.load("tests.cfg");
    mPluginDirectory = testConfig.getSetting("TestFolder");

    Ogre::ConfigFile::SectionIterator sections = testConfig.getSectionIterator();

    // parse for the test sets and plugins that they're made up of
    for (sections; sections.hasMoreElements(); sections.moveNext())
    {
        Ogre::String setName = sections.peekNextKey();
        if (setName != "")
        {
            mTestSets[setName] = Ogre::StringVector();
            Ogre::ConfigFile::SettingsMultiMap::iterator it = sections.peekNextValue()->begin();
            for (it; it != sections.peekNextValue()->end(); ++it)
                mTestSets[setName].push_back(it->second);
        }
    }

    // timestamp for the filename
    char temp[19];
    time_t raw = time(0);
    strftime(temp, 19, "%Y_%m_%d_%H%M_%S", gmtime(&raw));
    Ogre::String filestamp = Ogre::String(temp, 18);
    // name for this batch (used for naming the directory, and uniquely identifying this batch)
    Ogre::String batchName = mTestSetName + "_" + filestamp;

    // a nicer formatted version for display
    strftime(temp, 20, "%Y-%m-%d %H:%M:%S", gmtime(&raw));
    Ogre::String timestamp = Ogre::String(temp, 19);

    if (mReferenceSet)
        batchName = "Reference";
    else if (mBatchName != "AUTO")
        batchName = mBatchName;

    // set up output directories
    setupDirectories(batchName);

    // an object storing info about this set
    mBatch = new TestBatch(batchName, mTestSetName, timestamp,
        mWindow->getWidth(), mWindow->getHeight(), mOutputDir + batchName + "/");
    mBatch->comment = mComment;

    OgreBites::Sample* firstTest = loadTests(mTestSetName);
    runSample(firstTest);
}
//-----------------------------------------------------------------------

OgreBites::Sample* TestContext::loadTests(Ogre::String set)
{
    OgreBites::Sample* startSample = 0;
    Ogre::StringVector sampleList;

    // load all of the plugins in the set
    for (Ogre::StringVector::iterator it = mTestSets[set].begin(); it !=
        mTestSets[set].end(); ++it)
    {
        Ogre::String plugin = *it;

        // try loading up the test plugin
        try
        {
            mRoot->loadPlugin(mPluginDirectory + "/" + plugin);
        }
        // if it fails, just return right away
        catch (Ogre::Exception e)
        {
            return 0;
        }

        // grab the plugin and cast to SamplePlugin
        Ogre::Plugin* p = mRoot->getInstalledPlugins().back();
        OgreBites::SamplePlugin* sp = dynamic_cast<OgreBites::SamplePlugin*>(p);

        // if something's gone wrong return null
        if (!sp)
            return 0;

        // go through every sample (test) in the plugin...
        OgreBites::SampleSet newSamples = sp->getSamples();
        for (OgreBites::SampleSet::iterator j = newSamples.begin(); j != newSamples.end(); j++)
        {
            mTests.push_back(*j);
            Ogre::NameValuePairList& info = (*j)->getInfo();   // acquire custom sample info
            Ogre::NameValuePairList::iterator k;

            // give sample default title and category if none found
            k= info.find("Title");
            if (k == info.end() || k->second.empty()) info["Title"] = "Untitled";
            k = info.find("Category");
            if (k == info.end() || k->second.empty()) info["Category"] = "Unsorted";
            k = info.find("Thumbnail");
            if (k == info.end() || k->second.empty()) info["Thumbnail"] = "thumb_error.png";
        }
    }

    // start with the first one on the list
    startSample = mTests.front();
    return startSample;
}
//-----------------------------------------------------------------------

bool TestContext::frameStarted(const Ogre::FrameEvent& evt)
{
    captureInputDevices();

    // pass a fixed timestep along to the tests
    Ogre::FrameEvent fixed_evt = Ogre::FrameEvent();
    fixed_evt.timeSinceLastFrame = mTimestep;
    fixed_evt.timeSinceLastEvent = mTimestep;

    if (mCurrentTest) // if a test is running
    {
        // track frame number for screenshot purposes
        ++mCurrentFrame;

        // regular update function
        return mCurrentTest->frameStarted(fixed_evt);
    }
    else if (mCurrentSample) // if a generic sample is running
    {
        return mCurrentSample->frameStarted(evt);
    }
    else
    {
        // if no more tests are queued, generate output and exit
        finishedTests();
        return false;
    }
}
//-----------------------------------------------------------------------

bool TestContext::frameEnded(const Ogre::FrameEvent& evt)
{
    // pass a fixed timestep along to the tests
    Ogre::FrameEvent fixed_evt = Ogre::FrameEvent();
    fixed_evt.timeSinceLastFrame = mTimestep;
    fixed_evt.timeSinceLastEvent = mTimestep;

    if (mCurrentTest) // if a test is running
    {
        if (mCurrentTest->isScreenshotFrame(mCurrentFrame))
        {
            // take a screenshot
            Ogre::String filename = mOutputDir + mBatch->name + "/" +
                mCurrentTest->getInfo()["Title"] + "_" +
                Ogre::StringConverter::toString(mCurrentFrame) + ".png";
            // remember the name of the shot, for later comparison purposes
            mBatch->images.push_back(mCurrentTest->getInfo()["Title"] + "_" +
                Ogre::StringConverter::toString(mCurrentFrame));
            mWindow->writeContentsToFile(filename);
        }

        if (mCurrentTest->isDone())
        {
            // continue onto the next test
            runSample(0);
            return true;
        }

        // standard update function
        return mCurrentTest->frameEnded(fixed_evt);
    }
    else if (mCurrentSample) // if a generic sample is running
    {
        return mCurrentSample->frameEnded(evt);
    }
    else
    {
        // if no more tests are queued, generate output and exit
        finishedTests();
        return false;
    }
}
//-----------------------------------------------------------------------

void TestContext::runSample(OgreBites::Sample* s)
{
    // reset frame timing
    Ogre::ControllerManager::getSingleton().setFrameDelay(0);
    Ogre::ControllerManager::getSingleton().setTimeFactor(1.f);
    mCurrentFrame = 0;

    OgreBites::Sample* sampleToRun = s;

    // if a valid test is passed, then run it, if null, grab the next one from the deque
    if (s)
    {
        sampleToRun = s;
    }
    else if (!mTests.empty())
    {
        mTests.pop_front();
        if (!mTests.empty())
            sampleToRun = mTests.front();
    }

    // check if this is a VisualTest
    mCurrentTest = dynamic_cast<VisualTest*>(sampleToRun);

    // set things up to be deterministic
    if (mCurrentTest)
    {
       // Seed rand with a predictable value
        srand(5); // 5 is completely arbitrary, the idea is simply to use a constant

        // Give a fixed timestep for particles and other time-dependent things in OGRE
        Ogre::ControllerManager::getSingleton().setFrameDelay(mTimestep);
    }

    SampleContext::runSample(sampleToRun);
}
//-----------------------------------------------------------------------

void TestContext::createRoot()
{
// note that we use a separate config file here
#if OGRE_PLATFORM == OGRE_PLATFORM_ANDROID
    mRoot = Ogre::Root::getSingletonPtr();
#else
    Ogre::String pluginsPath = Ogre::StringUtil::BLANK;
    #ifndef OGRE_STATIC_LIB
        pluginsPath = mFSLayer->getConfigFilePath("plugins.cfg");
    #endif
    // we use separate config and log files for the tests
    mRoot = OGRE_NEW Ogre::Root(pluginsPath, mFSLayer->getWritablePath("ogretests.cfg"),
        mFSLayer->getWritablePath("ogretests.log"));
#endif

#ifdef OGRE_STATIC_LIB
    mStaticPluginLoader.load();
#endif
}
//-----------------------------------------------------------------------

bool TestContext::oneTimeConfig()
{
	// if forced, just do it and return
	if(mForceConfig)
	{
		bool temp = mRoot->showConfigDialog();
		if(!temp)
			mRoot->setRenderSystem(0);
		return temp;
	}

	// try restore
	bool success = mRoot->restoreConfig();

	// if restoring failed, show the dialog
	if(!success)
		success = mRoot->showConfigDialog();

	// set render system if user-defined
	if(success && mRenderSystemName != "SAVED" && mRoot->getRenderSystemByName(mRenderSystemName))
		mRoot->setRenderSystem(mRoot->getRenderSystemByName(mRenderSystemName));
	else if(!success)
		mRoot->setRenderSystem(0);

	return success;
}
//-----------------------------------------------------------------------

void TestContext::setupDirectories(Ogre::String batchName)
{
    // ensure there's a root directory for visual tests
    mOutputDir = mFSLayer->getWritablePath("VisualTests/");
    static_cast<OgreBites::FileSystemLayerImpl*>(mFSLayer)->createDirectory(mOutputDir);

    // make sure there's a directory for the test set
    mOutputDir += mTestSetName + "/";
    static_cast<OgreBites::FileSystemLayerImpl*>(mFSLayer)->createDirectory(mOutputDir);

    // add a directory for the render system
    Ogre::String rsysName = Ogre::Root::getSingleton().getRenderSystem()->getName();
    // strip spaces from render system name
    for (unsigned int i = 0;i < rsysName.size(); ++i)
        if (rsysName[i] != ' ')
            mOutputDir += rsysName[i];
    mOutputDir += "/";
    static_cast<OgreBites::FileSystemLayerImpl*>(mFSLayer)->createDirectory(mOutputDir);

    // and finally a directory for the test batch itself
    static_cast<OgreBites::FileSystemLayerImpl*>(mFSLayer)->createDirectory(mOutputDir
        + batchName + "/");
}
//-----------------------------------------------------------------------

void TestContext::finishedTests()
{
    Ogre::String html = "";

    if (mGenerateHtml && !mReferenceSet)
    {
        HtmlWriter* writer = 0;

        // look for a reference set first
        Ogre::ConfigFile info;
        bool foundReference = true;

        try
        {
            info.load(mOutputDir + mCompareWith + "/info.cfg");
        }
        catch (Ogre::FileNotFoundException e)
        {
            foundReference = false;
            TestBatchSetPtr batches = TestBatch::loadTestBatches(mOutputDir);

            TestBatchSet::iterator i = batches->begin();
            for (i; i != batches->end(); ++i)
            {
                if (mBatch->canCompareWith((*i)))
                {
                    writer = OGRE_NEW HtmlWriter(*i, *mBatch, mBatch->compare((*i)));
                    break;
                }
            }
        }

        if (foundReference)
        {
            TestBatch ref = TestBatch(info, mOutputDir + mCompareWith);
            if (mBatch->canCompareWith(ref))
                writer = OGRE_NEW HtmlWriter(ref, *mBatch, mBatch->compare(ref));
        }

        if (writer)
        {
            // we save a generally named "out.html" that gets overwritten each run,
            // plus a uniquely named one for this run
            writer->writeToFile(mOutputDir + "out.html");
            writer->writeToFile(mOutputDir + "TestResults_" + mBatch->name + ".html");
            OGRE_DELETE writer;
        }
    }

    // write this batch's config file
    mBatch->writeConfig();
}
//-----------------------------------------------------------------------

Ogre::Real TestContext::getTimestep()
{
    return mTimestep;
}
//-----------------------------------------------------------------------

void TestContext::setTimestep(Ogre::Real timestep)
{
    // ensure we're getting a positive value
    mTimestep = timestep >= 0.f ? timestep : mTimestep;
}
//-----------------------------------------------------------------------

// main, platform-specific stuff is copied from SampleBrowser and not guaranteed to work...

#if OGRE_PLATFORM != OGRE_PLATFORM_SYMBIAN

// TODO: setup CMake to use winmain rather than console
//#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
//INT WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, INT)
//#else
int main(int argc, char *argv[])
//#endif
{
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, @"UIApplication", @"AppDelegate");
    [pool release];
    return retVal;
#elif (OGRE_PLATFORM == OGRE_PLATFORM_APPLE) && __LP64__
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    mAppDelegate = [[AppDelegate alloc] init];
    [[NSApplication sharedApplication] setDelegate:mAppDelegate];
    int retVal = NSApplicationMain(argc, (const char **) argv);

    [pool release];

    return retVal;
#else

    try
    {
        TestContext tc = TestContext(argc, argv);
        tc.go();
    }
    catch (Ogre::Exception& e)
    {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
        MessageBoxA(NULL, e.getFullDescription().c_str(), "An exception has occurred!", MB_ICONERROR | MB_TASKMODAL);
#else
        std::cerr << "An exception has occurred: " << e.getFullDescription().c_str() << std::endl;
#endif
    }

#endif
    return 0;
}

#endif // OGRE_PLATFORM != OGRE_PLATFORM_SYMBIAN
<commit_msg>Fixed bug in output generation.<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-2009 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 "TestContext.h"
#include "VisualTest.h"
#include "SamplePlugin.h"
#include "TestResultWriter.h"
#include "HTMLWriter.h"
#include "OgreConfigFile.h"

#include "OgrePlatform.h"
#if OGRE_PLATFORM == OGRE_PLATFORM_SYMBIAN
#include <coecntrl.h>
#endif

#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif

TestContext::TestContext(int argc, char** argv) :mTimestep(0.01f), mBatch(0)
{
    Ogre::UnaryOptionList unOpt;
    Ogre::BinaryOptionList binOpt;

    // prepopulate expected options
    unOpt["-r"] = false;        // generate reference set
    unOpt["--no-html"] = false; // whether or not to generate html
    unOpt["-d"] = false;        // force config dialogue
    binOpt["-m"] = "";          // optional comment
    binOpt["-ts"] = "VTests";   // name of the test set to use
    binOpt["-c"] = "Reference"; // name of batch to compare against
    binOpt["-n"] = "AUTO";      // name for this batch
    binOpt["-rs"] = "SAVED";    // rendersystem to use (default: use name from the config file/dialog)

    // parse
    findCommandLineOpts(argc, argv, unOpt, binOpt);

    mReferenceSet = unOpt["-r"];
    mTestSetName = binOpt["-ts"];
    mComment = binOpt["-m"];
    mGenerateHtml = !unOpt["--no-html"];
    mBatchName = binOpt["-n"];
    mCompareWith = binOpt["-c"];
    mForceConfig = unOpt["-d"];
    mRenderSystemName = binOpt["-rs"];
}
//-----------------------------------------------------------------------

TestContext::~TestContext()
{
    if (mBatch)
        delete mBatch;
}
//-----------------------------------------------------------------------

void TestContext::setup()
{
    // standard setup
    SampleContext::setup();

    // get the path and list of test plugins from the config file
    Ogre::ConfigFile testConfig;
    testConfig.load("tests.cfg");
    mPluginDirectory = testConfig.getSetting("TestFolder");

    Ogre::ConfigFile::SectionIterator sections = testConfig.getSectionIterator();

    // parse for the test sets and plugins that they're made up of
    for (sections; sections.hasMoreElements(); sections.moveNext())
    {
        Ogre::String setName = sections.peekNextKey();
        if (setName != "")
        {
            mTestSets[setName] = Ogre::StringVector();
            Ogre::ConfigFile::SettingsMultiMap::iterator it = sections.peekNextValue()->begin();
            for (it; it != sections.peekNextValue()->end(); ++it)
                mTestSets[setName].push_back(it->second);
        }
    }

    // timestamp for the filename
    char temp[19];
    time_t raw = time(0);
    strftime(temp, 19, "%Y_%m_%d_%H%M_%S", gmtime(&raw));
    Ogre::String filestamp = Ogre::String(temp, 18);
    // name for this batch (used for naming the directory, and uniquely identifying this batch)
    Ogre::String batchName = mTestSetName + "_" + filestamp;

    // a nicer formatted version for display
    strftime(temp, 20, "%Y-%m-%d %H:%M:%S", gmtime(&raw));
    Ogre::String timestamp = Ogre::String(temp, 19);

    if (mReferenceSet)
        batchName = "Reference";
    else if (mBatchName != "AUTO")
        batchName = mBatchName;

    // set up output directories
    setupDirectories(batchName);

    // an object storing info about this set
    mBatch = new TestBatch(batchName, mTestSetName, timestamp,
        mWindow->getWidth(), mWindow->getHeight(), mOutputDir + batchName + "/");
    mBatch->comment = mComment;

    OgreBites::Sample* firstTest = loadTests(mTestSetName);
    runSample(firstTest);
}
//-----------------------------------------------------------------------

OgreBites::Sample* TestContext::loadTests(Ogre::String set)
{
    OgreBites::Sample* startSample = 0;
    Ogre::StringVector sampleList;

    // load all of the plugins in the set
    for (Ogre::StringVector::iterator it = mTestSets[set].begin(); it !=
        mTestSets[set].end(); ++it)
    {
        Ogre::String plugin = *it;

        // try loading up the test plugin
        try
        {
            mRoot->loadPlugin(mPluginDirectory + "/" + plugin);
        }
        // if it fails, just return right away
        catch (Ogre::Exception e)
        {
            return 0;
        }

        // grab the plugin and cast to SamplePlugin
        Ogre::Plugin* p = mRoot->getInstalledPlugins().back();
        OgreBites::SamplePlugin* sp = dynamic_cast<OgreBites::SamplePlugin*>(p);

        // if something's gone wrong return null
        if (!sp)
            return 0;

        // go through every sample (test) in the plugin...
        OgreBites::SampleSet newSamples = sp->getSamples();
        for (OgreBites::SampleSet::iterator j = newSamples.begin(); j != newSamples.end(); j++)
        {
            mTests.push_back(*j);
            Ogre::NameValuePairList& info = (*j)->getInfo();   // acquire custom sample info
            Ogre::NameValuePairList::iterator k;

            // give sample default title and category if none found
            k= info.find("Title");
            if (k == info.end() || k->second.empty()) info["Title"] = "Untitled";
            k = info.find("Category");
            if (k == info.end() || k->second.empty()) info["Category"] = "Unsorted";
            k = info.find("Thumbnail");
            if (k == info.end() || k->second.empty()) info["Thumbnail"] = "thumb_error.png";
        }
    }

    // start with the first one on the list
    startSample = mTests.front();
    return startSample;
}
//-----------------------------------------------------------------------

bool TestContext::frameStarted(const Ogre::FrameEvent& evt)
{
    captureInputDevices();

    // pass a fixed timestep along to the tests
    Ogre::FrameEvent fixed_evt = Ogre::FrameEvent();
    fixed_evt.timeSinceLastFrame = mTimestep;
    fixed_evt.timeSinceLastEvent = mTimestep;

    if (mCurrentTest) // if a test is running
    {
        // track frame number for screenshot purposes
        ++mCurrentFrame;

        // regular update function
        return mCurrentTest->frameStarted(fixed_evt);
    }
    else if (mCurrentSample) // if a generic sample is running
    {
        return mCurrentSample->frameStarted(evt);
    }
    else
    {
        // if no more tests are queued, generate output and exit
        finishedTests();
        return false;
    }
}
//-----------------------------------------------------------------------

bool TestContext::frameEnded(const Ogre::FrameEvent& evt)
{
    // pass a fixed timestep along to the tests
    Ogre::FrameEvent fixed_evt = Ogre::FrameEvent();
    fixed_evt.timeSinceLastFrame = mTimestep;
    fixed_evt.timeSinceLastEvent = mTimestep;

    if (mCurrentTest) // if a test is running
    {
        if (mCurrentTest->isScreenshotFrame(mCurrentFrame))
        {
            // take a screenshot
            Ogre::String filename = mOutputDir + mBatch->name + "/" +
                mCurrentTest->getInfo()["Title"] + "_" +
                Ogre::StringConverter::toString(mCurrentFrame) + ".png";
            // remember the name of the shot, for later comparison purposes
            mBatch->images.push_back(mCurrentTest->getInfo()["Title"] + "_" +
                Ogre::StringConverter::toString(mCurrentFrame));
            mWindow->writeContentsToFile(filename);
        }

        if (mCurrentTest->isDone())
        {
            // continue onto the next test
            runSample(0);
            return true;
        }

        // standard update function
        return mCurrentTest->frameEnded(fixed_evt);
    }
    else if (mCurrentSample) // if a generic sample is running
    {
        return mCurrentSample->frameEnded(evt);
    }
    else
    {
        // if no more tests are queued, generate output and exit
        finishedTests();
        return false;
    }
}
//-----------------------------------------------------------------------

void TestContext::runSample(OgreBites::Sample* s)
{
    // reset frame timing
    Ogre::ControllerManager::getSingleton().setFrameDelay(0);
    Ogre::ControllerManager::getSingleton().setTimeFactor(1.f);
    mCurrentFrame = 0;

    OgreBites::Sample* sampleToRun = s;

    // if a valid test is passed, then run it, if null, grab the next one from the deque
    if (s)
    {
        sampleToRun = s;
    }
    else if (!mTests.empty())
    {
        mTests.pop_front();
        if (!mTests.empty())
            sampleToRun = mTests.front();
    }

    // check if this is a VisualTest
    mCurrentTest = dynamic_cast<VisualTest*>(sampleToRun);

    // set things up to be deterministic
    if (mCurrentTest)
    {
       // Seed rand with a predictable value
        srand(5); // 5 is completely arbitrary, the idea is simply to use a constant

        // Give a fixed timestep for particles and other time-dependent things in OGRE
        Ogre::ControllerManager::getSingleton().setFrameDelay(mTimestep);
    }

    SampleContext::runSample(sampleToRun);
}
//-----------------------------------------------------------------------

void TestContext::createRoot()
{
// note that we use a separate config file here
#if OGRE_PLATFORM == OGRE_PLATFORM_ANDROID
    mRoot = Ogre::Root::getSingletonPtr();
#else
    Ogre::String pluginsPath = Ogre::StringUtil::BLANK;
    #ifndef OGRE_STATIC_LIB
        pluginsPath = mFSLayer->getConfigFilePath("plugins.cfg");
    #endif
    // we use separate config and log files for the tests
    mRoot = OGRE_NEW Ogre::Root(pluginsPath, mFSLayer->getWritablePath("ogretests.cfg"),
        mFSLayer->getWritablePath("ogretests.log"));
#endif

#ifdef OGRE_STATIC_LIB
    mStaticPluginLoader.load();
#endif
}
//-----------------------------------------------------------------------

bool TestContext::oneTimeConfig()
{
	// if forced, just do it and return
	if(mForceConfig)
	{
		bool temp = mRoot->showConfigDialog();
		if(!temp)
			mRoot->setRenderSystem(0);
		return temp;
	}

	// try restore
	bool success = mRoot->restoreConfig();

	// if restoring failed, show the dialog
	if(!success)
		success = mRoot->showConfigDialog();

	// set render system if user-defined
	if(success && mRenderSystemName != "SAVED" && mRoot->getRenderSystemByName(mRenderSystemName))
		mRoot->setRenderSystem(mRoot->getRenderSystemByName(mRenderSystemName));
	else if(!success)
		mRoot->setRenderSystem(0);

	return success;
}
//-----------------------------------------------------------------------

void TestContext::setupDirectories(Ogre::String batchName)
{
    // ensure there's a root directory for visual tests
    mOutputDir = mFSLayer->getWritablePath("VisualTests/");
    static_cast<OgreBites::FileSystemLayerImpl*>(mFSLayer)->createDirectory(mOutputDir);

    // make sure there's a directory for the test set
    mOutputDir += mTestSetName + "/";
    static_cast<OgreBites::FileSystemLayerImpl*>(mFSLayer)->createDirectory(mOutputDir);

    // add a directory for the render system
    Ogre::String rsysName = Ogre::Root::getSingleton().getRenderSystem()->getName();
    // strip spaces from render system name
    for (unsigned int i = 0;i < rsysName.size(); ++i)
        if (rsysName[i] != ' ')
            mOutputDir += rsysName[i];
    mOutputDir += "/";
    static_cast<OgreBites::FileSystemLayerImpl*>(mFSLayer)->createDirectory(mOutputDir);

    // and finally a directory for the test batch itself
    static_cast<OgreBites::FileSystemLayerImpl*>(mFSLayer)->createDirectory(mOutputDir
        + batchName + "/");
}
//-----------------------------------------------------------------------

void TestContext::finishedTests()
{
    Ogre::String html = "";

    if (mGenerateHtml && !mReferenceSet)
    {
        HtmlWriter* writer = 0;

        // look for a reference set first
        Ogre::ConfigFile info;
        bool foundReference = true;
		TestBatch* ref = 0;

        try
        {
            info.load(mOutputDir + mCompareWith + "/info.cfg");
        }
        catch (Ogre::FileNotFoundException e)
        {
            foundReference = false;
            TestBatchSetPtr batches = TestBatch::loadTestBatches(mOutputDir);

            TestBatchSet::iterator i = batches->begin();
            for (i; i != batches->end(); ++i)
            {
                if (mBatch->canCompareWith((*i)))
                {
                    writer = OGRE_NEW HtmlWriter(*i, *mBatch, mBatch->compare((*i)));
                    break;
                }
            }
        }

        if (foundReference)
        {
            ref = OGRE_NEW TestBatch(info, mOutputDir + mCompareWith);
            if (mBatch->canCompareWith(*ref))
                writer = OGRE_NEW HtmlWriter(*ref, *mBatch, mBatch->compare(*ref));
        }

        if (writer)
        {
            // we save a generally named "out.html" that gets overwritten each run,
            // plus a uniquely named one for this run
            writer->writeToFile(mOutputDir + "out.html");
            writer->writeToFile(mOutputDir + "TestResults_" + mBatch->name + ".html");
            OGRE_DELETE writer;
        }

		OGRE_DELETE ref;
    }

    // write this batch's config file
    mBatch->writeConfig();
}
//-----------------------------------------------------------------------

Ogre::Real TestContext::getTimestep()
{
    return mTimestep;
}
//-----------------------------------------------------------------------

void TestContext::setTimestep(Ogre::Real timestep)
{
    // ensure we're getting a positive value
    mTimestep = timestep >= 0.f ? timestep : mTimestep;
}
//-----------------------------------------------------------------------

// main, platform-specific stuff is copied from SampleBrowser and not guaranteed to work...

#if OGRE_PLATFORM != OGRE_PLATFORM_SYMBIAN

// TODO: setup CMake to use winmain rather than console
//#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
//INT WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, INT)
//#else
int main(int argc, char *argv[])
//#endif
{
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE_IOS
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, @"UIApplication", @"AppDelegate");
    [pool release];
    return retVal;
#elif (OGRE_PLATFORM == OGRE_PLATFORM_APPLE) && __LP64__
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    mAppDelegate = [[AppDelegate alloc] init];
    [[NSApplication sharedApplication] setDelegate:mAppDelegate];
    int retVal = NSApplicationMain(argc, (const char **) argv);

    [pool release];

    return retVal;
#else

    try
    {
        TestContext tc = TestContext(argc, argv);
        tc.go();
    }
    catch (Ogre::Exception& e)
    {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
        MessageBoxA(NULL, e.getFullDescription().c_str(), "An exception has occurred!", MB_ICONERROR | MB_TASKMODAL);
#else
        std::cerr << "An exception has occurred: " << e.getFullDescription().c_str() << std::endl;
#endif
    }

#endif
    return 0;
}

#endif // OGRE_PLATFORM != OGRE_PLATFORM_SYMBIAN
<|endoftext|>


<commit_before>/************************************************************************
	filename: 	TLComboEditbox.cpp
	created:	13/6/2004
	author:		Paul D Turner

	purpose:	Implements Taharez Look Combobox-Editbox widget
*************************************************************************/
/*************************************************************************
    Crazy Eddie's GUI System (http://www.cegui.org.uk)
    Copyright (C)2004 - 2005 Paul D Turner (paul@cegui.org.uk)

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

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

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*************************************************************************/
#include "TLComboEditbox.h"
#include "CEGUIImagesetManager.h"
#include "CEGUIImageset.h"
#include "CEGUIFont.h"

// Start of CEGUI namespace section
namespace CEGUI
{
/*************************************************************************
	Constants
*************************************************************************/
// type name for this widget
const utf8	TLComboEditbox::WidgetTypeName[]	= "TaharezLook/ComboEditbox";

// image name constants
const utf8	TLComboEditbox::ImagesetName[]				= "TaharezLook";
const utf8	TLComboEditbox::ContainerLeftImageName[]	= "ComboboxEditLeft";
const utf8	TLComboEditbox::ContainerMiddleImageName[]	= "ComboboxEditMiddle";
const utf8	TLComboEditbox::CaratImageName[]			= "EditBoxCarat";
const utf8	TLComboEditbox::SelectionBrushImageName[]	= "ComboboxSelectionBrush";
const utf8	TLComboEditbox::MouseCursorImageName[]		= "MouseTextBar";

// layout values
const float	TLComboEditbox::TextPaddingRatio		= 0.5f;

// implementation constantss
const uint	TLComboEditbox::SelectionLayer	= 1;
const uint	TLComboEditbox::TextLayer		= 2;
const uint	TLComboEditbox::CaratLayer		= 3;

/*************************************************************************
	Constructor for Taharez edit box widgets
*************************************************************************/
TLComboEditbox::TLComboEditbox(const String& type, const String& name) :
	Editbox(type, name),
	d_lastTextOffset(0)
{
	Imageset* iset = ImagesetManager::getSingleton().getImageset(ImagesetName);

	// cache images to be used
	d_left		= &iset->getImage(ContainerLeftImageName);
	d_middle	= &iset->getImage(ContainerMiddleImageName);
	d_carat		= &iset->getImage(CaratImageName);
	d_selection	= &iset->getImage(SelectionBrushImageName);

	setMouseCursor(&iset->getImage(MouseCursorImageName));
}

/*************************************************************************
	Destructor for Taharez edit box widgets
*************************************************************************/
TLComboEditbox::~TLComboEditbox(void)
{
}

/*************************************************************************
	Return the text code point index that is rendered closest to screen
	position 'pt'.
*************************************************************************/
ulong TLComboEditbox::getTextIndexFromPosition(const Point& pt) const
{
	//
	// calculate final window position to be checked
	//
	float wndx = screenToWindowX(pt.d_x);

	if (getMetricsMode() == Relative)
	{
		wndx = relativeToAbsoluteX(wndx);
	}

	wndx -= d_lastTextOffset;

	//
	// Return the proper index
	//
	if (isTextMasked())
	{
		return getFont()->getCharAtPixel(String(d_text.length(), getMaskCodePoint()), wndx);
	}
	else
	{
		return getFont()->getCharAtPixel(d_text, wndx);
	}

}

/*************************************************************************
	return text padding value to use in pixels
*************************************************************************/
float TLComboEditbox::getTextPaddingPixels(void) const
{
	return PixelAligned(d_left->getWidth() * TextPaddingRatio);
}

/*************************************************************************
	Perform the actual rendering for this Window.
*************************************************************************/
void TLComboEditbox::drawSelf(float z)
{
	Rect clipper(getPixelRect());

	// do nothing if the widget is totally clipped.
	if (clipper.getWidth() == 0)
	{
		return;
	}

	const Font* fnt = getFont();
	Renderer*	renderer = System::getSingleton().getRenderer();

	// get the destination screen rect for this window
	Rect absrect(getUnclippedPixelRect());

	// calculate colours to use.
	float alpha_comp = getEffectiveAlpha();
	ColourRect colours(colour(1, 1, 1, alpha_comp));

	bool hasFocus = hasInputFocus();

	//
	// render container
	//
	// calculate widths for container segments
	float leftWidth		= d_left->getWidth();
	float midWidth		= absrect.getWidth() - leftWidth;

	Vector3 pos(absrect.d_left, absrect.d_top, z);
	Size	sz(leftWidth, absrect.getHeight());

	// left end
	d_left->draw(pos, sz, clipper, colours);

	// stretchy middle segment
	pos.d_x += sz.d_width;
	sz.d_width = midWidth;
	d_middle->draw(pos, sz, clipper, colours);

	//
	// Required preliminary work for main rendering operations
	//
	// Create a 'masked' version of the string if needed.
	String editText;

	if (isTextMasked())
	{
		editText.insert(0, d_text.length(), getMaskCodePoint());
	}
	else
	{
		editText = d_text;
	}

	// calculate new area rect considering text padding value.
	float textpadding = getTextPaddingPixels();

	absrect.d_left		+= textpadding;
	absrect.d_top		+= textpadding;
	absrect.d_right		-= textpadding;
	absrect.d_bottom	-= textpadding;

	// calculate best position to render text to ensure carat is always visible
	float textOffset;
	float extentToCarat = fnt->getTextExtent(editText.substr(0, getCaratIndex()));

	// if box is inactive
	if (!hasFocus)
	{
		textOffset = d_lastTextOffset;
	}
	// if carat is to the left of the box
	else if ((d_lastTextOffset + extentToCarat) < 0)
	{
		textOffset = -extentToCarat;
	}
	// if carat is off to the right.
	else if ((d_lastTextOffset + extentToCarat) >= (absrect.getWidth() - d_carat->getWidth()))
	{
		textOffset = absrect.getWidth() - extentToCarat - d_carat->getWidth();
	}
	// else carat is already within the box
	else
	{
		textOffset = d_lastTextOffset;
	}

	// adjust clipper for new target area
	clipper = absrect.getIntersection(clipper);

	//
	// Render carat
	//
	if ((!isReadOnly()) && hasFocus)
	{
		Vector3 pos(absrect.d_left + textOffset + extentToCarat, absrect.d_top, renderer->getZLayer(CaratLayer));
		Size	sz(d_carat->getWidth(), absrect.getHeight());
		d_carat->draw(pos, sz, clipper, colours);
	}

	//
	// Draw label text
	//
	// setup initial rect for text formatting
	Rect text_rect(absrect);
	text_rect.d_top  += PixelAligned((text_rect.getHeight() - getFont()->getFontHeight()) * 0.5f);
	text_rect.d_left += textOffset;

	// draw pre-highlight text
	String sect = editText.substr(0, getSelectionStartIndex());
	colours.setColours(d_normalTextColour);
	colours.setAlpha(alpha_comp);
	fnt->drawText(sect, text_rect, renderer->getZLayer(TextLayer), clipper, LeftAligned, colours);

	text_rect.d_left += fnt->getTextExtent(sect);

	// draw highlight text
	sect = editText.substr(getSelectionStartIndex(), getSelectionLength());
	colours.setColours(d_selectTextColour);
	colours.setAlpha(alpha_comp);
	fnt->drawText(sect, text_rect, renderer->getZLayer(TextLayer), clipper, LeftAligned, colours);

	text_rect.d_left += fnt->getTextExtent(sect);

	// draw post-highlight text
	sect = editText.substr(getSelectionEndIndex());
	colours.setColours(d_normalTextColour);
	colours.setAlpha(alpha_comp);
	fnt->drawText(sect, text_rect, renderer->getZLayer(TextLayer), clipper, LeftAligned, colours);

	//
	// Render selection brush
	//
	if (getSelectionLength() != 0)
	{
		// calculate required start and end offsets
		float selStartOffset	= fnt->getTextExtent(editText.substr(0, getSelectionStartIndex()));
		float selEndOffset		= fnt->getTextExtent(editText.substr(0, getSelectionEndIndex()));

		// setup colours
		colours.setColours((hasFocus && (!isReadOnly())) ? d_selectBrushColour : d_inactiveSelectBrushColour);
		colours.setAlpha(alpha_comp);

		// calculate highlight area
		Rect hlarea;
		hlarea.d_left	= absrect.d_left + textOffset + selStartOffset;
		hlarea.d_right	= absrect.d_left + textOffset + selEndOffset;
		hlarea.d_top	= text_rect.d_top;
		hlarea.d_bottom = hlarea.d_top + fnt->getFontHeight();

		// render the highlight
		d_selection->draw(hlarea, renderer->getZLayer(SelectionLayer), clipper, colours);
	}

	d_lastTextOffset = textOffset;
}

//////////////////////////////////////////////////////////////////////////
/*************************************************************************

	Factory Methods

*************************************************************************/
//////////////////////////////////////////////////////////////////////////
/*************************************************************************
	Create, initialise and return a TLComboEditbox
*************************************************************************/
Window* TLComboEditboxFactory::createWindow(const String& name)
{
	TLComboEditbox* wnd = new TLComboEditbox(d_type, name);
	wnd->initialise();

	return wnd;
}

} // End of  CEGUI namespace section
<commit_msg>Minor adjustment to layout required to fix text clipping issue.<commit_after>/************************************************************************
	filename: 	TLComboEditbox.cpp
	created:	13/6/2004
	author:		Paul D Turner

	purpose:	Implements Taharez Look Combobox-Editbox widget
*************************************************************************/
/*************************************************************************
    Crazy Eddie's GUI System (http://www.cegui.org.uk)
    Copyright (C)2004 - 2005 Paul D Turner (paul@cegui.org.uk)

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

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

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*************************************************************************/
#include "TLComboEditbox.h"
#include "CEGUIImagesetManager.h"
#include "CEGUIImageset.h"
#include "CEGUIFont.h"

// Start of CEGUI namespace section
namespace CEGUI
{
/*************************************************************************
	Constants
*************************************************************************/
// type name for this widget
const utf8	TLComboEditbox::WidgetTypeName[]	= "TaharezLook/ComboEditbox";

// image name constants
const utf8	TLComboEditbox::ImagesetName[]				= "TaharezLook";
const utf8	TLComboEditbox::ContainerLeftImageName[]	= "ComboboxEditLeft";
const utf8	TLComboEditbox::ContainerMiddleImageName[]	= "ComboboxEditMiddle";
const utf8	TLComboEditbox::CaratImageName[]			= "EditBoxCarat";
const utf8	TLComboEditbox::SelectionBrushImageName[]	= "ComboboxSelectionBrush";
const utf8	TLComboEditbox::MouseCursorImageName[]		= "MouseTextBar";

// layout values
const float	TLComboEditbox::TextPaddingRatio		= 0.375f;

// implementation constantss
const uint	TLComboEditbox::SelectionLayer	= 1;
const uint	TLComboEditbox::TextLayer		= 2;
const uint	TLComboEditbox::CaratLayer		= 3;

/*************************************************************************
	Constructor for Taharez edit box widgets
*************************************************************************/
TLComboEditbox::TLComboEditbox(const String& type, const String& name) :
	Editbox(type, name),
	d_lastTextOffset(0)
{
	Imageset* iset = ImagesetManager::getSingleton().getImageset(ImagesetName);

	// cache images to be used
	d_left		= &iset->getImage(ContainerLeftImageName);
	d_middle	= &iset->getImage(ContainerMiddleImageName);
	d_carat		= &iset->getImage(CaratImageName);
	d_selection	= &iset->getImage(SelectionBrushImageName);

	setMouseCursor(&iset->getImage(MouseCursorImageName));
}

/*************************************************************************
	Destructor for Taharez edit box widgets
*************************************************************************/
TLComboEditbox::~TLComboEditbox(void)
{
}

/*************************************************************************
	Return the text code point index that is rendered closest to screen
	position 'pt'.
*************************************************************************/
ulong TLComboEditbox::getTextIndexFromPosition(const Point& pt) const
{
	//
	// calculate final window position to be checked
	//
	float wndx = screenToWindowX(pt.d_x);

	if (getMetricsMode() == Relative)
	{
		wndx = relativeToAbsoluteX(wndx);
	}

	wndx -= d_lastTextOffset;

	//
	// Return the proper index
	//
	if (isTextMasked())
	{
		return getFont()->getCharAtPixel(String(d_text.length(), getMaskCodePoint()), wndx);
	}
	else
	{
		return getFont()->getCharAtPixel(d_text, wndx);
	}

}

/*************************************************************************
	return text padding value to use in pixels
*************************************************************************/
float TLComboEditbox::getTextPaddingPixels(void) const
{
	return PixelAligned(d_left->getWidth() * TextPaddingRatio);
}

/*************************************************************************
	Perform the actual rendering for this Window.
*************************************************************************/
void TLComboEditbox::drawSelf(float z)
{
	Rect clipper(getPixelRect());

	// do nothing if the widget is totally clipped.
	if (clipper.getWidth() == 0)
	{
		return;
	}

	const Font* fnt = getFont();
	Renderer*	renderer = System::getSingleton().getRenderer();

	// get the destination screen rect for this window
	Rect absrect(getUnclippedPixelRect());

	// calculate colours to use.
	float alpha_comp = getEffectiveAlpha();
	ColourRect colours(colour(1, 1, 1, alpha_comp));

	bool hasFocus = hasInputFocus();

	//
	// render container
	//
	// calculate widths for container segments
	float leftWidth		= d_left->getWidth();
	float midWidth		= absrect.getWidth() - leftWidth;

	Vector3 pos(absrect.d_left, absrect.d_top, z);
	Size	sz(leftWidth, absrect.getHeight());

	// left end
	d_left->draw(pos, sz, clipper, colours);

	// stretchy middle segment
	pos.d_x += sz.d_width;
	sz.d_width = midWidth;
	d_middle->draw(pos, sz, clipper, colours);

	//
	// Required preliminary work for main rendering operations
	//
	// Create a 'masked' version of the string if needed.
	String editText;

	if (isTextMasked())
	{
		editText.insert(0, d_text.length(), getMaskCodePoint());
	}
	else
	{
		editText = d_text;
	}

	// calculate new area rect considering text padding value.
	float textpadding = getTextPaddingPixels();

	absrect.d_left		+= textpadding;
	absrect.d_top		+= textpadding;
	absrect.d_right		-= textpadding;
	absrect.d_bottom	-= textpadding;

	// calculate best position to render text to ensure carat is always visible
	float textOffset;
	float extentToCarat = fnt->getTextExtent(editText.substr(0, getCaratIndex()));

	// if box is inactive
	if (!hasFocus)
	{
		textOffset = d_lastTextOffset;
	}
	// if carat is to the left of the box
	else if ((d_lastTextOffset + extentToCarat) < 0)
	{
		textOffset = -extentToCarat;
	}
	// if carat is off to the right.
	else if ((d_lastTextOffset + extentToCarat) >= (absrect.getWidth() - d_carat->getWidth()))
	{
		textOffset = absrect.getWidth() - extentToCarat - d_carat->getWidth();
	}
	// else carat is already within the box
	else
	{
		textOffset = d_lastTextOffset;
	}

	// adjust clipper for new target area
	clipper = absrect.getIntersection(clipper);

	//
	// Render carat
	//
	if ((!isReadOnly()) && hasFocus)
	{
		Vector3 pos(absrect.d_left + textOffset + extentToCarat, absrect.d_top, renderer->getZLayer(CaratLayer));
		Size	sz(d_carat->getWidth(), absrect.getHeight());
		d_carat->draw(pos, sz, clipper, colours);
	}

	//
	// Draw label text
	//
	// setup initial rect for text formatting
	Rect text_rect(absrect);
	text_rect.d_top  += PixelAligned((text_rect.getHeight() - getFont()->getFontHeight()) * 0.5f);
	text_rect.d_left += textOffset;

	// draw pre-highlight text
	String sect = editText.substr(0, getSelectionStartIndex());
	colours.setColours(d_normalTextColour);
	colours.setAlpha(alpha_comp);
	fnt->drawText(sect, text_rect, renderer->getZLayer(TextLayer), clipper, LeftAligned, colours);

	text_rect.d_left += fnt->getTextExtent(sect);

	// draw highlight text
	sect = editText.substr(getSelectionStartIndex(), getSelectionLength());
	colours.setColours(d_selectTextColour);
	colours.setAlpha(alpha_comp);
	fnt->drawText(sect, text_rect, renderer->getZLayer(TextLayer), clipper, LeftAligned, colours);

	text_rect.d_left += fnt->getTextExtent(sect);

	// draw post-highlight text
	sect = editText.substr(getSelectionEndIndex());
	colours.setColours(d_normalTextColour);
	colours.setAlpha(alpha_comp);
	fnt->drawText(sect, text_rect, renderer->getZLayer(TextLayer), clipper, LeftAligned, colours);

	//
	// Render selection brush
	//
	if (getSelectionLength() != 0)
	{
		// calculate required start and end offsets
		float selStartOffset	= fnt->getTextExtent(editText.substr(0, getSelectionStartIndex()));
		float selEndOffset		= fnt->getTextExtent(editText.substr(0, getSelectionEndIndex()));

		// setup colours
		colours.setColours((hasFocus && (!isReadOnly())) ? d_selectBrushColour : d_inactiveSelectBrushColour);
		colours.setAlpha(alpha_comp);

		// calculate highlight area
		Rect hlarea;
		hlarea.d_left	= absrect.d_left + textOffset + selStartOffset;
		hlarea.d_right	= absrect.d_left + textOffset + selEndOffset;
		hlarea.d_top	= text_rect.d_top;
		hlarea.d_bottom = hlarea.d_top + fnt->getFontHeight();

		// render the highlight
		d_selection->draw(hlarea, renderer->getZLayer(SelectionLayer), clipper, colours);
	}

	d_lastTextOffset = textOffset;
}

//////////////////////////////////////////////////////////////////////////
/*************************************************************************

	Factory Methods

*************************************************************************/
//////////////////////////////////////////////////////////////////////////
/*************************************************************************
	Create, initialise and return a TLComboEditbox
*************************************************************************/
Window* TLComboEditboxFactory::createWindow(const String& name)
{
	TLComboEditbox* wnd = new TLComboEditbox(d_type, name);
	wnd->initialise();

	return wnd;
}

} // End of  CEGUI namespace section
<|endoftext|>


<commit_before>/////////////////////////////////////////
//
//             OpenLieroX
//
// code under LGPL, based on JasonBs work,
// enhanced by Dark Charlie and Albert Zeyer
//
//
/////////////////////////////////////////

// Client class - Sending routines
// Created 22/7/02
// Jason Boettcher

#include "CClientNetEngine.h"
#include "LieroX.h"
#include "StringUtils.h"
#include "CClient.h"
#include "Protocol.h"
#include "CWorm.h"
#include "MathLib.h"
#include "ChatCommand.h"
#include "Command.h"
#include "EndianSwap.h"
#include "CServer.h"
#include "AuxLib.h"

///////////////////
// Send the worm details
void CClientNetEngine::SendWormDetails(void)
{
	// Don't flood packets so often
	// we are checking in w->checkPacketNeeded() if we need to send an update
	// we are checking with bandwidth if we should add an update
	/*if ((tLX->fCurTime - fLastUpdateSent) <= tLXOptions->fUpdatePeriod)
		if (tGameInfo.iGameType != GME_LOCAL)
			return; */

	CBytestream bs;
	uint i;

	// If all my worms are dead, don't send
	bool Alive = false;
	for(i=0;i<client->iNumWorms;i++) {
		if(client->cLocalWorms[i]->getAlive()) {
			Alive = true;
			break;
		}
	}

	if(!Alive)
		return;

	// Check if we need to write the state update
/*	bool update = false;
	w = cLocalWorms[0];
	for(i = 0; i < iNumWorms; i++, w++)
		if (w->checkPacketNeeded())  {
			update = true;
			break;
		}

	// No update, just quit
	if (!update)
		return;
*/
	if(	tGameInfo.iGameType == GME_JOIN // we are a client in a netgame
	&& !GameServer::checkUploadBandwidth(client->getChannel()->getOutgoingRate()) )
		return;

	client->fLastUpdateSent = tLX->fCurTime;

	// Write the update
	bs.writeByte(C2S_UPDATE);

	for(i = 0; i < client->iNumWorms; i++)
		client->cLocalWorms[i]->writePacket(&bs, false, NULL);

	client->bsUnreliable.Append(&bs);
}

void CClientNetEngine::SendGameReady()
{
	CBytestream bs;
	bs.writeByte(C2S_IMREADY);
	bs.writeByte(client->iNumWorms);

	// Send my worm's weapon details
	for(unsigned int i=0;i<client->iNumWorms;i++)
		client->cLocalWorms[i]->writeWeapons( &bs );

	client->cNetChan->AddReliablePacketToSend(bs);
}

///////////////////
// Send a death
void CClientNetEngine::SendDeath(int victim, int killer)
{
	CBytestream bs;

	bs.writeByte(C2S_DEATH);
	bs.writeInt(victim,1);
	bs.writeInt(killer,1);

	client->cNetChan->AddReliablePacketToSend(bs);
}

///////////////////
// Send a string of text
void CClientNetEngine::SendText(const std::string& sText, std::string sWormName)
{
	if(HandleDebugCommand(sText)) return;

	bool chat_command = sText.size() >= 2 && sText[0] == '/' && sText[1] != '/';

	// We can safely send long messages to servers >= beta8
	if (client->getServerVersion() >= OLXBetaVersion(8))  {

		// HTML support since beta 8
		SendTextInternal(OldLxCompatibleString(sText), sWormName);

	// <=beta7 server
	} else {

		if (chat_command &&
			client->getServerVersion() < OLXBetaVersion(3) && // <beta3 clients don't have chat command support
			sText.find("/me ") > 0) // Ignores "/me" special command
		{
			// Try if we can execute the same command in console (mainly for "/suicide" command to work on all servers)
			if( ! Cmd_ParseLine(sText.substr(1)) ) {
				client->cChatbox.AddText("HINT: server cannot execute commands, only OLX beta3+ can", tLX->clNotice, TXT_NOTICE, tLX->fCurTime);
			}
			return;

		} else if (chat_command &&
				   client->getServerVersion() >= OLXBetaVersion(3)) {
			// we don't have to split chat commands
			SendTextInternal(OldLxCompatibleString(sText), sWormName);

		} else { // we can savely split the message (and we have to)

			// If the text is too long, split it in smaller pieces and then send (backward comaptibility)
			int name_w = tLX->cFont.GetWidth(sWormName + ": ");
			const std::vector<std::string>& split = splitstring(StripHtmlTags(sText), 63,
				client->iNetStatus == NET_CONNECTED ? 600 - name_w : 300 - name_w, tLX->cFont);

			// Check
			if (split.size() == 0)  {  // More than weird...
				SendTextInternal(OldLxCompatibleString(StripHtmlTags(sText)), sWormName);
				return;
			}

			// Send the first chunk
			SendTextInternal(OldLxCompatibleString(split[0]), sWormName);

			// Send the text
			for (std::vector<std::string>::const_iterator it = split.begin() + 1; it != split.end(); it++)
				SendTextInternal(OldLxCompatibleString(*it), "");
		}
	}
}

void CClientNetEngineBeta7::SendChatCommandCompletionRequest(const std::string& startStr) {
	CBytestream bs;
	bs.writeByte(C2S_CHATCMDCOMPLREQ);
	bs.writeString(startStr);

	client->cNetChan->AddReliablePacketToSend(bs);
}

void CClientNetEngineBeta7::SendAFK(int wormid, AFK_TYPE afkType, const std::string & message ) {
	if( client->getServerVersion() < OLXBetaVersion(7) )
		return;

	std::string msg = message;
	if( msg == "" )
	{
		if( afkType == AFK_TYPING_CHAT )
			msg = "(typing)";
		else
		if( afkType == AFK_AWAY )
			msg = "(away)";
	};

	for( int i=0; i < client->getNumWorms(); i++ )
		if(	client->getWorm(i)->getID() == wormid )
			client->getWorm(i)->setAFK(afkType, msg);

	CBytestream bs;
	bs.writeByte(C2S_AFK);
	bs.writeByte(wormid);
	bs.writeByte(afkType);
	if(msg.size() > 127)
		msg = msg.substr(0, 127);
	bs.writeString(msg);

	client->cNetChan->AddReliablePacketToSend(bs);
}

/////////////////////
// Internal function for text sending, does not do any checks or parsing
void CClientNetEngine::SendTextInternal(const std::string& sText, const std::string& sWormName)
{
	CBytestream bs;
	bs.writeByte(C2S_CHATTEXT);
	if (sWormName.size() == 0)
		bs.writeString(sText);
	else
		bs.writeString(sWormName + ": " + sText);

	client->cNetChan->AddReliablePacketToSend(bs);
}

#ifdef FUZZY_ERROR_TESTING
//////////////////
// Send a random packet to server (used for debugging)
void CClientNetEngine::SendRandomPacket()
{
	CBytestream bs;

	int random_length = GetRandomInt(50);
	for (int i=0; i < random_length; i++)
		bs.writeByte((uchar)GetRandomInt(255));

	client->cNetChan->AddReliablePacketToSend(bs);

	bs.Clear();

	// Test connectionless packets
	if (GetRandomInt(100) >= 75)  {
		bs.writeInt(-1, 4);
		static const std::string commands[] = { "lx::getchallenge", "lx::connect", "lx::ping", "lx::query",
			"lx::getinfo", "lx::wantsjoin", "lx::traverse", "lx::registered"};
		bs.writeString(commands[GetRandomInt(500) % (sizeof(commands)/sizeof(std::string))]);
		for (int i=0; i < random_length; i++)
			bs.writeByte((uchar)GetRandomInt(255));
		SetRemoteNetAddr(tSocket, client->cNetChan->getAddress());
		bs.Send(tSocket);
	}
}
#endif

void CClientNetEngine::SendGrabBonus(int id, int wormid)
{
	CBytestream bs;
	bs.writeByte(C2S_GRABBONUS);
	bs.writeByte(id);
	bs.writeByte(wormid);
	bs.writeByte(client->cRemoteWorms[wormid].getCurrentWeapon());

	client->cNetChan->AddReliablePacketToSend(bs);
};

void CClientNetEngine::SendUpdateLobby(bool ready)
{
	CBytestream bs;
	bs.writeByte(C2S_UPDATELOBBY);
	bs.writeByte(1);
	client->getChannel()->AddReliablePacketToSend(bs);
};

void CClientNetEngine::SendDisconnect()
{
	CBytestream bs;

	bs.writeByte(C2S_DISCONNECT);

	// Send the pack a few times to make sure the server gets the packet
	if( client->cNetChan != NULL)
		for(int i=0;i<3;i++)
			client->cNetChan->Transmit(&bs);
};

void CClientNetEngine::SendFileData()
{
	CBytestream bs;
	bs.writeByte(C2S_SENDFILE);
	client->getUdpFileDownloader()->send(&bs);
	client->getChannel()->AddReliablePacketToSend(bs);
};

<commit_msg>additional comment for chat command /me<commit_after>/////////////////////////////////////////
//
//             OpenLieroX
//
// code under LGPL, based on JasonBs work,
// enhanced by Dark Charlie and Albert Zeyer
//
//
/////////////////////////////////////////

// Client class - Sending routines
// Created 22/7/02
// Jason Boettcher

#include "CClientNetEngine.h"
#include "LieroX.h"
#include "StringUtils.h"
#include "CClient.h"
#include "Protocol.h"
#include "CWorm.h"
#include "MathLib.h"
#include "ChatCommand.h"
#include "Command.h"
#include "EndianSwap.h"
#include "CServer.h"
#include "AuxLib.h"

///////////////////
// Send the worm details
void CClientNetEngine::SendWormDetails(void)
{
	// Don't flood packets so often
	// we are checking in w->checkPacketNeeded() if we need to send an update
	// we are checking with bandwidth if we should add an update
	/*if ((tLX->fCurTime - fLastUpdateSent) <= tLXOptions->fUpdatePeriod)
		if (tGameInfo.iGameType != GME_LOCAL)
			return; */

	CBytestream bs;
	uint i;

	// If all my worms are dead, don't send
	bool Alive = false;
	for(i=0;i<client->iNumWorms;i++) {
		if(client->cLocalWorms[i]->getAlive()) {
			Alive = true;
			break;
		}
	}

	if(!Alive)
		return;

	// Check if we need to write the state update
/*	bool update = false;
	w = cLocalWorms[0];
	for(i = 0; i < iNumWorms; i++, w++)
		if (w->checkPacketNeeded())  {
			update = true;
			break;
		}

	// No update, just quit
	if (!update)
		return;
*/
	if(	tGameInfo.iGameType == GME_JOIN // we are a client in a netgame
	&& !GameServer::checkUploadBandwidth(client->getChannel()->getOutgoingRate()) )
		return;

	client->fLastUpdateSent = tLX->fCurTime;

	// Write the update
	bs.writeByte(C2S_UPDATE);

	for(i = 0; i < client->iNumWorms; i++)
		client->cLocalWorms[i]->writePacket(&bs, false, NULL);

	client->bsUnreliable.Append(&bs);
}

void CClientNetEngine::SendGameReady()
{
	CBytestream bs;
	bs.writeByte(C2S_IMREADY);
	bs.writeByte(client->iNumWorms);

	// Send my worm's weapon details
	for(unsigned int i=0;i<client->iNumWorms;i++)
		client->cLocalWorms[i]->writeWeapons( &bs );

	client->cNetChan->AddReliablePacketToSend(bs);
}

///////////////////
// Send a death
void CClientNetEngine::SendDeath(int victim, int killer)
{
	CBytestream bs;

	bs.writeByte(C2S_DEATH);
	bs.writeInt(victim,1);
	bs.writeInt(killer,1);

	client->cNetChan->AddReliablePacketToSend(bs);
}

///////////////////
// Send a string of text
void CClientNetEngine::SendText(const std::string& sText, std::string sWormName)
{
	if(HandleDebugCommand(sText)) return;

	bool chat_command = sText.size() >= 2 && sText[0] == '/' && sText[1] != '/';

	// We can safely send long messages to servers >= beta8
	if (client->getServerVersion() >= OLXBetaVersion(8))  {

		// HTML support since beta 8
		SendTextInternal(OldLxCompatibleString(sText), sWormName);

	// <=beta7 server
	} else {

		if (chat_command &&
			client->getServerVersion() < OLXBetaVersion(3) && // <beta3 clients don't have chat command support
			sText.find("/me ") > 0) // Ignores "/me" special command
		{
			// Try if we can execute the same command in console (mainly for "/suicide" command to work on all servers)
			if( ! Cmd_ParseLine(sText.substr(1)) ) {
				client->cChatbox.AddText("HINT: server cannot execute commands, only OLX beta3+ can", tLX->clNotice, TXT_NOTICE, tLX->fCurTime);
			}
			return;

		} else if (chat_command &&
				   client->getServerVersion() >= OLXBetaVersion(3)) {
			// we don't have to split chat commands
			// "/me ..." is also save, because server uses SendGlobalText for sending and it splits the text there
			SendTextInternal(OldLxCompatibleString(sText), sWormName);

		} else { // we can savely split the message (and we have to)

			// If the text is too long, split it in smaller pieces and then send (backward comaptibility)
			int name_w = tLX->cFont.GetWidth(sWormName + ": ");
			const std::vector<std::string>& split = splitstring(StripHtmlTags(sText), 63,
				client->iNetStatus == NET_CONNECTED ? 600 - name_w : 300 - name_w, tLX->cFont);

			// Check
			if (split.size() == 0)  {  // More than weird...
				SendTextInternal(OldLxCompatibleString(StripHtmlTags(sText)), sWormName);
				return;
			}

			// Send the first chunk
			SendTextInternal(OldLxCompatibleString(split[0]), sWormName);

			// Send the text
			for (std::vector<std::string>::const_iterator it = split.begin() + 1; it != split.end(); it++)
				SendTextInternal(OldLxCompatibleString(*it), "");
		}
	}
}

void CClientNetEngineBeta7::SendChatCommandCompletionRequest(const std::string& startStr) {
	CBytestream bs;
	bs.writeByte(C2S_CHATCMDCOMPLREQ);
	bs.writeString(startStr);

	client->cNetChan->AddReliablePacketToSend(bs);
}

void CClientNetEngineBeta7::SendAFK(int wormid, AFK_TYPE afkType, const std::string & message ) {
	if( client->getServerVersion() < OLXBetaVersion(7) )
		return;

	std::string msg = message;
	if( msg == "" )
	{
		if( afkType == AFK_TYPING_CHAT )
			msg = "(typing)";
		else
		if( afkType == AFK_AWAY )
			msg = "(away)";
	};

	for( int i=0; i < client->getNumWorms(); i++ )
		if(	client->getWorm(i)->getID() == wormid )
			client->getWorm(i)->setAFK(afkType, msg);

	CBytestream bs;
	bs.writeByte(C2S_AFK);
	bs.writeByte(wormid);
	bs.writeByte(afkType);
	if(msg.size() > 127)
		msg = msg.substr(0, 127);
	bs.writeString(msg);

	client->cNetChan->AddReliablePacketToSend(bs);
}

/////////////////////
// Internal function for text sending, does not do any checks or parsing
void CClientNetEngine::SendTextInternal(const std::string& sText, const std::string& sWormName)
{
	CBytestream bs;
	bs.writeByte(C2S_CHATTEXT);
	if (sWormName.size() == 0)
		bs.writeString(sText);
	else
		bs.writeString(sWormName + ": " + sText);

	client->cNetChan->AddReliablePacketToSend(bs);
}

#ifdef FUZZY_ERROR_TESTING
//////////////////
// Send a random packet to server (used for debugging)
void CClientNetEngine::SendRandomPacket()
{
	CBytestream bs;

	int random_length = GetRandomInt(50);
	for (int i=0; i < random_length; i++)
		bs.writeByte((uchar)GetRandomInt(255));

	client->cNetChan->AddReliablePacketToSend(bs);

	bs.Clear();

	// Test connectionless packets
	if (GetRandomInt(100) >= 75)  {
		bs.writeInt(-1, 4);
		static const std::string commands[] = { "lx::getchallenge", "lx::connect", "lx::ping", "lx::query",
			"lx::getinfo", "lx::wantsjoin", "lx::traverse", "lx::registered"};
		bs.writeString(commands[GetRandomInt(500) % (sizeof(commands)/sizeof(std::string))]);
		for (int i=0; i < random_length; i++)
			bs.writeByte((uchar)GetRandomInt(255));
		SetRemoteNetAddr(tSocket, client->cNetChan->getAddress());
		bs.Send(tSocket);
	}
}
#endif

void CClientNetEngine::SendGrabBonus(int id, int wormid)
{
	CBytestream bs;
	bs.writeByte(C2S_GRABBONUS);
	bs.writeByte(id);
	bs.writeByte(wormid);
	bs.writeByte(client->cRemoteWorms[wormid].getCurrentWeapon());

	client->cNetChan->AddReliablePacketToSend(bs);
};

void CClientNetEngine::SendUpdateLobby(bool ready)
{
	CBytestream bs;
	bs.writeByte(C2S_UPDATELOBBY);
	bs.writeByte(1);
	client->getChannel()->AddReliablePacketToSend(bs);
};

void CClientNetEngine::SendDisconnect()
{
	CBytestream bs;

	bs.writeByte(C2S_DISCONNECT);

	// Send the pack a few times to make sure the server gets the packet
	if( client->cNetChan != NULL)
		for(int i=0;i<3;i++)
			client->cNetChan->Transmit(&bs);
};

void CClientNetEngine::SendFileData()
{
	CBytestream bs;
	bs.writeByte(C2S_SENDFILE);
	client->getUdpFileDownloader()->send(&bs);
	client->getChannel()->AddReliablePacketToSend(bs);
};

<|endoftext|>


<commit_before>//! @Yue Wang
//!
//! Exercise 11.24:
//! What does the following program do?
//!     map<int, int> m;
//!     m[0] = 1;
//  add a key-value pair { 0, 1 } into the map.
//!
//! Exercise 11.25:
//! Contrast the following program with the one in the previous exercise
//!     vector<int> v;
//!     v[0] = 1;
//  UB, since it's trying to dereference an item out of range.
//!
//! Exercise 11.26:
//! What type can be used to subscript a map? What type does the subscript
//! operator return? Give a concrete example—that is, define a map and then
//! write the types that can be used to subscript the map and the type that
//! would be returned from the subscript operator.
//!

#include <iostream>
#include <map>
#include <string>
#include <typeinfo>

int main()
{
	//! ex11.26
	std::map<int, std::string> m = { { 1,"ss" },{ 2,"sz" } };
	using KeyType = std::map<int, std::string>::key_type;

	std::cout << "type to subscript: " << typeid(KeyType).name() << std::endl;
	std::cout << "returned from the subscript operator: " << typeid(decltype(m[1])).name() << std::endl;

	return 0;
}
<commit_msg>Update ex11_24_25_26.cpp<commit_after>//! @Yue Wang
//!
//! Exercise 11.24:
//! What does the following program do?
//!     map<int, int> m;
//!     m[0] = 1;
//  add a key-value pair { 0, 1 } into the map.
//!
//! Exercise 11.25:
//! Contrast the following program with the one in the previous exercise
//!     vector<int> v;
//!     v[0] = 1;
//  UB, since it's trying to dereference an item out of range.
//!
//! Exercise 11.26:
//! What type can be used to subscript a map? What type does the subscript
//! operator return? Give a concrete example—that is, define a map and then
//! write the types that can be used to subscript the map and the type that
//! would be returned from the subscript operator.
//!

#include <iostream>
#include <map>
#include <string>
#include <typeinfo>

int main()
{
    //! ex11.26
    std::map<int, std::string> m = { { 1,"ss" },{ 2,"sz" } };
    using KeyType = std::map<int, std::string>::key_type;

    std::cout << "type to subscript: " << typeid(KeyType).name() << std::endl;
    std::cout << "returned from the subscript operator: " << typeid(decltype(m[1])).name() << std::endl;

    return 0;
}
<|endoftext|>


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

// CLASS HEADER
#include "style-manager-impl.h"

// EXTERNAL INCLUDES
#include <dali/devel-api/adaptor-framework/singleton-service.h>
#include <dali/public-api/object/type-registry.h>
#include <dali/devel-api/object/type-registry-helper.h>
#include <dali/integration-api/debug.h>

// INTERNAL INCLUDES
#include <dali-toolkit/public-api/controls/control.h>
#include <dali-toolkit/public-api/controls/control-impl.h>
#include <dali-toolkit/devel-api/styling/style-manager.h>
#include <dali-toolkit/internal/feedback/feedback-style.h>

namespace
{

const char* LANDSCAPE_QUALIFIER = "landscape";
const char* PORTRAIT_QUALIFIER  = "portrait";
const char* FONT_SIZE_QUALIFIER = "font-size-";

const char* DEFAULT_THEME = DALI_STYLE_DIR "dali-toolkit-default-theme.json";

const char* PACKAGE_PATH_KEY = "PACKAGE_PATH";
const char* DEFAULT_PACKAGE_PATH = DALI_DATA_READ_ONLY_DIR "/toolkit/";

} // namespace

namespace Dali
{

namespace Toolkit
{

namespace Internal
{

namespace
{

BaseHandle Create()
{
  BaseHandle handle = StyleManager::Get();

  if ( !handle )
  {
    SingletonService singletonService( SingletonService::Get() );
    if ( singletonService )
    {
      Toolkit::StyleManager manager = Toolkit::StyleManager( new Internal::StyleManager() );
      singletonService.Register( typeid( manager ), manager );
      handle = manager;
    }
  }

  return handle;
}

DALI_TYPE_REGISTRATION_BEGIN_CREATE( Toolkit::StyleManager, Dali::BaseHandle, Create, true )
DALI_TYPE_REGISTRATION_END()

} // namespace

Toolkit::StyleManager StyleManager::Get()
{
  Toolkit::StyleManager manager;

  SingletonService singletonService( SingletonService::Get() );
  if ( singletonService )
  {
    // Check whether the style manager is already created
    Dali::BaseHandle handle = singletonService.GetSingleton( typeid( Toolkit::StyleManager ) );
    if( handle )
    {
      // If so, downcast the handle of singleton
      manager = Toolkit::StyleManager( dynamic_cast< StyleManager* >( handle.GetObjectPtr() ) );
    }
  }

  return manager;
}

StyleManager::StyleManager()
: mOrientationDegrees( 0 ),  // Portrait
  mDefaultFontSize( -1 ),
  mThemeFile( DEFAULT_THEME ),
  mFeedbackStyle( NULL )
{
  // Add theme builder constants
  mThemeBuilderConstants[ PACKAGE_PATH_KEY ] = DEFAULT_PACKAGE_PATH;

  mStyleMonitor = StyleMonitor::Get();
  if( mStyleMonitor )
  {
    mStyleMonitor.StyleChangeSignal().Connect( this, &StyleManager::StyleMonitorChange );

    mDefaultFontSize = mStyleMonitor.GetDefaultFontSize();
  }

  // Sound & haptic style
  mFeedbackStyle = new FeedbackStyle();

}

StyleManager::~StyleManager()
{
  delete mFeedbackStyle;
}

void StyleManager::SetOrientationValue( int orientation )
{
  if( orientation !=  mOrientationDegrees )
  {
    mOrientationDegrees = orientation;
    // TODO: if orientation changed, apply the new style to all controls
    // dont want to really do the whole load from file again if the bundle contains both portrait & landscape
    SetTheme();
  }
}

int StyleManager::GetOrientationValue()
{
  return mOrientationDegrees;
}

void StyleManager::SetOrientation( Orientation orientation )
{
  if( mOrientation )
  {
    mOrientation.ChangedSignal().Disconnect( this, &StyleManager::OnOrientationChanged );
  }

  OnOrientationChanged( orientation );

  if( mOrientation )
  {
    mOrientation.ChangedSignal().Connect( this, &StyleManager::OnOrientationChanged );
  }
}

Orientation StyleManager::GetOrientation()
{
  return mOrientation;
}

void StyleManager::SetStyleConstant( const std::string& key, const Property::Value& value )
{
  mStyleBuilderConstants[ key ] = value;
}

bool StyleManager::GetStyleConstant( const std::string& key, Property::Value& valueOut )
{
  Property::Value* value = mStyleBuilderConstants.Find( key );
  if( value )
  {
    valueOut = *value;
    return true;
  }

  return false;
}

void StyleManager::OnOrientationChanged( Orientation orientation )
{
  mOrientation = orientation;
  // TODO: if orientation changed, apply the new style to all controls
  // dont want to really do the whole load from file again if the bundle contains both portrait & landscape
  SetTheme();
}

Toolkit::Builder StyleManager::CreateBuilder( const Property::Map& constants )
{
  Toolkit::Builder builder = Toolkit::Builder::New();
  builder.AddConstants( constants );

  return builder;
}

bool StyleManager::LoadJSON( Toolkit::Builder builder, const std::string& jsonFilePath )
{
  std::string fileString;
  if( LoadFile( jsonFilePath, fileString ) )
  {
    builder.LoadFromString( fileString );
    return true;
  }
  else
  {
    DALI_LOG_WARNING("Error loading file '%s'\n", jsonFilePath.c_str());
    return false;
  }
}

void StyleManager::CollectQualifiers( StringList& qualifiersOut )
{
  // Append the relevant qualifier for orientation
  int orientation = mOrientationDegrees;

  if( mOrientation )
  {
    orientation = mOrientation.GetDegrees();
  }

  switch( orientation )
  {
    case 90:
    case 270:
    {
      qualifiersOut.push_back( std::string( LANDSCAPE_QUALIFIER ) );
      break;
    }
    case 180:
    case 0: // fall through
    default:
    {
      qualifiersOut.push_back( std::string( PORTRAIT_QUALIFIER ) );
      break;
    }
  }
}

void StyleManager::BuildQualifiedStyleName( const std::string& styleName, const StringList& qualifiers, std::string& qualifiedStyleOut )
{
  qualifiedStyleOut.append( styleName );

  for( StringList::const_iterator it = qualifiers.begin(), itEnd = qualifiers.end(); it != itEnd; ++it )
  {
    const std::string& str = *it;

    qualifiedStyleOut.append( "-" );
    qualifiedStyleOut.append( str );
  }
}

void StyleManager::ApplyStyle( Toolkit::Builder builder, Toolkit::Control control )
{
  std::string styleName = control.GetStyleName();

  if( styleName.empty() )
  {
    // Convert control name to lower case
    styleName = control.GetTypeName();
    std::transform( styleName.begin(), styleName.end(), styleName.begin(), ::tolower );
  }

  // Apply the style after choosing the correct actual style (e.g. landscape or portrait)
  StringList qualifiers;
  CollectQualifiers( qualifiers );

  while( true )
  {
    std::string qualifiedStyleName;
    BuildQualifiedStyleName( styleName, qualifiers, qualifiedStyleName );

    // Break if style found or we have tried the root style name (qualifiers is empty)
    if( builder.ApplyStyle( qualifiedStyleName, control ) || qualifiers.size() == 0 )
    {
      break;
    }

    // Remove the last qualifier in an attempt to find a style that is valid
    qualifiers.pop_back();
  }

  if( mDefaultFontSize >= 0 )
  {
    // Apply the style for logical font size
    std::stringstream fontSizeQualifier;
    fontSizeQualifier << styleName << "-" << FONT_SIZE_QUALIFIER << mDefaultFontSize;
    builder.ApplyStyle( fontSizeQualifier.str(), control );
  }
}

void StyleManager::ApplyThemeStyle( Toolkit::Control control )
{
  if( !mThemeBuilder )
  {
    RequestDefaultTheme();
  }

  if( mThemeBuilder )
  {
    ApplyStyle( mThemeBuilder, control );
  }
}

void StyleManager::ApplyThemeStyleAtInit( Toolkit::Control control )
{
  if( mThemeBuilder )
  {
    ApplyStyle( mThemeBuilder, control );
  }

  if(mFeedbackStyle)
  {
    mFeedbackStyle->ObjectCreated( control );
  }
}

void StyleManager::ApplyStyle( Toolkit::Control control, const std::string& jsonFileName, const std::string& styleName )
{
  bool builderReady = false;

  // First look in the cache
  Toolkit::Builder builder = FindCachedBuilder( jsonFileName );
  if( builder )
  {
    builderReady = true;
  }
  else
  {
    // Merge theme and style constants
    Property::Map constants( mThemeBuilderConstants );
    constants.Merge( mStyleBuilderConstants );

    // Create it
    builder = CreateBuilder( constants );

    if( LoadJSON( builder, jsonFileName ) )
    {
      CacheBuilder( builder, jsonFileName );
      builderReady = true;
    }
  }

  // Apply the style to the control
  if( builderReady )
  {
    builder.ApplyStyle( styleName, control );
  }
}

bool StyleManager::LoadFile( const std::string& filename, std::string& stringOut )
{
  DALI_ASSERT_DEBUG( 0 != filename.length());

  // as toolkit is platform agnostic, it cannot load files from filesystem
  // ask style monitor to load the style sheet
  if( mStyleMonitor )
  {
    return mStyleMonitor.LoadThemeFile( filename, stringOut );
  }

  return false;
}

Toolkit::StyleManager::StyleChangeSignalType& StyleManager::StyleChangeSignal()
{
  return mStyleChangeSignal;
}

void StyleManager::RequestThemeChange( const std::string& themeFile )
{
  mThemeFile = themeFile;

  // need to do style change synchronously as app might create a UI control on the next line
  SetTheme();
}

void StyleManager::RequestDefaultTheme()
{
  RequestThemeChange( DEFAULT_THEME );
}

void StyleManager::SetTheme()
{
  mThemeBuilder = CreateBuilder( mThemeBuilderConstants );

  if( LoadJSON( mThemeBuilder, mThemeFile ) )
  {
    if(mFeedbackStyle)
    {
      mFeedbackStyle->StyleChanged( mThemeFile, StyleChange::THEME_CHANGE );
    }

    mStyleChangeSignal.Emit( Toolkit::StyleManager::Get(), StyleChange::THEME_CHANGE );
  }
  else
  {
    mThemeBuilder.Reset();
  }
}

Toolkit::Builder StyleManager::FindCachedBuilder( const std::string& key )
{
  BuilderMap::iterator builderIt = mBuilderCache.find( key );
  if( builderIt != mBuilderCache.end() )
  {
    return builderIt->second;
  }

  return Toolkit::Builder();
}

void StyleManager::CacheBuilder( Toolkit::Builder builder, const std::string& key )
{
  mBuilderCache[ key ] = builder;
}

void StyleManager::StyleMonitorChange( StyleMonitor styleMonitor, StyleChange::Type styleChange )
{
  switch ( styleChange )
  {
    case StyleChange::DEFAULT_FONT_CHANGE:
    {
      break;
    }

    case StyleChange::DEFAULT_FONT_SIZE_CHANGE:
    {
      mDefaultFontSize = styleMonitor.GetDefaultFontSize();
      break;
    }

    case StyleChange::THEME_CHANGE:
    {
      const std::string& newTheme = styleMonitor.GetTheme();
      if( ! newTheme.empty() )
      {
        mThemeFile = newTheme;
      }
      else
      {
        mThemeFile = DEFAULT_THEME;
      }

      SetTheme();
      break;
    }
  }

  mStyleChangeSignal.Emit( Toolkit::StyleManager::Get(), styleChange );
}

} // namespace Internal

} // namespace Toolkit

} // namespace Dali
<commit_msg>Style Manager to create ThemeBuilder before Applying Style<commit_after>/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// CLASS HEADER
#include "style-manager-impl.h"

// EXTERNAL INCLUDES
#include <dali/devel-api/adaptor-framework/singleton-service.h>
#include <dali/public-api/object/type-registry.h>
#include <dali/devel-api/object/type-registry-helper.h>
#include <dali/integration-api/debug.h>

// INTERNAL INCLUDES
#include <dali-toolkit/public-api/controls/control.h>
#include <dali-toolkit/public-api/controls/control-impl.h>
#include <dali-toolkit/devel-api/styling/style-manager.h>
#include <dali-toolkit/internal/feedback/feedback-style.h>

namespace
{

const char* LANDSCAPE_QUALIFIER = "landscape";
const char* PORTRAIT_QUALIFIER  = "portrait";
const char* FONT_SIZE_QUALIFIER = "font-size-";

const char* DEFAULT_THEME = DALI_STYLE_DIR "dali-toolkit-default-theme.json";

const char* PACKAGE_PATH_KEY = "PACKAGE_PATH";
const char* DEFAULT_PACKAGE_PATH = DALI_DATA_READ_ONLY_DIR "/toolkit/";

} // namespace

namespace Dali
{

namespace Toolkit
{

namespace Internal
{

namespace
{

BaseHandle Create()
{
  BaseHandle handle = StyleManager::Get();

  if ( !handle )
  {
    SingletonService singletonService( SingletonService::Get() );
    if ( singletonService )
    {
      Toolkit::StyleManager manager = Toolkit::StyleManager( new Internal::StyleManager() );
      singletonService.Register( typeid( manager ), manager );
      handle = manager;
    }
  }

  return handle;
}

DALI_TYPE_REGISTRATION_BEGIN_CREATE( Toolkit::StyleManager, Dali::BaseHandle, Create, true )
DALI_TYPE_REGISTRATION_END()

} // namespace

Toolkit::StyleManager StyleManager::Get()
{
  Toolkit::StyleManager manager;

  SingletonService singletonService( SingletonService::Get() );
  if ( singletonService )
  {
    // Check whether the style manager is already created
    Dali::BaseHandle handle = singletonService.GetSingleton( typeid( Toolkit::StyleManager ) );
    if( handle )
    {
      // If so, downcast the handle of singleton
      manager = Toolkit::StyleManager( dynamic_cast< StyleManager* >( handle.GetObjectPtr() ) );
    }
  }

  return manager;
}

StyleManager::StyleManager()
: mOrientationDegrees( 0 ),  // Portrait
  mDefaultFontSize( -1 ),
  mThemeFile( DEFAULT_THEME ),
  mFeedbackStyle( NULL )
{
  // Add theme builder constants
  mThemeBuilderConstants[ PACKAGE_PATH_KEY ] = DEFAULT_PACKAGE_PATH;

  mStyleMonitor = StyleMonitor::Get();
  if( mStyleMonitor )
  {
    mStyleMonitor.StyleChangeSignal().Connect( this, &StyleManager::StyleMonitorChange );

    mDefaultFontSize = mStyleMonitor.GetDefaultFontSize();
  }

  // Sound & haptic style
  mFeedbackStyle = new FeedbackStyle();

}

StyleManager::~StyleManager()
{
  delete mFeedbackStyle;
}

void StyleManager::SetOrientationValue( int orientation )
{
  if( orientation !=  mOrientationDegrees )
  {
    mOrientationDegrees = orientation;
    // TODO: if orientation changed, apply the new style to all controls
    // dont want to really do the whole load from file again if the bundle contains both portrait & landscape
    SetTheme();
  }
}

int StyleManager::GetOrientationValue()
{
  return mOrientationDegrees;
}

void StyleManager::SetOrientation( Orientation orientation )
{
  if( mOrientation )
  {
    mOrientation.ChangedSignal().Disconnect( this, &StyleManager::OnOrientationChanged );
  }

  OnOrientationChanged( orientation );

  if( mOrientation )
  {
    mOrientation.ChangedSignal().Connect( this, &StyleManager::OnOrientationChanged );
  }
}

Orientation StyleManager::GetOrientation()
{
  return mOrientation;
}

void StyleManager::SetStyleConstant( const std::string& key, const Property::Value& value )
{
  mStyleBuilderConstants[ key ] = value;
}

bool StyleManager::GetStyleConstant( const std::string& key, Property::Value& valueOut )
{
  Property::Value* value = mStyleBuilderConstants.Find( key );
  if( value )
  {
    valueOut = *value;
    return true;
  }

  return false;
}

void StyleManager::OnOrientationChanged( Orientation orientation )
{
  mOrientation = orientation;
  // TODO: if orientation changed, apply the new style to all controls
  // dont want to really do the whole load from file again if the bundle contains both portrait & landscape
  SetTheme();
}

Toolkit::Builder StyleManager::CreateBuilder( const Property::Map& constants )
{
  Toolkit::Builder builder = Toolkit::Builder::New();
  builder.AddConstants( constants );

  return builder;
}

bool StyleManager::LoadJSON( Toolkit::Builder builder, const std::string& jsonFilePath )
{
  std::string fileString;
  if( LoadFile( jsonFilePath, fileString ) )
  {
    builder.LoadFromString( fileString );
    return true;
  }
  else
  {
    DALI_LOG_WARNING("Error loading file '%s'\n", jsonFilePath.c_str());
    return false;
  }
}

void StyleManager::CollectQualifiers( StringList& qualifiersOut )
{
  // Append the relevant qualifier for orientation
  int orientation = mOrientationDegrees;

  if( mOrientation )
  {
    orientation = mOrientation.GetDegrees();
  }

  switch( orientation )
  {
    case 90:
    case 270:
    {
      qualifiersOut.push_back( std::string( LANDSCAPE_QUALIFIER ) );
      break;
    }
    case 180:
    case 0: // fall through
    default:
    {
      qualifiersOut.push_back( std::string( PORTRAIT_QUALIFIER ) );
      break;
    }
  }
}

void StyleManager::BuildQualifiedStyleName( const std::string& styleName, const StringList& qualifiers, std::string& qualifiedStyleOut )
{
  qualifiedStyleOut.append( styleName );

  for( StringList::const_iterator it = qualifiers.begin(), itEnd = qualifiers.end(); it != itEnd; ++it )
  {
    const std::string& str = *it;

    qualifiedStyleOut.append( "-" );
    qualifiedStyleOut.append( str );
  }
}

void StyleManager::ApplyStyle( Toolkit::Builder builder, Toolkit::Control control )
{
  std::string styleName = control.GetStyleName();

  if( styleName.empty() )
  {
    // Convert control name to lower case
    styleName = control.GetTypeName();
    std::transform( styleName.begin(), styleName.end(), styleName.begin(), ::tolower );
  }

  // Apply the style after choosing the correct actual style (e.g. landscape or portrait)
  StringList qualifiers;
  CollectQualifiers( qualifiers );

  while( true )
  {
    std::string qualifiedStyleName;
    BuildQualifiedStyleName( styleName, qualifiers, qualifiedStyleName );

    // Break if style found or we have tried the root style name (qualifiers is empty)
    if( builder.ApplyStyle( qualifiedStyleName, control ) || qualifiers.size() == 0 )
    {
      break;
    }

    // Remove the last qualifier in an attempt to find a style that is valid
    qualifiers.pop_back();
  }

  if( mDefaultFontSize >= 0 )
  {
    // Apply the style for logical font size
    std::stringstream fontSizeQualifier;
    fontSizeQualifier << styleName << "-" << FONT_SIZE_QUALIFIER << mDefaultFontSize;
    builder.ApplyStyle( fontSizeQualifier.str(), control );
  }
}

void StyleManager::ApplyThemeStyle( Toolkit::Control control )
{
  if( !mThemeBuilder )
  {
    RequestDefaultTheme();
  }

  if( mThemeBuilder )
  {
    ApplyStyle( mThemeBuilder, control );
  }
}

void StyleManager::ApplyThemeStyleAtInit( Toolkit::Control control )
{
  ApplyThemeStyle( control );

  if(mFeedbackStyle)
  {
    mFeedbackStyle->ObjectCreated( control );
  }
}

void StyleManager::ApplyStyle( Toolkit::Control control, const std::string& jsonFileName, const std::string& styleName )
{
  bool builderReady = false;

  // First look in the cache
  Toolkit::Builder builder = FindCachedBuilder( jsonFileName );
  if( builder )
  {
    builderReady = true;
  }
  else
  {
    // Merge theme and style constants
    Property::Map constants( mThemeBuilderConstants );
    constants.Merge( mStyleBuilderConstants );

    // Create it
    builder = CreateBuilder( constants );

    if( LoadJSON( builder, jsonFileName ) )
    {
      CacheBuilder( builder, jsonFileName );
      builderReady = true;
    }
  }

  // Apply the style to the control
  if( builderReady )
  {
    builder.ApplyStyle( styleName, control );
  }
}

bool StyleManager::LoadFile( const std::string& filename, std::string& stringOut )
{
  DALI_ASSERT_DEBUG( 0 != filename.length());

  // as toolkit is platform agnostic, it cannot load files from filesystem
  // ask style monitor to load the style sheet
  if( mStyleMonitor )
  {
    return mStyleMonitor.LoadThemeFile( filename, stringOut );
  }

  return false;
}

Toolkit::StyleManager::StyleChangeSignalType& StyleManager::StyleChangeSignal()
{
  return mStyleChangeSignal;
}

void StyleManager::RequestThemeChange( const std::string& themeFile )
{
  mThemeFile = themeFile;

  // need to do style change synchronously as app might create a UI control on the next line
  SetTheme();
}

void StyleManager::RequestDefaultTheme()
{
  RequestThemeChange( DEFAULT_THEME );
}

void StyleManager::SetTheme()
{
  mThemeBuilder = CreateBuilder( mThemeBuilderConstants );

  if( LoadJSON( mThemeBuilder, mThemeFile ) )
  {
    if(mFeedbackStyle)
    {
      mFeedbackStyle->StyleChanged( mThemeFile, StyleChange::THEME_CHANGE );
    }

    mStyleChangeSignal.Emit( Toolkit::StyleManager::Get(), StyleChange::THEME_CHANGE );
  }
  else
  {
    mThemeBuilder.Reset();
  }
}

Toolkit::Builder StyleManager::FindCachedBuilder( const std::string& key )
{
  BuilderMap::iterator builderIt = mBuilderCache.find( key );
  if( builderIt != mBuilderCache.end() )
  {
    return builderIt->second;
  }

  return Toolkit::Builder();
}

void StyleManager::CacheBuilder( Toolkit::Builder builder, const std::string& key )
{
  mBuilderCache[ key ] = builder;
}

void StyleManager::StyleMonitorChange( StyleMonitor styleMonitor, StyleChange::Type styleChange )
{
  switch ( styleChange )
  {
    case StyleChange::DEFAULT_FONT_CHANGE:
    {
      break;
    }

    case StyleChange::DEFAULT_FONT_SIZE_CHANGE:
    {
      mDefaultFontSize = styleMonitor.GetDefaultFontSize();
      break;
    }

    case StyleChange::THEME_CHANGE:
    {
      const std::string& newTheme = styleMonitor.GetTheme();
      if( ! newTheme.empty() )
      {
        mThemeFile = newTheme;
      }
      else
      {
        mThemeFile = DEFAULT_THEME;
      }

      SetTheme();
      break;
    }
  }

  mStyleChangeSignal.Emit( Toolkit::StyleManager::Get(), styleChange );
}

} // namespace Internal

} // namespace Toolkit

} // namespace Dali
<|endoftext|>