#include "pch.h"
#include "catch.hpp"
#include "winrt/Component.h"

extern "C" BOOL __stdcall RoOriginateLanguageException(HRESULT error, void* message, void* languageException);

using namespace winrt;
using namespace Windows::Foundation;
using namespace Component;

template <typename T>
void test_exception(HRESULT const code, std::wstring_view message)
{
    R

    try
    {
        throw T();
    }
    catch (T const& e)
    {
        REQUIRE(code == e.code());
        REQUIRE(message == e.message());
    }

    try
    {
        throw T(hresult_error::from_abi);
    }
    catch (T const& e)
    {
        REQUIRE(code == e.code());
        REQUIRE(message == e.message());
    }

    try
    {
        throw T(L"custom message");
    }
    catch (T const& e)
    {
        REQUIRE(code == e.code());
        REQUIRE(L"custom message" == e.message());
    }

    try
    {
        RoOriginateLanguageException(code, get_abi(hstring(L"Correctly matched error info")), nullptr);
        throw T(hresult_error::from_abi);
    }
    catch (T const& e)
    {
        REQUIRE(code == e.code());
        REQUIRE(L"Correctly matched error info" == e.message());
    }

    try
    {
        RoOriginateLanguageException(E_FAIL, get_abi(hstring(L"Incorrectly matched error info")), nullptr);
        throw T(hresult_error::from_abi);
    }
    catch (T const& e)
    {
        REQUIRE(code == e.code());
        REQUIRE(message == e.message());
    }
}

#pragma warning(disable: 4702)  // unreachable code
TEST_CASE("Errors")
{
    check_hresult(S_OK);
    check_hresult(S_FALSE);

    // These are our HRESULT mappings.
    test_exception<hresult_access_denied>(E_ACCESSDENIED, L"Access is denied.");
    test_exception<hresult_class_not_available>(CLASS_E_CLASSNOTAVAILABLE, L"ClassFactory cannot supply requested class");
    test_exception<hresult_wrong_thread>(RPC_E_WRONG_THREAD, L"The application called an interface that was marshalled for a different thread.");
    test_exception<hresult_not_implemented>(E_NOTIMPL, L"Not implemented");
    test_exception<hresult_invalid_argument>(E_INVALIDARG, L"The parameter is incorrect.");
    test_exception<hresult_out_of_bounds>(E_BOUNDS, L"The operation attempted to access data outside the valid range");
    test_exception<hresult_no_interface>(E_NOINTERFACE, L"No such interface supported");
    test_exception<hresult_changed_state>(E_CHANGED_STATE, L"A concurrent or interleaved operation changed the state of the object, invalidating this operation.");
    test_exception<hresult_illegal_method_call>(E_ILLEGAL_METHOD_CALL, L"A method was called at an unexpected time.");
    test_exception<hresult_illegal_state_change>(E_ILLEGAL_STATE_CHANGE, L"An illegal state change was requested.");
    test_exception<hresult_illegal_delegate_assignment>(E_ILLEGAL_DELEGATE_ASSIGNMENT, L"A delegate was assigned when not allowed.");
    test_exception<hresult_canceled>(HRESULT_FROM_WIN32(ERROR_CANCELLED), L"The operation was canceled by the user.");

    // Basic validation of init_apartment (from an MTA).
    init_apartment();

    try
    {
        init_apartment(apartment_type::single_threaded);
        FAIL(L"Previous line should throw");
    }
    catch (hresult_error const & e)
    {
        REQUIRE(e.code() == RPC_E_CHANGED_MODE);
    }

    // An error originates in a component and is consumed within C++/WinRT.
    try
    {
        Uri uri(L"BAD");
        FAIL(L"Previous line should throw");
    }
    catch (hresult_invalid_argument const & e) // catching specific exception type
    {
        REQUIRE(L"BAD is not a valid absolute URI." == e.message());
    }

    // The same with catching base exception type.
    try
    {
        Uri uri(L"BAD");
        FAIL(L"Previous line should throw");
    }
    catch (hresult_error const& e)
    {
        REQUIRE(E_INVALIDARG == e.code());
        REQUIRE(L"BAD is not a valid absolute URI." == e.message());
    }

    // The same but flying all the way through the projection through a WinRT component written in C++/WinRT.
    try
    {
        Errors errors;
        errors.Propagate();
        FAIL(L"Previous line should throw");
    }
    catch (hresult_invalid_argument const & e) // catching specific exception type
    {
        REQUIRE(L"BAD is not a valid absolute URI." == e.message());
    }

    // An error originating in the C++/WinRT component.
    try
    {
        Errors errors;
        errors.Fail(L"Failure message");
        FAIL(L"Previous line should throw");
    }
    catch (hresult_not_implemented const& e)
    {
        REQUIRE(L"Failure message" == e.message());
    }

    // Support for C++ standard library exceptions.

    {
        Errors errors;
        REQUIRE_THROWS_AS(errors.std_bad_alloc(), std::bad_alloc);
    }

    try
    {
        Errors errors;
        errors.std_out_of_range();
        FAIL(L"Previous line should throw");
    }
    catch (hresult_out_of_bounds const& e)
    {
        REQUIRE(e.message() == L"std_out_of_range");
    }

    try
    {
        Errors errors;
        errors.std_invalid_argument();
        FAIL(L"Previous line should throw");
    }
    catch (hresult_invalid_argument const& e)
    {
        REQUIRE(e.message() == L"std_invalid_argument");
    }

    try
    {
        Errors errors;
        errors.std_exception();
        FAIL(L"Previous line should throw");
    }
    catch (hresult_error const& e)
    {
        REQUIRE(e.code() == E_FAIL);
        REQUIRE(e.message() == L"std_exception");
    }

    SetLastError(ERROR_CANCELLED);
    REQUIRE_THROWS_AS(throw_last_error(), hresult_canceled);

    SetLastError(ERROR_CANCELLED);
    REQUIRE_THROWS_AS(check_bool(false), hresult_canceled);

    SetLastError(ERROR_CANCELLED);
    REQUIRE_THROWS_AS(check_bool(static_cast<BOOLEAN>(false)), hresult_canceled);

    SetLastError(ERROR_CANCELLED);
    REQUIRE_THROWS_AS(check_bool(static_cast<BOOL>(false)), hresult_canceled);

    // Support for Win32 errors.
    check_win32(ERROR_SUCCESS);
    REQUIRE_THROWS_AS(check_win32(ERROR_CANCELLED), hresult_canceled);

    try
    {
        check_win32(ERROR_NO_NETWORK);
        FAIL(L"Previous line should throw");
    }
    catch (hresult_error const& e)
    {
        REQUIRE(e.code() == HRESULT_FROM_WIN32(ERROR_NO_NETWORK));
    }

    // Support for NT errors.
    check_nt(0);

    try
    {
        check_nt(STATUS_STACK_OVERFLOW);
        FAIL(L"Previous line should throw");
    }
    catch (hresult_error const& e)
    {
        REQUIRE(e.code() == HRESULT_FROM_NT(STATUS_STACK_OVERFLOW));
    }

    // Make sure trimming works.
    hresult_error e(E_FAIL, L":) is \u263A \n \t ");
    REQUIRE(e.message() == L":) is \u263A");

    // Make sure delegates propagate correctly.

    {
        EventHandler<int> d = [](IInspectable const&, int)
        {
            throw hresult_no_interface();
        };

        REQUIRE_THROWS_AS(d(nullptr, 2), hresult_no_interface);
    }

    {
        TypedEventHandler<int, int> d = [](int, int)
        {
            throw hresult_no_interface();
        };

        REQUIRE_THROWS_AS(d(1,2), hresult_no_interface);
    }

    {
        ReturnDelegate d = []()-> hstring
        {
            throw hresult_no_interface();
        };

        REQUIRE_THROWS_AS(d(), hresult_no_interface);
    }
}


try
    {
        RoOriginateLanguageException(E_FAIL, get_abi(hstring(L"Incorrectly matched error info")), nullptr);
        throw T(hresult_error::from_abi);
    }
    catch (T const& e)
    {
        REQUIRE(code == e.code());
        REQUIRE(message == e.message());
    }

#include "pch.h"
#include "catch.hpp"
#include "winrt/Component.h"

extern "C" BOOL __stdcall RoOriginateLanguageException(HRESULT error, void* message, void* languageException);

using namespace winrt;
using namespace Windows::Foundation;
using namespace Component;

template <typename T>
void test_exception(HRESULT const code, std::wstring_view message)
{
    REQUIRE_THROWS_AS(check_hresult(code), T);

    try
    {
        throw T();
    }
    catch (T const& e)
    {
        REQUIRE(code == e.code());
        REQUIRE(message == e.message());
    }

    try
    {
        throw T(hresult_error::from_abi);
    }
    catch (T const& e)
    {
        REQUIRE(code == e.code());
        REQUIRE(message == e.message());
    }

    try
    {
        throw T(L"custom message");
    }
    catch (T const& e)
    {
        REQUIRE(code == e.code());
        REQUIRE(L"custom message" == e.message());
    }

    try
    {
        RoOriginateLanguageException(code, get_abi(hstring(L"Correctly matched error info")), nullptr);
        throw T(hresult_error::from_abi);
    }
    catch (T const& e)
    {
        REQUIRE(code == e.code());
        REQUIRE(L"Correctly matched error info" == e.message());
    }

}

#pragma warning(disable: 4702)  // unreachable code
TEST_CASE("Errors")
{
    check_hresult(S_OK);
    check_hresult(S_FALSE);

    // These are our HRESULT mappings.
    test_exception<hresult_access_denied>(E_ACCESSDENIED, L"Access is denied.");
    test_exception<hresult_class_not_available>(CLASS_E_CLASSNOTAVAILABLE, L"ClassFactory cannot supply requested class");
    test_exception<hresult_wrong_thread>(RPC_E_WRONG_THREAD, L"The application called an interface that was marshalled for a different thread.");
    test_exception<hresult_not_implemented>(E_NOTIMPL, L"Not implemented");
    test_exception<hresult_invalid_argument>(E_INVALIDARG, L"The parameter is incorrect.");
    test_exception<hresult_out_of_bounds>(E_BOUNDS, L"The operation attempted to access data outside the valid range");
    test_exception<hresult_no_interface>(E_NOINTERFACE, L"No such interface supported");
    test_exception<hresult_changed_state>(E_CHANGED_STATE, L"A concurrent or interleaved operation changed the state of the object, invalidating this operation.");
    test_exception<hresult_illegal_method_call>(E_ILLEGAL_METHOD_CALL, L"A method was called at an unexpected time.");
    test_exception<hresult_illegal_state_change>(E_ILLEGAL_STATE_CHANGE, L"An illegal state change was requested.");
    test_exception<hresult_illegal_delegate_assignment>(E_ILLEGAL_DELEGATE_ASSIGNMENT, L"A delegate was assigned when not allowed.");
    test_exception<hresult_canceled>(HRESULT_FROM_WIN32(ERROR_CANCELLED), L"The operation was canceled by the user.");

    // Basic validation of init_apartment (from an MTA).
    init_apartment();

    try
    {
        init_apartment(apartment_type::single_threaded);
        FAIL(L"Previous line should throw");
    }
    catch (hresult_error const & e)
    {
        REQUIRE(e.code() == RPC_E_CHANGED_MODE);
    }

    // An error originates in a component and is consumed within C++/WinRT.
    try
    {
        Uri uri(L"BAD");
        FAIL(L"Previous line should throw");
    }
    catch (hresult_invalid_argument const & e) // catching specific exception type
    {
        REQUIRE(L"BAD is not a valid absolute URI." == e.message());
    }

    // The same with catching base exception type.
    try
    {
        Uri uri(L"BAD");
        FAIL(L"Previous line should throw");
    }
    catch (hresult_error const& e)
    {
        REQUIRE(E_INVALIDARG == e.code());
        REQUIRE(L"BAD is not a valid absolute URI." == e.message());
    }

    // The same but flying all the way through the projection through a WinRT component written in C++/WinRT.
    try
    {
        Errors errors;
        errors.Propagate();
        FAIL(L"Previous line should throw");
    }
    catch (hresult_invalid_argument const & e) // catching specific exception type
    {
        REQUIRE(L"BAD is not a valid absolute URI." == e.message());
    }

    // An error originating in the C++/WinRT component.
    try
    {
        Errors errors;
        errors.Fail(L"Failure message");
        FAIL(L"Previous line should throw");
    }
    catch (hresult_not_implemented const& e)
    {
        REQUIRE(L"Failure message" == e.message());
    }

    // Support for C++ standard library exceptions.

    {
        Errors errors;
        REQUIRE_THROWS_AS(errors.std_bad_alloc(), std::bad_alloc);
    }

    try
    {
        Errors errors;
        errors.std_out_of_range();
        FAIL(L"Previous line should throw");
    }
    catch (hresult_out_of_bounds const& e)
    {
        REQUIRE(e.message() == L"std_out_of_range");
    }

    try
    {
        Errors errors;
        errors.std_invalid_argument();
        FAIL(L"Previous line should throw");
    }
    catch (hresult_invalid_argument const& e)
    {
        REQUIRE(e.message() == L"std_invalid_argument");
    }

    try
    {
        Errors errors;
        errors.std_exception();
        FAIL(L"Previous line should throw");
    }
    catch (hresult_error const& e)
    {
        REQUIRE(e.code() == E_FAIL);
        REQUIRE(e.message() == L"std_exception");
    }

    SetLastError(ERROR_CANCELLED);
    REQUIRE_THROWS_AS(throw_last_error(), hresult_canceled);

    SetLastError(ERROR_CANCELLED);
    REQUIRE_THROWS_AS(check_bool(false), hresult_canceled);

    SetLastError(ERROR_CANCELLED);
    REQUIRE_THROWS_AS(check_bool(static_cast<BOOLEAN>(false)), hresult_canceled);

    SetLastError(ERROR_CANCELLED);
    REQUIRE_THROWS_AS(check_bool(static_cast<BOOL>(false)), hresult_canceled);

    // Support for Win32 errors.
    check_win32(ERROR_SUCCESS);
    REQUIRE_THROWS_AS(check_win32(ERROR_CANCELLED), hresult_canceled);

    try
    {
        check_win32(ERROR_NO_NETWORK);
        FAIL(L"Previous line should throw");
    }
    catch (hresult_error const& e)
    {
        REQUIRE(e.code() == HRESULT_FROM_WIN32(ERROR_NO_NETWORK));
    }

    // Support for NT errors.
    check_nt(0);

    try
    {
        check_nt(STATUS_STACK_OVERFLOW);
        FAIL(L"Previous line should throw");
    }
    catch (hresult_error const& e)
    {
        REQUIRE(e.code() == HRESULT_FROM_NT(STATUS_STACK_OVERFLOW));
    }

    // Make sure trimming works.
    hresult_error e(E_FAIL, L":) is \u263A \n \t ");
    REQUIRE(e.message() == L":) is \u263A");

    // Make sure delegates propagate correctly.

    {
        EventHandler<int> d = [](IInspectable const&, int)
        {
            throw hresult_no_interface();
        };

        REQUIRE_THROWS_AS(d(nullptr, 2), hresult_no_interface);
    }

    {
        TypedEventHandler<int, int> d = [](int, int)
        {
            throw hresult_no_interface();
        };

        REQUIRE_THROWS_AS(d(1,2), hresult_no_interface);
    }

    {
        ReturnDelegate d = []()-> hstring
        {
            throw hresult_no_interface();
        };

        REQUIRE_THROWS_AS(d(), hresult_no_interface);
    }
}


// Implementation of SensorData class. Refer to .h file
// for the functional description
#include "fboss/platform/fan_service/SensorData.h"

namespace facebook::fboss::platform {
SensorData::SensorData() {}

SensorData::~SensorData() {}

SensorEntry* SensorData::getOrCreateSensorEntry(std::string name) {
  bool entryExist = checkIfEntryExists(name);
  SensorEntry* pEntry;
  if (!entryExist)
    sensorEntry_[name] = SensorEntry();
  pEntry = &sensorEntry_[name];
  return pEntry;
}

SensorEntry::~SensorEntry() {
  // Defining this to make unique_ptr happy
}

void SensorData::updateEntryInt(
    std::string name,
    int data,
    uint64_t timeStamp) {
  SensorEntry* pEntry = getOrCreateSensorEntry(name);
  pEntry->timeStampSec = timeStamp;
  pEntry->sensorEntryType = kSensorEntryInt;
  pEntry->value.intValue = data;
  return;
}
void SensorData::updateEntryFloat(
    std::string name,
    float data,
    uint64_t timeStamp) {
  SensorEntry* pEntry = getOrCreateSensorEntry(name);
  pEntry->timeStampSec = timeStamp;
  pEntry->sensorEntryType = kSensorEntryFloat;
  pEntry->value.floatValue = data;
  return;
}
int SensorData::getSensorDataInt(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryInt)
    throw facebook::fboss::FbossError("Sensor Entry is not Int: ", name);
  return pEntry->value.intValue;
}

std::vector<std::string> SensorData::getKeyLists() {
  std::vector<std::string> retVal;
  retVal.reserve(sensorEntry_.size());
  for (auto kv : sensorEntry_) {
    retVal.push_back(kv.first);
  }
  return retVal;
}

{
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryFloat)
    throw facebook::fboss::FbossError("Sensor Entry is not Float: ", name);
  return pEntry->sensorEntryType;
}

float SensorData::getSensorDataFloat(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryFloat)
    throw facebook::fboss::FbossError("Sensor Entry is not Float: ", name);
  return pEntry->value.floatValue;
}

uint64_t SensorData::getLastUpdated(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  return pEntry->timeStampSec;
}

bool SensorData::checkIfEntryExists(std::string name) {
  return (sensorEntry_.find(name) != sensorEntry_.end());
}

SensorEntry* SensorData::getSensorEntry(std::string name) {
  if (!checkIfEntryExists(name))
    return NULL;
  return &sensorEntry_[name];
}
} // namespace facebook::fboss::platform


{
  // Defining this to make unique_ptr happy
}

// Implementation of SensorData class. Refer to .h file
// for the functional description
#include "fboss/platform/fan_service/SensorData.h"

namespace facebook::fboss::platform {
SensorData::SensorData() {}

SensorData::~SensorData() {}

SensorEntry* SensorData::getOrCreateSensorEntry(std::string name) {
  bool entryExist = checkIfEntryExists(name);
  SensorEntry* pEntry;
  if (!entryExist)
    sensorEntry_[name] = SensorEntry();
  pEntry = &sensorEntry_[name];
  return pEntry;
}

SensorEntry::~SensorEntry()

void SensorData::updateEntryInt(
    std::string name,
    int data,
    uint64_t timeStamp) {
  SensorEntry* pEntry = getOrCreateSensorEntry(name);
  pEntry->timeStampSec = timeStamp;
  pEntry->sensorEntryType = kSensorEntryInt;
  pEntry->value.intValue = data;
  return;
}
void SensorData::updateEntryFloat(
    std::string name,
    float data,
    uint64_t timeStamp) {
  SensorEntry* pEntry = getOrCreateSensorEntry(name);
  pEntry->timeStampSec = timeStamp;
  pEntry->sensorEntryType = kSensorEntryFloat;
  pEntry->value.floatValue = data;
  return;
}
int SensorData::getSensorDataInt(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryInt)
    throw facebook::fboss::FbossError("Sensor Entry is not Int: ", name);
  return pEntry->value.intValue;
}

std::vector<std::string> SensorData::getKeyLists() {
  std::vector<std::string> retVal;
  retVal.reserve(sensorEntry_.size());
  for (auto kv : sensorEntry_) {
    retVal.push_back(kv.first);
  }
  return retVal;
}

SensorEntryType SensorData::getSensorEntryType(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryFloat)
    throw facebook::fboss::FbossError("Sensor Entry is not Float: ", name);
  return pEntry->sensorEntryType;
}

float SensorData::getSensorDataFloat(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryFloat)
    throw facebook::fboss::FbossError("Sensor Entry is not Float: ", name);
  return pEntry->value.floatValue;
}

uint64_t SensorData::getLastUpdated(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  return pEntry->timeStampSec;
}

bool SensorData::checkIfEntryExists(std::string name) {
  return (sensorEntry_.find(name) != sensorEntry_.end());
}

SensorEntry* SensorData::getSensorEntry(std::string name) {
  if (!checkIfEntryExists(name))
    return NULL;
  return &sensorEntry_[name];
}
} // namespace facebook::fboss::platform


{
  bool entryExist = checkIfEntryExists(name);
  SensorEntry* pEntry;

// Implementation of SensorData class. Refer to .h file
// for the functional description
#include "fboss/platform/fan_service/SensorData.h"

namespace facebook::fboss::platform {
SensorData::SensorData() {}

SensorData::~SensorData() {}

SensorEntry* SensorData::getOrCreateSensorEntry(std::string name)

  if (!entryExist)
    sensorEntry_[name] = SensorEntry();
  pEntry = &sensorEntry_[name];
  return pEntry;
}

SensorEntry::~SensorEntry() {
  // Defining this to make unique_ptr happy
}

void SensorData::updateEntryInt(
    std::string name,
    int data,
    uint64_t timeStamp) {
  SensorEntry* pEntry = getOrCreateSensorEntry(name);
  pEntry->timeStampSec = timeStamp;
  pEntry->sensorEntryType = kSensorEntryInt;
  pEntry->value.intValue = data;
  return;
}
void SensorData::updateEntryFloat(
    std::string name,
    float data,
    uint64_t timeStamp) {
  SensorEntry* pEntry = getOrCreateSensorEntry(name);
  pEntry->timeStampSec = timeStamp;
  pEntry->sensorEntryType = kSensorEntryFloat;
  pEntry->value.floatValue = data;
  return;
}
int SensorData::getSensorDataInt(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryInt)
    throw facebook::fboss::FbossError("Sensor Entry is not Int: ", name);
  return pEntry->value.intValue;
}

std::vector<std::string> SensorData::getKeyLists() {
  std::vector<std::string> retVal;
  retVal.reserve(sensorEntry_.size());
  for (auto kv : sensorEntry_) {
    retVal.push_back(kv.first);
  }
  return retVal;
}

SensorEntryType SensorData::getSensorEntryType(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryFloat)
    throw facebook::fboss::FbossError("Sensor Entry is not Float: ", name);
  return pEntry->sensorEntryType;
}

float SensorData::getSensorDataFloat(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryFloat)
    throw facebook::fboss::FbossError("Sensor Entry is not Float: ", name);
  return pEntry->value.floatValue;
}

uint64_t SensorData::getLastUpdated(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  return pEntry->timeStampSec;
}

bool SensorData::checkIfEntryExists(std::string name) {
  return (sensorEntry_.find(name) != sensorEntry_.end());
}

SensorEntry* SensorData::getSensorEntry(std::string name) {
  if (!checkIfEntryExists(name))
    return NULL;
  return &sensorEntry_[name];
}
} // namespace facebook::fboss::platform


if (!entryExist)
    sensorEntry_[name] = SensorEntry();
  pEntry = &sensorEntry_[name];

// Implementation of SensorData class. Refer to .h file
// for the functional description
#include "fboss/platform/fan_service/SensorData.h"

namespace facebook::fboss::platform {
SensorData::SensorData() {}

SensorData::~SensorData() {}

SensorEntry* SensorData::getOrCreateSensorEntry(std::string name) {
  bool entryExist = checkIfEntryExists(name);
  SensorEntry* pEntry;

  return pEntry;
}

SensorEntry::~SensorEntry() {
  // Defining this to make unique_ptr happy
}

void SensorData::updateEntryInt(
    std::string name,
    int data,
    uint64_t timeStamp) {
  SensorEntry* pEntry = getOrCreateSensorEntry(name);
  pEntry->timeStampSec = timeStamp;
  pEntry->sensorEntryType = kSensorEntryInt;
  pEntry->value.intValue = data;
  return;
}
void SensorData::updateEntryFloat(
    std::string name,
    float data,
    uint64_t timeStamp) {
  SensorEntry* pEntry = getOrCreateSensorEntry(name);
  pEntry->timeStampSec = timeStamp;
  pEntry->sensorEntryType = kSensorEntryFloat;
  pEntry->value.floatValue = data;
  return;
}
int SensorData::getSensorDataInt(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryInt)
    throw facebook::fboss::FbossError("Sensor Entry is not Int: ", name);
  return pEntry->value.intValue;
}

std::vector<std::string> SensorData::getKeyLists() {
  std::vector<std::string> retVal;
  retVal.reserve(sensorEntry_.size());
  for (auto kv : sensorEntry_) {
    retVal.push_back(kv.first);
  }
  return retVal;
}

SensorEntryType SensorData::getSensorEntryType(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryFloat)
    throw facebook::fboss::FbossError("Sensor Entry is not Float: ", name);
  return pEntry->sensorEntryType;
}

float SensorData::getSensorDataFloat(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryFloat)
    throw facebook::fboss::FbossError("Sensor Entry is not Float: ", name);
  return pEntry->value.floatValue;
}

uint64_t SensorData::getLastUpdated(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  return pEntry->timeStampSec;
}

bool SensorData::checkIfEntryExists(std::string name) {
  return (sensorEntry_.find(name) != sensorEntry_.end());
}

SensorEntry* SensorData::getSensorEntry(std::string name) {
  if (!checkIfEntryExists(name))
    return NULL;
  return &sensorEntry_[name];
}
} // namespace facebook::fboss::platform


// Implementation of SensorData class. Refer to .h file
// for the functional description
#include "fboss/platform/fan_service/SensorData.h"

namespace facebook::fboss::platform {
SensorData::SensorData() {}

SensorData::~SensorData() {}

SensorEntry* SensorData::getOrCreateSensorEntry(std::string name) {
  bool entryExist = checkIfEntryExists(name);
  SensorEntry* pEntry;
  if (!entryExist)
    sensorEntry_[name] = SensorEntry();
  pEntry = &sensorEntry_[name];

SensorEntry::~SensorEntry() {
  // Defining this to make unique_ptr happy
}

void SensorData::updateEntryInt(
    std::string name,
    int data,
    uint64_t timeStamp) {
  SensorEntry* pEntry = getOrCreateSensorEntry(name);
  pEntry->timeStampSec = timeStamp;
  pEntry->sensorEntryType = kSensorEntryInt;
  pEntry->value.intValue = data;
  return;
}
void SensorData::updateEntryFloat(
    std::string name,
    float data,
    uint64_t timeStamp) {
  SensorEntry* pEntry = getOrCreateSensorEntry(name);
  pEntry->timeStampSec = timeStamp;
  pEntry->sensorEntryType = kSensorEntryFloat;
  pEntry->value.floatValue = data;
  return;
}
int SensorData::getSensorDataInt(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryInt)
    throw facebook::fboss::FbossError("Sensor Entry is not Int: ", name);
  return pEntry->value.intValue;
}

std::vector<std::string> SensorData::getKeyLists() {
  std::vector<std::string> retVal;
  retVal.reserve(sensorEntry_.size());
  for (auto kv : sensorEntry_) {
    retVal.push_back(kv.first);
  }
  return retVal;
}

SensorEntryType SensorData::getSensorEntryType(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryFloat)
    throw facebook::fboss::FbossError("Sensor Entry is not Float: ", name);
  return pEntry->sensorEntryType;
}

float SensorData::getSensorDataFloat(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryFloat)
    throw facebook::fboss::FbossError("Sensor Entry is not Float: ", name);
  return pEntry->value.floatValue;
}

uint64_t SensorData::getLastUpdated(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  return pEntry->timeStampSec;
}

bool SensorData::checkIfEntryExists(std::string name) {
  return (sensorEntry_.find(name) != sensorEntry_.end());
}

SensorEntry* SensorData::getSensorEntry(std::string name) {
  if (!checkIfEntryExists(name))
    return NULL;
  return &sensorEntry_[name];
}
} // namespace facebook::fboss::platform


// Implementation of SensorData class. Refer to .h file
// for the functional description
#include "fboss/platform/fan_service/SensorData.h"

namespace facebook::fboss::platform {
SensorData::SensorData() {}

SensorData::~SensorData() {}

SensorEntry* SensorData::getOrCreateSensorEntry(std::string name) {
  bool entryExist = checkIfEntryExists(name);
  SensorEntry* pEntry;
  if (!entryExist)
    sensorEntry_[name] = SensorEntry();
  pEntry = &sensorEntry_[name];
  return pEntry;
}

SensorEntry::~SensorEntry() {
  // Defining this to make unique_ptr happy
}

void SensorData::updateEntryInt(
    std::string name,
    int data,
    uint64_t timeStamp) {
  SensorEntry* pEntry = getOrCreateSensorEntry(name);
  pEntry->timeStampSec = timeStamp;
  pEntry->sensorEntryType = kSensorEntryInt;
  pEntry->value.intValue = data;
  return;
}
void SensorData::updateEntryFloat(
    std::string name,
    float data,
    uint64_t timeStamp) {
  SensorEntry* pEntry = getOrCreateSensorEntry(name);
  pEntry->timeStampSec = timeStamp;
  pEntry->sensorEntryType = kSensorEntryFloat;
  pEntry->value.floatValue = data;
  return;
}
int SensorData::getSensorDataInt(std::string name) {
  SensorEnt

  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryInt)
    throw facebook::fboss::FbossError("Sensor Entry is not Int: ", name);
  return pEntry->value.intValue;
}

std::vector<std::string> SensorData::getKeyLists() {
  std::vector<std::string> retVal;
  retVal.reserve(sensorEntry_.size());
  for (auto kv : sensorEntry_) {
    retVal.push_back(kv.first);
  }
  return retVal;
}

SensorEntryType SensorData::getSensorEntryType(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryFloat)
    throw facebook::fboss::FbossError("Sensor Entry is not Float: ", name);
  return pEntry->sensorEntryType;
}

float SensorData::getSensorDataFloat(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryFloat)
    throw facebook::fboss::FbossError("Sensor Entry is not Float: ", name);
  return pEntry->value.floatValue;
}

uint64_t SensorData::getLastUpdated(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  return pEntry->timeStampSec;
}

bool SensorData::checkIfEntryExists(std::string name) {
  return (sensorEntry_.find(name) != sensorEntry_.end());
}

SensorEntry* SensorData::getSensorEntry(std::string name) {
  if (!checkIfEntryExists(name))
    return NULL;
  return &sensorEntry_[name];
}
} // namespace facebook::fboss::platform


if (!entryExist)
    sensorEntry_[name] = SensorEntry();

  pEntry = &sensorEntry_[name];
  return pEntry;
}

SensorEntry::~SensorEntry() {
  // Defining this to make unique_ptr happy
}

void SensorData::updateEntryInt(
    std::string name,
    int data,
    uint64_t timeStamp) {
  SensorEntry* pEntry = getOrCreateSensorEntry(name);
  pEntry->timeStampSec = timeStamp;
  pEntry->sensorEntryType = kSensorEntryInt;
  pEntry->value.intValue = data;
  return;
}
void SensorData::updateEntryFloat(
    std::string name,
    float data,
    uint64_t timeStamp) {
  SensorEntry* pEntry = getOrCreateSensorEntry(name);
  pEntry->timeStampSec = timeStamp;
  pEntry->sensorEntryType = kSensorEntryFloat;
  pEntry->value.floatValue = data;
  return;
}
int SensorData::getSensorDataInt(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryInt)
    throw facebook::fboss::FbossError("Sensor Entry is not Int: ", name);
  return pEntry->value.intValue;
}

std::vector<std::string> SensorData::getKeyLists() {
  std::vector<std::string> retVal;
  retVal.reserve(sensorEntry_.size());
  for (auto kv : sensorEntry_) {
    retVal.push_back(kv.first);
  }
  return retVal;
}

SensorEntryType SensorData::getSensorEntryType(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryFloat)
    throw facebook::fboss::FbossError("Sensor Entry is not Float: ", name);
  return pEntry->sensorEntryType;
}

float SensorData::getSensorDataFloat(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  if (pEntry->sensorEntryType != kSensorEntryFloat)
    throw facebook::fboss::FbossError("Sensor Entry is not Float: ", name);
  return pEntry->value.floatValue;
}

uint64_t SensorData::getLastUpdated(std::string name) {
  SensorEntry* pEntry = getSensorEntry(name);
  if (pEntry == NULL)
    throw facebook::fboss::FbossError("Unable to find the sensor data ", name);
  return pEntry->timeStampSec;
}

bool SensorData::checkIfEntryExists(std::string name) {
  return (sensorEntry_.find(name) != sensorEntry_.end());
}

SensorEntry* SensorData::getSensorEntry(std::string name) {
  if (!checkIfEntryExists(name))
    return NULL;
  return &sensorEntry_[name];
}
} // namespace facebook::fboss::platform


LRESULT CSampleOptionsListCtrlDlg::OnRegisteredMessage(WPARAM wParam, LPARAM lParam)

#include "stdafx.h"
#include "SampleOptionsListCtrlDlg.h"

CSampleOptionsListCtrl::CSampleOptionsListCtrl()
= default;

CSampleOptionsListCtrl::~CSampleOptionsListCtrl()
{
}

void CSampleOptionsListCtrl::OnEndEditItem(CRhinoUiOptionsListCtrlItem& item)
{
  CRhinoUiOptionsListCtrl::OnEndEditItem(item);
}

bool CSampleOptionsListCtrl::OnItemButtonClicked(CRhinoUiOptionsListCtrlPushButton& item)
{
  CRhinoUiOptionsListCtrl::OnItemButtonClicked(item);
  return true;
}

bool CSampleOptionsListCtrl::OnItemButtonClicked(CRhinoUiOptionsListCtrlCheckBox& item)
{
  CRhinoUiOptionsListCtrl::OnItemButtonClicked(item);

  CSampleOptionsListCtrlDlg* pDlg = static_cast<CSampleOptionsListCtrlDlg*>(GetParent());
  if (0 == pDlg)
    return false;

  return true;
}

bool CSampleOptionsListCtrl::OnComboBoxPopulated(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::OnComboBoxPopulated(item);

  int cnt = item.GetCount();
  if (cnt < 1)
    return false; // Nothing added
  // Current item text
  CString sText = item.Text();
  // place holder for new text
  CString s;
  // current selection index
  int iCurSel = -1;
  // process combo box for string that matches sText
  for (int i = 0; i < cnt; i++)
  {
    item.GetLBText(i, s);
    if (0 == sText.CompareNoCase(s))
      iCurSel = i;
  }
  // Set current selection to matching string
  item.SetCurSel(iCurSel);
  return(cnt > 0);
}

bool CSampleOptionsListCtrl::PopulateItemComboBox(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::PopulateItemComboBox(item);

  CSampleOptionsListCtrlDlg* pDlg = static_cast<CSampleOptionsListCtrlDlg*>(GetParent());
  if (0 == pDlg)
    return false;

  return true;
}

void CSampleOptionsListCtrl::OnItemComboBoxSelChanged(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::OnItemComboBoxSelChanged(item);
  int iCurSel = item.GetCurSel();
  if (iCurSel < 0)
    return;
  // TODO: the combo box changed, update whatever it is connected to.
}

IMPLEMENT_DYNCREATE(CSampleOptionsListCtrlDlg, CRhinoDialog)

CSampleOptionsListCtrlDlg::CSampleOptionsListCtrlDlg()
  : CRhinoDialog(CSampleOptionsListCtrlDlg::IDD)
{
}

void CSampleOptionsListCtrlDlg::DoDataExchange(CDataExchange* pDX)
{
  CRhinoDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CSampleOptionsListCtrlDlg, CRhinoDialog)
END_MESSAGE_MAP()

BOOL CSampleOptionsListCtrlDlg::OnInitDialog()
{
  AddResizeControl(TRUE);
  SetMinSizeToCurrentSize();
  EnableSaveDialogPositionAndSize(TRUE);

  m_Resize.Add(IDOK, CRhinoUiDialogItemResizer::resize_lockbottomleft);
  m_Resize.Add(IDCANCEL, CRhinoUiDialogItemResizer::resize_lockbottomleft);

  CWnd* pWnd = GetDlgItem(IDC_FRAME);
  ASSERT(pWnd);

  CRect rect;
  pWnd->GetWindowRect(rect);
  ScreenToClient(rect);

  DWORD dwStyle = pWnd->GetStyle();

  pWnd->DestroyWindow();
  pWnd = 0;

  m_options_list.SetBorders(0, 0, 0, 0);

  if (m_options_list.CreateOptionsList(this, dwStyle, rect))
  {
    m_options_list.SetIndentItems(true);

    m_options_list.m_RadioColumn.SetLabel(L"Radio button test");
    m_options_list.m_RadioColumn.SetAutoDelete(false);
    m_options_list.m_RadioColumn.AddRadioButton(L"One");
    m_options_list.m_RadioColumn.AddRadioButton(L"Two");
    m_options_list.m_RadioColumn.AddRadioButton(L"Three");
    m_options_list.m_RadioColumn.SetSelectedButton(1);
    m_options_list.m_RadioColumn.SetIndentLevel(1);
    m_options_list.AddItem(&m_options_list.m_RadioColumn);

    // Need to adjust the dialog to fit its contents then save the initial size
    // for use by the scroll bar calculator later on

    CRect rectBefore;
    m_options_list.GetWindowRect(rectBefore);
    m_options_list.SizeToContent(true);

    CRect rectAfter;
    m_options_list.GetWindowRect(rectAfter);
    if (rectAfter.Height() != rectBefore.Height())
    {
      CRect rectWindow;
      GetWindowRect(rectWindow);
      int cy = rectWindow.Height() + (rectAfter.Height() - rectBefore.Height());
      SetWindowPos(NULL, 0, 0, rectWindow.Width(), cy, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
      m_sizeMin.cy = cy;
    }
    else
    {
      m_sizeMin.cy = rectAfter.Height();
    }

    m_Resize.Add(&m_options_list, CRhinoUiDialogItemResizer::resize_lockwidth);
  }

  CRhinoDialog::OnInitDialog();

  return TRUE;
}

{
  LPRhinoUiOptionsListCtrlNM pNM = (LPRhinoUiOptionsListCtrlNM)wParam;
  LRESULT* pResult = (LRESULT*)lParam;

  *pResult = false;

  CRhinoUiOptionsListCtrlItem* pItem = pNM->m_item;
  if (NULL == pItem)
    return 0L;

  UINT nNotificationCode = pNM->m_NMHDR.code;

  switch (nNotificationCode)
  {
  case CRhinoUiOptionsListCtrl::nc_on_item_button_clicked:
  {
    CRhinoUiOptionsListCtrlPushButton* pItemButton = static_cast<CRhinoUiOptionsListCtrlPushButton*>(pItem);
    CString s, sLabel = pItemButton->Label();
    if (sLabel.IsEmpty())
      sLabel = L"Untitled";
    s.Format(L"Button %ls clicked", static_cast<const wchar_t*>(sLabel));
    MessageBox(s);
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_item_check_box_clicked:
    if (static_cast<CRhinoUiOptionsListCtrlCheckBox*>(pItem)->GetCheck() == BST_UNCHECKED)
      pItem->SetText(L"Unchecked");
    else
      pItem->SetText(L"Checked");
    break;

  case CRhinoUiOptionsListCtrl::nc_on_combo_box_sel_changed:
  {
    CRhinoUiOptionsListCtrlComboBox* pItemComboBox = static_cast<CRhinoUiOptionsListCtrlComboBox*>(pItem);
    if (::IsWindow(pItemComboBox->m_hWnd))
    {
      CString s;
      pItemComboBox->GetTextFromListIndex(pItemComboBox->GetCurSel(), s);
      pItemComboBox->SetText(s);
    }
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_radio_button_checked:
  {
    const CRhinoUiOptionsListCtrlRadioButtonColumnBtn* pBtn = static_cast<CRhinoUiOptionsListCtrlRadioButtonColumn*>(pItem)->GetSelectedButton();
    CString s;
    if (pBtn)
      s = pBtn->ButtonLabel();
    pItem->SetText(s);
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_end_edit_item:
  {
    CString sLabel = pItem->Label();
    if (!sLabel.IsEmpty())
    {
      CString s;
      s.Format(L"nc_on_end_edit_item Label(%ls)", static_cast<const wchar_t*>(sLabel));
    }
  }
  break;
  }

  return 0L;
}


{
  CRhinoDialog::DoDataExchange(pDX);
}

#include "stdafx.h"
#include "SampleOptionsListCtrlDlg.h"

CSampleOptionsListCtrl::CSampleOptionsListCtrl()
= default;

CSampleOptionsListCtrl::~CSampleOptionsListCtrl()
{
}

void CSampleOptionsListCtrl::OnEndEditItem(CRhinoUiOptionsListCtrlItem& item)
{
  CRhinoUiOptionsListCtrl::OnEndEditItem(item);
}

bool CSampleOptionsListCtrl::OnItemButtonClicked(CRhinoUiOptionsListCtrlPushButton& item)
{
  CRhinoUiOptionsListCtrl::OnItemButtonClicked(item);
  return true;
}

bool CSampleOptionsListCtrl::OnItemButtonClicked(CRhinoUiOptionsListCtrlCheckBox& item)
{
  CRhinoUiOptionsListCtrl::OnItemButtonClicked(item);

  CSampleOptionsListCtrlDlg* pDlg = static_cast<CSampleOptionsListCtrlDlg*>(GetParent());
  if (0 == pDlg)
    return false;

  return true;
}

bool CSampleOptionsListCtrl::OnComboBoxPopulated(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::OnComboBoxPopulated(item);

  int cnt = item.GetCount();
  if (cnt < 1)
    return false; // Nothing added
  // Current item text
  CString sText = item.Text();
  // place holder for new text
  CString s;
  // current selection index
  int iCurSel = -1;
  // process combo box for string that matches sText
  for (int i = 0; i < cnt; i++)
  {
    item.GetLBText(i, s);
    if (0 == sText.CompareNoCase(s))
      iCurSel = i;
  }
  // Set current selection to matching string
  item.SetCurSel(iCurSel);
  return(cnt > 0);
}

bool CSampleOptionsListCtrl::PopulateItemComboBox(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::PopulateItemComboBox(item);

  CSampleOptionsListCtrlDlg* pDlg = static_cast<CSampleOptionsListCtrlDlg*>(GetParent());
  if (0 == pDlg)
    return false;

  return true;
}

void CSampleOptionsListCtrl::OnItemComboBoxSelChanged(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::OnItemComboBoxSelChanged(item);
  int iCurSel = item.GetCurSel();
  if (iCurSel < 0)
    return;
  // TODO: the combo box changed, update whatever it is connected to.
}

IMPLEMENT_DYNCREATE(CSampleOptionsListCtrlDlg, CRhinoDialog)

CSampleOptionsListCtrlDlg::CSampleOptionsListCtrlDlg()
  : CRhinoDialog(CSampleOptionsListCtrlDlg::IDD)
{
}

void CSampleOptionsListCtrlDlg::DoDataExchange(CDataExchange* pDX)

BEGIN_MESSAGE_MAP(CSampleOptionsListCtrlDlg, CRhinoDialog)
END_MESSAGE_MAP()

BOOL CSampleOptionsListCtrlDlg::OnInitDialog()
{
  AddResizeControl(TRUE);
  SetMinSizeToCurrentSize();
  EnableSaveDialogPositionAndSize(TRUE);

  m_Resize.Add(IDOK, CRhinoUiDialogItemResizer::resize_lockbottomleft);
  m_Resize.Add(IDCANCEL, CRhinoUiDialogItemResizer::resize_lockbottomleft);

  CWnd* pWnd = GetDlgItem(IDC_FRAME);
  ASSERT(pWnd);

  CRect rect;
  pWnd->GetWindowRect(rect);
  ScreenToClient(rect);

  DWORD dwStyle = pWnd->GetStyle();

  pWnd->DestroyWindow();
  pWnd = 0;

  m_options_list.SetBorders(0, 0, 0, 0);

  if (m_options_list.CreateOptionsList(this, dwStyle, rect))
  {
    m_options_list.SetIndentItems(true);

    m_options_list.m_RadioColumn.SetLabel(L"Radio button test");
    m_options_list.m_RadioColumn.SetAutoDelete(false);
    m_options_list.m_RadioColumn.AddRadioButton(L"One");
    m_options_list.m_RadioColumn.AddRadioButton(L"Two");
    m_options_list.m_RadioColumn.AddRadioButton(L"Three");
    m_options_list.m_RadioColumn.SetSelectedButton(1);
    m_options_list.m_RadioColumn.SetIndentLevel(1);
    m_options_list.AddItem(&m_options_list.m_RadioColumn);

    // Need to adjust the dialog to fit its contents then save the initial size
    // for use by the scroll bar calculator later on

    CRect rectBefore;
    m_options_list.GetWindowRect(rectBefore);
    m_options_list.SizeToContent(true);

    CRect rectAfter;
    m_options_list.GetWindowRect(rectAfter);
    if (rectAfter.Height() != rectBefore.Height())
    {
      CRect rectWindow;
      GetWindowRect(rectWindow);
      int cy = rectWindow.Height() + (rectAfter.Height() - rectBefore.Height());
      SetWindowPos(NULL, 0, 0, rectWindow.Width(), cy, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
      m_sizeMin.cy = cy;
    }
    else
    {
      m_sizeMin.cy = rectAfter.Height();
    }

    m_Resize.Add(&m_options_list, CRhinoUiDialogItemResizer::resize_lockwidth);
  }

  CRhinoDialog::OnInitDialog();

  return TRUE;
}

LRESULT CSampleOptionsListCtrlDlg::OnRegisteredMessage(WPARAM wParam, LPARAM lParam)
{
  LPRhinoUiOptionsListCtrlNM pNM = (LPRhinoUiOptionsListCtrlNM)wParam;
  LRESULT* pResult = (LRESULT*)lParam;

  *pResult = false;

  CRhinoUiOptionsListCtrlItem* pItem = pNM->m_item;
  if (NULL == pItem)
    return 0L;

  UINT nNotificationCode = pNM->m_NMHDR.code;

  switch (nNotificationCode)
  {
  case CRhinoUiOptionsListCtrl::nc_on_item_button_clicked:
  {
    CRhinoUiOptionsListCtrlPushButton* pItemButton = static_cast<CRhinoUiOptionsListCtrlPushButton*>(pItem);
    CString s, sLabel = pItemButton->Label();
    if (sLabel.IsEmpty())
      sLabel = L"Untitled";
    s.Format(L"Button %ls clicked", static_cast<const wchar_t*>(sLabel));
    MessageBox(s);
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_item_check_box_clicked:
    if (static_cast<CRhinoUiOptionsListCtrlCheckBox*>(pItem)->GetCheck() == BST_UNCHECKED)
      pItem->SetText(L"Unchecked");
    else
      pItem->SetText(L"Checked");
    break;

  case CRhinoUiOptionsListCtrl::nc_on_combo_box_sel_changed:
  {
    CRhinoUiOptionsListCtrlComboBox* pItemComboBox = static_cast<CRhinoUiOptionsListCtrlComboBox*>(pItem);
    if (::IsWindow(pItemComboBox->m_hWnd))
    {
      CString s;
      pItemComboBox->GetTextFromListIndex(pItemComboBox->GetCurSel(), s);
      pItemComboBox->SetText(s);
    }
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_radio_button_checked:
  {
    const CRhinoUiOptionsListCtrlRadioButtonColumnBtn* pBtn = static_cast<CRhinoUiOptionsListCtrlRadioButtonColumn*>(pItem)->GetSelectedButton();
    CString s;
    if (pBtn)
      s = pBtn->ButtonLabel();
    pItem->SetText(s);
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_end_edit_item:
  {
    CString sLabel = pItem->Label();
    if (!sLabel.IsEmpty())
    {
      CString s;
      s.Format(L"nc_on_end_edit_item Label(%ls)", static_cast<const wchar_t*>(sLabel));
    }
  }
  break;
  }

  return 0L;
}


#include "stdafx.h"
#include "SampleOptionsListCtrlDlg.h"

CSampleOptionsListCtrl::CSampleOptionsListCtrl()
= default;

CSampleOptionsListCtrl::~CSampleOptionsListCtrl()
{
}

void CSampleOptionsListCtrl::OnEndEditItem(CRhinoUiOptionsListCtrlItem& item)
{
  CRhinoUiOptionsListCtrl::OnEndEditItem(item);
}

bool CSampleOptionsListCtrl::OnItemButtonClicked(CRhinoUiOptionsListCtrlPushButton& item)
{
  CRhinoUiOptionsListCtrl::OnItemButtonClicked(item);
  return true;
}

bool CSampleOptionsListCtrl::OnItemButtonClicked(CRhinoUiOptionsListCtrlCheckBox& item)

  CSampleOptionsListCtrlDlg* pDlg = static_cast<CSampleOptionsListCtrlDlg*>(GetParent());
  if (0 == pDlg)
    return false;

  return true;
}

bool CSampleOptionsListCtrl::OnComboBoxPopulated(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::OnComboBoxPopulated(item);

  int cnt = item.GetCount();
  if (cnt < 1)
    return false; // Nothing added
  // Current item text
  CString sText = item.Text();
  // place holder for new text
  CString s;
  // current selection index
  int iCurSel = -1;
  // process combo box for string that matches sText
  for (int i = 0; i < cnt; i++)
  {
    item.GetLBText(i, s);
    if (0 == sText.CompareNoCase(s))
      iCurSel = i;
  }
  // Set current selection to matching string
  item.SetCurSel(iCurSel);
  return(cnt > 0);
}

bool CSampleOptionsListCtrl::PopulateItemComboBox(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::PopulateItemComboBox(item);

  CSampleOptionsListCtrlDlg* pDlg = static_cast<CSampleOptionsListCtrlDlg*>(GetParent());
  if (0 == pDlg)
    return false;

  return true;
}

void CSampleOptionsListCtrl::OnItemComboBoxSelChanged(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::OnItemComboBoxSelChanged(item);
  int iCurSel = item.GetCurSel();
  if (iCurSel < 0)
    return;
  // TODO: the combo box changed, update whatever it is connected to.
}

IMPLEMENT_DYNCREATE(CSampleOptionsListCtrlDlg, CRhinoDialog)

CSampleOptionsListCtrlDlg::CSampleOptionsListCtrlDlg()
  : CRhinoDialog(CSampleOptionsListCtrlDlg::IDD)
{
}

void CSampleOptionsListCtrlDlg::DoDataExchange(CDataExchange* pDX)
{
  CRhinoDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CSampleOptionsListCtrlDlg, CRhinoDialog)
END_MESSAGE_MAP()

BOOL CSampleOptionsListCtrlDlg::OnInitDialog()
{
  AddResizeControl(TRUE);
  SetMinSizeToCurrentSize();
  EnableSaveDialogPositionAndSize(TRUE);

  m_Resize.Add(IDOK, CRhinoUiDialogItemResizer::resize_lockbottomleft);
  m_Resize.Add(IDCANCEL, CRhinoUiDialogItemResizer::resize_lockbottomleft);

  CWnd* pWnd = GetDlgItem(IDC_FRAME);
  ASSERT(pWnd);

  CRect rect;
  pWnd->GetWindowRect(rect);
  ScreenToClient(rect);

  DWORD dwStyle = pWnd->GetStyle();

  pWnd->DestroyWindow();
  pWnd = 0;

  m_options_list.SetBorders(0, 0, 0, 0);

  if (m_options_list.CreateOptionsList(this, dwStyle, rect))
  {
    m_options_list.SetIndentItems(true);

    m_options_list.m_RadioColumn.SetLabel(L"Radio button test");
    m_options_list.m_RadioColumn.SetAutoDelete(false);
    m_options_list.m_RadioColumn.AddRadioButton(L"One");
    m_options_list.m_RadioColumn.AddRadioButton(L"Two");
    m_options_list.m_RadioColumn.AddRadioButton(L"Three");
    m_options_list.m_RadioColumn.SetSelectedButton(1);
    m_options_list.m_RadioColumn.SetIndentLevel(1);
    m_options_list.AddItem(&m_options_list.m_RadioColumn);

    // Need to adjust the dialog to fit its contents then save the initial size
    // for use by the scroll bar calculator later on

    CRect rectBefore;
    m_options_list.GetWindowRect(rectBefore);
    m_options_list.SizeToContent(true);

    CRect rectAfter;
    m_options_list.GetWindowRect(rectAfter);
    if (rectAfter.Height() != rectBefore.Height())
    {
      CRect rectWindow;
      GetWindowRect(rectWindow);
      int cy = rectWindow.Height() + (rectAfter.Height() - rectBefore.Height());
      SetWindowPos(NULL, 0, 0, rectWindow.Width(), cy, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
      m_sizeMin.cy = cy;
    }
    else
    {
      m_sizeMin.cy = rectAfter.Height();
    }

    m_Resize.Add(&m_options_list, CRhinoUiDialogItemResizer::resize_lockwidth);
  }

  CRhinoDialog::OnInitDialog();

  return TRUE;
}

LRESULT CSampleOptionsListCtrlDlg::OnRegisteredMessage(WPARAM wParam, LPARAM lParam)
{
  LPRhinoUiOptionsListCtrlNM pNM = (LPRhinoUiOptionsListCtrlNM)wParam;
  LRESULT* pResult = (LRESULT*)lParam;

  *pResult = false;

  CRhinoUiOptionsListCtrlItem* pItem = pNM->m_item;
  if (NULL == pItem)
    return 0L;

  UINT nNotificationCode = pNM->m_NMHDR.code;

  switch (nNotificationCode)
  {
  case CRhinoUiOptionsListCtrl::nc_on_item_button_clicked:
  {
    CRhinoUiOptionsListCtrlPushButton* pItemButton = static_cast<CRhinoUiOptionsListCtrlPushButton*>(pItem);
    CString s, sLabel = pItemButton->Label();
    if (sLabel.IsEmpty())
      sLabel = L"Untitled";
    s.Format(L"Button %ls clicked", static_cast<const wchar_t*>(sLabel));
    MessageBox(s);
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_item_check_box_clicked:
    if (static_cast<CRhinoUiOptionsListCtrlCheckBox*>(pItem)->GetCheck() == BST_UNCHECKED)
      pItem->SetText(L"Unchecked");
    else
      pItem->SetText(L"Checked");
    break;

  case CRhinoUiOptionsListCtrl::nc_on_combo_box_sel_changed:
  {
    CRhinoUiOptionsListCtrlComboBox* pItemComboBox = static_cast<CRhinoUiOptionsListCtrlComboBox*>(pItem);
    if (::IsWindow(pItemComboBox->m_hWnd))
    {
      CString s;
      pItemComboBox->GetTextFromListIndex(pItemComboBox->GetCurSel(), s);
      pItemComboBox->SetText(s);
    }
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_radio_button_checked:
  {
    const CRhinoUiOptionsListCtrlRadioButtonColumnBtn* pBtn = static_cast<CRhinoUiOptionsListCtrlRadioButtonColumn*>(pItem)->GetSelectedButton();
    CString s;
    if (pBtn)
      s = pBtn->ButtonLabel();
    pItem->SetText(s);
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_end_edit_item:
  {
    CString sLabel = pItem->Label();
    if (!sLabel.IsEmpty())
    {
      CString s;
      s.Format(L"nc_on_end_edit_item Label(%ls)", static_cast<const wchar_t*>(sLabel));
    }
  }
  break;
  }

  return 0L;
}


#include "stdafx.h"
#include "SampleOptionsListCtrlDlg.h"

CSampleOptionsListCtrl::CSampleOptionsListCtrl()
= default;

CSampleOptionsListCtrl::~CSampleOptionsListCtrl()
{
}

void CSampleOptionsListCtrl::OnEndEditItem(CRhinoUiOptionsListCtrlItem& item)
{
  CRhinoUiOptionsListCtrl::OnEndEditItem(item);
}

bool CSampleOptionsListCtrl::OnItemButtonClicked(CRhinoUiOptionsListCtrlPushButton& item)
{
  CRhinoUiOptionsListCtrl::OnItemButtonClicked(item);
  return true;
}

bool CSampleOptionsListCtrl::OnItemButtonClicked(CRhinoUiOptionsListCtrlCheckBox& item)
{
  CRhinoUiOptionsListCtrl::OnItemButtonClicked(item);

  return true;
}

bool CSampleOptionsListCtrl::OnComboBoxPopulated(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::OnComboBoxPopulated(item);

  int cnt = item.GetCount();
  if (cnt < 1)
    return false; // Nothing added
  // Current item text
  CString sText = item.Text();
  // place holder for new text
  CString s;
  // current selection index
  int iCurSel = -1;
  // process combo box for string that matches sText
  for (int i = 0; i < cnt; i++)
  {
    item.GetLBText(i, s);
    if (0 == sText.CompareNoCase(s))
      iCurSel = i;
  }
  // Set current selection to matching string
  item.SetCurSel(iCurSel);
  return(cnt > 0);
}

bool CSampleOptionsListCtrl::PopulateItemComboBox(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::PopulateItemComboBox(item);

  CSampleOptionsListCtrlDlg* pDlg = static_cast<CSampleOptionsListCtrlDlg*>(GetParent());
  if (0 == pDlg)
    return false;

  return true;
}

void CSampleOptionsListCtrl::OnItemComboBoxSelChanged(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::OnItemComboBoxSelChanged(item);
  int iCurSel = item.GetCurSel();
  if (iCurSel < 0)
    return;
  // TODO: the combo box changed, update whatever it is connected to.
}

IMPLEMENT_DYNCREATE(CSampleOptionsListCtrlDlg, CRhinoDialog)

CSampleOptionsListCtrlDlg::CSampleOptionsListCtrlDlg()
  : CRhinoDialog(CSampleOptionsListCtrlDlg::IDD)
{
}

void CSampleOptionsListCtrlDlg::DoDataExchange(CDataExchange* pDX)
{
  CRhinoDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CSampleOptionsListCtrlDlg, CRhinoDialog)
END_MESSAGE_MAP()

BOOL CSampleOptionsListCtrlDlg::OnInitDialog()
{
  AddResizeControl(TRUE);
  SetMinSizeToCurrentSize();
  EnableSaveDialogPositionAndSize(TRUE);

  m_Resize.Add(IDOK, CRhinoUiDialogItemResizer::resize_lockbottomleft);
  m_Resize.Add(IDCANCEL, CRhinoUiDialogItemResizer::resize_lockbottomleft);

  CWnd* pWnd = GetDlgItem(IDC_FRAME);
  ASSERT(pWnd);

  CRect rect;
  pWnd->GetWindowRect(rect);
  ScreenToClient(rect);

  DWORD dwStyle = pWnd->GetStyle();

  pWnd->DestroyWindow();
  pWnd = 0;

  m_options_list.SetBorders(0, 0, 0, 0);

  if (m_options_list.CreateOptionsList(this, dwStyle, rect))
  {
    m_options_list.SetIndentItems(true);

    m_options_list.m_RadioColumn.SetLabel(L"Radio button test");
    m_options_list.m_RadioColumn.SetAutoDelete(false);
    m_options_list.m_RadioColumn.AddRadioButton(L"One");
    m_options_list.m_RadioColumn.AddRadioButton(L"Two");
    m_options_list.m_RadioColumn.AddRadioButton(L"Three");
    m_options_list.m_RadioColumn.SetSelectedButton(1);
    m_options_list.m_RadioColumn.SetIndentLevel(1);
    m_options_list.AddItem(&m_options_list.m_RadioColumn);

    // Need to adjust the dialog to fit its contents then save the initial size
    // for use by the scroll bar calculator later on

    CRect rectBefore;
    m_options_list.GetWindowRect(rectBefore);
    m_options_list.SizeToContent(true);

    CRect rectAfter;
    m_options_list.GetWindowRect(rectAfter);
    if (rectAfter.Height() != rectBefore.Height())
    {
      CRect rectWindow;
      GetWindowRect(rectWindow);
      int cy = rectWindow.Height() + (rectAfter.Height() - rectBefore.Height());
      SetWindowPos(NULL, 0, 0, rectWindow.Width(), cy, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
      m_sizeMin.cy = cy;
    }
    else
    {
      m_sizeMin.cy = rectAfter.Height();
    }

    m_Resize.Add(&m_options_list, CRhinoUiDialogItemResizer::resize_lockwidth);
  }

  CRhinoDialog::OnInitDialog();

  return TRUE;
}

LRESULT CSampleOptionsListCtrlDlg::OnRegisteredMessage(WPARAM wParam, LPARAM lParam)
{
  LPRhinoUiOptionsListCtrlNM pNM = (LPRhinoUiOptionsListCtrlNM)wParam;
  LRESULT* pResult = (LRESULT*)lParam;

  *pResult = false;

  CRhinoUiOptionsListCtrlItem* pItem = pNM->m_item;
  if (NULL == pItem)
    return 0L;

  UINT nNotificationCode = pNM->m_NMHDR.code;

  switch (nNotificationCode)
  {
  case CRhinoUiOptionsListCtrl::nc_on_item_button_clicked:
  {
    CRhinoUiOptionsListCtrlPushButton* pItemButton = static_cast<CRhinoUiOptionsListCtrlPushButton*>(pItem);
    CString s, sLabel = pItemButton->Label();
    if (sLabel.IsEmpty())
      sLabel = L"Untitled";
    s.Format(L"Button %ls clicked", static_cast<const wchar_t*>(sLabel));
    MessageBox(s);
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_item_check_box_clicked:
    if (static_cast<CRhinoUiOptionsListCtrlCheckBox*>(pItem)->GetCheck() == BST_UNCHECKED)
      pItem->SetText(L"Unchecked");
    else
      pItem->SetText(L"Checked");
    break;

  case CRhinoUiOptionsListCtrl::nc_on_combo_box_sel_changed:
  {
    CRhinoUiOptionsListCtrlComboBox* pItemComboBox = static_cast<CRhinoUiOptionsListCtrlComboBox*>(pItem);
    if (::IsWindow(pItemComboBox->m_hWnd))
    {
      CString s;
      pItemComboBox->GetTextFromListIndex(pItemComboBox->GetCurSel(), s);
      pItemComboBox->SetText(s);
    }
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_radio_button_checked:
  {
    const CRhinoUiOptionsListCtrlRadioButtonColumnBtn* pBtn = static_cast<CRhinoUiOptionsListCtrlRadioButtonColumn*>(pItem)->GetSelectedButton();
    CString s;
    if (pBtn)
      s = pBtn->ButtonLabel();
    pItem->SetText(s);
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_end_edit_item:
  {
    CString sLabel = pItem->Label();
    if (!sLabel.IsEmpty())
    {
      CString s;
      s.Format(L"nc_on_end_edit_item Label(%ls)", static_cast<const wchar_t*>(sLabel));
    }
  }
  break;
  }

  return 0L;
}


#include "stdafx.h"
#include "SampleOptionsListCtrlDlg.h"

CSampleOptionsListCtrl::CSampleOptionsListCtrl()
= default;

CSampleOptionsListCtrl::~CSampleOptionsListCtrl()
{
}

void CSampleOptionsListCtrl::OnEndEditItem(CRhinoUiOptionsListCtrlItem& item)
{
  CRhinoUiOptionsListCtrl::OnEndEditItem(item);
}

bool CSampleOptionsListCtrl::OnItemButtonClicked(CRhinoUiOptionsListCtrlPushButton& item)
{
  CRhinoUiOptionsListCtrl::OnItemButtonClicked(item);
  return true;
}

bool CSampleOptionsListCtrl::OnItemButtonClicked(CRhinoUiOptionsListCtrlCheckBox& item)
{
  CRhinoUiOptionsListCtrl::OnItemButtonClicked(item);

  CSampleOptionsListCtrlDlg* pDlg = static_cast<CSampleOptionsListCtrlDlg*>(GetParent());
  if (0 == pDlg)
    return false;

bool CSampleOptionsListCtrl::OnComboBoxPopulated(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::OnComboBoxPopulated(item);

  int cnt = item.GetCount();
  if (cnt < 1)
    return false; // Nothing added
  // Current item text
  CString sText = item.Text();
  // place holder for new text
  CString s;
  // current selection index
  int iCurSel = -1;
  // process combo box for string that matches sText
  for (int i = 0; i < cnt; i++)
  {
    item.GetLBText(i, s);
    if (0 == sText.CompareNoCase(s))
      iCurSel = i;
  }
  // Set current selection to matching string
  item.SetCurSel(iCurSel);
  return(cnt > 0);
}

bool CSampleOptionsListCtrl::PopulateItemComboBox(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::PopulateItemComboBox(item);

  CSampleOptionsListCtrlDlg* pDlg = static_cast<CSampleOptionsListCtrlDlg*>(GetParent());
  if (0 == pDlg)
    return false;

  return true;
}

void CSampleOptionsListCtrl::OnItemComboBoxSelChanged(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::OnItemComboBoxSelChanged(item);
  int iCurSel = item.GetCurSel();
  if (iCurSel < 0)
    return;
  // TODO: the combo box changed, update whatever it is connected to.
}

IMPLEMENT_DYNCREATE(CSampleOptionsListCtrlDlg, CRhinoDialog)

CSampleOptionsListCtrlDlg::CSampleOptionsListCtrlDlg()
  : CRhinoDialog(CSampleOptionsListCtrlDlg::IDD)
{
}

void CSampleOptionsListCtrlDlg::DoDataExchange(CDataExchange* pDX)
{
  CRhinoDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CSampleOptionsListCtrlDlg, CRhinoDialog)
END_MESSAGE_MAP()

BOOL CSampleOptionsListCtrlDlg::OnInitDialog()
{
  AddResizeControl(TRUE);
  SetMinSizeToCurrentSize();
  EnableSaveDialogPositionAndSize(TRUE);

  m_Resize.Add(IDOK, CRhinoUiDialogItemResizer::resize_lockbottomleft);
  m_Resize.Add(IDCANCEL, CRhinoUiDialogItemResizer::resize_lockbottomleft);

  CWnd* pWnd = GetDlgItem(IDC_FRAME);
  ASSERT(pWnd);

  CRect rect;
  pWnd->GetWindowRect(rect);
  ScreenToClient(rect);

  DWORD dwStyle = pWnd->GetStyle();

  pWnd->DestroyWindow();
  pWnd = 0;

  m_options_list.SetBorders(0, 0, 0, 0);

  if (m_options_list.CreateOptionsList(this, dwStyle, rect))
  {
    m_options_list.SetIndentItems(true);

    m_options_list.m_RadioColumn.SetLabel(L"Radio button test");
    m_options_list.m_RadioColumn.SetAutoDelete(false);
    m_options_list.m_RadioColumn.AddRadioButton(L"One");
    m_options_list.m_RadioColumn.AddRadioButton(L"Two");
    m_options_list.m_RadioColumn.AddRadioButton(L"Three");
    m_options_list.m_RadioColumn.SetSelectedButton(1);
    m_options_list.m_RadioColumn.SetIndentLevel(1);
    m_options_list.AddItem(&m_options_list.m_RadioColumn);

    // Need to adjust the dialog to fit its contents then save the initial size
    // for use by the scroll bar calculator later on

    CRect rectBefore;
    m_options_list.GetWindowRect(rectBefore);
    m_options_list.SizeToContent(true);

    CRect rectAfter;
    m_options_list.GetWindowRect(rectAfter);
    if (rectAfter.Height() != rectBefore.Height())
    {
      CRect rectWindow;
      GetWindowRect(rectWindow);
      int cy = rectWindow.Height() + (rectAfter.Height() - rectBefore.Height());
      SetWindowPos(NULL, 0, 0, rectWindow.Width(), cy, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
      m_sizeMin.cy = cy;
    }
    else
    {
      m_sizeMin.cy = rectAfter.Height();
    }

    m_Resize.Add(&m_options_list, CRhinoUiDialogItemResizer::resize_lockwidth);
  }

  CRhinoDialog::OnInitDialog();

  return TRUE;
}

LRESULT CSampleOptionsListCtrlDlg::OnRegisteredMessage(WPARAM wParam, LPARAM lParam)
{
  LPRhinoUiOptionsListCtrlNM pNM = (LPRhinoUiOptionsListCtrlNM)wParam;
  LRESULT* pResult = (LRESULT*)lParam;

  *pResult = false;

  CRhinoUiOptionsListCtrlItem* pItem = pNM->m_item;
  if (NULL == pItem)
    return 0L;

  UINT nNotificationCode = pNM->m_NMHDR.code;

  switch (nNotificationCode)
  {
  case CRhinoUiOptionsListCtrl::nc_on_item_button_clicked:
  {
    CRhinoUiOptionsListCtrlPushButton* pItemButton = static_cast<CRhinoUiOptionsListCtrlPushButton*>(pItem);
    CString s, sLabel = pItemButton->Label();
    if (sLabel.IsEmpty())
      sLabel = L"Untitled";
    s.Format(L"Button %ls clicked", static_cast<const wchar_t*>(sLabel));
    MessageBox(s);
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_item_check_box_clicked:
    if (static_cast<CRhinoUiOptionsListCtrlCheckBox*>(pItem)->GetCheck() == BST_UNCHECKED)
      pItem->SetText(L"Unchecked");
    else
      pItem->SetText(L"Checked");
    break;

  case CRhinoUiOptionsListCtrl::nc_on_combo_box_sel_changed:
  {
    CRhinoUiOptionsListCtrlComboBox* pItemComboBox = static_cast<CRhinoUiOptionsListCtrlComboBox*>(pItem);
    if (::IsWindow(pItemComboBox->m_hWnd))
    {
      CString s;
      pItemComboBox->GetTextFromListIndex(pItemComboBox->GetCurSel(), s);
      pItemComboBox->SetText(s);
    }
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_radio_button_checked:
  {
    const CRhinoUiOptionsListCtrlRadioButtonColumnBtn* pBtn = static_cast<CRhinoUiOptionsListCtrlRadioButtonColumn*>(pItem)->GetSelectedButton();
    CString s;
    if (pBtn)
      s = pBtn->ButtonLabel();
    pItem->SetText(s);
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_end_edit_item:
  {
    CString sLabel = pItem->Label();
    if (!sLabel.IsEmpty())
    {
      CString s;
      s.Format(L"nc_on_end_edit_item Label(%ls)", static_cast<const wchar_t*>(sLabel));
    }
  }
  break;
  }

  return 0L;
}


#include "stdafx.h"
#include "SampleOptionsListCtrlDlg.h"

CSampleOptionsListCtrl::CSampleOptionsListCtrl()
= default;

CSampleOptionsListCtrl::~CSampleOptionsListCtrl()
{
}

void CSampleOptionsListCtrl::OnEndEditItem(CRhinoUiOptionsListCtrlItem& item)
{
  CRhinoUiOptionsListCtrl::OnEndEditItem(item);
}

bool CSampleOptionsListCtrl::OnItemButtonClicked(CRhinoUiOptionsListCtrlPushButton& item)
{
  CRhinoUiOptionsListCtrl::OnItemButtonClicked(item);
  return true;
}

bool CSampleOptionsListCtrl::OnItemButtonClicked(CRhinoUiOptionsListCtrlCheckBox& item)
{
  CRhinoUiOptionsListCtrl::OnItemButtonClicked(item);

  CSampleOptionsListCtrlDlg* pDlg = static_cast<CSampleOptionsListCtrlDlg*>(GetParent());
  if (0 == pDlg)
    return false;

  return true;
}

bool CSampleOptionsListCtrl::OnComboBoxPopulated(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::OnComboBoxPopulated(item);

  int cnt = item.GetCount();
  if (cnt < 1)
    return false; // Nothing added
  // Current item text
  CString sText =

  // place holder for new text
  CString s;
  // current selection index
  int iCurSel = -1;
  // process combo box for string that matches sText
  for (int i = 0; i < cnt; i++)
  {
    item.GetLBText(i, s);
    if (0 == sText.CompareNoCase(s))
      iCurSel = i;
  }
  // Set current selection to matching string
  item.SetCurSel(iCurSel);
  return(cnt > 0);
}

bool CSampleOptionsListCtrl::PopulateItemComboBox(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::PopulateItemComboBox(item);

  CSampleOptionsListCtrlDlg* pDlg = static_cast<CSampleOptionsListCtrlDlg*>(GetParent());
  if (0 == pDlg)
    return false;

  return true;
}

void CSampleOptionsListCtrl::OnItemComboBoxSelChanged(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::OnItemComboBoxSelChanged(item);
  int iCurSel = item.GetCurSel();
  if (iCurSel < 0)
    return;
  // TODO: the combo box changed, update whatever it is connected to.
}

IMPLEMENT_DYNCREATE(CSampleOptionsListCtrlDlg, CRhinoDialog)

CSampleOptionsListCtrlDlg::CSampleOptionsListCtrlDlg()
  : CRhinoDialog(CSampleOptionsListCtrlDlg::IDD)
{
}

void CSampleOptionsListCtrlDlg::DoDataExchange(CDataExchange* pDX)
{
  CRhinoDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CSampleOptionsListCtrlDlg, CRhinoDialog)
END_MESSAGE_MAP()

BOOL CSampleOptionsListCtrlDlg::OnInitDialog()
{
  AddResizeControl(TRUE);
  SetMinSizeToCurrentSize();
  EnableSaveDialogPositionAndSize(TRUE);

  m_Resize.Add(IDOK, CRhinoUiDialogItemResizer::resize_lockbottomleft);
  m_Resize.Add(IDCANCEL, CRhinoUiDialogItemResizer::resize_lockbottomleft);

  CWnd* pWnd = GetDlgItem(IDC_FRAME);
  ASSERT(pWnd);

  CRect rect;
  pWnd->GetWindowRect(rect);
  ScreenToClient(rect);

  DWORD dwStyle = pWnd->GetStyle();

  pWnd->DestroyWindow();
  pWnd = 0;

  m_options_list.SetBorders(0, 0, 0, 0);

  if (m_options_list.CreateOptionsList(this, dwStyle, rect))
  {
    m_options_list.SetIndentItems(true);

    m_options_list.m_RadioColumn.SetLabel(L"Radio button test");
    m_options_list.m_RadioColumn.SetAutoDelete(false);
    m_options_list.m_RadioColumn.AddRadioButton(L"One");
    m_options_list.m_RadioColumn.AddRadioButton(L"Two");
    m_options_list.m_RadioColumn.AddRadioButton(L"Three");
    m_options_list.m_RadioColumn.SetSelectedButton(1);
    m_options_list.m_RadioColumn.SetIndentLevel(1);
    m_options_list.AddItem(&m_options_list.m_RadioColumn);

    // Need to adjust the dialog to fit its contents then save the initial size
    // for use by the scroll bar calculator later on

    CRect rectBefore;
    m_options_list.GetWindowRect(rectBefore);
    m_options_list.SizeToContent(true);

    CRect rectAfter;
    m_options_list.GetWindowRect(rectAfter);
    if (rectAfter.Height() != rectBefore.Height())
    {
      CRect rectWindow;
      GetWindowRect(rectWindow);
      int cy = rectWindow.Height() + (rectAfter.Height() - rectBefore.Height());
      SetWindowPos(NULL, 0, 0, rectWindow.Width(), cy, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
      m_sizeMin.cy = cy;
    }
    else
    {
      m_sizeMin.cy = rectAfter.Height();
    }

    m_Resize.Add(&m_options_list, CRhinoUiDialogItemResizer::resize_lockwidth);
  }

  CRhinoDialog::OnInitDialog();

  return TRUE;
}

LRESULT CSampleOptionsListCtrlDlg::OnRegisteredMessage(WPARAM wParam, LPARAM lParam)
{
  LPRhinoUiOptionsListCtrlNM pNM = (LPRhinoUiOptionsListCtrlNM)wParam;
  LRESULT* pResult = (LRESULT*)lParam;

  *pResult = false;

  CRhinoUiOptionsListCtrlItem* pItem = pNM->m_item;
  if (NULL == pItem)
    return 0L;

  UINT nNotificationCode = pNM->m_NMHDR.code;

  switch (nNotificationCode)
  {
  case CRhinoUiOptionsListCtrl::nc_on_item_button_clicked:
  {
    CRhinoUiOptionsListCtrlPushButton* pItemButton = static_cast<CRhinoUiOptionsListCtrlPushButton*>(pItem);
    CString s, sLabel = pItemButton->Label();
    if (sLabel.IsEmpty())
      sLabel = L"Untitled";
    s.Format(L"Button %ls clicked", static_cast<const wchar_t*>(sLabel));
    MessageBox(s);
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_item_check_box_clicked:
    if (static_cast<CRhinoUiOptionsListCtrlCheckBox*>(pItem)->GetCheck() == BST_UNCHECKED)
      pItem->SetText(L"Unchecked");
    else
      pItem->SetText(L"Checked");
    break;

  case CRhinoUiOptionsListCtrl::nc_on_combo_box_sel_changed:
  {
    CRhinoUiOptionsListCtrlComboBox* pItemComboBox = static_cast<CRhinoUiOptionsListCtrlComboBox*>(pItem);
    if (::IsWindow(pItemComboBox->m_hWnd))
    {
      CString s;
      pItemComboBox->GetTextFromListIndex(pItemComboBox->GetCurSel(), s);
      pItemComboBox->SetText(s);
    }
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_radio_button_checked:
  {
    const CRhinoUiOptionsListCtrlRadioButtonColumnBtn* pBtn = static_cast<CRhinoUiOptionsListCtrlRadioButtonColumn*>(pItem)->GetSelectedButton();
    CString s;
    if (pBtn)
      s = pBtn->ButtonLabel();
    pItem->SetText(s);
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_end_edit_item:
  {
    CString sLabel = pItem->Label();
    if (!sLabel.IsEmpty())
    {
      CString s;
      s.Format(L"nc_on_end_edit_item Label(%ls)", static_cast<const wchar_t*>(sLabel));
    }
  }
  break;
  }

  return 0L;
}


if (pBtn)
      s = pBtn->ButtonLabel();

#include "stdafx.h"
#include "SampleOptionsListCtrlDlg.h"

CSampleOptionsListCtrl::CSampleOptionsListCtrl()
= default;

CSampleOptionsListCtrl::~CSampleOptionsListCtrl()
{
}

void CSampleOptionsListCtrl::OnEndEditItem(CRhinoUiOptionsListCtrlItem& item)
{
  CRhinoUiOptionsListCtrl::OnEndEditItem(item);
}

bool CSampleOptionsListCtrl::OnItemButtonClicked(CRhinoUiOptionsListCtrlPushButton& item)
{
  CRhinoUiOptionsListCtrl::OnItemButtonClicked(item);
  return true;
}

bool CSampleOptionsListCtrl::OnItemButtonClicked(CRhinoUiOptionsListCtrlCheckBox& item)
{
  CRhinoUiOptionsListCtrl::OnItemButtonClicked(item);

  CSampleOptionsListCtrlDlg* pDlg = static_cast<CSampleOptionsListCtrlDlg*>(GetParent());
  if (0 == pDlg)
    return false;

  return true;
}

bool CSampleOptionsListCtrl::OnComboBoxPopulated(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::OnComboBoxPopulated(item);

  int cnt = item.GetCount();
  if (cnt < 1)
    return false; // Nothing added
  // Current item text
  CString sText = item.Text();
  // place holder for new text
  CString s;
  // current selection index
  int iCurSel = -1;
  // process combo box for string that matches sText
  for (int i = 0; i < cnt; i++)
  {
    item.GetLBText(i, s);
    if (0 == sText.CompareNoCase(s))
      iCurSel = i;
  }
  // Set current selection to matching string
  item.SetCurSel(iCurSel);
  return(cnt > 0);
}

bool CSampleOptionsListCtrl::PopulateItemComboBox(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::PopulateItemComboBox(item);

  CSampleOptionsListCtrlDlg* pDlg = static_cast<CSampleOptionsListCtrlDlg*>(GetParent());
  if (0 == pDlg)
    return false;

  return true;
}

void CSampleOptionsListCtrl::OnItemComboBoxSelChanged(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::OnItemComboBoxSelChanged(item);
  int iCurSel = item.GetCurSel();
  if (iCurSel < 0)
    return;
  // TODO: the combo box changed, update whatever it is connected to.
}

IMPLEMENT_DYNCREATE(CSampleOptionsListCtrlDlg, CRhinoDialog)

CSampleOptionsListCtrlDlg::CSampleOptionsListCtrlDlg()
  : CRhinoDialog(CSampleOptionsListCtrlDlg::IDD)
{
}

void CSampleOptionsListCtrlDlg::DoDataExchange(CDataExchange* pDX)
{
  CRhinoDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CSampleOptionsListCtrlDlg, CRhinoDialog)
END_MESSAGE_MAP()

BOOL CSampleOptionsListCtrlDlg::OnInitDialog()
{
  AddResizeControl(TRUE);
  SetMinSizeToCurrentSize();
  EnableSaveDialogPositionAndSize(TRUE);

  m_Resize.Add(IDOK, CRhinoUiDialogItemResizer::resize_lockbottomleft);
  m_Resize.Add(IDCANCEL, CRhinoUiDialogItemResizer::resize_lockbottomleft);

  CWnd* pWnd = GetDlgItem(IDC_FRAME);
  ASSERT(pWnd);

  CRect rect;
  pWnd->GetWindowRect(rect);
  ScreenToClient(rect);

  DWORD dwStyle = pWnd->GetStyle();

  pWnd->DestroyWindow();
  pWnd = 0;

  m_options_list.SetBorders(0, 0, 0, 0);

  if (m_options_list.CreateOptionsList(this, dwStyle, rect))
  {
    m_options_list.SetIndentItems(true);

    m_options_list.m_RadioColumn.SetLabel(L"Radio button test");
    m_options_list.m_RadioColumn.SetAutoDelete(false);
    m_options_list.m_RadioColumn.AddRadioButton(L"One");
    m_options_list.m_RadioColumn.AddRadioButton(L"Two");
    m_options_list.m_RadioColumn.AddRadioButton(L"Three");
    m_options_list.m_RadioColumn.SetSelectedButton(1);
    m_options_list.m_RadioColumn.SetIndentLevel(1);
    m_options_list.AddItem(&m_options_list.m_RadioColumn);

    // Need to adjust the dialog to fit its contents then save the initial size
    // for use by the scroll bar calculator later on

    CRect rectBefore;
    m_options_list.GetWindowRect(rectBefore);
    m_options_list.SizeToContent(true);

    CRect rectAfter;
    m_options_list.GetWindowRect(rectAfter);
    if (rectAfter.Height() != rectBefore.Height())
    {
      CRect rectWindow;
      GetWindowRect(rectWindow);
      int cy = rectWindow.Height() + (rectAfter.Height() - rectBefore.Height());
      SetWindowPos(NULL, 0, 0, rectWindow.Width(), cy, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
      m_sizeMin.cy = cy;
    }
    else
    {
      m_sizeMin.cy = rectAfter.Height();
    }

    m_Resize.Add(&m_options_list, CRhinoUiDialogItemResizer::resize_lockwidth);
  }

  CRhinoDialog::OnInitDialog();

  return TRUE;
}

LRESULT CSampleOptionsListCtrlDlg::OnRegisteredMessage(WPARAM wParam, LPARAM lParam)
{
  LPRhinoUiOptionsListCtrlNM pNM = (LPRhinoUiOptionsListCtrlNM)wParam;
  LRESULT* pResult = (LRESULT*)lParam;

  *pResult = false;

  CRhinoUiOptionsListCtrlItem* pItem = pNM->m_item;
  if (NULL == pItem)
    return 0L;

  UINT nNotificationCode = pNM->m_NMHDR.code;

  switch (nNotificationCode)
  {
  case CRhinoUiOptionsListCtrl::nc_on_item_button_clicked:
  {
    CRhinoUiOptionsListCtrlPushButton* pItemButton = static_cast<CRhinoUiOptionsListCtrlPushButton*>(pItem);
    CString s, sLabel = pItemButton->Label();
    if (sLabel.IsEmpty())
      sLabel = L"Untitled";
    s.Format(L"Button %ls clicked", static_cast<const wchar_t*>(sLabel));
    MessageBox(s);
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_item_check_box_clicked:
    if (static_cast<CRhinoUiOptionsListCtrlCheckBox*>(pItem)->GetCheck() == BST_UNCHECKED)
      pItem->SetText(L"Unchecked");
    else
      pItem->SetText(L"Checked");
    break;

  case CRhinoUiOptionsListCtrl::nc_on_combo_box_sel_changed:
  {
    CRhinoUiOptionsListCtrlComboBox* pItemComboBox = static_cast<CRhinoUiOptionsListCtrlComboBox*>(pItem);
    if (::IsWindow(pItemComboBox->m_hWnd))
    {
      CString s;
      pItemComboBox->GetTextFromListIndex(pItemComboBox->GetCurSel(), s);
      pItemComboBox->SetText(s);
    }
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_radio_button_checked:
  {
    const CRhinoUiOptionsListCtrlRadioButtonColumnBtn* pBtn = static_cast<CRhinoUiOptionsListCtrlRadioButtonColumn*>(pItem)->GetSelectedButton();
    CString s;

    pItem->SetText(s);
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_end_edit_item:
  {
    CString sLabel = pItem->Label();
    if (!sLabel.IsEmpty())
    {
      CString s;
      s.Format(L"nc_on_end_edit_item Label(%ls)", static_cast<const wchar_t*>(sLabel));
    }
  }
  break;
  }

  return 0L;
}


for (int i = 0; i < cnt; i++)
  {
    item.GetLBText(i, s);
    if (0 == sText.CompareNoCase(s))
      iCurSel = i;
  }

#include "stdafx.h"
#include "SampleOptionsListCtrlDlg.h"

CSampleOptionsListCtrl::CSampleOptionsListCtrl()
= default;

CSampleOptionsListCtrl::~CSampleOptionsListCtrl()
{
}

void CSampleOptionsListCtrl::OnEndEditItem(CRhinoUiOptionsListCtrlItem& item)
{
  CRhinoUiOptionsListCtrl::OnEndEditItem(item);
}

bool CSampleOptionsListCtrl::OnItemButtonClicked(CRhinoUiOptionsListCtrlPushButton& item)
{
  CRhinoUiOptionsListCtrl::OnItemButtonClicked(item);
  return true;
}

bool CSampleOptionsListCtrl::OnItemButtonClicked(CRhinoUiOptionsListCtrlCheckBox& item)
{
  CRhinoUiOptionsListCtrl::OnItemButtonClicked(item);

  CSampleOptionsListCtrlDlg* pDlg = static_cast<CSampleOptionsListCtrlDlg*>(GetParent());
  if (0 == pDlg)
    return false;

  return true;
}

bool CSampleOptionsListCtrl::OnComboBoxPopulated(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::OnComboBoxPopulated(item);

  int cnt = item.GetCount();
  if (cnt < 1)
    return false; // Nothing added
  // Current item text
  CString sText = item.Text();
  // place holder for new text
  CString s;
  // current selection index
  int iCurSel = -1;
  // process combo box for string that matches sText

  // Set current selection to matching string
  item.SetCurSel(iCurSel);
  return(cnt > 0);
}

bool CSampleOptionsListCtrl::PopulateItemComboBox(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::PopulateItemComboBox(item);

  CSampleOptionsListCtrlDlg* pDlg = static_cast<CSampleOptionsListCtrlDlg*>(GetParent());
  if (0 == pDlg)
    return false;

  return true;
}

void CSampleOptionsListCtrl::OnItemComboBoxSelChanged(CRhinoUiOptionsListCtrlComboBox& item)
{
  CRhinoUiOptionsListCtrl::OnItemComboBoxSelChanged(item);
  int iCurSel = item.GetCurSel();
  if (iCurSel < 0)
    return;
  // TODO: the combo box changed, update whatever it is connected to.
}

IMPLEMENT_DYNCREATE(CSampleOptionsListCtrlDlg, CRhinoDialog)

CSampleOptionsListCtrlDlg::CSampleOptionsListCtrlDlg()
  : CRhinoDialog(CSampleOptionsListCtrlDlg::IDD)
{
}

void CSampleOptionsListCtrlDlg::DoDataExchange(CDataExchange* pDX)
{
  CRhinoDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CSampleOptionsListCtrlDlg, CRhinoDialog)
END_MESSAGE_MAP()

BOOL CSampleOptionsListCtrlDlg::OnInitDialog()
{
  AddResizeControl(TRUE);
  SetMinSizeToCurrentSize();
  EnableSaveDialogPositionAndSize(TRUE);

  m_Resize.Add(IDOK, CRhinoUiDialogItemResizer::resize_lockbottomleft);
  m_Resize.Add(IDCANCEL, CRhinoUiDialogItemResizer::resize_lockbottomleft);

  CWnd* pWnd = GetDlgItem(IDC_FRAME);
  ASSERT(pWnd);

  CRect rect;
  pWnd->GetWindowRect(rect);
  ScreenToClient(rect);

  DWORD dwStyle = pWnd->GetStyle();

  pWnd->DestroyWindow();
  pWnd = 0;

  m_options_list.SetBorders(0, 0, 0, 0);

  if (m_options_list.CreateOptionsList(this, dwStyle, rect))
  {
    m_options_list.SetIndentItems(true);

    m_options_list.m_RadioColumn.SetLabel(L"Radio button test");
    m_options_list.m_RadioColumn.SetAutoDelete(false);
    m_options_list.m_RadioColumn.AddRadioButton(L"One");
    m_options_list.m_RadioColumn.AddRadioButton(L"Two");
    m_options_list.m_RadioColumn.AddRadioButton(L"Three");
    m_options_list.m_RadioColumn.SetSelectedButton(1);
    m_options_list.m_RadioColumn.SetIndentLevel(1);
    m_options_list.AddItem(&m_options_list.m_RadioColumn);

    // Need to adjust the dialog to fit its contents then save the initial size
    // for use by the scroll bar calculator later on

    CRect rectBefore;
    m_options_list.GetWindowRect(rectBefore);
    m_options_list.SizeToContent(true);

    CRect rectAfter;
    m_options_list.GetWindowRect(rectAfter);
    if (rectAfter.Height() != rectBefore.Height())
    {
      CRect rectWindow;
      GetWindowRect(rectWindow);
      int cy = rectWindow.Height() + (rectAfter.Height() - rectBefore.Height());
      SetWindowPos(NULL, 0, 0, rectWindow.Width(), cy, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
      m_sizeMin.cy = cy;
    }
    else
    {
      m_sizeMin.cy = rectAfter.Height();
    }

    m_Resize.Add(&m_options_list, CRhinoUiDialogItemResizer::resize_lockwidth);
  }

  CRhinoDialog::OnInitDialog();

  return TRUE;
}

LRESULT CSampleOptionsListCtrlDlg::OnRegisteredMessage(WPARAM wParam, LPARAM lParam)
{
  LPRhinoUiOptionsListCtrlNM pNM = (LPRhinoUiOptionsListCtrlNM)wParam;
  LRESULT* pResult = (LRESULT*)lParam;

  *pResult = false;

  CRhinoUiOptionsListCtrlItem* pItem = pNM->m_item;
  if (NULL == pItem)
    return 0L;

  UINT nNotificationCode = pNM->m_NMHDR.code;

  switch (nNotificationCode)
  {
  case CRhinoUiOptionsListCtrl::nc_on_item_button_clicked:
  {
    CRhinoUiOptionsListCtrlPushButton* pItemButton = static_cast<CRhinoUiOptionsListCtrlPushButton*>(pItem);
    CString s, sLabel = pItemButton->Label();
    if (sLabel.IsEmpty())
      sLabel = L"Untitled";
    s.Format(L"Button %ls clicked", static_cast<const wchar_t*>(sLabel));
    MessageBox(s);
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_item_check_box_clicked:
    if (static_cast<CRhinoUiOptionsListCtrlCheckBox*>(pItem)->GetCheck() == BST_UNCHECKED)
      pItem->SetText(L"Unchecked");
    else
      pItem->SetText(L"Checked");
    break;

  case CRhinoUiOptionsListCtrl::nc_on_combo_box_sel_changed:
  {
    CRhinoUiOptionsListCtrlComboBox* pItemComboBox = static_cast<CRhinoUiOptionsListCtrlComboBox*>(pItem);
    if (::IsWindow(pItemComboBox->m_hWnd))
    {
      CString s;
      pItemComboBox->GetTextFromListIndex(pItemComboBox->GetCurSel(), s);
      pItemComboBox->SetText(s);
    }
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_radio_button_checked:
  {
    const CRhinoUiOptionsListCtrlRadioButtonColumnBtn* pBtn = static_cast<CRhinoUiOptionsListCtrlRadioButtonColumn*>(pItem)->GetSelectedButton();
    CString s;
    if (pBtn)
      s = pBtn->ButtonLabel();
    pItem->SetText(s);
  }
  break;

  case CRhinoUiOptionsListCtrl::nc_on_end_edit_item:
  {
    CString sLabel = pItem->Label();
    if (!sLabel.IsEmpty())
    {
      CString s;
      s.Format(L"nc_on_end_edit_item Label(%ls)", static_cast<const wchar_t*>(sLabel));
    }
  }
  break;
  }

  return 0L;
}


#include "StdAfx.h"
#include "ChromaticRing.h"
#include "../Textures/Texture.h"
#include "../CryNameR.h"
#include "../../RenderDll/XRenderD3D9/DriverD3D.h"

#if defined(FLARES_SUPPORT_EDITING)
	#define MFPtr(FUNC_NAME) (Optics_MFPtr)(&ChromaticRing::FUNC_NAME)
void ChromaticRing::InitEditorParamGroups(DynArray<FuncVariableGroup>& groups)
{
	COpticsElement::InitEditorParamGroups(groups);

	FuncVariableGroup crGroup;
	crGroup.SetName("ChromaticRing", "Chromatic Ring");
	crGroup.AddVariable(new OpticsMFPVariable(e_BOOL, "Lock to light", "Lock to light", this, MFPtr(SetLockMovement), MFPtr(IsLockMovement)));
	crGroup.AddVariable(new OpticsMFPVariable(e_FLOAT, "Thickness", "Thickness", this, MFPtr(SetWidth), MFPtr(GetWidth)));

	crGroup.AddVariable(new OpticsMFPVariable(e_INT, "Polygon complexity", "Polygon complexity", this, MFPtr(SetPolyComplexity), MFPtr(GetPolyComplexity), 0, 1024.0f));
	crGroup.AddVariable(new OpticsMFPVariable(e_TEXTURE2D, "Gradient Texture", "Gradient Texture", this, MFPtr(SetSpectrumTex), MFPtr(GetSpectrumTex)));
	crGroup.AddVariable(new OpticsMFPVariable(e_BOOL, "Enable Gradient Texture", "Enable Gradient Texture", this, MFPtr(SetUsingSpectrumTex), MFPtr(IsUsingSpectrumTex)));

	crGroup.AddVariable(new OpticsMFPVariable(e_INT, "Noise seed", "Noise seed", this, MFPtr(SetNoiseSeed), MFPtr(GetNoiseSeed), -255.0f, 255.0f));
	crGroup.AddVariable(new OpticsMFPVariable(e_FLOAT, "Noise strength", "Noise strength", this, MFPtr(SetNoiseStrength), MFPtr(GetNoiseStrength)));
	crGroup.AddVariable(new OpticsMFPVariable(e_FLOAT, "Completion fading", "the fading ratio at the ends of this arc", this, MFPtr(SetCompletionFading), MFPtr(GetCompletionFading)));
	crGroup.AddVariable(new OpticsMFPVariable(e_FLOAT, "Completion span angle", "The span of this arc in degree", this, MFPtr(SetCompletionSpanAngle), MFPtr(GetCompletionSpanAngle)));
	crGroup.AddVariable(new OpticsMFPVariable(e_FLOAT, "Completion rotation", "The rotation of this arc", this, MFPtr(SetCompletionRotation), MFPtr(GetCompletionRotation)));

	groups.push_back(crGroup);
}
	#undef MFPtr
#endif

ChromaticRing::ChromaticRing(const char* name)
	: COpticsElement(name)
	, m_bUseSpectrumTex(false)
	, m_nPolyComplexity(160)
	, m_nColorComplexity(2)
	, m_fWidth(0.5f)
	, m_fNoiseStrength(0.0f)
	, m_fCompletionStart(90.f)
	, m_fCompletionEnd(270.f)
	, m_fCompletionFading(45.f)
{
	SetSize(0.9f);
	SetAutoRotation(true);
	SetAspectRatioCorrection(false);

	m_meshDirty = true;

	CConstantBufferPtr pSharedCB = gcpRendD3D->m_DevBufMan.CreateConstantBuffer(sizeof(SShaderParams), true, true);
	if (pSharedCB) pSharedCB->SetDebugName("ChromaticRing Per-Primitive CB");

	m_primitive.SetInlineConstantBuffer(eConstantBufferShaderSlot_PerPrimitive, pSharedCB, EShaderStage_Vertex | EShaderStage_Pixel);
	m_wireframePrimitive.SetInlineConstantBuffer(eConstantBufferShaderSlot_PerPrimitive, pSharedCB, EShaderStage_Vertex | EShaderStage_Pixel);
}

void ChromaticRing::Load(IXmlNode* pNode)
{
	COpticsElement::Load(pNode);

	XmlNodeRef pCameraOrbsNode = pNode->findChild("ChromaticRing");
	if (pCameraOrbsNode)
	{
		bool bLockMovement(m_bLockMovement);
		if (pCameraOrbsNode->getAttr("Locktolight", bLockMovement))
			SetLockMovement(bLockMovement);

		float fWidth(m_fWidth);
		if (pCameraOrbsNode->getAttr("Thickness", fWidth))
			SetWidth(fWidth);

		int nPolyComplexity(m_nPolyComplexity);
		if (pCameraOrbsNode->getAttr("Polygoncomplexity", nPolyComplexity))
			SetPolyComplexity(nPolyComplexity);

		const char* gradientTexName = NULL;
		if (pCameraOrbsNode->getAttr("GradientTexture", &gradientTexName))
		{
			if (gradientTexName && gradientTexName[0])
			{
				ITexture* pTexture = gEnv->pRenderer->EF_LoadTexture(gradientTexName);
				SetSpectrumTex((CTexture*)pTexture);
				if (pTexture)
					pTexture->Release();
			}
		}

		bool bUseSpectrumTex(m_bUseSpectrumTex);
		if (pCameraOrbsNode->getAttr("EnableGradientTexture", bUseSpectrumTex))
			SetUsingSpectrumTex(bUseSpectrumTex);

		int nNoiseSeed(m_nNoiseSeed);
		if (pCameraOrbsNode->getAttr("Noiseseed", nNoiseSeed))
			SetNoiseSeed(nNoiseSeed);

		float fNoiseStrength(m_fNoiseStrength);
		if (pCameraOrbsNode->getAttr("Noisestrength", fNoiseStrength))
			SetNoiseStrength(fNoiseStrength);

		float fCompletionFading(m_fCompletionFading);
		if (pCameraOrbsNode->getAttr("Completionfading", fCompletionFading))
			SetCompletionFading(fCompletionFading);

		float fTotalAngle(0);
		if (pCameraOrbsNode->getAttr("Completionspanangle", fTotalAngle))
			SetCompletionSpanAngle(fTotalAngle);

		float fCompletionRotation(0);
		if (pCameraOrbsNode->getAttr("Completionrotation", fCompletionRotation))
			SetCompletionRotation(fCompletionRotation);
	}
}

float ChromaticRing::computeDynamicSize(const Vec3& vSrcProjPos, const float maxSize)
{
	Vec2 dir(vSrcProjPos.x - 0.5f, vSrcProjPos.y - 0.5f);
	float len = dir.GetLength();
	const float hoopDistFactor = 2.3f;
	return len * hoopDistFactor * maxSize;
}

{
	if (m_pSpectrumTex == nullptr)
	{
		m_pSpectrumTex = std::move(CTexture::ForName("%ENGINE%/EngineAssets/Textures/flares/spectrum_full.tif", FT_DONT_STREAM, eTF_Unknown));
	}

	return m_pSpectrumTex;
}

bool ChromaticRing::PreparePrimitives(const SPreparePrimitivesContext& context)
{
	if (!IsVisible())
		return true;

	// get rt flags
	uint64 rtFlags = 0;
	ApplyGeneralFlags(rtFlags);
	ApplySpectrumTexFlag(rtFlags, m_bUseSpectrumTex);

	// update constants (constant buffer is shared by both primitives, so we only have to update it once)
	{
		auto constants = m_primitive.GetConstantManager().BeginTypedConstantUpdate<SShaderParams>(eConstantBufferShaderSlot_PerPrimitive, EShaderStage_Vertex | EShaderStage_Pixel);

		for (int i = 0; i < context.viewInfoCount; ++i)
		{
			Vec3 screenPos = computeOrbitPos(context.lightScreenPos[i], m_globalOrbitAngle);
			float newSize  = computeDynamicSize(screenPos, m_globalSize);

			ApplyCommonParams(constants, context.pViewInfo->viewport, context.lightScreenPos[0], Vec2(newSize));

			constants->meshCenterAndBrt[0] = m_bLockMovement ? screenPos.x : computeMovementLocationX(screenPos);
			constants->meshCenterAndBrt[1] = m_bLockMovement ? screenPos.y : computeMovementLocationY(screenPos);
			constants->meshCenterAndBrt[2] = screenPos.z;
			constants->meshCenterAndBrt[3] = m_globalFlareBrightness;

			if (i < context.viewInfoCount - 1)
				constants.BeginStereoOverride(false);
		}

		m_primitive.GetConstantManager().EndTypedConstantUpdate(constants);
	}

	// update mesh
	ValidateMesh();

	// now prepare primitives
	CRenderPrimitive* pPrimitives[] = { &m_primitive, &m_wireframePrimitive };

	for (int i = 0; i < CRY_ARRAY_COUNT(pPrimitives); ++i)
	{
		static CCryNameTSCRC techChromaticRing("ChromaticRing");

		auto& prim = *pPrimitives[i];
		prim.SetTechnique(CShaderMan::s_ShaderLensOptics, techChromaticRing, rtFlags);
		prim.SetRenderState(GS_NODEPTHTEST | GS_BLSRC_ONE | GS_BLDST_ONE | (i==0 ? 0 : GS_WIREFRAME));
		prim.SetTexture(0, GetOrLoadSpectrumTex());
		prim.SetSampler(0, EDefaultSamplerStates::LinearBorder_Black);
		prim.SetCustomVertexStream(m_vertexBuffer, EDefaultInputLayouts::P3F_C4B_T2F, sizeof(SVF_P3F_C4B_T2F));
		prim.SetCustomIndexStream(m_indexBuffer, Index16);
		prim.SetDrawInfo(eptTriangleList, 0, 0, GetIndexCount());
		prim.Compile(context.pass);

		context.pass.AddPrimitive(&prim);
	}

	return true;
}


{
	if (m_pSpectrumTex == nullptr)
	{
		m_pSpectrumTex = std::move(CTexture::ForName("%ENGINE%/EngineAssets/Textures/flares/spectrum_full.tif", FT_DONT_STREAM, eTF_Unknown));
	}

	return m_pSpectrumTex;
}

#include "StdAfx.h"
#include "ChromaticRing.h"
#include "../Textures/Texture.h"
#include "../CryNameR.h"
#include "../../RenderDll/XRenderD3D9/DriverD3D.h"

#if defined(FLARES_SUPPORT_EDITING)
	#define MFPtr(FUNC_NAME) (Optics_MFPtr)(&ChromaticRing::FUNC_NAME)
void ChromaticRing::InitEditorParamGroups(DynArray<FuncVariableGroup>& groups)
{
	COpticsElement::InitEditorParamGroups(groups);

	FuncVariableGroup crGroup;
	crGroup.SetName("ChromaticRing", "Chromatic Ring");
	crGroup.AddVariable(new OpticsMFPVariable(e_BOOL, "Lock to light", "Lock to light", this, MFPtr(SetLockMovement), MFPtr(IsLockMovement)));
	crGroup.AddVariable(new OpticsMFPVariable(e_FLOAT, "Thickness", "Thickness", this, MFPtr(SetWidth), MFPtr(GetWidth)));

	crGroup.AddVariable(new OpticsMFPVariable(e_INT, "Polygon complexity", "Polygon complexity", this, MFPtr(SetPolyComplexity), MFPtr(GetPolyComplexity), 0, 1024.0f));
	crGroup.AddVariable(new OpticsMFPVariable(e_TEXTURE2D, "Gradient Texture", "Gradient Texture", this, MFPtr(SetSpectrumTex), MFPtr(GetSpectrumTex)));
	crGroup.AddVariable(new OpticsMFPVariable(e_BOOL, "Enable Gradient Texture", "Enable Gradient Texture", this, MFPtr(SetUsingSpectrumTex), MFPtr(IsUsingSpectrumTex)));

	crGroup.AddVariable(new OpticsMFPVariable(e_INT, "Noise seed", "Noise seed", this, MFPtr(SetNoiseSeed), MFPtr(GetNoiseSeed), -255.0f, 255.0f));
	crGroup.AddVariable(new OpticsMFPVariable(e_FLOAT, "Noise strength", "Noise strength", this, MFPtr(SetNoiseStrength), MFPtr(GetNoiseStrength)));
	crGroup.AddVariable(new OpticsMFPVariable(e_FLOAT, "Completion fading", "the fading ratio at the ends of this arc", this, MFPtr(SetCompletionFading), MFPtr(GetCompletionFading)));
	crGroup.AddVariable(new OpticsMFPVariable(e_FLOAT, "Completion span angle", "The span of this arc in degree", this, MFPtr(SetCompletionSpanAngle), MFPtr(GetCompletionSpanAngle)));
	crGroup.AddVariable(new OpticsMFPVariable(e_FLOAT, "Completion rotation", "The rotation of this arc", this, MFPtr(SetCompletionRotation), MFPtr(GetCompletionRotation)));

	groups.push_back(crGroup);
}
	#undef MFPtr
#endif

ChromaticRing::ChromaticRing(const char* name)
	: COpticsElement(name)
	, m_bUseSpectrumTex(false)
	, m_nPolyComplexity(160)
	, m_nColorComplexity(2)
	, m_fWidth(0.5f)
	, m_fNoiseStrength(0.0f)
	, m_fCompletionStart(90.f)
	, m_fCompletionEnd(270.f)
	, m_fCompletionFading(45.f)
{
	SetSize(0.9f);
	SetAutoRotation(true);
	SetAspectRatioCorrection(false);

	m_meshDirty = true;

	CConstantBufferPtr pSharedCB = gcpRendD3D->m_DevBufMan.CreateConstantBuffer(sizeof(SShaderParams), true, true);
	if (pSharedCB) pSharedCB->SetDebugName("ChromaticRing Per-Primitive CB");

	m_primitive.SetInlineConstantBuffer(eConstantBufferShaderSlot_PerPrimitive, pSharedCB, EShaderStage_Vertex | EShaderStage_Pixel);
	m_wireframePrimitive.SetInlineConstantBuffer(eConstantBufferShaderSlot_PerPrimitive, pSharedCB, EShaderStage_Vertex | EShaderStage_Pixel);
}

void ChromaticRing::Load(IXmlNode* pNode)
{
	COpticsElement::Load(pNode);

	XmlNodeRef pCameraOrbsNode = pNode->findChild("ChromaticRing");
	if (pCameraOrbsNode)
	{
		bool bLockMovement(m_bLockMovement);
		if (pCameraOrbsNode->getAttr("Locktolight", bLockMovement))
			SetLockMovement(bLockMovement);

		float fWidth(m_fWidth);
		if (pCameraOrbsNode->getAttr("Thickness", fWidth))
			SetWidth(fWidth);

		int nPolyComplexity(m_nPolyComplexity);
		if (pCameraOrbsNode->getAttr("Polygoncomplexity", nPolyComplexity))
			SetPolyComplexity(nPolyComplexity);

		const char* gradientTexName = NULL;
		if (pCameraOrbsNode->getAttr("GradientTexture", &gradientTexName))
		{
			if (gradientTexName && gradientTexName[0])
			{
				ITexture* pTexture = gEnv->pRenderer->EF_LoadTexture(gradientTexName);
				SetSpectrumTex((CTexture*)pTexture);
				if (pTexture)
					pTexture->Release();
			}
		}

		bool bUseSpectrumTex(m_bUseSpectrumTex);
		if (pCameraOrbsNode->getAttr("EnableGradientTexture", bUseSpectrumTex))
			SetUsingSpectrumTex(bUseSpectrumTex);

		int nNoiseSeed(m_nNoiseSeed);
		if (pCameraOrbsNode->getAttr("Noiseseed", nNoiseSeed))
			SetNoiseSeed(nNoiseSeed);

		float fNoiseStrength(m_fNoiseStrength);
		if (pCameraOrbsNode->getAttr("Noisestrength", fNoiseStrength))
			SetNoiseStrength(fNoiseStrength);

		float fCompletionFading(m_fCompletionFading);
		if (pCameraOrbsNode->getAttr("Completionfading", fCompletionFading))
			SetCompletionFading(fCompletionFading);

		float fTotalAngle(0);
		if (pCameraOrbsNode->getAttr("Completionspanangle", fTotalAngle))
			SetCompletionSpanAngle(fTotalAngle);

		float fCompletionRotation(0);
		if (pCameraOrbsNode->getAttr("Completionrotation", fCompletionRotation))
			SetCompletionRotation(fCompletionRotation);
	}
}

float ChromaticRing::computeDynamicSize(const Vec3& vSrcProjPos, const float maxSize)
{
	Vec2 dir(vSrcProjPos.x - 0.5f, vSrcProjPos.y - 0.5f);
	float len = dir.GetLength();
	const float hoopDistFactor = 2.3f;
	return len * hoopDistFactor * maxSize;
}

CTexture* ChromaticRing::GetOrLoadSpectrumTex()

bool ChromaticRing::PreparePrimitives(const SPreparePrimitivesContext& context)
{
	if (!IsVisible())
		return true;

	// get rt flags
	uint64 rtFlags = 0;
	ApplyGeneralFlags(rtFlags);
	ApplySpectrumTexFlag(rtFlags, m_bUseSpectrumTex);

	// update constants (constant buffer is shared by both primitives, so we only have to update it once)
	{
		auto constants = m_primitive.GetConstantManager().BeginTypedConstantUpdate<SShaderParams>(eConstantBufferShaderSlot_PerPrimitive, EShaderStage_Vertex | EShaderStage_Pixel);

		for (int i = 0; i < context.viewInfoCount; ++i)
		{
			Vec3 screenPos = computeOrbitPos(context.lightScreenPos[i], m_globalOrbitAngle);
			float newSize  = computeDynamicSize(screenPos, m_globalSize);

			ApplyCommonParams(constants, context.pViewInfo->viewport, context.lightScreenPos[0], Vec2(newSize));

			constants->meshCenterAndBrt[0] = m_bLockMovement ? screenPos.x : computeMovementLocationX(screenPos);
			constants->meshCenterAndBrt[1] = m_bLockMovement ? screenPos.y : computeMovementLocationY(screenPos);
			constants->meshCenterAndBrt[2] = screenPos.z;
			constants->meshCenterAndBrt[3] = m_globalFlareBrightness;

			if (i < context.viewInfoCount - 1)
				constants.BeginStereoOverride(false);
		}

		m_primitive.GetConstantManager().EndTypedConstantUpdate(constants);
	}

	// update mesh
	ValidateMesh();

	// now prepare primitives
	CRenderPrimitive* pPrimitives[] = { &m_primitive, &m_wireframePrimitive };

	for (int i = 0; i < CRY_ARRAY_COUNT(pPrimitives); ++i)
	{
		static CCryNameTSCRC techChromaticRing("ChromaticRing");

		auto& prim = *pPrimitives[i];
		prim.SetTechnique(CShaderMan::s_ShaderLensOptics, techChromaticRing, rtFlags);
		prim.SetRenderState(GS_NODEPTHTEST | GS_BLSRC_ONE | GS_BLDST_ONE | (i==0 ? 0 : GS_WIREFRAME));
		prim.SetTexture(0, GetOrLoadSpectrumTex());
		prim.SetSampler(0, EDefaultSamplerStates::LinearBorder_Black);
		prim.SetCustomVertexStream(m_vertexBuffer, EDefaultInputLayouts::P3F_C4B_T2F, sizeof(SVF_P3F_C4B_T2F));
		prim.SetCustomIndexStream(m_indexBuffer, Index16);
		prim.SetDrawInfo(eptTriangleList, 0, 0, GetIndexCount());
		prim.Compile(context.pass);

		context.pass.AddPrimitive(&prim);
	}

	return true;
}