Compare commits
7 Commits
version/3.
...
version/3.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fbdd9ad2f | ||
|
|
24539968fd | ||
|
|
f13b356531 | ||
|
|
477594d7bc | ||
|
|
e955a18126 | ||
|
|
adec1c4507 | ||
|
|
172b8a032c |
@@ -100,6 +100,7 @@ set(SYSTEM_EXPAT OFF)
|
||||
endif()
|
||||
|
||||
option(SIMGEAR_HEADLESS "Set to ON to build SimGear without GUI/graphics support" OFF)
|
||||
option(JPEG_FACTORY "Enable JPEG-factory support" OFF)
|
||||
option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
|
||||
option(ENABLE_TESTS "Set to OFF to disable building SimGear's test applications" ON)
|
||||
option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" ON)
|
||||
@@ -160,12 +161,9 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
endif (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
|
||||
if(APPLE)
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
find_library(CORE_SERVICES_LIBRARY CoreServices)
|
||||
endif()
|
||||
|
||||
# Somehow this only works if included before searching for Boost...
|
||||
include(BoostTestTargets)
|
||||
|
||||
find_package(Boost REQUIRED)
|
||||
set (BOOST_CXX_FLAGS "-DBOOST_MULTI_INDEX_DISABLE_SERIALIZATION -DBOOST_BIMAP_DISABLE_SERIALIZATION")
|
||||
|
||||
@@ -181,9 +179,17 @@ else()
|
||||
message(STATUS "Sound support: ENABLED")
|
||||
endif(ENABLE_SOUND)
|
||||
|
||||
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgUtil)
|
||||
find_package(OpenSceneGraph 3.0.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgUtil)
|
||||
endif(SIMGEAR_HEADLESS)
|
||||
|
||||
if(JPEG_FACTORY)
|
||||
message(STATUS "JPEG-factory: ENABLED")
|
||||
find_package(JPEG REQUIRED)
|
||||
include_directories(${JPEG_INCLUDE_DIR})
|
||||
else()
|
||||
message(STATUS "JPEG-factory: DISABLED")
|
||||
endif(JPEG_FACTORY)
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
@@ -263,9 +269,7 @@ check_cxx_source_compiles(
|
||||
|
||||
if(HAVE_DLFCN_H)
|
||||
check_library_exists(dl dlerror "" HAVE_DL)
|
||||
if(HAVE_DL)
|
||||
set(DL_LIBRARY "dl")
|
||||
endif()
|
||||
set(DL_LIBRARY "dl")
|
||||
endif()
|
||||
|
||||
SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "add a postfix, usually 'd' on windows")
|
||||
@@ -338,11 +342,9 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS_C} ${MSVC_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS_CXX} ${MSVC_FLAGS} ${BOOST_CXX_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MSVC_LD_FLAGS}")
|
||||
|
||||
# use BEFORE to ensure local directories are used first,
|
||||
# ahead of system-installed libs
|
||||
include_directories(BEFORE ${PROJECT_SOURCE_DIR})
|
||||
include_directories(BEFORE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
|
||||
include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear)
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
|
||||
include_directories(${PROJECT_BINARY_DIR}/simgear)
|
||||
|
||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}
|
||||
${Boost_INCLUDE_DIRS}
|
||||
@@ -376,7 +378,7 @@ set(TEST_LIBS_INTERNAL_CORE
|
||||
${WINSOCK_LIBRARY}
|
||||
${RT_LIBRARY}
|
||||
${DL_LIBRARY}
|
||||
${COCOA_LIBRARY})
|
||||
${CORE_SERVICES_LIBRARY})
|
||||
set(TEST_LIBS SimGearCore ${TEST_LIBS_INTERNAL_CORE})
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
# - Add tests using boost::test
|
||||
#
|
||||
# Add this line to your test files in place of including a basic boost test header:
|
||||
# #include <BoostTestTargetConfig.h>
|
||||
#
|
||||
# If you cannot do that and must use the included form for a given test,
|
||||
# include the line
|
||||
# // OVERRIDE_BOOST_TEST_INCLUDED_WARNING
|
||||
# in the same file with the boost test include.
|
||||
#
|
||||
# include(BoostTestTargets)
|
||||
# add_boost_test(<testdriver_name> SOURCES <source1> [<more sources...>]
|
||||
# [FAIL_REGULAR_EXPRESSION <additional fail regex>]
|
||||
# [LAUNCHER <generic launcher script>]
|
||||
# [LIBRARIES <library> [<library>...]]
|
||||
# [RESOURCES <resource> [<resource>...]]
|
||||
# [TESTS <testcasename> [<testcasename>...]])
|
||||
#
|
||||
# If for some reason you need access to the executable target created,
|
||||
# it can be found in ${${testdriver_name}_TARGET_NAME} as specified when
|
||||
# you called add_boost_test
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Requires:
|
||||
# GetForceIncludeDefinitions
|
||||
# CopyResourcesToBuildTree
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2009-2010.
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
if(__add_boost_test)
|
||||
return()
|
||||
endif()
|
||||
set(__add_boost_test YES)
|
||||
|
||||
set(BOOST_TEST_TARGET_PREFIX "test")
|
||||
|
||||
if(NOT Boost_FOUND)
|
||||
find_package(Boost 1.34.0 QUIET)
|
||||
endif()
|
||||
if("${Boost_VERSION}0" LESS "1034000")
|
||||
set(_shared_msg
|
||||
"NOTE: boost::test-based targets and tests cannot "
|
||||
"be added: boost >= 1.34.0 required but not found. "
|
||||
"(found: '${Boost_VERSION}'; want >=103400) ")
|
||||
if(BUILD_TESTING)
|
||||
message(FATAL_ERROR
|
||||
${_shared_msg}
|
||||
"You may disable BUILD_TESTING to continue without the "
|
||||
"tests.")
|
||||
else()
|
||||
message(STATUS
|
||||
${_shared_msg}
|
||||
"BUILD_TESTING disabled, so continuing anyway.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(GetForceIncludeDefinitions)
|
||||
include(CopyResourcesToBuildTree)
|
||||
|
||||
if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
|
||||
set(_boosttesttargets_libs)
|
||||
set(_boostConfig "BoostTestTargetsIncluded.h")
|
||||
if(NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
|
||||
find_package(Boost 1.34.0 QUIET COMPONENTS unit_test_framework)
|
||||
endif()
|
||||
if(Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
|
||||
set(_boosttesttargets_libs "${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}")
|
||||
if(Boost_USE_STATIC_LIBS)
|
||||
set(_boostConfig "BoostTestTargetsStatic.h")
|
||||
else()
|
||||
set(_boostConfig "BoostTestTargetsDynamic.h")
|
||||
endif()
|
||||
endif()
|
||||
get_filename_component(_moddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||
configure_file("${_moddir}/${_boostConfig}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/BoostTestTargetConfig.h"
|
||||
COPYONLY)
|
||||
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
|
||||
function(add_boost_test _name)
|
||||
if(NOT BUILD_TESTING)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# parse arguments
|
||||
set(_nowhere)
|
||||
set(_curdest _nowhere)
|
||||
set(_val_args
|
||||
SOURCES
|
||||
FAIL_REGULAR_EXPRESSION
|
||||
LAUNCHER
|
||||
LIBRARIES
|
||||
RESOURCES
|
||||
TESTS)
|
||||
set(_bool_args
|
||||
USE_COMPILED_LIBRARY)
|
||||
foreach(_arg ${_val_args} ${_bool_args})
|
||||
set(${_arg})
|
||||
endforeach()
|
||||
foreach(_element ${ARGN})
|
||||
list(FIND _val_args "${_element}" _val_arg_find)
|
||||
list(FIND _bool_args "${_element}" _bool_arg_find)
|
||||
if("${_val_arg_find}" GREATER "-1")
|
||||
set(_curdest "${_element}")
|
||||
elseif("${_bool_arg_find}" GREATER "-1")
|
||||
set("${_element}" ON)
|
||||
set(_curdest _nowhere)
|
||||
else()
|
||||
list(APPEND ${_curdest} "${_element}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(_nowhere)
|
||||
message(FATAL_ERROR "Syntax error in use of add_boost_test!")
|
||||
endif()
|
||||
|
||||
if(NOT SOURCES)
|
||||
message(FATAL_ERROR
|
||||
"Syntax error in use of add_boost_test: at least one source file required!")
|
||||
endif()
|
||||
|
||||
if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
|
||||
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
|
||||
set(includeType)
|
||||
foreach(src ${SOURCES})
|
||||
file(READ ${src} thefile)
|
||||
if("${thefile}" MATCHES ".*BoostTestTargetConfig.h.*")
|
||||
set(includeType CONFIGURED)
|
||||
set(includeFileLoc ${src})
|
||||
break()
|
||||
elseif("${thefile}" MATCHES ".*boost/test/included/unit_test.hpp.*")
|
||||
set(includeType INCLUDED)
|
||||
set(includeFileLoc ${src})
|
||||
set(_boosttesttargets_libs) # clear this out - linking would be a bad idea
|
||||
if(NOT
|
||||
"${thefile}"
|
||||
MATCHES
|
||||
".*OVERRIDE_BOOST_TEST_INCLUDED_WARNING.*")
|
||||
message("Please replace the include line in ${src} with this alternate include line instead:")
|
||||
message(" \#include <BoostTestTargetConfig.h>")
|
||||
message("Once you've saved your changes, re-run CMake. (See BoostTestTargets.cmake for more info)")
|
||||
endif()
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT _boostTestTargetsNagged${_name} STREQUAL "${includeType}")
|
||||
if("includeType" STREQUAL "CONFIGURED")
|
||||
message(STATUS
|
||||
"Test '${_name}' uses the CMake-configurable form of the boost test framework - congrats! (Including File: ${includeFileLoc})")
|
||||
elseif("${includeType}" STREQUAL "INCLUDED")
|
||||
message("In test '${_name}': ${includeFileLoc} uses the 'included' form of the boost unit test framework.")
|
||||
else()
|
||||
message("In test '${_name}': Didn't detect the CMake-configurable boost test include.")
|
||||
message("Please replace your existing boost test include in that test with the following:")
|
||||
message(" \#include <BoostTestTargetConfig.h>")
|
||||
message("Once you've saved your changes, re-run CMake. (See BoostTestTargets.cmake for more info)")
|
||||
endif()
|
||||
endif()
|
||||
set(_boostTestTargetsNagged${_name}
|
||||
"${includeType}"
|
||||
CACHE
|
||||
INTERNAL
|
||||
""
|
||||
FORCE)
|
||||
|
||||
|
||||
if(RESOURCES)
|
||||
list(APPEND SOURCES ${RESOURCES})
|
||||
endif()
|
||||
|
||||
# Generate a unique target name, using the relative binary dir
|
||||
# and provided name. (transform all / into _ and remove all other
|
||||
# non-alphabet characters)
|
||||
file(RELATIVE_PATH
|
||||
targetpath
|
||||
"${CMAKE_BINARY_DIR}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}")
|
||||
string(REGEX REPLACE "[^A-Za-z/_]" "" targetpath "${targetpath}")
|
||||
string(REPLACE "/" "_" targetpath "${targetpath}")
|
||||
|
||||
set(_target_name ${BOOST_TEST_TARGET_PREFIX}-${targetpath}-${_name})
|
||||
set(${_name}_TARGET_NAME "${_target_name}" PARENT_SCOPE)
|
||||
|
||||
# Build the test.
|
||||
add_executable(${_target_name} ${SOURCES})
|
||||
|
||||
list(APPEND LIBRARIES ${_boosttesttargets_libs})
|
||||
|
||||
if(LIBRARIES)
|
||||
target_link_libraries(${_target_name} ${LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(RESOURCES)
|
||||
set_property(TARGET ${_target_name} PROPERTY RESOURCE ${RESOURCES})
|
||||
copy_resources_to_build_tree(${_target_name})
|
||||
endif()
|
||||
|
||||
if(NOT Boost_TEST_FLAGS)
|
||||
# set(Boost_TEST_FLAGS --catch_system_error=yes --output_format=XML)
|
||||
set(Boost_TEST_FLAGS --catch_system_error=yes)
|
||||
endif()
|
||||
|
||||
# TODO: Figure out why only recent boost handles individual test running properly
|
||||
|
||||
if(LAUNCHER)
|
||||
set(_test_command ${LAUNCHER} "\$<TARGET_FILE:${_target_name}>")
|
||||
else()
|
||||
set(_test_command ${_target_name})
|
||||
endif()
|
||||
|
||||
if(TESTS AND ( "${Boost_VERSION}" VERSION_GREATER "103799" ))
|
||||
foreach(_test ${TESTS})
|
||||
add_test(
|
||||
${_name}-${_test}
|
||||
${_test_command} --run_test=${_test} ${Boost_TEST_FLAGS}
|
||||
)
|
||||
if(FAIL_REGULAR_EXPRESSION)
|
||||
set_tests_properties(${_name}-${_test}
|
||||
PROPERTIES
|
||||
FAIL_REGULAR_EXPRESSION
|
||||
"${FAIL_REGULAR_EXPRESSION}")
|
||||
endif()
|
||||
endforeach()
|
||||
else()
|
||||
add_test(
|
||||
${_name}-boost_test
|
||||
${_test_command} ${Boost_TEST_FLAGS}
|
||||
)
|
||||
if(FAIL_REGULAR_EXPRESSION)
|
||||
set_tests_properties(${_name}-boost_test
|
||||
PROPERTIES
|
||||
FAIL_REGULAR_EXPRESSION
|
||||
"${FAIL_REGULAR_EXPRESSION}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# CppCheck the test if we can.
|
||||
if(COMMAND add_cppcheck)
|
||||
add_cppcheck(${_target_name} STYLE UNUSED_FUNCTIONS)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -1,8 +0,0 @@
|
||||
// Small header computed by CMake to set up boost test.
|
||||
// include AFTER #define BOOST_TEST_MODULE whatever
|
||||
// but before any other boost test includes.
|
||||
|
||||
// Using the Boost UTF dynamic library
|
||||
|
||||
#define BOOST_TEST_DYN_LINK
|
||||
#include <boost/test/unit_test.hpp>
|
||||
@@ -1,7 +0,0 @@
|
||||
// Small header computed by CMake to set up boost test.
|
||||
// include AFTER #define BOOST_TEST_MODULE whatever
|
||||
// but before any other boost test includes.
|
||||
|
||||
// Using the Boost UTF included framework
|
||||
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
@@ -1,7 +0,0 @@
|
||||
// Small header computed by CMake to set up boost test.
|
||||
// include AFTER #define BOOST_TEST_MODULE whatever
|
||||
// but before any other boost test includes.
|
||||
|
||||
// Using the Boost UTF static library
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
@@ -1,83 +0,0 @@
|
||||
# - Copy the resources your app needs to the build tree.
|
||||
#
|
||||
# copy_resources_to_build_tree(<target_name>)
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2009-2010.
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
if(__copy_resources_to_build_tree)
|
||||
return()
|
||||
endif()
|
||||
set(__copy_resources_to_build_tree YES)
|
||||
|
||||
function(copy_resources_to_build_tree _target)
|
||||
get_target_property(_resources ${_target} RESOURCE)
|
||||
if(NOT _resources)
|
||||
# Bail if no resources
|
||||
message(STATUS
|
||||
"Told to copy resources for target ${_target}, but "
|
||||
"no resources are set!")
|
||||
return()
|
||||
endif()
|
||||
|
||||
get_target_property(_path ${_target} LOCATION)
|
||||
get_filename_component(_path "${_path}" PATH)
|
||||
|
||||
if(NOT MSVC AND NOT "${CMAKE_GENERATOR}" MATCHES "Makefiles")
|
||||
foreach(_config ${CMAKE_CONFIGURATION_TYPES})
|
||||
get_target_property(_path${_config} ${_target} LOCATION_${_config})
|
||||
get_filename_component(_path${_config} "${_path${_config}}" PATH)
|
||||
add_custom_command(TARGET ${_target}
|
||||
POST_BUILD
|
||||
COMMAND
|
||||
${CMAKE_COMMAND}
|
||||
ARGS -E make_directory "${_path${_config}}/"
|
||||
COMMENT "Creating directory ${_path${_config}}/")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
foreach(_res ${_resources})
|
||||
if(NOT IS_ABSOLUTE "${_res}")
|
||||
get_filename_component(_res "${_res}" ABSOLUTE)
|
||||
endif()
|
||||
get_filename_component(_name "${_res}" NAME)
|
||||
|
||||
if(MSVC)
|
||||
# Working dir is solution file dir, not exe file dir.
|
||||
add_custom_command(TARGET ${_target}
|
||||
POST_BUILD
|
||||
COMMAND
|
||||
${CMAKE_COMMAND}
|
||||
ARGS -E copy "${_res}" "${CMAKE_BINARY_DIR}/"
|
||||
COMMENT "Copying ${_name} to ${CMAKE_BINARY_DIR}/ for MSVC")
|
||||
else()
|
||||
if("${CMAKE_GENERATOR}" MATCHES "Makefiles")
|
||||
add_custom_command(TARGET ${_target}
|
||||
POST_BUILD
|
||||
COMMAND
|
||||
${CMAKE_COMMAND}
|
||||
ARGS -E copy "${_res}" "${_path}/"
|
||||
COMMENT "Copying ${_name} to ${_path}/")
|
||||
else()
|
||||
foreach(_config ${CMAKE_CONFIGURATION_TYPES})
|
||||
add_custom_command(TARGET ${_target}
|
||||
POST_BUILD
|
||||
COMMAND
|
||||
${CMAKE_COMMAND}
|
||||
ARGS -E copy "${_res}" "${_path${_config}}"
|
||||
COMMENT "Copying ${_name} to ${_path${_config}}")
|
||||
endforeach()
|
||||
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
@@ -1,44 +0,0 @@
|
||||
# - Get the platform-appropriate flags to add to force inclusion of a file
|
||||
#
|
||||
# The most common use of this is to use a generated config.h-type file
|
||||
# placed out of the source tree in all files.
|
||||
#
|
||||
# get_force_include_definitions(var forcedincludefiles...) -
|
||||
# where var is the name of your desired output variable, and everything
|
||||
# else is a source file to forcibly include.
|
||||
# a list item to be filtered.
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2009-2010.
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
if(__get_force_include_definitions)
|
||||
return()
|
||||
endif()
|
||||
set(__get_force_include_definitions YES)
|
||||
|
||||
function(get_force_include_definitions var)
|
||||
set(_flagprefix)
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(_flag "-include")
|
||||
elseif(MSVC)
|
||||
set(_flag "/FI")
|
||||
else()
|
||||
message(SEND_ERROR "You don't seem to be using MSVC or GCC, but")
|
||||
message(SEND_ERROR "the project called get_force_include_definitions.")
|
||||
message(SEND_ERROR "Contact this project with the name of your")
|
||||
message(FATAL_ERROR "compiler and preferably the flag to force includes")
|
||||
endif()
|
||||
|
||||
set(_out)
|
||||
foreach(_item ${ARGN})
|
||||
list(APPEND _out "${_flag} \"${_item}\"")
|
||||
endforeach()
|
||||
set(${var} "${_out}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -122,7 +122,7 @@ target_link_libraries(SimGearCore
|
||||
${DL_LIBRARY}
|
||||
${EXPAT_LIBRARIES}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${COCOA_LIBRARY})
|
||||
${CORE_SERVICES_LIBRARY})
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
target_link_libraries(SimGearScene
|
||||
|
||||
@@ -4,13 +4,4 @@ include (SimGearComponent)
|
||||
set(HEADERS newbucket.hxx)
|
||||
set(SOURCES newbucket.cxx)
|
||||
|
||||
simgear_component(bucket bucket "${SOURCES}" "${HEADERS}")
|
||||
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
|
||||
add_executable(test_bucket test_bucket.cxx)
|
||||
add_test(test_bucket ${EXECUTABLE_OUTPUT_PATH}/test_bucket)
|
||||
target_link_libraries(test_bucket ${TEST_LIBS})
|
||||
|
||||
endif(ENABLE_TESTS)
|
||||
simgear_component(bucket bucket "${SOURCES}" "${HEADERS}")
|
||||
@@ -27,56 +27,36 @@
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio> // some platforms need this for ::snprintf
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#include "newbucket.hxx"
|
||||
|
||||
|
||||
// default constructor
|
||||
SGBucket::SGBucket() :
|
||||
lon(-1000),
|
||||
lat(-1000),
|
||||
x(0),
|
||||
y(0)
|
||||
{
|
||||
SGBucket::SGBucket() {
|
||||
}
|
||||
|
||||
bool SGBucket::isValid() const
|
||||
{
|
||||
// The most northerly valid latitude is 89, not 90. There is no tile
|
||||
// whose *bottom* latitude is 90. Similar there is no tile whose left egde
|
||||
// is 180 longitude.
|
||||
return (lon >= -180) &&
|
||||
(lon < 180) &&
|
||||
(lat >= -90) &&
|
||||
(lat < 90) &&
|
||||
(x < 8) && (y < 8);
|
||||
}
|
||||
|
||||
void SGBucket::make_bad()
|
||||
{
|
||||
lon = -1000;
|
||||
lat = -1000;
|
||||
}
|
||||
|
||||
#ifndef NO_DEPRECATED_API
|
||||
|
||||
// constructor for specified location
|
||||
SGBucket::SGBucket(const double dlon, const double dlat) {
|
||||
set_bucket(dlon, dlat);
|
||||
}
|
||||
#endif
|
||||
|
||||
SGBucket::SGBucket(const SGGeod& geod) {
|
||||
innerSet(geod.getLongitudeDeg(),
|
||||
geod.getLatitudeDeg());
|
||||
set_bucket(geod);
|
||||
}
|
||||
|
||||
// create an impossible bucket if false
|
||||
SGBucket::SGBucket(const bool is_good) {
|
||||
set_bucket(0.0, 0.0);
|
||||
if ( !is_good ) {
|
||||
lon = -1000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Parse a unique scenery tile index and find the lon, lat, x, and y
|
||||
SGBucket::SGBucket(const long int bindex) {
|
||||
long int index = bindex;
|
||||
@@ -95,59 +75,48 @@ SGBucket::SGBucket(const long int bindex) {
|
||||
x = index;
|
||||
}
|
||||
|
||||
/* Calculate the greatest integral value less than
|
||||
* or equal to the given value (floor(x)),
|
||||
* but attribute coordinates close to the boundary to the next
|
||||
* (increasing) integral
|
||||
*/
|
||||
static int floorWithEpsilon(double x)
|
||||
{
|
||||
return static_cast<int>(floor(x + SG_EPSILON));
|
||||
}
|
||||
|
||||
#ifndef NO_DEPRECATED_API
|
||||
|
||||
void SGBucket::set_bucket(double dlon, double dlat)
|
||||
{
|
||||
innerSet(dlon, dlat);
|
||||
}
|
||||
|
||||
|
||||
void SGBucket::set_bucket(const SGGeod& geod)
|
||||
{
|
||||
innerSet(geod.getLongitudeDeg(), geod.getLatitudeDeg());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Set the bucket params for the specified lat and lon
|
||||
void SGBucket::innerSet( double dlon, double dlat )
|
||||
{
|
||||
if ((dlon < -180.0) || (dlon >= 180.0)) {
|
||||
SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::set_bucket: passed longitude:" << dlon);
|
||||
dlon = SGMiscd::normalizePeriodic(-180.0, 180.0, dlon);
|
||||
}
|
||||
|
||||
if ((dlat < -90.0) || (dlat > 90.0)) {
|
||||
SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::set_bucket: passed latitude" << dlat);
|
||||
dlat = SGMiscd::clip(dlat, -90.0, 90.0);
|
||||
}
|
||||
|
||||
void SGBucket::set_bucket( double *lonlat ) {
|
||||
set_bucket( lonlat[0], lonlat[1] );
|
||||
}
|
||||
|
||||
|
||||
// Set the bucket params for the specified lat and lon
|
||||
void SGBucket::set_bucket( double dlon, double dlat ) {
|
||||
//
|
||||
// longitude first
|
||||
// latitude first
|
||||
//
|
||||
double span = sg_bucket_span( dlat );
|
||||
// we do NOT need to special case lon=180 here, since
|
||||
// normalizePeriodic will never return 180; it will
|
||||
// return -180, which is what we want.
|
||||
lon = floorWithEpsilon(dlon);
|
||||
|
||||
double diff = dlon - (double)(int)dlon;
|
||||
|
||||
// cout << "diff = " << diff << " span = " << span << endl;
|
||||
|
||||
/* Calculate the greatest integral longitude less than
|
||||
* or equal to the given longitude (floor(dlon)),
|
||||
* but attribute coordinates near the east border
|
||||
* to the next tile.
|
||||
*/
|
||||
if ( (dlon >= 0) || (fabs(diff) < SG_EPSILON) ) {
|
||||
lon = (int)dlon;
|
||||
} else {
|
||||
lon = (int)dlon - 1;
|
||||
}
|
||||
|
||||
// find subdivision or super lon if needed
|
||||
if ( span <= 1.0 ) {
|
||||
if ( span < SG_EPSILON ) {
|
||||
/* sg_bucket_span() never returns 0.0
|
||||
* or anything near it, so this really
|
||||
* should not occur at any time.
|
||||
*/
|
||||
// polar cap
|
||||
lon = 0;
|
||||
x = 0;
|
||||
} else if ( span <= 1.0 ) {
|
||||
/* We have more than one tile per degree of
|
||||
* longitude, so we need an x offset.
|
||||
*/
|
||||
x = floorWithEpsilon((dlon - lon) / span);
|
||||
x = (int)((dlon - lon) / span);
|
||||
} else {
|
||||
/* We have one or more degrees per tile,
|
||||
* so we need to find the base longitude
|
||||
@@ -160,28 +129,48 @@ void SGBucket::innerSet( double dlon, double dlat )
|
||||
*
|
||||
* That way, the Greenwich Meridian is always
|
||||
* a tile border.
|
||||
*
|
||||
* This gets us into trouble with the polar caps,
|
||||
* which have width 360 and thus either span
|
||||
* the range from 0 to 360 or from -360 to 0
|
||||
* degrees, depending on whether lon is positive
|
||||
* or negative!
|
||||
*
|
||||
* We also get into trouble with the 8 degree tiles
|
||||
* north of 88N and south of 88S, because the west-
|
||||
* and east-most tiles in that range will cover 184W
|
||||
* to 176W and 176E to 184E respectively, with their
|
||||
* center at 180E/W!
|
||||
*/
|
||||
lon=static_cast<int>(floor(lon / span) * span);
|
||||
x = 0;
|
||||
lon=(int)floor(floor((lon+SG_EPSILON)/span)*span);
|
||||
/* Correct the polar cap issue */
|
||||
if ( lon < -180 ) {
|
||||
lon = -180;
|
||||
}
|
||||
x = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// then latitude
|
||||
//
|
||||
lat = floorWithEpsilon(dlat);
|
||||
|
||||
// special case when passing in the north pole point (possibly due to
|
||||
// clipping latitude above). Ensures we generate a valid bucket in this
|
||||
// scenario
|
||||
if (lat == 90) {
|
||||
lat = 89;
|
||||
y = 7;
|
||||
diff = dlat - (double)(int)dlat;
|
||||
|
||||
/* Again, a modified floor() function (see longitude) */
|
||||
if ( (dlat >= 0) || (fabs(diff) < SG_EPSILON) ) {
|
||||
lat = (int)dlat;
|
||||
} else {
|
||||
/* Latitude base and offset are easier, as
|
||||
* tiles always are 1/8 degree of latitude wide.
|
||||
*/
|
||||
y = floorWithEpsilon((dlat - lat) * 8);
|
||||
lat = (int)dlat - 1;
|
||||
}
|
||||
/* Latitude base and offset are easier, as
|
||||
* tiles always are 1/8 degree of latitude wide.
|
||||
*/
|
||||
y = (int)((dlat - lat) * 8);
|
||||
}
|
||||
|
||||
|
||||
void SGBucket::set_bucket(const SGGeod& geod)
|
||||
{
|
||||
set_bucket(geod.getLongitudeDeg(), geod.getLatitudeDeg());
|
||||
}
|
||||
|
||||
// Build the path name for this bucket
|
||||
@@ -223,7 +212,7 @@ std::string SGBucket::gen_base_path() const {
|
||||
main_lat *= -1;
|
||||
}
|
||||
|
||||
::snprintf(raw_path, 256, "%c%03d%c%02d/%c%03d%c%02d",
|
||||
snprintf(raw_path, 256, "%c%03d%c%02d/%c%03d%c%02d",
|
||||
hem, top_lon, pole, top_lat,
|
||||
hem, main_lon, pole, main_lat);
|
||||
|
||||
@@ -244,32 +233,17 @@ double SGBucket::get_height() const {
|
||||
return SG_BUCKET_SPAN;
|
||||
}
|
||||
|
||||
double SGBucket::get_highest_lat() const
|
||||
{
|
||||
unsigned char adjustedY = y;
|
||||
if (lat >= 0) {
|
||||
// tile is north of the equator, so we want the top edge. Add one
|
||||
// to y to achieve this.
|
||||
++adjustedY;
|
||||
|
||||
// return width of the tile in meters
|
||||
double SGBucket::get_width_m() const {
|
||||
double clat = (int)get_center_lat();
|
||||
if ( clat > 0 ) {
|
||||
clat = (int)clat + 0.5;
|
||||
} else {
|
||||
clat = (int)clat - 0.5;
|
||||
}
|
||||
|
||||
return lat + (adjustedY / 8.0);
|
||||
}
|
||||
|
||||
|
||||
// return width of the tile in meters. This function is used by the
|
||||
// tile-manager to estimate how many tiles are in the view distance, so
|
||||
// we care about the smallest width, which occurs at the highest latitude.
|
||||
double SGBucket::get_width_m() const
|
||||
{
|
||||
double clat_rad = get_highest_lat() * SGD_DEGREES_TO_RADIANS;
|
||||
double clat_rad = clat * SGD_DEGREES_TO_RADIANS;
|
||||
double cos_lat = cos( clat_rad );
|
||||
if (fabs(cos_lat) < SG_EPSILON) {
|
||||
// happens for polar tiles, since we pass in a latitude of 90
|
||||
// return an arbitrary small value so all tiles are loaded
|
||||
return 10.0;
|
||||
}
|
||||
|
||||
double local_radius = cos_lat * SG_EQUATORIAL_RADIUS_M;
|
||||
double local_perimeter = local_radius * SGD_2PI;
|
||||
double degree_width = local_perimeter / 360.0;
|
||||
@@ -286,41 +260,7 @@ double SGBucket::get_height_m() const {
|
||||
return SG_BUCKET_SPAN * degree_height;
|
||||
}
|
||||
|
||||
SGBucket SGBucket::sibling(int dx, int dy) const
|
||||
{
|
||||
if (!isValid()) {
|
||||
SG_LOG(SG_TERRAIN, SG_WARN, "SGBucket::sibling: requesting sibling of invalid bucket");
|
||||
return SGBucket();
|
||||
}
|
||||
|
||||
double clat = get_center_lat() + dy * SG_BUCKET_SPAN;
|
||||
// return invalid here instead of clipping, so callers can discard
|
||||
// invalid buckets without having to check if it's an existing one
|
||||
if ((clat < -90.0) || (clat > 90.0)) {
|
||||
return SGBucket();
|
||||
}
|
||||
|
||||
// find the lon span for the new latitude
|
||||
double span = sg_bucket_span( clat );
|
||||
|
||||
double tmp = get_center_lon() + dx * span;
|
||||
tmp = SGMiscd::normalizePeriodic(-180.0, 180.0, tmp);
|
||||
|
||||
SGBucket b;
|
||||
b.innerSet(tmp, clat);
|
||||
return b;
|
||||
}
|
||||
|
||||
std::string SGBucket::gen_index_str() const
|
||||
{
|
||||
char tmp[20];
|
||||
::snprintf(tmp, 20, "%ld",
|
||||
(((long)lon + 180) << 14) + ((lat + 90) << 6)
|
||||
+ (y << 3) + x);
|
||||
return (std::string)tmp;
|
||||
}
|
||||
|
||||
#ifndef NO_DEPRECATED_API
|
||||
// find the bucket which is offset by the specified tile units in the
|
||||
// X & Y direction. We need the current lon and lat to resolve
|
||||
// ambiguities when going from a wider tile to a narrower one above or
|
||||
@@ -347,7 +287,7 @@ SGBucket sgBucketOffset( double dlon, double dlat, int dx, int dy ) {
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// calculate the offset between two buckets
|
||||
void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy ) {
|
||||
@@ -408,22 +348,10 @@ void sgBucketDiff( const SGBucket& b1, const SGBucket& b2, int *dx, int *dy ) {
|
||||
void sgGetBuckets( const SGGeod& min, const SGGeod& max, std::vector<SGBucket>& list ) {
|
||||
double lon, lat, span;
|
||||
|
||||
for (lat = min.getLatitudeDeg(); lat < max.getLatitudeDeg()+SG_BUCKET_SPAN; lat += SG_BUCKET_SPAN) {
|
||||
for (lat = min.getLatitudeDeg(); lat <= max.getLatitudeDeg(); lat += SG_BUCKET_SPAN) {
|
||||
span = sg_bucket_span( lat );
|
||||
for (lon = min.getLongitudeDeg(); lon <= max.getLongitudeDeg(); lon += span)
|
||||
{
|
||||
SGBucket b(SGGeod::fromDeg(lon, lat));
|
||||
if (!b.isValid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
list.push_back(b);
|
||||
for (lon = min.getLongitudeDeg(); lon <= max.getLongitudeDeg(); lon += span) {
|
||||
list.push_back( SGBucket(lon , lat) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<< ( std::ostream& out, const SGBucket& b )
|
||||
{
|
||||
return out << b.lon << ":" << (int)b.x << ", " << b.lat << ":" << (int)b.y;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,12 +39,11 @@
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio> // sprintf()
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
#include <vector>
|
||||
|
||||
// #define NO_DEPRECATED_API
|
||||
|
||||
/**
|
||||
* standard size of a bucket in degrees (1/8 of a degree)
|
||||
*/
|
||||
@@ -100,31 +99,23 @@ class SGBucket {
|
||||
private:
|
||||
short lon; // longitude index (-180 to 179)
|
||||
short lat; // latitude index (-90 to 89)
|
||||
unsigned char x; // x subdivision (0 to 7)
|
||||
unsigned char y; // y subdivision (0 to 7)
|
||||
char x; // x subdivision (0 to 7)
|
||||
char y; // y subdivision (0 to 7)
|
||||
|
||||
void innerSet( double dlon, double dlat );
|
||||
public:
|
||||
|
||||
/**
|
||||
* Default constructor, creates an invalid SGBucket
|
||||
* Default constructor.
|
||||
*/
|
||||
SGBucket();
|
||||
|
||||
/**
|
||||
* Check if this bucket refers to a valid tile, or not.
|
||||
*/
|
||||
bool isValid() const;
|
||||
|
||||
#ifndef NO_DEPRECATED_API
|
||||
/**
|
||||
* Construct a bucket given a specific location.
|
||||
* @param dlon longitude specified in degrees
|
||||
* @param dlat latitude specified in degrees
|
||||
*/
|
||||
SGBucket(const double dlon, const double dlat);
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Construct a bucket given a specific location.
|
||||
* @param dlon longitude specified in degrees
|
||||
@@ -132,36 +123,51 @@ public:
|
||||
*/
|
||||
SGBucket(const SGGeod& geod);
|
||||
|
||||
/** Construct a bucket.
|
||||
* @param is_good if false, create an invalid bucket. This is
|
||||
* useful * if you are comparing cur_bucket to last_bucket and
|
||||
* you want to * make sure last_bucket starts out as something
|
||||
* impossible.
|
||||
*/
|
||||
SGBucket(const bool is_good);
|
||||
|
||||
/** Construct a bucket given a unique bucket index number.
|
||||
* @param bindex unique bucket index
|
||||
*/
|
||||
SGBucket(const long int bindex);
|
||||
|
||||
#ifndef NO_DEPRECATED_API
|
||||
/**
|
||||
* Reset a bucket to represent a new lat and lon
|
||||
* @param dlon longitude specified in degrees
|
||||
* @param dlat latitude specified in degrees
|
||||
*/
|
||||
void set_bucket(const SGGeod& geod);
|
||||
|
||||
|
||||
/**
|
||||
* Reset a bucket to represent a new lat and lon
|
||||
* @param dlon longitude specified in degrees
|
||||
* @param dlat latitude specified in degrees
|
||||
*/
|
||||
void set_bucket( double dlon, double dlat );
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Reset a bucket to represent a new lat and lon
|
||||
* @param lonlat an array of double[2] holding lon and lat
|
||||
* (specified) in degrees
|
||||
*/
|
||||
void set_bucket( double *lonlat );
|
||||
|
||||
/**
|
||||
* Reset a bucket to represent a new lat and lon
|
||||
* @param dlon longitude specified in degrees
|
||||
* @param dlat latitude specified in degrees
|
||||
*/
|
||||
void set_bucket(const SGGeod& geod);
|
||||
|
||||
/**
|
||||
* Create an impossible bucket.
|
||||
* This is useful if you are comparing cur_bucket to last_bucket
|
||||
* and you want to make sure last_bucket starts out as something
|
||||
* impossible.
|
||||
*/
|
||||
void make_bad();
|
||||
|
||||
inline void make_bad() {
|
||||
set_bucket(0.0, 0.0);
|
||||
lon = -1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the unique scenery tile index for this bucket
|
||||
*
|
||||
@@ -186,8 +192,14 @@ public:
|
||||
* string form.
|
||||
* @return tile index in string form
|
||||
*/
|
||||
std::string gen_index_str() const;
|
||||
|
||||
inline std::string gen_index_str() const {
|
||||
char tmp[20];
|
||||
std::sprintf(tmp, "%ld",
|
||||
(((long)lon + 180) << 14) + ((lat + 90) << 6)
|
||||
+ (y << 3) + x);
|
||||
return (std::string)tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the base path name for this bucket.
|
||||
* @return base path in string form
|
||||
@@ -214,13 +226,6 @@ public:
|
||||
return lat + y / 8.0 + SG_HALF_BUCKET_SPAN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the highest (furthest from the equator) latitude of this
|
||||
* tile. This is the top edge for tiles north of the equator, and
|
||||
* the bottom edge for tiles south
|
||||
*/
|
||||
double get_highest_lat() const;
|
||||
|
||||
/**
|
||||
* @return the width of the tile in degrees.
|
||||
*/
|
||||
@@ -282,11 +287,6 @@ public:
|
||||
*/
|
||||
inline int get_y() const { return y; }
|
||||
|
||||
/**
|
||||
* @return bucket offset from this by dx,dy
|
||||
*/
|
||||
SGBucket sibling(int dx, int dy) const;
|
||||
|
||||
// friends
|
||||
|
||||
friend std::ostream& operator<< ( std::ostream&, const SGBucket& );
|
||||
@@ -298,7 +298,7 @@ inline bool operator!= (const SGBucket& lhs, const SGBucket& rhs)
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
#ifndef NO_DEPRECATED_API
|
||||
|
||||
/**
|
||||
* \relates SGBucket
|
||||
* Return the bucket which is offset from the specified dlon, dlat by
|
||||
@@ -310,7 +310,6 @@ inline bool operator!= (const SGBucket& lhs, const SGBucket& rhs)
|
||||
* @return offset bucket
|
||||
*/
|
||||
SGBucket sgBucketOffset( double dlon, double dlat, int x, int y );
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
@@ -338,7 +337,12 @@ void sgGetBuckets( const SGGeod& min, const SGGeod& max, std::vector<SGBucket>&
|
||||
* @param out output stream
|
||||
* @param b bucket
|
||||
*/
|
||||
std::ostream& operator<< ( std::ostream& out, const SGBucket& b );
|
||||
inline std::ostream&
|
||||
operator<< ( std::ostream& out, const SGBucket& b )
|
||||
{
|
||||
return out << b.lon << ":" << (int)b.x << ", " << b.lat << ":" << (int)b.y;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compare two bucket structures for equality.
|
||||
|
||||
@@ -1,283 +0,0 @@
|
||||
/**************************************************************************
|
||||
* test_bucket.cxx -- unit-tests for SGBucket class
|
||||
*
|
||||
* Copyright (C) 2014 James Turner - <zakalawe@mac.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
#include <simgear/bucket/newbucket.hxx>
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
void testBucketSpans()
|
||||
{
|
||||
COMPARE(sg_bucket_span(0.0), 0.125);
|
||||
COMPARE(sg_bucket_span(-20), 0.125);
|
||||
COMPARE(sg_bucket_span(-40), 0.25);
|
||||
COMPARE(sg_bucket_span(89.9), 12.0);
|
||||
COMPARE(sg_bucket_span(88.1), 4.0);
|
||||
COMPARE(sg_bucket_span(-89.9), 12.0);
|
||||
}
|
||||
|
||||
void testBasic()
|
||||
{
|
||||
SGBucket b1(5.1, 55.05);
|
||||
COMPARE(b1.get_chunk_lon(), 5);
|
||||
COMPARE(b1.get_chunk_lat(), 55);
|
||||
COMPARE(b1.get_x(), 0);
|
||||
COMPARE(b1.get_y(), 0);
|
||||
COMPARE(b1.gen_index(), 3040320);
|
||||
COMPARE(b1.gen_base_path(), "e000n50/e005n55");
|
||||
VERIFY(b1.isValid());
|
||||
|
||||
SGBucket b2(-10.1, -43.8);
|
||||
COMPARE(b2.get_chunk_lon(), -11);
|
||||
COMPARE(b2.get_chunk_lat(), -44);
|
||||
COMPARE(b2.get_x(), 3);
|
||||
COMPARE(b2.get_y(), 1); // latitude chunks numbered bottom to top, it seems
|
||||
COMPARE(b2.gen_base_path(), "w020s50/w011s44");
|
||||
VERIFY(b2.isValid());
|
||||
|
||||
SGBucket b3(123.48, 9.01);
|
||||
COMPARE(b3.get_chunk_lon(), 123);
|
||||
COMPARE(b3.get_chunk_lat(), 9);
|
||||
COMPARE(b3.get_x(), 3);
|
||||
COMPARE(b3.get_y(), 0);
|
||||
COMPARE(b3.gen_base_path(), "e120n00/e123n09");
|
||||
VERIFY(b3.isValid());
|
||||
|
||||
SGBucket defBuck;
|
||||
VERIFY(!defBuck.isValid());
|
||||
|
||||
b3.make_bad();
|
||||
VERIFY(!b3.isValid());
|
||||
|
||||
SGBucket atAntiMeridian(180.0, 12.3);
|
||||
VERIFY(atAntiMeridian.isValid());
|
||||
COMPARE(atAntiMeridian.get_chunk_lon(), -180);
|
||||
COMPARE(atAntiMeridian.get_x(), 0);
|
||||
|
||||
SGBucket atAntiMeridian2(-180.0, -78.1);
|
||||
VERIFY(atAntiMeridian2.isValid());
|
||||
COMPARE(atAntiMeridian2.get_chunk_lon(), -180);
|
||||
COMPARE(atAntiMeridian2.get_x(), 0);
|
||||
|
||||
// check comparisom operator overload
|
||||
SGBucket b4(5.11, 55.1);
|
||||
VERIFY(b1 == b4); // should be equal
|
||||
VERIFY(b1 == b1);
|
||||
VERIFY(b1 != defBuck);
|
||||
VERIFY(b1 != b2);
|
||||
|
||||
// check wrapping/clipping of inputs
|
||||
SGBucket wrapMeridian(-200.0, 45.0);
|
||||
COMPARE(wrapMeridian.get_chunk_lon(), 160);
|
||||
|
||||
SGBucket clipPole(48.9, 91);
|
||||
COMPARE(clipPole.get_chunk_lat(), 89);
|
||||
}
|
||||
|
||||
void testPolar()
|
||||
{
|
||||
SGBucket b1(0.0, 89.92);
|
||||
SGBucket b2(10.0, 89.96);
|
||||
COMPARE(b1.get_chunk_lat(), 89);
|
||||
COMPARE(b1.get_chunk_lon(), 0);
|
||||
COMPARE(b1.get_x(), 0);
|
||||
COMPARE(b1.get_y(), 7);
|
||||
|
||||
COMPARE_EP(b1.get_highest_lat(), 90.0);
|
||||
COMPARE_EP(b1.get_width_m(), 10.0);
|
||||
|
||||
COMPARE(b2.get_chunk_lat(), 89);
|
||||
COMPARE(b2.get_chunk_lon(), 0);
|
||||
COMPARE(b2.get_x(), 0);
|
||||
COMPARE(b2.get_y(), 7);
|
||||
|
||||
COMPARE(b1.gen_index(), b2.gen_index());
|
||||
|
||||
SGGeod actualNorthPole1 = b1.get_corner(2);
|
||||
SGGeod actualNorthPole2 = b1.get_corner(3);
|
||||
COMPARE_EP(actualNorthPole1.getLatitudeDeg(), 90.0);
|
||||
COMPARE_EP(actualNorthPole1.getLongitudeDeg(), 12.0);
|
||||
COMPARE_EP(actualNorthPole2.getLatitudeDeg(), 90.0);
|
||||
COMPARE_EP(actualNorthPole2.getLongitudeDeg(), 0.0);
|
||||
|
||||
SGBucket b3(-2, 89.88);
|
||||
SGBucket b4(-7, 89.88);
|
||||
COMPARE(b3.gen_index(), b4.gen_index());
|
||||
|
||||
// south pole
|
||||
SGBucket b5(-170, -89.88);
|
||||
SGBucket b6(-179, -89.88);
|
||||
|
||||
COMPARE(b5.get_chunk_lat(), -90);
|
||||
COMPARE(b5.get_chunk_lon(), -180);
|
||||
COMPARE(b5.get_x(), 0);
|
||||
COMPARE(b5.get_y(), 0);
|
||||
COMPARE(b5.gen_index(), b6.gen_index());
|
||||
COMPARE_EP(b5.get_highest_lat(), -90.0);
|
||||
COMPARE_EP(b5.get_width_m(), 10.0);
|
||||
|
||||
SGGeod actualSouthPole1 = b5.get_corner(0);
|
||||
SGGeod actualSouthPole2 = b5.get_corner(1);
|
||||
COMPARE_EP(actualSouthPole1.getLatitudeDeg(), -90.0);
|
||||
COMPARE_EP(actualSouthPole1.getLongitudeDeg(), -180);
|
||||
COMPARE_EP(actualSouthPole2.getLatitudeDeg(), -90.0);
|
||||
COMPARE_EP(actualSouthPole2.getLongitudeDeg(), -168);
|
||||
|
||||
SGBucket b7(200, 89.88);
|
||||
COMPARE(b7.get_chunk_lon(), -168);
|
||||
|
||||
}
|
||||
|
||||
// test the tiles just below the pole (between 86 & 89 degrees N/S)
|
||||
void testNearPolar()
|
||||
{
|
||||
SGBucket b1(1, 88.5);
|
||||
SGBucket b2(-1, 88.8);
|
||||
COMPARE(b1.get_chunk_lon(), 0);
|
||||
COMPARE(b1.get_chunk_lat(), 88);
|
||||
VERIFY(b1.gen_index() != b2.gen_index());
|
||||
|
||||
SGBucket b3(176.1, 88.5);
|
||||
COMPARE(b3.get_chunk_lon(), 176);
|
||||
|
||||
SGBucket b4(-178, 88.5);
|
||||
COMPARE(b4.get_chunk_lon(), -180);
|
||||
}
|
||||
|
||||
void testOffset()
|
||||
{
|
||||
// bucket just below the 22 degree cutoff, so the next tile north
|
||||
// is twice the width
|
||||
SGBucket b1(-59.8, 21.9);
|
||||
COMPARE(b1.get_chunk_lat(), 21);
|
||||
COMPARE(b1.get_chunk_lon(), -60);
|
||||
COMPARE(b1.get_x(), 1);
|
||||
COMPARE(b1.get_y(), 7);
|
||||
|
||||
// offset vertically
|
||||
SGBucket b2(b1.sibling(0, 1));
|
||||
COMPARE(b2.get_chunk_lat(), 22);
|
||||
COMPARE(b2.get_chunk_lon(), -60);
|
||||
COMPARE(b2.get_x(), 0);
|
||||
COMPARE(b2.get_y(), 0);
|
||||
|
||||
COMPARE(b2.gen_index(), sgBucketOffset(-59.8, 21.9, 0, 1));
|
||||
|
||||
// offset vertically and horizontally. We compute horizontal (x)
|
||||
// movement at the target latitude, so this should move 0.25 * -3 degrees,
|
||||
// NOT 0.125 * -3 degrees.
|
||||
SGBucket b3(b1.sibling(-3, 1));
|
||||
COMPARE(b3.get_chunk_lat(), 22);
|
||||
COMPARE(b3.get_chunk_lon(), -61);
|
||||
COMPARE(b3.get_x(), 1);
|
||||
COMPARE(b3.get_y(), 0);
|
||||
|
||||
COMPARE(b3.gen_index(), sgBucketOffset(-59.8, 21.9, -3, 1));
|
||||
}
|
||||
|
||||
void testPolarOffset()
|
||||
{
|
||||
SGBucket b1(-11.7, -89.6);
|
||||
COMPARE(b1.get_chunk_lat(), -90);
|
||||
COMPARE(b1.get_chunk_lon(), -12);
|
||||
COMPARE(b1.get_x(), 0);
|
||||
COMPARE(b1.get_y(), 3);
|
||||
|
||||
// offset horizontally
|
||||
SGBucket b2(b1.sibling(-2, 0));
|
||||
COMPARE(b2.get_chunk_lat(), -90);
|
||||
COMPARE(b2.get_chunk_lon(), -36);
|
||||
COMPARE(b2.get_x(), 0);
|
||||
COMPARE(b2.get_y(), 3);
|
||||
|
||||
COMPARE(b2.gen_index(), sgBucketOffset(-11.7, -89.6, -2, 0));
|
||||
|
||||
// offset and wrap
|
||||
SGBucket b3(-170, 89.1);
|
||||
SGBucket b4(b3.sibling(-1, 0));
|
||||
COMPARE(b4.get_chunk_lat(), 89);
|
||||
COMPARE(b4.get_chunk_lon(), 168);
|
||||
COMPARE(b4.get_x(), 0);
|
||||
COMPARE(b4.get_y(), 0);
|
||||
|
||||
COMPARE(b4.gen_index(), sgBucketOffset(-170, 89.1, -1, 0));
|
||||
|
||||
|
||||
SGBucket b5(177, 87.3);
|
||||
SGBucket b6(b5.sibling(1, 1));
|
||||
COMPARE(b6.get_chunk_lat(), 87);
|
||||
COMPARE(b6.get_chunk_lon(), -180);
|
||||
COMPARE(b6.get_x(), 0);
|
||||
COMPARE(b6.get_y(), 3);
|
||||
|
||||
COMPARE(b6.gen_index(), sgBucketOffset(177, 87.3, 1, 1));
|
||||
|
||||
// offset vertically towards the pole
|
||||
SGBucket b7(b1.sibling(0, -5));
|
||||
VERIFY(!b7.isValid());
|
||||
|
||||
VERIFY(!SGBucket(0, 90).sibling(0, 1).isValid());
|
||||
}
|
||||
|
||||
// test behaviour of bucket-offset near the anti-meridian (180-meridian)
|
||||
void testOffsetWrap()
|
||||
{
|
||||
// near the equator
|
||||
SGBucket b1(-179.8, 16.8);
|
||||
COMPARE(b1.get_chunk_lat(), 16);
|
||||
COMPARE(b1.get_chunk_lon(), -180);
|
||||
COMPARE(b1.get_x(), 1);
|
||||
COMPARE(b1.get_y(), 6);
|
||||
|
||||
SGBucket b2(b1.sibling(-2, 0));
|
||||
COMPARE(b2.get_chunk_lat(), 16);
|
||||
COMPARE(b2.get_chunk_lon(), 179);
|
||||
COMPARE(b2.get_x(), 7);
|
||||
COMPARE(b2.get_y(), 6);
|
||||
COMPARE(b2.gen_index(), sgBucketOffset(-179.8, 16.8, -2, 0));
|
||||
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
testBucketSpans();
|
||||
|
||||
testBasic();
|
||||
testPolar();
|
||||
testNearPolar();
|
||||
testOffset();
|
||||
testOffsetWrap();
|
||||
testPolarOffset();
|
||||
|
||||
cout << "all tests passed OK" << endl;
|
||||
return 0; // passed
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ set(HEADERS
|
||||
CanvasObjectPlacement.hxx
|
||||
CanvasPlacement.hxx
|
||||
CanvasSystemAdapter.hxx
|
||||
CanvasWindow.hxx
|
||||
MouseEvent.hxx
|
||||
ODGauge.hxx
|
||||
VGInitOperation.hxx
|
||||
)
|
||||
@@ -24,14 +24,11 @@ set(SOURCES
|
||||
CanvasMgr.cxx
|
||||
CanvasObjectPlacement.cxx
|
||||
CanvasPlacement.cxx
|
||||
CanvasWindow.cxx
|
||||
ODGauge.cxx
|
||||
VGInitOperation.cxx
|
||||
)
|
||||
|
||||
add_subdirectory(ShivaVG/src)
|
||||
add_subdirectory(elements)
|
||||
add_subdirectory(events)
|
||||
add_subdirectory(layout)
|
||||
|
||||
simgear_scene_component(canvas canvas "${SOURCES}" "${HEADERS}")
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
#include "Canvas.hxx"
|
||||
#include "CanvasEventManager.hxx"
|
||||
#include "CanvasEventVisitor.hxx"
|
||||
#include "CanvasPlacement.hxx"
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/canvas/MouseEvent.hxx>
|
||||
#include <simgear/canvas/CanvasPlacement.hxx>
|
||||
#include <simgear/scene/util/parse_color.hxx>
|
||||
#include <simgear/scene/util/RenderConstants.hxx>
|
||||
|
||||
@@ -48,12 +48,8 @@ namespace canvas
|
||||
void Canvas::CullCallback::operator()( osg::Node* node,
|
||||
osg::NodeVisitor* nv )
|
||||
{
|
||||
if( (nv->getTraversalMask() & simgear::MODEL_BIT) )
|
||||
{
|
||||
CanvasPtr canvas = _canvas.lock();
|
||||
if( canvas )
|
||||
canvas->enableRendering();
|
||||
}
|
||||
if( (nv->getTraversalMask() & simgear::MODEL_BIT) && !_canvas.expired() )
|
||||
_canvas.lock()->enableRendering();
|
||||
|
||||
traverse(node, nv);
|
||||
}
|
||||
@@ -76,13 +72,6 @@ namespace canvas
|
||||
{
|
||||
_status = 0;
|
||||
setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
|
||||
|
||||
_root_group.reset( new Group(this, _node) );
|
||||
|
||||
// Remove automatically created property listener as we forward them on our
|
||||
// own
|
||||
_root_group->removeListener();
|
||||
_cull_callback = new CullCallback(this);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -191,14 +180,6 @@ namespace canvas
|
||||
return _root_group;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::setLayout(const LayoutRef& layout)
|
||||
{
|
||||
_layout = layout;
|
||||
_layout->setCanvas(this);
|
||||
_status |= LAYOUT_DIRTY;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::enableRendering(bool force)
|
||||
{
|
||||
@@ -210,10 +191,11 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::update(double delta_time_sec)
|
||||
{
|
||||
if( _status & (CREATE_FAILED | MISSING_SIZE) )
|
||||
if( (!_texture.serviceable() && _status != STATUS_DIRTY)
|
||||
|| (_status & CREATE_FAILED) )
|
||||
return;
|
||||
|
||||
if( _status & STATUS_DIRTY )
|
||||
if( _status == STATUS_DIRTY )
|
||||
{
|
||||
_texture.setSize(_size_x, _size_y);
|
||||
|
||||
@@ -255,36 +237,23 @@ namespace canvas
|
||||
}
|
||||
}
|
||||
|
||||
if( _layout )
|
||||
{
|
||||
if( (_status & LAYOUT_DIRTY) )
|
||||
{
|
||||
_layout->setGeometry(SGRecti(0, 0, _view_width, _view_height));
|
||||
_status &= ~LAYOUT_DIRTY;
|
||||
}
|
||||
else
|
||||
_layout->update();
|
||||
}
|
||||
|
||||
if( _visible || _render_always )
|
||||
{
|
||||
BOOST_FOREACH(CanvasWeakPtr canvas_weak, _child_canvases)
|
||||
BOOST_FOREACH(CanvasWeakPtr canvas, _child_canvases)
|
||||
{
|
||||
// TODO should we check if the image the child canvas is displayed
|
||||
// within is really visible?
|
||||
CanvasPtr canvas = canvas_weak.lock();
|
||||
if( canvas )
|
||||
canvas->_visible = true;
|
||||
if( !canvas.expired() )
|
||||
canvas.lock()->_visible = true;
|
||||
}
|
||||
|
||||
if( _render_dirty )
|
||||
{
|
||||
// Also mark all canvases this canvas is displayed within as dirty
|
||||
BOOST_FOREACH(CanvasWeakPtr canvas_weak, _parent_canvases)
|
||||
BOOST_FOREACH(CanvasWeakPtr canvas, _parent_canvases)
|
||||
{
|
||||
CanvasPtr canvas = canvas_weak.lock();
|
||||
if( canvas )
|
||||
canvas->_render_dirty = true;
|
||||
if( !canvas.expired() )
|
||||
canvas.lock()->_render_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,7 +296,11 @@ namespace canvas
|
||||
if( placement_factory != _placement_factories.end() )
|
||||
{
|
||||
Placements& placements = _placements[ node->getIndex() ] =
|
||||
placement_factory->second(node, this);
|
||||
placement_factory->second
|
||||
(
|
||||
node,
|
||||
boost::static_pointer_cast<Canvas>(_self.lock())
|
||||
);
|
||||
node->setStringValue
|
||||
(
|
||||
"status-msg",
|
||||
@@ -344,20 +317,11 @@ namespace canvas
|
||||
const EventListener& cb )
|
||||
{
|
||||
if( !_root_group.get() )
|
||||
throw std::runtime_error("Canvas::addEventListener: no root group!");
|
||||
throw std::runtime_error("Canvas::AddEventListener: no root group!");
|
||||
|
||||
return _root_group->addEventListener(type, cb);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Canvas::dispatchEvent(const EventPtr& event)
|
||||
{
|
||||
if( !_root_group.get() )
|
||||
throw std::runtime_error("Canvas::dispatchEvent: no root group!");
|
||||
|
||||
return _root_group->dispatchEvent(event);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::setSizeX(int sx)
|
||||
{
|
||||
@@ -410,7 +374,6 @@ namespace canvas
|
||||
if( _view_width == w )
|
||||
return;
|
||||
_view_width = w;
|
||||
_status |= LAYOUT_DIRTY;
|
||||
|
||||
_texture.setViewSize(_view_width, _view_height);
|
||||
}
|
||||
@@ -421,7 +384,6 @@ namespace canvas
|
||||
if( _view_height == h )
|
||||
return;
|
||||
_view_height = h;
|
||||
_status |= LAYOUT_DIRTY;
|
||||
|
||||
_texture.setViewSize(_view_width, _view_height);
|
||||
}
|
||||
@@ -447,11 +409,12 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
bool Canvas::handleMouseEvent(const MouseEventPtr& event)
|
||||
{
|
||||
if( !_root_group )
|
||||
if( !_root_group.get() )
|
||||
return false;
|
||||
|
||||
EventVisitor visitor( EventVisitor::TRAVERSE_DOWN,
|
||||
event->getClientPos(),
|
||||
event->getDelta(),
|
||||
_root_group );
|
||||
if( !_root_group->accept(visitor) )
|
||||
return false;
|
||||
@@ -459,13 +422,6 @@ namespace canvas
|
||||
return _event_manager->handleEvent(event, visitor.getPropagationPath());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Canvas::propagateEvent( EventPtr const& event,
|
||||
EventPropagationPath const& path )
|
||||
{
|
||||
return _event_manager->propagateEvent(event, path);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::childAdded( SGPropertyNode * parent,
|
||||
SGPropertyNode * child )
|
||||
@@ -497,10 +453,8 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::valueChanged(SGPropertyNode* node)
|
||||
{
|
||||
const std::string& name = node->getNameString();
|
||||
|
||||
if( boost::starts_with(name, "status")
|
||||
|| boost::starts_with(name, "data-") )
|
||||
if( boost::starts_with(node->getNameString(), "status")
|
||||
|| node->getParent()->getNameString() == "bounding-box" )
|
||||
return;
|
||||
_render_dirty = true;
|
||||
|
||||
@@ -539,7 +493,7 @@ namespace canvas
|
||||
}
|
||||
else if( node->getParent() == _node )
|
||||
{
|
||||
if( name == "background" )
|
||||
if( node->getNameString() == "background" )
|
||||
{
|
||||
osg::Vec4 color;
|
||||
if( _texture.getCamera() && parseColor(node->getStringValue(), color) )
|
||||
@@ -548,41 +502,35 @@ namespace canvas
|
||||
_render_dirty = true;
|
||||
}
|
||||
}
|
||||
else if( name == "mipmapping"
|
||||
|| name == "coverage-samples"
|
||||
|| name == "color-samples" )
|
||||
else if( node->getNameString() == "mipmapping"
|
||||
|| node->getNameString() == "coverage-samples"
|
||||
|| node->getNameString() == "color-samples" )
|
||||
{
|
||||
_sampling_dirty = true;
|
||||
}
|
||||
else if( name == "additive-blend" )
|
||||
else if( node->getNameString() == "additive-blend" )
|
||||
{
|
||||
_texture.useAdditiveBlend( node->getBoolValue() );
|
||||
}
|
||||
else if( name == "render-always" )
|
||||
else if( node->getNameString() == "render-always" )
|
||||
{
|
||||
_render_always = node->getBoolValue();
|
||||
}
|
||||
else if( name == "size" )
|
||||
else if( node->getNameString() == "size" )
|
||||
{
|
||||
if( node->getIndex() == 0 )
|
||||
setSizeX( node->getIntValue() );
|
||||
else if( node->getIndex() == 1 )
|
||||
setSizeY( node->getIntValue() );
|
||||
}
|
||||
else if( name == "update" )
|
||||
{
|
||||
if( _root_group )
|
||||
_root_group->update(0);
|
||||
return update(0);
|
||||
}
|
||||
else if( name == "view" )
|
||||
else if( node->getNameString() == "view" )
|
||||
{
|
||||
if( node->getIndex() == 0 )
|
||||
setViewWidth( node->getIntValue() );
|
||||
else if( node->getIndex() == 1 )
|
||||
setViewHeight( node->getIntValue() );
|
||||
}
|
||||
else if( name == "freeze" )
|
||||
else if( node->getNameString() == "freeze" )
|
||||
_texture.setRender( node->getBoolValue() );
|
||||
else
|
||||
handled = false;
|
||||
@@ -667,6 +615,23 @@ namespace canvas
|
||||
return _system_adapter;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::setSelf(const PropertyBasedElementPtr& self)
|
||||
{
|
||||
PropertyBasedElement::setSelf(self);
|
||||
|
||||
CanvasPtr canvas = boost::static_pointer_cast<Canvas>(self);
|
||||
|
||||
_root_group.reset( new Group(canvas, _node) );
|
||||
_root_group->setSelf(_root_group);
|
||||
|
||||
// Remove automatically created property listener as we forward them on our
|
||||
// own
|
||||
_root_group->removeListener();
|
||||
|
||||
_cull_callback = new CullCallback(canvas);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Canvas::setStatusFlags(unsigned int flags, bool set)
|
||||
{
|
||||
@@ -683,7 +648,7 @@ namespace canvas
|
||||
_status_msg = "Missing size-y";
|
||||
else if( _status & CREATE_FAILED )
|
||||
_status_msg = "Creating render target failed";
|
||||
else if( _status & STATUS_DIRTY )
|
||||
else if( _status == STATUS_DIRTY )
|
||||
_status_msg = "Creation pending...";
|
||||
else
|
||||
_status_msg = "Ok";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
///@file The canvas for rendering with the 2d API
|
||||
// The canvas for rendering with the 2d API
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -23,12 +23,9 @@
|
||||
#include "ODGauge.hxx"
|
||||
|
||||
#include <simgear/canvas/elements/CanvasGroup.hxx>
|
||||
#include <simgear/canvas/layout/Layout.hxx>
|
||||
#include <simgear/math/SGRect.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalObject.hxx>
|
||||
#include <simgear/props/PropertyBasedElement.hxx>
|
||||
#include <simgear/props/propertyObject.hxx>
|
||||
|
||||
#include <osg/NodeCallback>
|
||||
#include <osg/observer_ptr>
|
||||
|
||||
@@ -37,26 +34,22 @@
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
/// Canvas 2D drawing API
|
||||
namespace canvas
|
||||
{
|
||||
class CanvasMgr;
|
||||
class MouseEvent;
|
||||
|
||||
class Canvas:
|
||||
public PropertyBasedElement,
|
||||
public nasal::Object
|
||||
public PropertyBasedElement
|
||||
{
|
||||
public:
|
||||
|
||||
enum StatusFlags
|
||||
{
|
||||
STATUS_OK,
|
||||
STATUS_DIRTY = 1,
|
||||
LAYOUT_DIRTY = STATUS_DIRTY << 1,
|
||||
MISSING_SIZE_X = LAYOUT_DIRTY << 1,
|
||||
STATUS_DIRTY = 1,
|
||||
MISSING_SIZE_X = STATUS_DIRTY << 1,
|
||||
MISSING_SIZE_Y = MISSING_SIZE_X << 1,
|
||||
MISSING_SIZE = MISSING_SIZE_X | MISSING_SIZE_Y,
|
||||
CREATE_FAILED = MISSING_SIZE_Y << 1
|
||||
};
|
||||
|
||||
@@ -127,12 +120,6 @@ namespace canvas
|
||||
*/
|
||||
GroupPtr getRootGroup();
|
||||
|
||||
/**
|
||||
* Set the layout of the canvas (the layout will automatically update with
|
||||
* the viewport size of the canvas)
|
||||
*/
|
||||
void setLayout(const LayoutRef& layout);
|
||||
|
||||
/**
|
||||
* Enable rendering for the next frame
|
||||
*
|
||||
@@ -144,7 +131,6 @@ namespace canvas
|
||||
void update(double delta_time_sec);
|
||||
|
||||
bool addEventListener(const std::string& type, const EventListener& cb);
|
||||
bool dispatchEvent(const EventPtr& event);
|
||||
|
||||
void setSizeX(int sx);
|
||||
void setSizeY(int sy);
|
||||
@@ -160,8 +146,6 @@ namespace canvas
|
||||
SGRect<int> getViewport() const;
|
||||
|
||||
bool handleMouseEvent(const MouseEventPtr& event);
|
||||
bool propagateEvent( EventPtr const& event,
|
||||
EventPropagationPath const& path );
|
||||
|
||||
virtual void childAdded( SGPropertyNode * parent,
|
||||
SGPropertyNode * child );
|
||||
@@ -207,9 +191,7 @@ namespace canvas
|
||||
_visible;
|
||||
|
||||
ODGauge _texture;
|
||||
|
||||
GroupPtr _root_group;
|
||||
LayoutRef _layout;
|
||||
GroupPtr _root_group;
|
||||
|
||||
CullCallbackPtr _cull_callback;
|
||||
bool _render_always; //<! Used to disable automatic lazy rendering (culling)
|
||||
@@ -223,6 +205,7 @@ namespace canvas
|
||||
typedef std::map<std::string, PlacementFactory> PlacementFactoryMap;
|
||||
static PlacementFactoryMap _placement_factories;
|
||||
|
||||
virtual void setSelf(const PropertyBasedElementPtr& self);
|
||||
void setStatusFlags(unsigned int flags, bool set = true);
|
||||
|
||||
private:
|
||||
|
||||
@@ -26,7 +26,6 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
Event::Event():
|
||||
type(UNKNOWN),
|
||||
time(-1),
|
||||
propagation_stopped(false)
|
||||
{
|
||||
|
||||
@@ -39,13 +38,7 @@ namespace canvas
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Event::canBubble() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Event::getType() const
|
||||
Event::Type Event::getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
@@ -53,7 +46,14 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
std::string Event::getTypeString() const
|
||||
{
|
||||
return typeToStr(type);
|
||||
switch( type )
|
||||
{
|
||||
# define ENUM_MAPPING(name, str) case name: return str;
|
||||
# include "CanvasEventTypes.hxx"
|
||||
# undef ENUM_MAPPING
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -81,57 +81,23 @@ namespace canvas
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Event::getOrRegisterType(const std::string& type_str)
|
||||
{
|
||||
int type = strToType(type_str);
|
||||
|
||||
if( type == UNKNOWN )
|
||||
{
|
||||
// Register new type
|
||||
TypeMap& type_map = getTypeMap();
|
||||
type = type_map.size() + 1; // ids start with 1 (after UNKNOWN)
|
||||
type_map.insert(TypeMap::value_type(type_str, type));
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Event::strToType(const std::string& str)
|
||||
{
|
||||
TypeMap const& type_map = getTypeMap();
|
||||
|
||||
TypeMap::map_by<name>::const_iterator it = type_map.by<name>().find(str);
|
||||
if( it == type_map.by<name>().end() )
|
||||
return UNKNOWN;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
std::string Event::typeToStr(int type)
|
||||
{
|
||||
TypeMap const& type_map = getTypeMap();
|
||||
|
||||
TypeMap::map_by<id>::const_iterator it = type_map.by<id>().find(type);
|
||||
if( it == type_map.by<id>().end() )
|
||||
return "unknown";
|
||||
return it->second;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Event::TypeMap& Event::getTypeMap()
|
||||
Event::Type Event::strToType(const std::string& str)
|
||||
{
|
||||
typedef std::map<std::string, Type> TypeMap;
|
||||
static TypeMap type_map;
|
||||
|
||||
if( type_map.empty() )
|
||||
{
|
||||
# define ENUM_MAPPING(type, str)\
|
||||
type_map.insert(TypeMap::value_type(str, type));
|
||||
# include "CanvasEventTypes.hxx"
|
||||
# undef ENUM_MAPPING
|
||||
# define ENUM_MAPPING(type, str) type_map[ str ] = type;
|
||||
# include "CanvasEventTypes.hxx"
|
||||
# undef ENUM_MAPPING
|
||||
}
|
||||
|
||||
return type_map;
|
||||
TypeMap::const_iterator it = type_map.find(str);
|
||||
if( it == type_map.end() )
|
||||
return UNKNOWN;
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
|
||||
@@ -20,15 +20,13 @@
|
||||
#define CANVAS_EVENT_HXX_
|
||||
|
||||
#include "canvas_fwd.hxx"
|
||||
#include <boost/bimap.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
class Event:
|
||||
public SGReferenced
|
||||
class Event
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -38,11 +36,11 @@ namespace canvas
|
||||
# define ENUM_MAPPING(name, str) name,
|
||||
# include "CanvasEventTypes.hxx"
|
||||
# undef ENUM_MAPPING
|
||||
CUSTOM_EVENT ///< all user defined event types share the same id. They
|
||||
/// are just differentiated by using the type string.
|
||||
USER_TYPE ///<! first unused id to be used for user defined types (not
|
||||
/// implemented yet)
|
||||
};
|
||||
|
||||
int type;
|
||||
Type type;
|
||||
ElementWeakPtr target,
|
||||
current_target;
|
||||
double time;
|
||||
@@ -54,19 +52,7 @@ namespace canvas
|
||||
// of the actual event instances.
|
||||
virtual ~Event();
|
||||
|
||||
/**
|
||||
* Get whether this events support bubbling
|
||||
*/
|
||||
virtual bool canBubble() const;
|
||||
|
||||
/**
|
||||
* Set type of event.
|
||||
*
|
||||
* If no such type exists it is registered.
|
||||
*/
|
||||
void setType(const std::string& type);
|
||||
|
||||
int getType() const;
|
||||
Type getType() const;
|
||||
std::string getTypeString() const;
|
||||
|
||||
ElementWeakPtr getTarget() const;
|
||||
@@ -76,19 +62,7 @@ namespace canvas
|
||||
|
||||
void stopPropagation();
|
||||
|
||||
static int getOrRegisterType(const std::string& type);
|
||||
static int strToType(const std::string& type);
|
||||
static std::string typeToStr(int type);
|
||||
|
||||
protected:
|
||||
struct name {};
|
||||
struct id {};
|
||||
typedef boost::bimaps::bimap<
|
||||
boost::bimaps::tagged<std::string, name>,
|
||||
boost::bimaps::tagged<int, id>
|
||||
> TypeMap;
|
||||
|
||||
static TypeMap& getTypeMap();
|
||||
static Type strToType(const std::string& str);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "CanvasEventManager.hxx"
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include "MouseEvent.hxx"
|
||||
#include <simgear/canvas/elements/CanvasElement.hxx>
|
||||
#include <cmath>
|
||||
|
||||
@@ -61,16 +61,6 @@ namespace canvas
|
||||
return !path.empty() && time > 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void EventManager::MouseEventInfo::set( const MouseEventPtr& event,
|
||||
const EventPropagationPath& p )
|
||||
{
|
||||
path = p;
|
||||
time = event->time;
|
||||
button = event->button;
|
||||
pos = event->screen_pos;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
EventManager::EventManager():
|
||||
_current_click_count(0)
|
||||
@@ -86,7 +76,7 @@ namespace canvas
|
||||
switch( event->type )
|
||||
{
|
||||
case Event::MOUSE_DOWN:
|
||||
_last_mouse_down.set(event, path);
|
||||
_last_mouse_down = StampedPropagationPath(path, event->getTime());
|
||||
break;
|
||||
case Event::MOUSE_UP:
|
||||
{
|
||||
@@ -99,12 +89,12 @@ namespace canvas
|
||||
// normal mouseup
|
||||
handled |= propagateEvent(event, path);
|
||||
|
||||
if( !_last_mouse_down.valid() )
|
||||
if( _last_mouse_down.path.empty() )
|
||||
// Ignore mouse up without any previous mouse down
|
||||
return handled;
|
||||
|
||||
// now handle click/dblclick
|
||||
if( checkClickDistance(event->screen_pos, _last_mouse_down.pos) )
|
||||
if( checkClickDistance(path, _last_mouse_down.path) )
|
||||
handled |=
|
||||
handleClick(event, getCommonAncestor(_last_mouse_down.path, path));
|
||||
|
||||
@@ -116,11 +106,7 @@ namespace canvas
|
||||
if( !_last_mouse_down.valid() )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
// OSG does not set button for drag events.
|
||||
event->button = _last_mouse_down.button;
|
||||
return propagateEvent(event, _last_mouse_down.path);
|
||||
}
|
||||
case Event::MOUSE_MOVE:
|
||||
handled |= handleMove(event, path);
|
||||
break;
|
||||
@@ -144,71 +130,6 @@ namespace canvas
|
||||
return handled | propagateEvent(event, path);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool EventManager::propagateEvent( const EventPtr& event,
|
||||
const EventPropagationPath& path )
|
||||
{
|
||||
event->target = path.back().element;
|
||||
MouseEventPtr mouse_event = dynamic_cast<MouseEvent*>(event.get());
|
||||
|
||||
// Event propagation similar to DOM Level 3 event flow:
|
||||
// http://www.w3.org/TR/DOM-Level-3-Events/#event-flow
|
||||
|
||||
// Position update only needed for drag event (as event needs to be
|
||||
// delivered to element of initial mousedown, but with update positions)
|
||||
if( mouse_event && mouse_event->type == MouseEvent::DRAG )
|
||||
{
|
||||
osg::Vec2f local_pos = mouse_event->client_pos;
|
||||
|
||||
// Capturing phase (currently just update position)
|
||||
for( EventPropagationPath::const_iterator it = path.begin();
|
||||
it != path.end();
|
||||
++it )
|
||||
{
|
||||
ElementPtr el = it->element.lock();
|
||||
if( !el )
|
||||
continue;
|
||||
|
||||
it->local_pos = local_pos = el->posToLocal(local_pos);
|
||||
}
|
||||
}
|
||||
|
||||
bool const do_bubble = event->canBubble();
|
||||
|
||||
// Bubbling phase
|
||||
for( EventPropagationPath::const_reverse_iterator
|
||||
it = path.rbegin();
|
||||
it != path.rend();
|
||||
++it )
|
||||
{
|
||||
ElementPtr el = it->element.lock();
|
||||
|
||||
if( !el )
|
||||
{
|
||||
// Ignore element if it has been destroyed while traversing the event
|
||||
// (eg. removed by another event handler)
|
||||
if( do_bubble )
|
||||
continue;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO provide functions to convert delta to local coordinates on demand.
|
||||
// Maybe also provide a clone method for events as local coordinates
|
||||
// might differ between different elements receiving the same event.
|
||||
if( mouse_event )
|
||||
mouse_event->local_pos = it->local_pos;
|
||||
|
||||
event->current_target = el;
|
||||
el->handleEvent(event);
|
||||
|
||||
if( event->propagation_stopped || !do_bubble )
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool EventManager::handleClick( const MouseEventPtr& event,
|
||||
const EventPropagationPath& path )
|
||||
@@ -225,10 +146,8 @@ namespace canvas
|
||||
|
||||
if( _current_click_count > 1 )
|
||||
{
|
||||
// Reset current click count if moved too far or different button has
|
||||
// been clicked
|
||||
if( !checkClickDistance(event->screen_pos, _last_click.pos)
|
||||
|| _last_click.button != event->button )
|
||||
// Reset current click count if moved too far
|
||||
if( !checkClickDistance(path, _last_click.path) )
|
||||
_current_click_count = 1;
|
||||
}
|
||||
}
|
||||
@@ -248,7 +167,7 @@ namespace canvas
|
||||
handled |= propagateEvent( dbl_click,
|
||||
getCommonAncestor(_last_click.path, path) );
|
||||
|
||||
_last_click.set(event, path);
|
||||
_last_click = StampedPropagationPath(path, event->getTime());
|
||||
|
||||
return handled;
|
||||
}
|
||||
@@ -315,11 +234,88 @@ namespace canvas
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
EventManager::checkClickDistance( const osg::Vec2f& pos1,
|
||||
const osg::Vec2f& pos2 ) const
|
||||
bool EventManager::propagateEvent( const EventPtr& event,
|
||||
const EventPropagationPath& path )
|
||||
{
|
||||
osg::Vec2 delta = pos1 - pos2;
|
||||
event->target = path.back().element;
|
||||
MouseEventPtr mouse_event = boost::dynamic_pointer_cast<MouseEvent>(event);
|
||||
|
||||
// Event propagation similar to DOM Level 3 event flow:
|
||||
// http://www.w3.org/TR/DOM-Level-3-Events/#event-flow
|
||||
|
||||
// Capturing phase
|
||||
// for( EventPropagationPath::const_iterator it = path.begin();
|
||||
// it != path.end();
|
||||
// ++it )
|
||||
// {
|
||||
// if( !it->element.expired() )
|
||||
// std::cout << it->element.lock()->getProps()->getPath() << std::endl;
|
||||
// }
|
||||
|
||||
// Check if event supports bubbling
|
||||
const Event::Type types_no_bubbling[] = {
|
||||
Event::MOUSE_ENTER,
|
||||
Event::MOUSE_LEAVE,
|
||||
};
|
||||
const size_t num_types_no_bubbling = sizeof(types_no_bubbling)
|
||||
/ sizeof(types_no_bubbling[0]);
|
||||
bool do_bubble = true;
|
||||
for( size_t i = 0; i < num_types_no_bubbling; ++i )
|
||||
if( event->type == types_no_bubbling[i] )
|
||||
{
|
||||
do_bubble = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Bubbling phase
|
||||
for( EventPropagationPath::const_reverse_iterator
|
||||
it = path.rbegin();
|
||||
it != path.rend();
|
||||
++it )
|
||||
{
|
||||
ElementPtr el = it->element.lock();
|
||||
|
||||
if( !el )
|
||||
{
|
||||
// Ignore element if it has been destroyed while traversing the event
|
||||
// (eg. removed by another event handler)
|
||||
if( do_bubble )
|
||||
continue;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO provide functions to convert delta to local coordinates on demand.
|
||||
// Maybe also provide a clone method for events as local coordinates
|
||||
// might differ between different elements receiving the same event.
|
||||
if( mouse_event ) //&& event->type != Event::DRAG )
|
||||
{
|
||||
// TODO transform pos and delta for drag events. Maybe we should just
|
||||
// store the global coordinates and convert to local coordinates
|
||||
// on demand.
|
||||
|
||||
// Position and delta are specified in local coordinate system of
|
||||
// current element
|
||||
mouse_event->local_pos = it->local_pos;
|
||||
//mouse_event->delta = it->local_delta;
|
||||
}
|
||||
|
||||
event->current_target = el;
|
||||
el->handleEvent(event);
|
||||
|
||||
if( event->propagation_stopped || !do_bubble )
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
EventManager::checkClickDistance( const EventPropagationPath& path1,
|
||||
const EventPropagationPath& path2 ) const
|
||||
{
|
||||
osg::Vec2 delta = path1.front().local_pos - path2.front().local_pos;
|
||||
return std::fabs(delta.x()) < drag_threshold
|
||||
&& std::fabs(delta.y()) < drag_threshold;
|
||||
}
|
||||
|
||||
@@ -29,18 +29,11 @@ namespace canvas
|
||||
|
||||
struct EventTarget
|
||||
{
|
||||
ElementWeakPtr element;
|
||||
|
||||
// Used as storage by EventManager during event propagation
|
||||
mutable osg::Vec2f local_pos;
|
||||
|
||||
EventTarget( Element* el,
|
||||
const osg::Vec2f pos = osg::Vec2f() ):
|
||||
element(el),
|
||||
local_pos(pos)
|
||||
{}
|
||||
ElementWeakPtr element;
|
||||
osg::Vec2f local_pos,
|
||||
local_delta;
|
||||
};
|
||||
|
||||
typedef std::deque<EventTarget> EventPropagationPath;
|
||||
inline bool operator==(const EventTarget& t1, const EventTarget& t2)
|
||||
{
|
||||
return t1.element.lock() == t2.element.lock();
|
||||
@@ -54,9 +47,6 @@ namespace canvas
|
||||
bool handleEvent( const MouseEventPtr& event,
|
||||
const EventPropagationPath& path );
|
||||
|
||||
bool propagateEvent( const EventPtr& event,
|
||||
const EventPropagationPath& path );
|
||||
|
||||
protected:
|
||||
struct StampedPropagationPath
|
||||
{
|
||||
@@ -72,20 +62,11 @@ namespace canvas
|
||||
|
||||
// TODO if we really need the paths modify to not copy around the paths
|
||||
// that much.
|
||||
StampedPropagationPath _last_mouse_over;
|
||||
StampedPropagationPath _last_mouse_down,
|
||||
_last_click,
|
||||
_last_mouse_over;
|
||||
size_t _current_click_count;
|
||||
|
||||
struct MouseEventInfo:
|
||||
public StampedPropagationPath
|
||||
{
|
||||
int button;
|
||||
osg::Vec2f pos;
|
||||
|
||||
void set( const MouseEventPtr& event,
|
||||
const EventPropagationPath& path );
|
||||
} _last_mouse_down,
|
||||
_last_click;
|
||||
|
||||
/**
|
||||
* Propagate click event and handle multi-click (eg. create dblclick)
|
||||
*/
|
||||
@@ -98,13 +79,16 @@ namespace canvas
|
||||
bool handleMove( const MouseEventPtr& event,
|
||||
const EventPropagationPath& path );
|
||||
|
||||
bool propagateEvent( const EventPtr& event,
|
||||
const EventPropagationPath& path );
|
||||
|
||||
/**
|
||||
* Check if two click events (either mousedown/up or two consecutive
|
||||
* clicks) are inside a maximum distance to still create a click or
|
||||
* dblclick event respectively.
|
||||
*/
|
||||
bool checkClickDistance( const osg::Vec2f& pos1,
|
||||
const osg::Vec2f& pos2 ) const;
|
||||
bool checkClickDistance( const EventPropagationPath& path1,
|
||||
const EventPropagationPath& path2 ) const;
|
||||
EventPropagationPath
|
||||
getCommonAncestor( const EventPropagationPath& path1,
|
||||
const EventPropagationPath& path2 ) const;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "CanvasEvent.hxx"
|
||||
#include "CanvasEventVisitor.hxx"
|
||||
#include <simgear/canvas/elements/CanvasElement.hxx>
|
||||
#include <iostream>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
@@ -29,12 +30,16 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
EventVisitor::EventVisitor( TraverseMode mode,
|
||||
const osg::Vec2f& pos,
|
||||
const osg::Vec2f& delta,
|
||||
const ElementPtr& root ):
|
||||
_traverse_mode( mode ),
|
||||
_root(root)
|
||||
{
|
||||
if( mode == TRAVERSE_DOWN )
|
||||
_target_path.push_back( EventTarget(NULL, pos) );
|
||||
{
|
||||
EventTarget target = {ElementWeakPtr(), pos, delta};
|
||||
_target_path.push_back(target);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -58,18 +63,31 @@ namespace canvas
|
||||
// We only need to check for hits while traversing down
|
||||
if( _traverse_mode == TRAVERSE_DOWN )
|
||||
{
|
||||
// Transform event to local coordinates
|
||||
const osg::Matrix& m = el.getMatrixTransform()->getInverseMatrix();
|
||||
const osg::Vec2f& pos = _target_path.back().local_pos;
|
||||
const osg::Vec2f local_pos = el.posToLocal(pos);
|
||||
const osg::Vec2f local_pos
|
||||
(
|
||||
m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0),
|
||||
m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1)
|
||||
);
|
||||
|
||||
// Don't check specified root element for collision, as its purpose is to
|
||||
// catch all events which have no target. This allows for example calling
|
||||
// event listeners attached to the canvas itself (its root group) even if
|
||||
// no element has been hit.
|
||||
if( _root.get() != &el
|
||||
&& !el.hitBound(_target_path.front().local_pos, pos, local_pos) )
|
||||
if( _root.get() != &el && !el.hitBound(pos, local_pos) )
|
||||
return false;
|
||||
|
||||
_target_path.push_back( EventTarget(&el, local_pos) );
|
||||
const osg::Vec2f& delta = _target_path.back().local_delta;
|
||||
const osg::Vec2f local_delta
|
||||
(
|
||||
m(0, 0) * delta[0] + m(1, 0) * delta[1],
|
||||
m(0, 1) * delta[0] + m(1, 1) * delta[1]
|
||||
);
|
||||
|
||||
EventTarget target = {el.getWeakPtr(), local_pos, local_delta};
|
||||
_target_path.push_back(target);
|
||||
|
||||
if( el.traverse(*this) || &el == _root.get() )
|
||||
return true;
|
||||
|
||||
@@ -42,10 +42,12 @@ namespace canvas
|
||||
*
|
||||
* @param mode
|
||||
* @param pos Mouse position
|
||||
* @param delta Mouse movement since last mouse move event
|
||||
* @param root Element to dispatch events to if no element is hit
|
||||
*/
|
||||
EventVisitor( TraverseMode mode,
|
||||
const osg::Vec2f& pos,
|
||||
const osg::Vec2f& delta,
|
||||
const ElementPtr& root = ElementPtr() );
|
||||
virtual ~EventVisitor();
|
||||
virtual bool traverse(Element& el);
|
||||
|
||||
@@ -45,25 +45,25 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasPtr CanvasMgr::createCanvas(const std::string& name)
|
||||
{
|
||||
return static_cast<Canvas*>( createElement(name).get() );
|
||||
return boost::static_pointer_cast<Canvas>( createElement(name) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasPtr CanvasMgr::getCanvas(size_t index) const
|
||||
{
|
||||
return static_cast<Canvas*>( getElement(index).get() );
|
||||
return boost::static_pointer_cast<Canvas>( getElement(index) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasPtr CanvasMgr::getCanvas(const std::string& name) const
|
||||
{
|
||||
return static_cast<Canvas*>( getElement(name).get() );
|
||||
return boost::static_pointer_cast<Canvas>( getElement(name) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void CanvasMgr::elementCreated(PropertyBasedElementPtr element)
|
||||
{
|
||||
CanvasPtr canvas = static_cast<Canvas*>(element.get());
|
||||
CanvasPtr canvas = boost::static_pointer_cast<Canvas>(element);
|
||||
canvas->setCanvasMgr(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace canvas
|
||||
public:
|
||||
|
||||
/**
|
||||
* @param node Root node of branch used to control canvasses
|
||||
* @param node Root node of branch used to control canvasses
|
||||
*/
|
||||
CanvasMgr(SGPropertyNode_ptr node);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "Canvas.hxx"
|
||||
#include "CanvasObjectPlacement.hxx"
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include "MouseEvent.hxx"
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/scene/util/SGPickCallback.hxx>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
///@file Placement for putting a canvas texture onto OpenSceneGraph objects.
|
||||
// Canvas placement for placing a canvas texture onto osg objects.
|
||||
//
|
||||
// It also provides a SGPickCallback for passing mouse events to the canvas and
|
||||
// manages emissive lighting of the placed canvas.
|
||||
@@ -19,7 +19,7 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef CANVAS_OBJECT_PLACEMENT_HXX_
|
||||
#ifndef CANVAS_PICK_PLACEMENT_HXX_
|
||||
#define CANVAS_OBJECT_PLACEMENT_HXX_
|
||||
|
||||
#include "CanvasPlacement.hxx"
|
||||
@@ -33,9 +33,6 @@ namespace simgear
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
/**
|
||||
* Place a Canvas onto an osg object (as texture).
|
||||
*/
|
||||
class ObjectPlacement:
|
||||
public Placement
|
||||
{
|
||||
@@ -75,4 +72,4 @@ namespace canvas
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* CANVAS_OBJECT_PLACEMENT_HXX_ */
|
||||
#endif /* CANVAS_PICK_PLACEMENT_HXX_ */
|
||||
|
||||
@@ -21,11 +21,8 @@
|
||||
|
||||
#include "canvas_fwd.hxx"
|
||||
|
||||
class SGSubsystem;
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace HTTP { class Client; }
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
@@ -38,8 +35,6 @@ namespace canvas
|
||||
virtual void addCamera(osg::Camera* camera) const = 0;
|
||||
virtual void removeCamera(osg::Camera* camera) const = 0;
|
||||
virtual osg::Image* getImage(const std::string& path) const = 0;
|
||||
virtual SGSubsystem* getSubsystem(const std::string& name) const = 0;
|
||||
virtual HTTP::Client* getHTTPClient() const = 0;
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
|
||||
@@ -1,330 +0,0 @@
|
||||
// Window for placing a Canvas onto it (for dialogs, menus, etc.)
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "CanvasMgr.hxx"
|
||||
#include "CanvasSystemAdapter.hxx"
|
||||
#include "CanvasWindow.hxx"
|
||||
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/scene/util/OsgMath.hxx>
|
||||
|
||||
#include <osgGA/GUIEventHandler>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const std::string Window::TYPE_NAME = "window";
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Window::Window( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent ):
|
||||
Image(canvas, node, parent_style, parent),
|
||||
_attributes_dirty(0),
|
||||
_resizable(false),
|
||||
_capture_events(true),
|
||||
_resize_top(node, "resize-top"),
|
||||
_resize_right(node, "resize-right"),
|
||||
_resize_bottom(node, "resize-bottom"),
|
||||
_resize_left(node, "resize-left"),
|
||||
_resize_status(node, "resize-status")
|
||||
{
|
||||
node->setFloatValue("source/right", 1);
|
||||
node->setFloatValue("source/bottom", 1);
|
||||
node->setBoolValue("source/normalized", true);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Window::~Window()
|
||||
{
|
||||
if( _canvas_decoration )
|
||||
_canvas_decoration->destroy();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Window::update(double delta_time_sec)
|
||||
{
|
||||
if( _attributes_dirty & DECORATION )
|
||||
{
|
||||
updateDecoration();
|
||||
_attributes_dirty &= ~DECORATION;
|
||||
}
|
||||
|
||||
Image::update(delta_time_sec);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Window::valueChanged(SGPropertyNode * node)
|
||||
{
|
||||
bool handled = false;
|
||||
if( node->getParent() == _node )
|
||||
{
|
||||
handled = true;
|
||||
const std::string& name = node->getNameString();
|
||||
if( name == "resize" )
|
||||
_resizable = node->getBoolValue();
|
||||
else if( name == "update" )
|
||||
update(0);
|
||||
else if( name == "capture-events" )
|
||||
_capture_events = node->getBoolValue();
|
||||
else if( name == "decoration-border" )
|
||||
parseDecorationBorder(node->getStringValue());
|
||||
else if( boost::starts_with(name, "shadow-")
|
||||
|| name == "content-size" )
|
||||
_attributes_dirty |= DECORATION;
|
||||
else
|
||||
handled = false;
|
||||
}
|
||||
|
||||
if( !handled )
|
||||
Image::valueChanged(node);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Group* Window::getGroup()
|
||||
{
|
||||
return getMatrixTransform();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const SGVec2<float> Window::getPosition() const
|
||||
{
|
||||
const osg::Matrix& m = getMatrixTransform()->getMatrix();
|
||||
return SGVec2<float>( m(3, 0), m(3, 1) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const SGRect<float> Window::getScreenRegion() const
|
||||
{
|
||||
return getPosition() + getRegion();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Window::setCanvasContent(CanvasPtr canvas)
|
||||
{
|
||||
_canvas_content = canvas;
|
||||
if( _layout )
|
||||
{
|
||||
canvas->setLayout(_layout);
|
||||
_layout.clear();
|
||||
}
|
||||
|
||||
if( _image_content )
|
||||
// Placement within decoration canvas
|
||||
_image_content->setSrcCanvas(canvas);
|
||||
else
|
||||
setSrcCanvas(canvas);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasWeakPtr Window::getCanvasContent() const
|
||||
{
|
||||
return _canvas_content;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Window::setLayout(const LayoutRef& layout)
|
||||
{
|
||||
CanvasPtr canvas = _canvas_content.lock();
|
||||
if( canvas )
|
||||
canvas->setLayout(layout);
|
||||
else
|
||||
_layout = layout; // keep layout until content canvas is set
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasPtr Window::getCanvasDecoration() const
|
||||
{
|
||||
return _canvas_decoration;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Window::isResizable() const
|
||||
{
|
||||
return _resizable;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Window::isCapturingEvents() const
|
||||
{
|
||||
return _capture_events;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Window::raise()
|
||||
{
|
||||
// on writing the z-index the window always is moved to the top of all other
|
||||
// windows with the same z-index.
|
||||
set<int>("z-index", get<int>("z-index", 0));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Window::handleResize( uint8_t mode,
|
||||
const osg::Vec2f& offset )
|
||||
{
|
||||
if( mode == NONE )
|
||||
{
|
||||
_resize_status = 0;
|
||||
return;
|
||||
}
|
||||
else if( mode & INIT )
|
||||
{
|
||||
_resize_top = getRegion().t();
|
||||
_resize_right = getRegion().r();
|
||||
_resize_bottom = getRegion().b();
|
||||
_resize_left = getRegion().l();
|
||||
_resize_status = 1;
|
||||
}
|
||||
|
||||
if( mode & BOTTOM )
|
||||
_resize_bottom = getRegion().b() + offset.y();
|
||||
else if( mode & TOP )
|
||||
_resize_top = getRegion().t() + offset.y();
|
||||
|
||||
if( mode & canvas::Window::RIGHT )
|
||||
_resize_right = getRegion().r() + offset.x();
|
||||
else if( mode & canvas::Window::LEFT )
|
||||
_resize_left = getRegion().l() + offset.x();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Window::parseDecorationBorder(const std::string& str)
|
||||
{
|
||||
_decoration_border = simgear::CSSBorder::parse(str);
|
||||
_attributes_dirty |= DECORATION;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Window::updateDecoration()
|
||||
{
|
||||
int shadow_radius = get<float>("shadow-radius") + 0.5;
|
||||
if( shadow_radius < 2 )
|
||||
shadow_radius = 0;
|
||||
|
||||
CanvasPtr content = _canvas_content.lock();
|
||||
SGRect<int> content_view
|
||||
(
|
||||
0,
|
||||
0,
|
||||
get<int>("content-size[0]", content ? content->getViewWidth() : 400),
|
||||
get<int>("content-size[1]", content ? content->getViewHeight() : 300)
|
||||
);
|
||||
|
||||
if( _decoration_border.isNone() && !shadow_radius )
|
||||
{
|
||||
setSrcCanvas(content);
|
||||
set<int>("size[0]", content_view.width());
|
||||
set<int>("size[1]", content_view.height());
|
||||
|
||||
_image_content.reset();
|
||||
_image_shadow.reset();
|
||||
if( _canvas_decoration )
|
||||
_canvas_decoration->destroy();
|
||||
_canvas_decoration.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if( !_canvas_decoration )
|
||||
{
|
||||
CanvasMgr* mgr = dynamic_cast<CanvasMgr*>(
|
||||
Canvas::getSystemAdapter()->getSubsystem("Canvas")
|
||||
);
|
||||
|
||||
if( !mgr )
|
||||
{
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "canvas::Window: no canvas manager!");
|
||||
return;
|
||||
}
|
||||
|
||||
_canvas_decoration = mgr->createCanvas("window-decoration");
|
||||
_canvas_decoration->getProps()
|
||||
->setStringValue("background", "rgba(0,0,0,0)");
|
||||
setSrcCanvas(_canvas_decoration);
|
||||
|
||||
_image_content = _canvas_decoration->getRootGroup()
|
||||
->createChild<Image>("content");
|
||||
_image_content->setSrcCanvas(content);
|
||||
|
||||
// Draw content on top of decoration
|
||||
_image_content->set<int>("z-index", 1);
|
||||
}
|
||||
|
||||
GroupPtr group_decoration =
|
||||
_canvas_decoration->getOrCreateGroup("decoration");
|
||||
group_decoration->set<int>("tf/t[0]", shadow_radius);
|
||||
group_decoration->set<int>("tf/t[1]", shadow_radius);
|
||||
// TODO do we need clipping or shall we trust the decorator not to draw over
|
||||
// the shadow?
|
||||
|
||||
CSSBorder::Offsets const border =
|
||||
_decoration_border.getAbsOffsets(content_view);
|
||||
|
||||
int shad2 = 2 * shadow_radius,
|
||||
outer_width = border.l + content_view.width() + border.r + shad2,
|
||||
outer_height = border.t + content_view.height() + border.b + shad2;
|
||||
|
||||
_canvas_decoration->setSizeX( outer_width );
|
||||
_canvas_decoration->setSizeY( outer_height );
|
||||
_canvas_decoration->setViewWidth( outer_width );
|
||||
_canvas_decoration->setViewHeight( outer_height );
|
||||
|
||||
set<int>("size[0]", outer_width - shad2);
|
||||
set<int>("size[1]", outer_height - shad2);
|
||||
set<int>("outset", shadow_radius);
|
||||
|
||||
assert(_image_content);
|
||||
_image_content->set<int>("x", shadow_radius + border.l);
|
||||
_image_content->set<int>("y", shadow_radius + border.t);
|
||||
_image_content->set<int>("size[0]", content_view.width());
|
||||
_image_content->set<int>("size[1]", content_view.height());
|
||||
|
||||
if( !shadow_radius )
|
||||
{
|
||||
if( _image_shadow )
|
||||
{
|
||||
_image_shadow->destroy();
|
||||
_image_shadow.reset();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int shadow_inset = std::max<int>(get<float>("shadow-inset") + 0.5, 0),
|
||||
slice_width = shadow_radius + shadow_inset;
|
||||
|
||||
_image_shadow = _canvas_decoration->getRootGroup()
|
||||
->getOrCreateChild<Image>("shadow");
|
||||
_image_shadow->set<std::string>("src", "gui/images/shadow.png");
|
||||
_image_shadow->set<float>("slice", 7);
|
||||
_image_shadow->set<std::string>("fill", "#000000");
|
||||
_image_shadow->set<float>("slice-width", slice_width);
|
||||
_image_shadow->set<int>("size[0]", outer_width);
|
||||
_image_shadow->set<int>("size[1]", outer_height);
|
||||
|
||||
// Draw shadow below decoration
|
||||
_image_shadow->set<int>("z-index", -1);
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,131 +0,0 @@
|
||||
// Window for placing a Canvas onto it (for dialogs, menus, etc.)
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef CANVAS_WINDOW_HXX_
|
||||
#define CANVAS_WINDOW_HXX_
|
||||
|
||||
#include <simgear/canvas/elements/CanvasImage.hxx>
|
||||
#include <simgear/canvas/layout/Layout.hxx>
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/props/PropertyBasedElement.hxx>
|
||||
#include <simgear/props/propertyObject.hxx>
|
||||
#include <simgear/misc/CSSBorder.hxx>
|
||||
|
||||
#include <osg/Geode>
|
||||
#include <osg/Geometry>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
class Window:
|
||||
public Image,
|
||||
public LayoutItem
|
||||
{
|
||||
public:
|
||||
static const std::string TYPE_NAME;
|
||||
|
||||
enum Resize
|
||||
{
|
||||
NONE = 0,
|
||||
LEFT = 1,
|
||||
RIGHT = LEFT << 1,
|
||||
TOP = RIGHT << 1,
|
||||
BOTTOM = TOP << 1,
|
||||
INIT = BOTTOM << 1
|
||||
};
|
||||
|
||||
/**
|
||||
* @param node Property node containing settings for this window:
|
||||
* capture-events Disable/Enable event capturing
|
||||
* content-size[0-1] Size of content area (excluding
|
||||
* decoration border)
|
||||
* decoration-border Size of decoration border
|
||||
* resize Enable resize cursor and properties
|
||||
* shadow-inset Inset of shadow image
|
||||
* shadow-radius Radius/outset of shadow image
|
||||
*/
|
||||
Window( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style = Style(),
|
||||
Element* parent = 0 );
|
||||
virtual ~Window();
|
||||
|
||||
virtual void update(double delta_time_sec);
|
||||
virtual void valueChanged(SGPropertyNode* node);
|
||||
|
||||
osg::Group* getGroup();
|
||||
const SGVec2<float> getPosition() const;
|
||||
const SGRect<float> getScreenRegion() const;
|
||||
|
||||
void setCanvasContent(CanvasPtr canvas);
|
||||
simgear::canvas::CanvasWeakPtr getCanvasContent() const;
|
||||
|
||||
void setLayout(const LayoutRef& layout);
|
||||
|
||||
CanvasPtr getCanvasDecoration() const;
|
||||
|
||||
bool isResizable() const;
|
||||
bool isCapturingEvents() const;
|
||||
|
||||
/**
|
||||
* Moves window on top of all other windows with the same z-index.
|
||||
*
|
||||
* @note If no z-index is set it defaults to 0.
|
||||
*/
|
||||
void raise();
|
||||
|
||||
void handleResize( uint8_t mode,
|
||||
const osg::Vec2f& offset = osg::Vec2f() );
|
||||
|
||||
protected:
|
||||
|
||||
enum Attributes
|
||||
{
|
||||
DECORATION = 1
|
||||
};
|
||||
|
||||
uint32_t _attributes_dirty;
|
||||
|
||||
CanvasPtr _canvas_decoration;
|
||||
CanvasWeakPtr _canvas_content;
|
||||
LayoutRef _layout;
|
||||
|
||||
ImagePtr _image_content,
|
||||
_image_shadow;
|
||||
|
||||
bool _resizable,
|
||||
_capture_events;
|
||||
|
||||
PropertyObject<int> _resize_top,
|
||||
_resize_right,
|
||||
_resize_bottom,
|
||||
_resize_left,
|
||||
_resize_status;
|
||||
|
||||
CSSBorder _decoration_border;
|
||||
|
||||
void parseDecorationBorder(const std::string& str);
|
||||
void updateDecoration();
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* CANVAS_WINDOW_HXX_ */
|
||||
@@ -1,4 +1,4 @@
|
||||
///@file Mouse event
|
||||
// Mouse event
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
#ifndef CANVAS_MOUSE_EVENT_HXX_
|
||||
#define CANVAS_MOUSE_EVENT_HXX_
|
||||
|
||||
#include <simgear/canvas/CanvasEvent.hxx>
|
||||
#include "CanvasEvent.hxx"
|
||||
#include <osgGA/GUIEventAdapter>
|
||||
|
||||
namespace simgear
|
||||
@@ -31,10 +31,21 @@ namespace canvas
|
||||
public Event
|
||||
{
|
||||
public:
|
||||
MouseEvent();
|
||||
MouseEvent(const osgGA::GUIEventAdapter& ea);
|
||||
MouseEvent():
|
||||
button(-1),
|
||||
state(-1),
|
||||
mod(-1),
|
||||
click_count(0)
|
||||
{}
|
||||
|
||||
virtual bool canBubble() const;
|
||||
MouseEvent(const osgGA::GUIEventAdapter& ea):
|
||||
button(ea.getButton()),
|
||||
state(ea.getButtonMask()),
|
||||
mod(ea.getModKeyMask()),
|
||||
click_count(0)
|
||||
{
|
||||
time = ea.getTime();
|
||||
}
|
||||
|
||||
osg::Vec2f getScreenPos() const { return screen_pos; }
|
||||
osg::Vec2f getClientPos() const { return client_pos; }
|
||||
@@ -53,10 +64,6 @@ namespace canvas
|
||||
float getDeltaX() const { return delta.x(); }
|
||||
float getDeltaY() const { return delta.y(); }
|
||||
|
||||
int getButton() const { return button; }
|
||||
int getButtonMask() const { return buttons; }
|
||||
int getModifiers() const { return modifiers; }
|
||||
|
||||
int getCurrentClickCount() const { return click_count; }
|
||||
|
||||
osg::Vec2f screen_pos, //<! Position in screen coordinates
|
||||
@@ -64,8 +71,8 @@ namespace canvas
|
||||
local_pos, //<! Position in local/element coordinates
|
||||
delta;
|
||||
int button, //<! Button for this event
|
||||
buttons, //<! Current button state
|
||||
modifiers, //<! Keyboard modifier state
|
||||
state, //<! Current button state
|
||||
mod, //<! Keyboard modifier state
|
||||
click_count; //<! Current click count
|
||||
};
|
||||
|
||||
@@ -38,9 +38,4 @@
|
||||
|
||||
#endif
|
||||
|
||||
// We currently do not support using images (inside paths). If we were going to
|
||||
// use it loading and unloading needs to happen within OpenSceneGraph to handle
|
||||
// synchronization correctly in multithreading mode.
|
||||
#define SH_NO_IMAGE
|
||||
|
||||
#endif // __SHCONFIG_H
|
||||
|
||||
@@ -53,7 +53,7 @@ VG_API_CALL VGboolean vgCreateContextSH(VGint width, VGint height)
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, width, 0, height, -1, 1);
|
||||
gluOrtho2D(0,width,0,height);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();*/
|
||||
@@ -79,7 +79,7 @@ VG_API_CALL void vgResizeSurfaceSH(VGint width, VGint height)
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, width, 0, height, -1, 1);
|
||||
gluOrtho2D(0,width,0,height);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
#if !defined(VG_API_MACOSX) && !defined(__FreeBSD__)
|
||||
#ifndef VG_API_MACOSX
|
||||
# include <malloc.h>
|
||||
#endif
|
||||
|
||||
@@ -158,14 +158,18 @@ SHfloat getMaxFloat();
|
||||
|
||||
#if defined(VG_API_LINUX)
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
#include <GL/glx.h>
|
||||
#elif defined(VG_API_MACOSX)
|
||||
#include <OpenGL/gl.h>
|
||||
#include <OpenGL/glu.h>
|
||||
#elif defined(VG_API_WINDOWS)
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
#else
|
||||
#define GL_GLEXT_LEGACY /* don't include glext.h */
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
#include <GL/glx.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
#define _ARRAY_DEFINE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
#ifndef SH_NO_IMAGE
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
* Prepares the proper pixel pack/unpack info for the given
|
||||
@@ -441,7 +440,6 @@ void shLoadColor(SHColor *c, const void *data, SHImageFormatDesc *f)
|
||||
if (f->rmask == 0x0) { c->r = 1.0f; c->g = 1.0f; c->b = 1.0f; }
|
||||
}
|
||||
|
||||
#endif // SH_NO_IMAGE
|
||||
|
||||
/*----------------------------------------------
|
||||
* Color and Image constructors and destructors
|
||||
@@ -463,28 +461,18 @@ void SHImage_ctor(SHImage *i)
|
||||
i->data = NULL;
|
||||
i->width = 0;
|
||||
i->height = 0;
|
||||
|
||||
#ifdef SH_NO_IMAGE
|
||||
printf("ShivaVG: images not supported!");
|
||||
#else
|
||||
glGenTextures(1, &i->texture);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SHImage_dtor(SHImage *i)
|
||||
{
|
||||
if (i->data != NULL)
|
||||
free(i->data);
|
||||
|
||||
#ifdef SH_NO_IMAGE
|
||||
printf("ShivaVG: images not supported!");
|
||||
#else
|
||||
|
||||
if (glIsTexture(i->texture))
|
||||
glDeleteTextures(1, &i->texture);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef SH_NO_IMAGE
|
||||
/*--------------------------------------------------------
|
||||
* Finds appropriate OpenGL texture size for the size of
|
||||
* the given image
|
||||
@@ -565,7 +553,6 @@ void shUpdateImageTexture(SHImage *i, VGContext *c)
|
||||
i->texwidth, i->texheight, 0,
|
||||
i->fd.glformat, i->fd.gltype, i->data);
|
||||
}
|
||||
#endif // SH_NO_IMAGE
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* Creates a new image object and returns the handle to it
|
||||
@@ -575,10 +562,6 @@ VG_API_CALL VGImage vgCreateImage(VGImageFormat format,
|
||||
VGint width, VGint height,
|
||||
VGbitfield allowedQuality)
|
||||
{
|
||||
#ifdef SH_NO_IMAGE
|
||||
printf("ShivaVG: images not supported!");
|
||||
return VG_INVALID_HANDLE;
|
||||
#else
|
||||
SHImage *i = NULL;
|
||||
SHImageFormatDesc fd;
|
||||
VG_GETCONTEXT(VG_INVALID_HANDLE);
|
||||
@@ -631,16 +614,12 @@ VG_API_CALL VGImage vgCreateImage(VGImageFormat format,
|
||||
|
||||
/* Add to resource list */
|
||||
shImageArrayPushBack(&context->images, i);
|
||||
|
||||
|
||||
VG_RETURN((VGImage)i);
|
||||
#endif // SH_NO_IMAGE
|
||||
}
|
||||
|
||||
VG_API_CALL void vgDestroyImage(VGImage image)
|
||||
{
|
||||
#ifdef SH_NO_IMAGE
|
||||
printf("ShivaVG: images not supported!");
|
||||
#else
|
||||
SHint index;
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
@@ -651,9 +630,8 @@ VG_API_CALL void vgDestroyImage(VGImage image)
|
||||
/* Delete object and remove resource */
|
||||
SH_DELETEOBJ(SHImage, (SHImage*)image);
|
||||
shImageArrayRemoveAt(&context->images, index);
|
||||
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
#endif // SH_NO_IMAGE
|
||||
}
|
||||
|
||||
/*---------------------------------------------------
|
||||
@@ -664,9 +642,6 @@ VG_API_CALL void vgDestroyImage(VGImage image)
|
||||
VG_API_CALL void vgClearImage(VGImage image,
|
||||
VGint x, VGint y, VGint width, VGint height)
|
||||
{
|
||||
#ifdef SH_NO_IMAGE
|
||||
printf("ShivaVG: images not supported!");
|
||||
#else
|
||||
SHImage *i;
|
||||
SHColor clear;
|
||||
SHuint8 *data;
|
||||
@@ -707,13 +682,9 @@ VG_API_CALL void vgClearImage(VGImage image,
|
||||
}}
|
||||
|
||||
shUpdateImageTexture(i, context);
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
#endif // SH_NO_IMAGE
|
||||
}
|
||||
|
||||
#ifndef SH_NO_IMAGE
|
||||
|
||||
/*------------------------------------------------------------
|
||||
* Generic function for copying a rectangle area of pixels
|
||||
* of size (width,height) among two data buffers. The size of
|
||||
@@ -824,8 +795,6 @@ void shCopyPixels(SHuint8 *dst, VGImageFormat dstFormat, SHint dstStride,
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SH_NO_IMAGE
|
||||
|
||||
/*---------------------------------------------------------
|
||||
* Copies a rectangle area of pixels of size (width,height)
|
||||
* from given data buffer to image surface at destination
|
||||
@@ -837,9 +806,6 @@ VG_API_CALL void vgImageSubData(VGImage image,
|
||||
VGImageFormat dataFormat,
|
||||
VGint x, VGint y, VGint width, VGint height)
|
||||
{
|
||||
#ifdef SH_NO_IMAGE
|
||||
printf("ShivaVG: images not supported!");
|
||||
#else
|
||||
SHImage *i;
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
@@ -870,9 +836,7 @@ VG_API_CALL void vgImageSubData(VGImage image,
|
||||
x, y, 0, 0, width, height);
|
||||
|
||||
shUpdateImageTexture(i, context);
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
#endif // SH_NO_IMAGE
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------
|
||||
@@ -887,9 +851,6 @@ VG_API_CALL void vgGetImageSubData(VGImage image,
|
||||
VGint x, VGint y,
|
||||
VGint width, VGint height)
|
||||
{
|
||||
#ifdef SH_NO_IMAGE
|
||||
printf("ShivaVG: images not supported!");
|
||||
#else
|
||||
SHImage *i;
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
@@ -917,10 +878,9 @@ VG_API_CALL void vgGetImageSubData(VGImage image,
|
||||
shCopyPixels(data, dataFormat, dataStride,
|
||||
i->data, i->fd.vgformat, i->texwidth * i->fd.bytes,
|
||||
width, height, i->width, i->height,
|
||||
0,0,x,y,width,height);
|
||||
|
||||
0,0,x,x,width,height);
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
#endif // SH_NO_IMAGE
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------
|
||||
@@ -934,9 +894,6 @@ VG_API_CALL void vgCopyImage(VGImage dst, VGint dx, VGint dy,
|
||||
VGint width, VGint height,
|
||||
VGboolean dither)
|
||||
{
|
||||
#ifdef SH_NO_IMAGE
|
||||
printf("ShivaVG: images not supported!");
|
||||
#else
|
||||
SHImage *s, *d;
|
||||
SHuint8 *pixels;
|
||||
|
||||
@@ -976,9 +933,7 @@ VG_API_CALL void vgCopyImage(VGImage dst, VGint dx, VGint dy,
|
||||
free(pixels);
|
||||
|
||||
shUpdateImageTexture(d, context);
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
#endif // SH_NO_IMAGE
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------
|
||||
@@ -991,9 +946,6 @@ VG_API_CALL void vgSetPixels(VGint dx, VGint dy,
|
||||
VGImage src, VGint sx, VGint sy,
|
||||
VGint width, VGint height)
|
||||
{
|
||||
#ifdef SH_NO_IMAGE
|
||||
printf("ShivaVG: images not supported!");
|
||||
#else
|
||||
SHImage *i;
|
||||
SHuint8 *pixels;
|
||||
SHImageFormatDesc winfd;
|
||||
@@ -1034,7 +986,6 @@ VG_API_CALL void vgSetPixels(VGint dx, VGint dy,
|
||||
free(pixels);
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
#endif // SH_NO_IMAGE
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------
|
||||
@@ -1048,9 +999,6 @@ VG_API_CALL void vgWritePixels(const void * data, VGint dataStride,
|
||||
VGint dx, VGint dy,
|
||||
VGint width, VGint height)
|
||||
{
|
||||
#ifdef SH_NO_IMAGE
|
||||
printf("ShivaVG: images not supported!");
|
||||
#else
|
||||
SHuint8 *pixels;
|
||||
SHImageFormatDesc winfd;
|
||||
|
||||
@@ -1095,8 +1043,7 @@ VG_API_CALL void vgWritePixels(const void * data, VGint dataStride,
|
||||
|
||||
free(pixels);
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
#endif // SH_NO_IMAGE
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
@@ -1109,9 +1056,6 @@ VG_API_CALL void vgGetPixels(VGImage dst, VGint dx, VGint dy,
|
||||
VGint sx, VGint sy,
|
||||
VGint width, VGint height)
|
||||
{
|
||||
#ifdef SH_NO_IMAGE
|
||||
printf("ShivaVG: images not supported!");
|
||||
#else
|
||||
SHImage *i;
|
||||
SHuint8 *pixels;
|
||||
SHImageFormatDesc winfd;
|
||||
@@ -1149,9 +1093,7 @@ VG_API_CALL void vgGetPixels(VGImage dst, VGint dx, VGint dy,
|
||||
free(pixels);
|
||||
|
||||
shUpdateImageTexture(i, context);
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
#endif // SH_NO_IMAGE
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------
|
||||
@@ -1165,9 +1107,6 @@ VG_API_CALL void vgReadPixels(void * data, VGint dataStride,
|
||||
VGint sx, VGint sy,
|
||||
VGint width, VGint height)
|
||||
{
|
||||
#ifdef SH_NO_IMAGE
|
||||
printf("ShivaVG: images not supported!");
|
||||
#else
|
||||
SHuint8 *pixels;
|
||||
SHImageFormatDesc winfd;
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
@@ -1207,9 +1146,8 @@ VG_API_CALL void vgReadPixels(void * data, VGint dataStride,
|
||||
0, 0, 0, 0, width, height);
|
||||
|
||||
free(pixels);
|
||||
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
#endif // SH_NO_IMAGE
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------
|
||||
@@ -1222,9 +1160,6 @@ VG_API_CALL void vgCopyPixels(VGint dx, VGint dy,
|
||||
VGint sx, VGint sy,
|
||||
VGint width, VGint height)
|
||||
{
|
||||
#ifdef SH_NO_IMAGE
|
||||
printf("ShivaVG: images not supported!");
|
||||
#else
|
||||
VG_GETCONTEXT(VG_NO_RETVAL);
|
||||
|
||||
VG_RETURN_ERR_IF(width <= 0 || height <= 0,
|
||||
@@ -1235,9 +1170,8 @@ VG_API_CALL void vgCopyPixels(VGint dx, VGint dy,
|
||||
glRasterPos2i(dx, dy);
|
||||
glCopyPixels(sx, sy, width, height, GL_COLOR);
|
||||
glRasterPos2i(0, 0);
|
||||
|
||||
|
||||
VG_RETURN(VG_NO_RETVAL);
|
||||
#endif // SH_NO_IMAGE
|
||||
}
|
||||
|
||||
VG_API_CALL VGImage vgChildImage(VGImage parent,
|
||||
|
||||
@@ -36,9 +36,6 @@
|
||||
#define _ARRAY_DEFINE
|
||||
#include "shArrayBase.h"
|
||||
|
||||
// We currently do not use gradients which need textures, so disable them to
|
||||
// prevent freeing resources outside the correct OpenGL thread/context.
|
||||
#define SH_NO_PAINT_TEXTURE
|
||||
|
||||
void SHPaint_ctor(SHPaint *p)
|
||||
{
|
||||
@@ -54,26 +51,20 @@ void SHPaint_ctor(SHPaint *p)
|
||||
for (i=0; i<4; ++i) p->linearGradient[i] = 0.0f;
|
||||
for (i=0; i<5; ++i) p->radialGradient[i] = 0.0f;
|
||||
p->pattern = VG_INVALID_HANDLE;
|
||||
|
||||
#ifndef SH_NO_PAINT_TEXTURE
|
||||
|
||||
glGenTextures(1, &p->texture);
|
||||
glBindTexture(GL_TEXTURE_1D, p->texture);
|
||||
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, SH_GRADIENT_TEX_SIZE, 0,
|
||||
GL_RGBA, GL_FLOAT, NULL);
|
||||
#else
|
||||
p->texture = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SHPaint_dtor(SHPaint *p)
|
||||
{
|
||||
SH_DEINITOBJ(SHStopArray, p->instops);
|
||||
SH_DEINITOBJ(SHStopArray, p->stops);
|
||||
|
||||
#ifndef SH_NO_PAINT_TEXTURE
|
||||
|
||||
if (glIsTexture(p->texture))
|
||||
glDeleteTextures(1, &p->texture);
|
||||
#endif
|
||||
}
|
||||
|
||||
VG_API_CALL VGPaint vgCreatePaint(void)
|
||||
@@ -152,7 +143,6 @@ VG_API_CALL void vgPaintPattern(VGPaint paint, VGImage pattern)
|
||||
|
||||
void shUpdateColorRampTexture(SHPaint *p)
|
||||
{
|
||||
#ifndef SH_NO_PAINT_TEXTURE
|
||||
SHint s=0;
|
||||
SHStop *stop1, *stop2;
|
||||
SHfloat rgba[SH_GRADIENT_TEX_COORDSIZE];
|
||||
@@ -187,15 +177,12 @@ void shUpdateColorRampTexture(SHPaint *p)
|
||||
CSTORE_RGBA1D_F(c, rgba, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Update texture image */
|
||||
glBindTexture(GL_TEXTURE_1D, p->texture);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, SH_GRADIENT_TEX_SIZE,
|
||||
GL_RGBA, GL_FLOAT, rgba);
|
||||
#else
|
||||
printf("ShivaVG: gradients not supported!");
|
||||
#endif
|
||||
}
|
||||
|
||||
void shValidateInputStops(SHPaint *p)
|
||||
@@ -357,7 +344,6 @@ void shGenerateStops(SHPaint *p, SHfloat minOffset, SHfloat maxOffset,
|
||||
|
||||
void shSetGradientTexGLState(SHPaint *p)
|
||||
{
|
||||
#ifndef SH_NO_PAINT_TEXTURE
|
||||
glBindTexture(GL_TEXTURE_1D, p->texture);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
@@ -373,9 +359,6 @@ void shSetGradientTexGLState(SHPaint *p)
|
||||
|
||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||||
glColor4f(1,1,1,1);
|
||||
#else
|
||||
printf("ShivaVG: gradients not supported!");
|
||||
#endif
|
||||
}
|
||||
|
||||
void shSetPatternTexGLState(SHPaint *p, VGContext *c)
|
||||
|
||||
@@ -314,9 +314,9 @@ VG_API_CALL void vgDrawPath(VGPath path, VGbitfield paintModes)
|
||||
}
|
||||
|
||||
/* TODO: Turn antialiasing on/off */
|
||||
// glDisable(GL_LINE_SMOOTH);
|
||||
// glDisable(GL_POLYGON_SMOOTH);
|
||||
// glEnable(GL_MULTISAMPLE);
|
||||
glDisable(GL_LINE_SMOOTH);
|
||||
glDisable(GL_POLYGON_SMOOTH);
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
|
||||
/* Pick paint if available or default*/
|
||||
fill = (context->fillPaint ? context->fillPaint : &context->defaultPaint);
|
||||
@@ -364,27 +364,26 @@ VG_API_CALL void vgDrawPath(VGPath path, VGbitfield paintModes)
|
||||
/* TODO: Is there any way to do this safely along
|
||||
with the paint generation pass?? */
|
||||
glDisable(GL_BLEND);
|
||||
// glDisable(GL_MULTISAMPLE);
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
shDrawBoundBox(context, p, VG_FILL_PATH);
|
||||
|
||||
/* Reset state */
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
// glDisable(GL_BLEND);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
/* TODO: Turn antialiasing on/off */
|
||||
// glDisable(GL_LINE_SMOOTH);
|
||||
// glDisable(GL_POLYGON_SMOOTH);
|
||||
// glEnable(GL_MULTISAMPLE);
|
||||
glDisable(GL_LINE_SMOOTH);
|
||||
glDisable(GL_POLYGON_SMOOTH);
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
|
||||
if ((paintModes & VG_STROKE_PATH) &&
|
||||
context->strokeLineWidth > 0.0f) {
|
||||
|
||||
#if 0
|
||||
|
||||
if (1) {/*context->strokeLineWidth > 1.0f) {*/
|
||||
#endif
|
||||
|
||||
if (shIsStrokeCacheValid( context, p ) == VG_FALSE)
|
||||
{
|
||||
/* Generate stroke triangles in user space */
|
||||
@@ -412,15 +411,15 @@ VG_API_CALL void vgDrawPath(VGPath path, VGbitfield paintModes)
|
||||
|
||||
/* Clear stencil for sure */
|
||||
glDisable(GL_BLEND);
|
||||
// glDisable(GL_MULTISAMPLE);
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
shDrawBoundBox(context, p, VG_STROKE_PATH);
|
||||
|
||||
/* Reset state */
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
// glDisable(GL_BLEND);
|
||||
#if 0
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
}else{
|
||||
|
||||
/* Simulate thin stroke by alpha */
|
||||
@@ -439,7 +438,6 @@ VG_API_CALL void vgDrawPath(VGPath path, VGbitfield paintModes)
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_LINE_SMOOTH);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -124,13 +124,11 @@ int shLineLineXsection(SHVector2 *o1, SHVector2 *v1,
|
||||
SHfloat DX = rightU * (-v2->y) - rightD * (-v2->x);
|
||||
/*SHfloat DY = v1.x * rightD - v1.y * rightU;*/
|
||||
|
||||
SHfloat t1;
|
||||
SHfloat t1 = DX / D;
|
||||
|
||||
if (D == 0.0f)
|
||||
return 0;
|
||||
|
||||
t1 = DX / D;
|
||||
|
||||
|
||||
xsection->x = o1->x + t1*v1->x;
|
||||
xsection->y = o1->y + t1*v1->y;
|
||||
return 1;
|
||||
|
||||
@@ -39,8 +39,8 @@ namespace canvas
|
||||
|
||||
#define SG_FWD_DECL(name)\
|
||||
class name;\
|
||||
typedef SGSharedPtr<name> name##Ptr;\
|
||||
typedef SGWeakPtr<name> name##WeakPtr;
|
||||
typedef boost::shared_ptr<name> name##Ptr;\
|
||||
typedef boost::weak_ptr<name> name##WeakPtr;
|
||||
|
||||
SG_FWD_DECL(Canvas)
|
||||
SG_FWD_DECL(Element)
|
||||
@@ -49,19 +49,9 @@ namespace canvas
|
||||
SG_FWD_DECL(Map)
|
||||
SG_FWD_DECL(Path)
|
||||
SG_FWD_DECL(Text)
|
||||
SG_FWD_DECL(Window)
|
||||
|
||||
SG_FWD_DECL(Event)
|
||||
SG_FWD_DECL(CustomEvent)
|
||||
SG_FWD_DECL(MouseEvent)
|
||||
|
||||
#undef SG_FWD_DECL
|
||||
|
||||
#define SG_FWD_DECL(name)\
|
||||
class name;\
|
||||
typedef boost::shared_ptr<name> name##Ptr;\
|
||||
typedef boost::weak_ptr<name> name##WeakPtr;
|
||||
|
||||
SG_FWD_DECL(Placement)
|
||||
SG_FWD_DECL(SystemAdapter)
|
||||
|
||||
@@ -70,9 +60,6 @@ namespace canvas
|
||||
class EventManager;
|
||||
class EventVisitor;
|
||||
|
||||
struct EventTarget;
|
||||
typedef std::deque<EventTarget> EventPropagationPath;
|
||||
|
||||
typedef std::map<std::string, const SGPropertyNode*> Style;
|
||||
typedef ElementPtr (*ElementFactory)( const CanvasWeakPtr&,
|
||||
const SGPropertyNode_ptr&,
|
||||
|
||||
@@ -23,9 +23,4 @@ set(SOURCES
|
||||
)
|
||||
|
||||
simgear_scene_component(canvas-elements canvas/elements "${SOURCES}" "${HEADERS}")
|
||||
simgear_component(canvas-elements/detail canvas/elements/detail "" "${DETAIL_HEADERS}")
|
||||
|
||||
add_boost_test(canvas_element
|
||||
SOURCES canvas_element_test.cpp
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
)
|
||||
simgear_component(canvas-elements/detail canvas/elements/detail "" "${DETAIL_HEADERS}")
|
||||
@@ -17,9 +17,8 @@
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "CanvasElement.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/canvas/CanvasEventVisitor.hxx>
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/canvas/MouseEvent.hxx>
|
||||
#include <simgear/math/SGMisc.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/scene/material/parseBlendFunc.hxx>
|
||||
@@ -50,50 +49,15 @@ namespace canvas
|
||||
{
|
||||
public:
|
||||
|
||||
ReferenceFrame _coord_reference;
|
||||
osg::observer_ptr<osg::Node> _node;
|
||||
ReferenceFrame _coord_reference;
|
||||
osg::Matrix _parent_inverse;
|
||||
|
||||
RelativeScissor(osg::Node* node = NULL):
|
||||
_coord_reference(GLOBAL),
|
||||
_node(node)
|
||||
{
|
||||
_width = 0;
|
||||
_height = 0;
|
||||
}
|
||||
|
||||
/** Copy constructor using CopyOp to manage deep vs shallow copy. */
|
||||
RelativeScissor( const RelativeScissor& vp,
|
||||
const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY ):
|
||||
Scissor(vp, copyop),
|
||||
_coord_reference(vp._coord_reference),
|
||||
_node(vp._node)
|
||||
RelativeScissor():
|
||||
_coord_reference(GLOBAL)
|
||||
{}
|
||||
|
||||
META_StateAttribute(simgear, RelativeScissor, SCISSOR);
|
||||
|
||||
/** Return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs. */
|
||||
virtual int compare(const StateAttribute& sa) const
|
||||
{
|
||||
// check the types are equal and then create the rhs variable
|
||||
// used by the COMPARE_StateAttribute_Parameter macros below.
|
||||
COMPARE_StateAttribute_Types(RelativeScissor,sa)
|
||||
|
||||
// compare each parameter in turn against the rhs.
|
||||
COMPARE_StateAttribute_Parameter(_x)
|
||||
COMPARE_StateAttribute_Parameter(_y)
|
||||
COMPARE_StateAttribute_Parameter(_width)
|
||||
COMPARE_StateAttribute_Parameter(_height)
|
||||
COMPARE_StateAttribute_Parameter(_coord_reference)
|
||||
COMPARE_StateAttribute_Parameter(_node)
|
||||
|
||||
return 0; // passed all the above comparison macros, must be equal.
|
||||
}
|
||||
|
||||
virtual void apply(osg::State& state) const
|
||||
{
|
||||
if( _width <= 0 || _height <= 0 )
|
||||
return;
|
||||
|
||||
const osg::Viewport* vp = state.getCurrentViewport();
|
||||
float w2 = 0.5 * vp->width(),
|
||||
h2 = 0.5 * vp->height();
|
||||
@@ -109,28 +73,17 @@ namespace canvas
|
||||
|
||||
if( _coord_reference != GLOBAL )
|
||||
{
|
||||
osg::Node* ref_obj = _node.get();
|
||||
model_view.preMult(state.getModelViewMatrix());
|
||||
|
||||
if( _coord_reference == PARENT )
|
||||
{
|
||||
if( _node->getNumParents() < 1 )
|
||||
{
|
||||
SG_LOG(SG_GL, SG_WARN, "RelativeScissor: missing parent.");
|
||||
return;
|
||||
}
|
||||
|
||||
ref_obj = _node->getParent(0);
|
||||
}
|
||||
|
||||
osg::MatrixList const& parent_matrices = ref_obj->getWorldMatrices();
|
||||
assert( !parent_matrices.empty() );
|
||||
model_view.preMult(parent_matrices.front());
|
||||
model_view.preMult(_parent_inverse);
|
||||
}
|
||||
|
||||
const osg::Vec2 scale( model_view(0,0), model_view(1,1)),
|
||||
offset(model_view(3,0), model_view(3,1));
|
||||
|
||||
// TODO check/warn for rotation?
|
||||
|
||||
GLint x = SGMiscf::roundToInt(scale.x() * _x + offset.x()),
|
||||
y = SGMiscf::roundToInt(scale.y() * _y + offset.y()),
|
||||
w = SGMiscf::roundToInt(std::fabs(scale.x()) * _width),
|
||||
@@ -143,26 +96,6 @@ namespace canvas
|
||||
|
||||
glScissor(x, y, w, h);
|
||||
}
|
||||
|
||||
bool contains(const osg::Vec2f& pos) const
|
||||
{
|
||||
return _x <= pos.x() && pos.x() <= _x + _width
|
||||
&& _y <= pos.y() && pos.y() <= _y + _height;
|
||||
}
|
||||
|
||||
bool contains( const osg::Vec2f& global_pos,
|
||||
const osg::Vec2f& parent_pos,
|
||||
const osg::Vec2f& local_pos ) const
|
||||
{
|
||||
switch( _coord_reference )
|
||||
{
|
||||
case GLOBAL: return contains(global_pos);
|
||||
case PARENT: return contains(parent_pos);
|
||||
case LOCAL: return contains(local_pos);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -190,6 +123,17 @@ namespace canvas
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setSelf(const PropertyBasedElementPtr& self)
|
||||
{
|
||||
PropertyBasedElement::setSelf(self);
|
||||
|
||||
_transform->setUserData
|
||||
(
|
||||
new OSGUserData(boost::static_pointer_cast<Element>(self))
|
||||
);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::onDestroy()
|
||||
{
|
||||
@@ -202,41 +146,43 @@ namespace canvas
|
||||
{
|
||||
parent->removeChild(_transform.get());
|
||||
}
|
||||
|
||||
// Hide in case someone still holds a reference
|
||||
setVisible(false);
|
||||
removeListener();
|
||||
|
||||
_parent = 0;
|
||||
_transform = 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
ElementPtr Element::getParent() const
|
||||
ElementWeakPtr Element::getWeakPtr() const
|
||||
{
|
||||
return _parent;
|
||||
return boost::static_pointer_cast<Element>(_self.lock());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasWeakPtr Element::getCanvas() const
|
||||
ElementPtr Element::getParent()
|
||||
{
|
||||
return _canvas;
|
||||
return _parent ? _parent->getWeakPtr().lock() : ElementPtr();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::update(double dt)
|
||||
{
|
||||
if( !isVisible() )
|
||||
if( !_transform->getNodeMask() )
|
||||
// Don't do anything if element is hidden
|
||||
return;
|
||||
|
||||
// Trigger matrix update
|
||||
getMatrix();
|
||||
|
||||
if( _attributes_dirty & SCISSOR_COORDS )
|
||||
{
|
||||
if( _scissor && _scissor->_coord_reference != GLOBAL )
|
||||
_scissor->_parent_inverse = _transform->getInverseMatrix();
|
||||
|
||||
_attributes_dirty &= ~SCISSOR_COORDS;
|
||||
}
|
||||
|
||||
// Update bounding box on manual update (manual updates pass zero dt)
|
||||
if( dt == 0 && _drawable )
|
||||
_drawable->getBound();
|
||||
|
||||
if( (_attributes_dirty & BLEND_FUNC) && _transform.valid() )
|
||||
if( _attributes_dirty & BLEND_FUNC )
|
||||
{
|
||||
parseBlendFunc(
|
||||
_transform->getOrCreateStateSet(),
|
||||
@@ -262,7 +208,17 @@ namespace canvas
|
||||
"addEventListener(" << _node->getPath() << ", " << type_str << ")"
|
||||
);
|
||||
|
||||
_listener[ Event::getOrRegisterType(type_str) ].push_back(cb);
|
||||
Event::Type type = Event::strToType(type_str);
|
||||
if( type == Event::UNKNOWN )
|
||||
{
|
||||
SG_LOG( SG_GENERAL,
|
||||
SG_WARN,
|
||||
"addEventListener: Unknown event type " << type_str );
|
||||
return false;
|
||||
}
|
||||
|
||||
_listener[ type ].push_back(cb);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -296,74 +252,30 @@ namespace canvas
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::handleEvent(const EventPtr& event)
|
||||
bool Element::handleEvent(canvas::EventPtr event)
|
||||
{
|
||||
ListenerMap::iterator listeners = _listener.find(event->getType());
|
||||
if( listeners == _listener.end() )
|
||||
return false;
|
||||
|
||||
BOOST_FOREACH(EventListener const& listener, listeners->second)
|
||||
try
|
||||
{
|
||||
listener(event);
|
||||
}
|
||||
catch( std::exception const& ex )
|
||||
{
|
||||
SG_LOG(
|
||||
SG_GENERAL,
|
||||
SG_WARN,
|
||||
"canvas::Element: event handler error: '" << ex.what() << "'"
|
||||
);
|
||||
}
|
||||
listener(event);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::dispatchEvent(const EventPtr& event)
|
||||
{
|
||||
EventPropagationPath path;
|
||||
path.push_back( EventTarget(this) );
|
||||
|
||||
for( Element* parent = _parent;
|
||||
parent != NULL;
|
||||
parent = parent->_parent )
|
||||
path.push_front( EventTarget(parent) );
|
||||
|
||||
CanvasPtr canvas = _canvas.lock();
|
||||
if( !canvas )
|
||||
return false;
|
||||
|
||||
return canvas->propagateEvent(event, path);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::hitBound( const osg::Vec2f& global_pos,
|
||||
const osg::Vec2f& parent_pos,
|
||||
bool Element::hitBound( const osg::Vec2f& pos,
|
||||
const osg::Vec2f& local_pos ) const
|
||||
{
|
||||
if( _scissor && !_scissor->contains(global_pos, parent_pos, local_pos) )
|
||||
return false;
|
||||
|
||||
const osg::Vec3f pos3(parent_pos, 0);
|
||||
const osg::Vec3f pos3(pos, 0);
|
||||
|
||||
// Drawables have a bounding box...
|
||||
if( _drawable )
|
||||
return _drawable->getBound().contains(osg::Vec3f(local_pos, 0));
|
||||
else if( _transform.valid() )
|
||||
// ... for other elements, i.e. groups only a bounding sphere is available
|
||||
return _transform->getBound().contains(osg::Vec3f(parent_pos, 0));
|
||||
// ... for other elements, i.e. groups only a bounding sphere is available
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setVisible(bool visible)
|
||||
{
|
||||
if( _transform.valid() )
|
||||
// TODO check if we need another nodemask
|
||||
_transform->setNodeMask(visible ? 0xffffffff : 0);
|
||||
return _transform->getBound().contains(osg::Vec3f(pos, 0));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -384,18 +296,6 @@ namespace canvas
|
||||
return _transform.get();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Vec2f Element::posToLocal(const osg::Vec2f& pos) const
|
||||
{
|
||||
getMatrix();
|
||||
const osg::Matrix& m = _transform->getInverseMatrix();
|
||||
return osg::Vec2f
|
||||
(
|
||||
m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0),
|
||||
m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1)
|
||||
);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
|
||||
{
|
||||
@@ -480,9 +380,7 @@ namespace canvas
|
||||
if( parent == _node )
|
||||
{
|
||||
const std::string& name = child->getNameString();
|
||||
if( boost::starts_with(name, "data-") )
|
||||
return;
|
||||
else if( StyleInfo const* style_info = getStyleInfo(name) )
|
||||
if( StyleInfo const* style_info = getStyleInfo(name) )
|
||||
{
|
||||
SGPropertyNode const* style = child;
|
||||
if( isStyleEmpty(child) )
|
||||
@@ -495,11 +393,13 @@ namespace canvas
|
||||
}
|
||||
else if( name == "update" )
|
||||
return update(0);
|
||||
else if( name == "visible" )
|
||||
// TODO check if we need another nodemask
|
||||
return _transform->setNodeMask( child->getBoolValue() ? 0xffffffff : 0 );
|
||||
else if( boost::starts_with(name, "blend-") )
|
||||
return (void)(_attributes_dirty |= BLEND_FUNC);
|
||||
}
|
||||
else if( parent
|
||||
&& parent->getParent() == _node
|
||||
else if( parent->getParent() == _node
|
||||
&& parent->getNameString() == NAME_TRANSFORM )
|
||||
{
|
||||
_attributes_dirty |= TRANSFORM;
|
||||
@@ -519,13 +419,9 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setClip(const std::string& clip)
|
||||
{
|
||||
osg::StateSet* ss = getOrCreateStateSet();
|
||||
if( !ss )
|
||||
return;
|
||||
|
||||
if( clip.empty() || clip == "auto" )
|
||||
{
|
||||
ss->removeAttribute(osg::StateAttribute::SCISSOR);
|
||||
getOrCreateStateSet()->removeAttribute(osg::StateAttribute::SCISSOR);
|
||||
_scissor = 0;
|
||||
return;
|
||||
}
|
||||
@@ -572,49 +468,47 @@ namespace canvas
|
||||
return;
|
||||
}
|
||||
|
||||
if( !_scissor )
|
||||
_scissor = new RelativeScissor(_transform.get());
|
||||
|
||||
_scissor = new RelativeScissor();
|
||||
// <top>, <right>, <bottom>, <left>
|
||||
_scissor->x() = SGMiscf::roundToInt(values[3]);
|
||||
_scissor->y() = SGMiscf::roundToInt(values[0]);
|
||||
_scissor->width() = SGMiscf::roundToInt(width);
|
||||
_scissor->height() = SGMiscf::roundToInt(height);
|
||||
|
||||
getOrCreateStateSet()->setAttributeAndModes(_scissor);
|
||||
|
||||
SGPropertyNode* clip_frame = _node->getChild("clip-frame", 0);
|
||||
if( clip_frame )
|
||||
valueChanged(clip_frame);
|
||||
else
|
||||
_scissor->_coord_reference = GLOBAL;
|
||||
|
||||
ss->setAttributeAndModes(_scissor);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::setClipFrame(ReferenceFrame rf)
|
||||
{
|
||||
if( _scissor )
|
||||
{
|
||||
_scissor->_coord_reference = rf;
|
||||
_attributes_dirty |= SCISSOR_COORDS;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::BoundingBox Element::getBoundingBox() const
|
||||
void Element::setBoundingBox(const osg::BoundingBox& bb)
|
||||
{
|
||||
if( _drawable )
|
||||
return _drawable->getBound();
|
||||
if( _bounding_box.empty() )
|
||||
{
|
||||
SGPropertyNode* bb_node = _node->getChild("bounding-box", 0, true);
|
||||
_bounding_box.resize(4);
|
||||
_bounding_box[0] = bb_node->getChild("min-x", 0, true);
|
||||
_bounding_box[1] = bb_node->getChild("min-y", 0, true);
|
||||
_bounding_box[2] = bb_node->getChild("max-x", 0, true);
|
||||
_bounding_box[3] = bb_node->getChild("max-y", 0, true);
|
||||
}
|
||||
|
||||
osg::BoundingBox bb;
|
||||
|
||||
if( _transform.valid() )
|
||||
bb.expandBy(_transform->getBound());
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::BoundingBox Element::getTightBoundingBox() const
|
||||
{
|
||||
return getTransformedBounds(getMatrix());
|
||||
_bounding_box[0]->setFloatValue(bb._min.x());
|
||||
_bounding_box[1]->setFloatValue(bb._min.y());
|
||||
_bounding_box[2]->setFloatValue(bb._max.x());
|
||||
_bounding_box[3]->setFloatValue(bb._max.y());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -634,9 +528,6 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Matrix Element::getMatrix() const
|
||||
{
|
||||
if( !_transform )
|
||||
return osg::Matrix::identity();
|
||||
|
||||
if( !(_attributes_dirty & TRANSFORM) )
|
||||
return _transform->getMatrix();
|
||||
|
||||
@@ -696,6 +587,7 @@ namespace canvas
|
||||
}
|
||||
_transform->setMatrix(m);
|
||||
_attributes_dirty &= ~TRANSFORM;
|
||||
_attributes_dirty |= SCISSOR_COORDS;
|
||||
|
||||
return m;
|
||||
}
|
||||
@@ -734,8 +626,6 @@ namespace canvas
|
||||
"PreOrderBin",
|
||||
osg::StateSet::OVERRIDE_RENDERBIN_DETAILS
|
||||
);
|
||||
|
||||
_transform->setUserData( new OSGUserData(this) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -746,7 +636,6 @@ namespace canvas
|
||||
|
||||
addStyle("clip", "", &Element::setClip, false);
|
||||
addStyle("clip-frame", "", &Element::setClipFrame, false);
|
||||
addStyle("visible", "", &Element::setVisible, false);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -832,12 +721,8 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
osg::StateSet* Element::getOrCreateStateSet()
|
||||
{
|
||||
if( _drawable.valid() )
|
||||
return _drawable->getOrCreateStateSet();
|
||||
if( _transform.valid() )
|
||||
return _transform->getOrCreateStateSet();
|
||||
|
||||
return 0;
|
||||
return _drawable ? _drawable->getOrCreateStateSet()
|
||||
: _transform->getOrCreateStateSet();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
///@file Interface for 2D Canvas elements
|
||||
// Interface for 2D Canvas elements
|
||||
//
|
||||
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
@@ -41,9 +41,6 @@ namespace simgear
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
/**
|
||||
* Baseclass for Elements displayed inside a Canvas.
|
||||
*/
|
||||
class Element:
|
||||
public PropertyBasedElement
|
||||
{
|
||||
@@ -72,10 +69,10 @@ namespace canvas
|
||||
};
|
||||
struct StyleInfo
|
||||
{
|
||||
StyleSetter setter; ///< Function(s) for setting this style
|
||||
std::string type; ///< Interpolation type
|
||||
bool inheritable; ///< Whether children can inherit this style from
|
||||
/// their parents
|
||||
StyleSetter setter; ///!< Function(s) for setting this style
|
||||
std::string type; ///!< Interpolation type
|
||||
bool inheritable; ///!< Whether children can inherit this style from
|
||||
/// their parents
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -83,20 +80,22 @@ namespace canvas
|
||||
*/
|
||||
enum ReferenceFrame
|
||||
{
|
||||
GLOBAL, ///< Global coordinates
|
||||
PARENT, ///< Coordinates relative to parent coordinate frame
|
||||
LOCAL ///< Coordinates relative to local coordinates (parent
|
||||
/// coordinates with local transformations applied)
|
||||
GLOBAL, ///!< Global coordinates
|
||||
PARENT, ///!< Coordinates relative to parent coordinate frame
|
||||
LOCAL ///!< Coordinates relative to local coordinates (parent
|
||||
/// coordinates with local transformations applied)
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
virtual ~Element() = 0;
|
||||
|
||||
virtual void setSelf(const PropertyBasedElementPtr& self);
|
||||
virtual void onDestroy();
|
||||
|
||||
ElementPtr getParent() const;
|
||||
CanvasWeakPtr getCanvas() const;
|
||||
ElementWeakPtr getWeakPtr() const;
|
||||
ElementPtr getParent();
|
||||
|
||||
/**
|
||||
* Called every frame to update internal state
|
||||
@@ -112,37 +111,20 @@ namespace canvas
|
||||
virtual bool ascend(EventVisitor& visitor);
|
||||
virtual bool traverse(EventVisitor& visitor);
|
||||
|
||||
virtual bool handleEvent(const EventPtr& event);
|
||||
bool dispatchEvent(const EventPtr& event);
|
||||
virtual bool handleEvent(canvas::EventPtr event);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param global_pos Position in global (canvas) coordinate frame
|
||||
* @param parent_pos Position in parent coordinate frame
|
||||
* @param local_pos Position in local (element) coordinate frame
|
||||
*/
|
||||
virtual bool hitBound( const osg::Vec2f& global_pos,
|
||||
const osg::Vec2f& parent_pos,
|
||||
virtual bool hitBound( const osg::Vec2f& pos,
|
||||
const osg::Vec2f& local_pos ) const;
|
||||
|
||||
/**
|
||||
* Set visibility of the element.
|
||||
*/
|
||||
void setVisible(bool visible);
|
||||
|
||||
/**
|
||||
* Get whether the element is visible or hidden.
|
||||
* Get whether the element is visible or hidden (Can be changed with
|
||||
* setting property "visible" accordingly).
|
||||
*/
|
||||
bool isVisible() const;
|
||||
|
||||
osg::MatrixTransform* getMatrixTransform();
|
||||
osg::MatrixTransform const* getMatrixTransform() const;
|
||||
|
||||
/**
|
||||
* Transform position to local coordinages.
|
||||
*/
|
||||
osg::Vec2f posToLocal(const osg::Vec2f& pos) const;
|
||||
|
||||
virtual void childAdded( SGPropertyNode * parent,
|
||||
SGPropertyNode * child );
|
||||
virtual void childRemoved( SGPropertyNode * parent,
|
||||
@@ -166,16 +148,9 @@ namespace canvas
|
||||
void setClipFrame(ReferenceFrame rf);
|
||||
|
||||
/**
|
||||
* Get bounding box (may not be as tight as bounding box returned by
|
||||
* #getTightBoundingBox)
|
||||
* Write the given bounding box to the property tree
|
||||
*/
|
||||
osg::BoundingBox getBoundingBox() const;
|
||||
|
||||
/**
|
||||
* Get tight bounding box (child points are transformed to elements
|
||||
* coordinate space before calculating the bounding box).
|
||||
*/
|
||||
osg::BoundingBox getTightBoundingBox() const;
|
||||
void setBoundingBox(const osg::BoundingBox& bb);
|
||||
|
||||
/**
|
||||
* Get bounding box with children/drawables transformed by passed matrix
|
||||
@@ -199,10 +174,12 @@ namespace canvas
|
||||
ElementPtr
|
||||
>::type create( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& style = Style(),
|
||||
Element* parent = NULL )
|
||||
const Style& style,
|
||||
Element* parent )
|
||||
{
|
||||
return ElementPtr( new Derived(canvas, node, style, parent) );
|
||||
ElementPtr el( new Derived(canvas, node, style, parent) );
|
||||
el->setSelf(el);
|
||||
return el;
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -211,7 +188,8 @@ namespace canvas
|
||||
{
|
||||
TRANSFORM = 1,
|
||||
BLEND_FUNC = TRANSFORM << 1,
|
||||
LAST_ATTRIBUTE = BLEND_FUNC << 1
|
||||
SCISSOR_COORDS = BLEND_FUNC << 1,
|
||||
LAST_ATTRIBUTE = SCISSOR_COORDS << 1
|
||||
};
|
||||
|
||||
enum TransformType
|
||||
@@ -233,16 +211,17 @@ namespace canvas
|
||||
osg::observer_ptr<osg::MatrixTransform> _transform;
|
||||
std::vector<TransformType> _transform_types;
|
||||
|
||||
Style _style;
|
||||
RelativeScissor *_scissor;
|
||||
Style _style;
|
||||
std::vector<SGPropertyNode_ptr> _bounding_box;
|
||||
RelativeScissor *_scissor;
|
||||
|
||||
typedef std::vector<EventListener> Listener;
|
||||
typedef std::map<int, Listener> ListenerMap;
|
||||
typedef std::map<Event::Type, Listener> ListenerMap;
|
||||
|
||||
ListenerMap _listener;
|
||||
|
||||
typedef std::map<std::string, StyleInfo> StyleSetters;
|
||||
static StyleSetters _style_setters;
|
||||
static StyleSetters _style_setters;
|
||||
|
||||
static void staticInit();
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "CanvasPath.hxx"
|
||||
#include "CanvasText.hxx"
|
||||
#include <simgear/canvas/CanvasEventVisitor.hxx>
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/canvas/MouseEvent.hxx>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
@@ -146,7 +146,7 @@ namespace canvas
|
||||
if( el->get<std::string>("id") == id )
|
||||
return el;
|
||||
|
||||
Group* group = dynamic_cast<Group*>(el.get());
|
||||
GroupPtr group = boost::dynamic_pointer_cast<Group>(el);
|
||||
if( group )
|
||||
groups.push_back(group);
|
||||
}
|
||||
@@ -320,12 +320,10 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Group::childChanged(SGPropertyNode* node)
|
||||
{
|
||||
SGPropertyNode* parent = node->getParent();
|
||||
SGPropertyNode* grand_parent = parent ? parent->getParent() : NULL;
|
||||
|
||||
if( grand_parent == _node
|
||||
if( node->getParent()->getParent() == _node
|
||||
&& node->getNameString() == "z-index" )
|
||||
return handleZIndexChanged(getChild(parent), node->getIntValue());
|
||||
return handleZIndexChanged( getChild(node->getParent()),
|
||||
node->getIntValue() );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
@@ -56,27 +56,28 @@ namespace canvas
|
||||
const std::string& id );
|
||||
|
||||
template<class T>
|
||||
SGSharedPtr<T> createChild(const std::string& id = "")
|
||||
boost::shared_ptr<T> createChild(const std::string& id = "")
|
||||
{
|
||||
return dynamic_cast<T*>( createChild(T::TYPE_NAME, id).get() );
|
||||
return boost::dynamic_pointer_cast<T>( createChild(T::TYPE_NAME, id) );
|
||||
}
|
||||
|
||||
template<class T>
|
||||
SGSharedPtr<T> getChild(const SGPropertyNode* node)
|
||||
boost::shared_ptr<T> getChild(const SGPropertyNode* node)
|
||||
{
|
||||
return dynamic_cast<T*>( getChild(node).get() );
|
||||
return boost::dynamic_pointer_cast<T>( getChild(node) );
|
||||
}
|
||||
|
||||
template<class T>
|
||||
SGSharedPtr<T> getChild(const std::string& id)
|
||||
boost::shared_ptr<T> getChild(const std::string& id)
|
||||
{
|
||||
return dynamic_cast<T*>( getChild(id).get() );
|
||||
return boost::dynamic_pointer_cast<T>( getChild(id) );
|
||||
}
|
||||
|
||||
template<class T>
|
||||
SGSharedPtr<T> getOrCreateChild(const std::string& id)
|
||||
boost::shared_ptr<T> getOrCreateChild(const std::string& id)
|
||||
{
|
||||
return dynamic_cast<T*>( getOrCreateChild(T::TYPE_NAME, id).get() );
|
||||
return
|
||||
boost::dynamic_pointer_cast<T>( getOrCreateChild(T::TYPE_NAME, id) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/canvas/CanvasMgr.hxx>
|
||||
#include <simgear/canvas/CanvasSystemAdapter.hxx>
|
||||
#include <simgear/canvas/events/MouseEvent.hxx>
|
||||
#include <simgear/canvas/MouseEvent.hxx>
|
||||
#include <simgear/scene/util/OsgMath.hxx>
|
||||
#include <simgear/scene/util/parse_color.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
@@ -29,7 +29,8 @@
|
||||
#include <osg/Array>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/PrimitiveSet>
|
||||
#include <osgDB/Registry>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
@@ -74,9 +75,8 @@ namespace canvas
|
||||
osg::Drawable* drawable,
|
||||
osg::RenderInfo* renderInfo ) const
|
||||
{
|
||||
CanvasPtr canvas = _canvas.lock();
|
||||
if( canvas )
|
||||
canvas->enableRendering();
|
||||
if( !_canvas.expired() )
|
||||
_canvas.lock()->enableRendering();
|
||||
|
||||
if( !_cull_next_frame )
|
||||
// TODO check if window/image should be culled
|
||||
@@ -96,10 +96,9 @@ namespace canvas
|
||||
return;
|
||||
|
||||
addStyle("fill", "color", &Image::setFill);
|
||||
addStyle("outset", "", &Image::setOutset);
|
||||
addStyle("preserveAspectRatio", "", &Image::setPreserveAspectRatio);
|
||||
addStyle("slice", "", &Image::setSlice);
|
||||
addStyle("slice-width", "", &Image::setSliceWidth);
|
||||
addStyle("outset", "", &Image::setOutset);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -129,11 +128,12 @@ namespace canvas
|
||||
|
||||
_texCoords = new osg::Vec2Array(4);
|
||||
_texCoords->setDataVariance(osg::Object::DYNAMIC);
|
||||
_geom->setTexCoordArray(0, _texCoords, osg::Array::BIND_PER_VERTEX);
|
||||
_geom->setTexCoordArray(0, _texCoords);
|
||||
|
||||
_colors = new osg::Vec4Array(1);
|
||||
_colors->setDataVariance(osg::Object::DYNAMIC);
|
||||
_geom->setColorArray(_colors, osg::Array::BIND_OVERALL);
|
||||
_geom->setColorArray(_colors);
|
||||
_geom->setColorBinding(osg::Geometry::BIND_OVERALL);
|
||||
|
||||
_prim = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
|
||||
_prim->set(osg::PrimitiveSet::QUADS, 0, 4);
|
||||
@@ -149,8 +149,7 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
Image::~Image()
|
||||
{
|
||||
if( _http_request )
|
||||
_http_request->abort("image destroyed");
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -215,10 +214,6 @@ namespace canvas
|
||||
if( !_slice.isValid() )
|
||||
{
|
||||
setQuad(0, region.getMin(), region.getMax());
|
||||
|
||||
if( !_preserve_aspect_ratio.scaleToFill() )
|
||||
// We need to update texture coordinates to keep the aspect ratio
|
||||
_attributes_dirty |= SRC_RECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -277,6 +272,7 @@ namespace canvas
|
||||
_vertices->dirty();
|
||||
_attributes_dirty &= ~DEST_SIZE;
|
||||
_geom->dirtyBound();
|
||||
setBoundingBox(_geom->getBound());
|
||||
}
|
||||
|
||||
if( _attributes_dirty & SRC_RECT )
|
||||
@@ -295,66 +291,6 @@ namespace canvas
|
||||
|
||||
if( !_slice.isValid() )
|
||||
{
|
||||
// Image scaling preserving aspect ratio. Change texture coordinates to
|
||||
// scale image accordingly.
|
||||
//
|
||||
// TODO allow to specify what happens to not filled space (eg. color,
|
||||
// or texture repeat/mirror)
|
||||
//
|
||||
// http://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
|
||||
if( !_preserve_aspect_ratio.scaleToFill() )
|
||||
{
|
||||
osg::BoundingBox const& bb = getBoundingBox();
|
||||
float dst_width = bb._max.x() - bb._min.x(),
|
||||
dst_height = bb._max.y() - bb._min.y();
|
||||
float scale_x = dst_width / tex_dim.width(),
|
||||
scale_y = dst_height / tex_dim.height();
|
||||
|
||||
float scale = _preserve_aspect_ratio.scaleToFit()
|
||||
? std::min(scale_x, scale_y)
|
||||
: std::max(scale_x, scale_y);
|
||||
|
||||
if( scale_x != scale )
|
||||
{
|
||||
float d = scale_x / scale - 1;
|
||||
if( _preserve_aspect_ratio.alignX()
|
||||
== SVGpreserveAspectRatio::ALIGN_MIN )
|
||||
{
|
||||
src_rect.r() += d;
|
||||
}
|
||||
else if( _preserve_aspect_ratio.alignX()
|
||||
== SVGpreserveAspectRatio::ALIGN_MAX )
|
||||
{
|
||||
src_rect.l() -= d;
|
||||
}
|
||||
else
|
||||
{
|
||||
src_rect.l() -= d / 2;
|
||||
src_rect.r() += d / 2;
|
||||
}
|
||||
}
|
||||
|
||||
if( scale_y != scale )
|
||||
{
|
||||
float d = scale_y / scale - 1;
|
||||
if( _preserve_aspect_ratio.alignY()
|
||||
== SVGpreserveAspectRatio::ALIGN_MIN )
|
||||
{
|
||||
src_rect.b() -= d;
|
||||
}
|
||||
else if( _preserve_aspect_ratio.alignY()
|
||||
== SVGpreserveAspectRatio::ALIGN_MAX )
|
||||
{
|
||||
src_rect.t() += d;
|
||||
}
|
||||
else
|
||||
{
|
||||
src_rect.t() += d / 2;
|
||||
src_rect.b() -= d / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setQuadUV(0, src_rect.getMin(), src_rect.getMax());
|
||||
}
|
||||
else
|
||||
@@ -478,20 +414,6 @@ namespace canvas
|
||||
_colors->dirty();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setOutset(const std::string& outset)
|
||||
{
|
||||
_outset = CSSBorder::parse(outset);
|
||||
_attributes_dirty |= DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setPreserveAspectRatio(const std::string& scale)
|
||||
{
|
||||
_preserve_aspect_ratio = SVGpreserveAspectRatio::parse(scale);
|
||||
_attributes_dirty |= SRC_RECT;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setSlice(const std::string& slice)
|
||||
{
|
||||
@@ -506,6 +428,13 @@ namespace canvas
|
||||
_attributes_dirty |= DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setOutset(const std::string& outset)
|
||||
{
|
||||
_outset = CSSBorder::parse(outset);
|
||||
_attributes_dirty |= DEST_SIZE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const SGRect<float>& Image::getRegion() const
|
||||
{
|
||||
@@ -513,7 +442,7 @@ namespace canvas
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Image::handleEvent(const EventPtr& event)
|
||||
bool Image::handleEvent(EventPtr event)
|
||||
{
|
||||
bool handled = Element::handleEvent(event);
|
||||
|
||||
@@ -521,10 +450,11 @@ namespace canvas
|
||||
if( !src_canvas )
|
||||
return handled;
|
||||
|
||||
MouseEventPtr mouse_event = dynamic_cast<MouseEvent*>(event.get());
|
||||
MouseEventPtr mouse_event = boost::dynamic_pointer_cast<MouseEvent>(event);
|
||||
if( mouse_event )
|
||||
{
|
||||
mouse_event.reset( new MouseEvent(*mouse_event) );
|
||||
event = mouse_event;
|
||||
|
||||
mouse_event->client_pos = mouse_event->local_pos
|
||||
- toOsg(_region.getMin());
|
||||
@@ -544,11 +474,9 @@ namespace canvas
|
||||
mouse_event->client_pos.x() *= src_canvas->getViewWidth() / size.x();
|
||||
mouse_event->client_pos.y() *= src_canvas->getViewHeight()/ size.y();
|
||||
mouse_event->local_pos = mouse_event->client_pos;
|
||||
|
||||
handled |= src_canvas->handleMouseEvent(mouse_event);
|
||||
}
|
||||
|
||||
return handled;
|
||||
return handled | src_canvas->handleMouseEvent(mouse_event);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -593,41 +521,20 @@ namespace canvas
|
||||
|
||||
_attributes_dirty |= DEST_SIZE;
|
||||
}
|
||||
else if( name == "src" || name == "file" )
|
||||
else if( name == "file" )
|
||||
{
|
||||
if( name == "file" )
|
||||
SG_LOG(SG_GL, SG_WARN, "'file' is deprecated. Use 'src' instead");
|
||||
static const std::string CANVAS_PROTOCOL = "canvas://";
|
||||
const std::string& path = child->getStringValue();
|
||||
|
||||
// Abort pending request
|
||||
if( _http_request )
|
||||
CanvasPtr canvas = _canvas.lock();
|
||||
if( !canvas )
|
||||
{
|
||||
_http_request->abort("setting new image");
|
||||
_http_request.reset();
|
||||
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No canvas available");
|
||||
return;
|
||||
}
|
||||
|
||||
static const std::string PROTOCOL_SEP = "://";
|
||||
|
||||
std::string url = child->getStringValue(),
|
||||
protocol, path;
|
||||
|
||||
size_t sep_pos = url.find(PROTOCOL_SEP);
|
||||
if( sep_pos != std::string::npos )
|
||||
if( boost::starts_with(path, CANVAS_PROTOCOL) )
|
||||
{
|
||||
protocol = url.substr(0, sep_pos);
|
||||
path = url.substr(sep_pos + PROTOCOL_SEP.length());
|
||||
}
|
||||
else
|
||||
path = url;
|
||||
|
||||
if( protocol == "canvas" )
|
||||
{
|
||||
CanvasPtr canvas = _canvas.lock();
|
||||
if( !canvas )
|
||||
{
|
||||
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No canvas available");
|
||||
return;
|
||||
}
|
||||
|
||||
CanvasMgr* canvas_mgr = canvas->getCanvasMgr();
|
||||
if( !canvas_mgr )
|
||||
{
|
||||
@@ -638,7 +545,7 @@ namespace canvas
|
||||
const SGPropertyNode* canvas_node =
|
||||
canvas_mgr->getPropertyRoot()
|
||||
->getParent()
|
||||
->getNode( path );
|
||||
->getNode( path.substr(CANVAS_PROTOCOL.size()) );
|
||||
if( !canvas_node )
|
||||
{
|
||||
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No such canvas: " << path);
|
||||
@@ -656,16 +563,6 @@ namespace canvas
|
||||
|
||||
setSrcCanvas(src_canvas);
|
||||
}
|
||||
else if( protocol == "http" || protocol == "https" )
|
||||
// TODO check https
|
||||
{
|
||||
_http_request =
|
||||
Canvas::getSystemAdapter()
|
||||
->getHTTPClient()
|
||||
->load(url)
|
||||
// TODO handle capture of 'this'
|
||||
->done(this, &Image::handleImageLoadDone);
|
||||
}
|
||||
else
|
||||
{
|
||||
setImage( Canvas::getSystemAdapter()->getImage(path) );
|
||||
@@ -741,65 +638,5 @@ namespace canvas
|
||||
(*_texCoords)[i + 3].set(tl.x(), br.y());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::handleImageLoadDone(HTTP::Request* req)
|
||||
{
|
||||
// Ignore stale/expired requests
|
||||
if( _http_request != req )
|
||||
return;
|
||||
_http_request.reset();
|
||||
|
||||
if( req->responseCode() != 200 )
|
||||
{
|
||||
SG_LOG(SG_IO, SG_WARN, "failed to download '" << req->url() << "': "
|
||||
<< req->responseReason());
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string ext = SGPath(req->path()).extension(),
|
||||
mime = req->responseMime();
|
||||
|
||||
SG_LOG(SG_IO, SG_INFO, "received " << req->url() <<
|
||||
" (ext=" << ext << ", MIME=" << mime << ")");
|
||||
|
||||
const std::string& img_data =
|
||||
static_cast<HTTP::MemoryRequest*>(req)->responseBody();
|
||||
osgDB::Registry* reg = osgDB::Registry::instance();
|
||||
|
||||
// First try to detect image type by extension
|
||||
osgDB::ReaderWriter* rw = reg->getReaderWriterForExtension(ext);
|
||||
if( rw && loadImage(*rw, img_data, *req, "extension") )
|
||||
return;
|
||||
|
||||
// Now try with MIME type
|
||||
rw = reg->getReaderWriterForMimeType(mime);
|
||||
if( rw && loadImage(*rw, img_data, *req, "MIME type") )
|
||||
return;
|
||||
|
||||
SG_LOG(SG_IO, SG_WARN, "unable to read image '" << req->url() << "'");
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool Image::loadImage( osgDB::ReaderWriter& reader,
|
||||
const std::string& data,
|
||||
HTTP::Request& request,
|
||||
const std::string& type )
|
||||
{
|
||||
SG_LOG(SG_IO, SG_DEBUG, "use image reader detected by " << type);
|
||||
|
||||
std::istringstream data_strm(data);
|
||||
osgDB::ReaderWriter::ReadResult result = reader.readImage(data_strm);
|
||||
if( result.success() )
|
||||
{
|
||||
setImage( result.takeImage() );
|
||||
return true;
|
||||
}
|
||||
|
||||
SG_LOG(SG_IO, SG_WARN, "failed to read image '" << request.url() << "': "
|
||||
<< result.message());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
@@ -22,14 +22,11 @@
|
||||
#include "CanvasElement.hxx"
|
||||
|
||||
#include <simgear/canvas/canvas_fwd.hxx>
|
||||
#include <simgear/io/HTTPClient.hxx>
|
||||
#include <simgear/misc/CSSBorder.hxx>
|
||||
#include <simgear/misc/SVGpreserveAspectRatio.hxx>
|
||||
#include <osg/Texture2D>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace HTTP { class Request; }
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
@@ -62,17 +59,6 @@ namespace canvas
|
||||
void setImage(osg::Image *img);
|
||||
void setFill(const std::string& fill);
|
||||
|
||||
/**
|
||||
* @see http://www.w3.org/TR/css3-background/#border-image-outset
|
||||
*/
|
||||
void setOutset(const std::string& outset);
|
||||
|
||||
/**
|
||||
* @see
|
||||
* http://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
|
||||
*/
|
||||
void setPreserveAspectRatio(const std::string& scale);
|
||||
|
||||
/**
|
||||
* Set image slice (aka. 9-scale)
|
||||
*
|
||||
@@ -91,9 +77,14 @@ namespace canvas
|
||||
*/
|
||||
void setSliceWidth(const std::string& width);
|
||||
|
||||
/**
|
||||
* http://www.w3.org/TR/css3-background/#border-image-outset
|
||||
*/
|
||||
void setOutset(const std::string& outset);
|
||||
|
||||
const SGRect<float>& getRegion() const;
|
||||
|
||||
bool handleEvent(const EventPtr& event);
|
||||
bool handleEvent(EventPtr event);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -112,16 +103,9 @@ namespace canvas
|
||||
void setQuad(size_t index, const SGVec2f& tl, const SGVec2f& br);
|
||||
void setQuadUV(size_t index, const SGVec2f& tl, const SGVec2f& br);
|
||||
|
||||
void handleImageLoadDone(HTTP::Request*);
|
||||
bool loadImage( osgDB::ReaderWriter& reader,
|
||||
const std::string& data,
|
||||
HTTP::Request& request,
|
||||
const std::string& type );
|
||||
|
||||
osg::ref_ptr<osg::Texture2D> _texture;
|
||||
// TODO optionally forward events to canvas
|
||||
CanvasWeakPtr _src_canvas;
|
||||
HTTP::Request_ptr _http_request;
|
||||
|
||||
osg::ref_ptr<osg::Geometry> _geom;
|
||||
osg::ref_ptr<osg::DrawArrays>_prim;
|
||||
@@ -133,11 +117,9 @@ namespace canvas
|
||||
SGRect<float> _src_rect,
|
||||
_region;
|
||||
|
||||
SVGpreserveAspectRatio _preserve_aspect_ratio;
|
||||
|
||||
CSSBorder _outset,
|
||||
_slice,
|
||||
_slice_width;
|
||||
CSSBorder _slice,
|
||||
_slice_width,
|
||||
_outset;
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
|
||||
@@ -217,10 +217,9 @@ namespace canvas
|
||||
state->setClientActiveTextureUnit(0);
|
||||
state->disableAllVertexArrays();
|
||||
|
||||
bool was_blend_enabled = state->getLastAppliedMode(GL_BLEND);
|
||||
bool was_stencil_enabled = state->getLastAppliedMode(GL_STENCIL_TEST);
|
||||
osg::StateAttribute const* blend_func =
|
||||
state->getLastAppliedAttribute(osg::StateAttribute::BLENDFUNC);
|
||||
glPushAttrib(~0u); // Don't use GL_ALL_ATTRIB_BITS as on my machine it
|
||||
// eg. doesn't include GL_MULTISAMPLE_BIT
|
||||
glPushClientAttrib(~0u);
|
||||
|
||||
// Initialize/Update the paint
|
||||
if( _attributes_dirty & STROKE_COLOR )
|
||||
@@ -270,11 +269,8 @@ namespace canvas
|
||||
if( err != VG_NO_ERROR )
|
||||
SG_LOG(SG_GL, SG_ALERT, "vgError: " << err);
|
||||
|
||||
// Restore OpenGL state (TODO check if more is needed or integrate
|
||||
// better with OpenSceneGraph)
|
||||
if( was_blend_enabled ) glEnable(GL_BLEND);
|
||||
if( was_stencil_enabled ) glEnable(GL_STENCIL_TEST);
|
||||
if( blend_func ) blend_func->apply(*state);
|
||||
glPopAttrib();
|
||||
glPopClientAttrib();
|
||||
}
|
||||
|
||||
osg::BoundingBox getTransformedBounds(const osg::Matrix& mat) const
|
||||
@@ -385,11 +381,14 @@ namespace canvas
|
||||
// vgPathBounds doesn't take stroke width into account
|
||||
float ext = 0.5 * _stroke_width;
|
||||
|
||||
return osg::BoundingBox
|
||||
osg::BoundingBox bb
|
||||
(
|
||||
min[0] - ext, min[1] - ext, -0.1,
|
||||
min[0] + size[0] + ext, min[1] + size[1] + ext, 0.1
|
||||
);
|
||||
_path_element->setBoundingBox(bb);
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -464,12 +463,7 @@ namespace canvas
|
||||
}
|
||||
|
||||
if( _attributes_dirty & BOUNDING_BOX )
|
||||
{
|
||||
dirtyBound();
|
||||
|
||||
// Recalculate bounding box now (prevent race condition)
|
||||
getBound();
|
||||
}
|
||||
}
|
||||
|
||||
struct PathUpdateCallback:
|
||||
|
||||
@@ -40,7 +40,6 @@ namespace canvas
|
||||
void setFill(const std::string& fill);
|
||||
void setBackgroundColor(const std::string& fill);
|
||||
|
||||
SGVec2i sizeForWidth(int w) const;
|
||||
osg::Vec2 handleHit(const osg::Vec2f& pos);
|
||||
|
||||
virtual osg::BoundingBox computeBound() const;
|
||||
@@ -96,281 +95,6 @@ namespace canvas
|
||||
setBoundingBoxColor( color );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// simplified version of osgText::Text::computeGlyphRepresentation() to
|
||||
// just calculate the size for a given weight. Glpyh calculations/creating
|
||||
// is not necessary for this...
|
||||
SGVec2i Text::TextOSG::sizeForWidth(int w) const
|
||||
{
|
||||
if( _text.empty() )
|
||||
return SGVec2i(0, 0);
|
||||
|
||||
osgText::Font* activefont = const_cast<osgText::Font*>(getActiveFont());
|
||||
if( !activefont )
|
||||
return SGVec2i(-1, -1);
|
||||
|
||||
float max_width_safe = _maximumWidth;
|
||||
const_cast<TextOSG*>(this)->_maximumWidth = w;
|
||||
|
||||
SGRecti bb;
|
||||
|
||||
osg::Vec2 startOfLine_coords(0.0f,0.0f);
|
||||
osg::Vec2 cursor(startOfLine_coords);
|
||||
osg::Vec2 local(0.0f,0.0f);
|
||||
|
||||
unsigned int previous_charcode = 0;
|
||||
unsigned int line_length = 0;
|
||||
bool horizontal = _layout != VERTICAL;
|
||||
bool kerning = true;
|
||||
|
||||
float hr = _characterHeight;
|
||||
float wr = hr / getCharacterAspectRatio();
|
||||
|
||||
// osg should really care more about const :-/
|
||||
osgText::String& text = const_cast<osgText::String&>(_text);
|
||||
typedef osgText::String::iterator TextIterator;
|
||||
|
||||
for( TextIterator itr = text.begin(); itr != text.end(); )
|
||||
{
|
||||
// record the start of the current line
|
||||
TextIterator startOfLine_itr = itr;
|
||||
|
||||
// find the end of the current line.
|
||||
osg::Vec2 endOfLine_coords(cursor);
|
||||
TextIterator endOfLine_itr =
|
||||
const_cast<TextOSG*>(this)->computeLastCharacterOnLine(
|
||||
endOfLine_coords, itr, text.end()
|
||||
);
|
||||
|
||||
line_length = endOfLine_itr - startOfLine_itr;
|
||||
|
||||
// Set line position to correct alignment.
|
||||
switch( _layout )
|
||||
{
|
||||
case LEFT_TO_RIGHT:
|
||||
{
|
||||
switch( _alignment )
|
||||
{
|
||||
// nothing to be done for these
|
||||
//case LEFT_TOP:
|
||||
//case LEFT_CENTER:
|
||||
//case LEFT_BOTTOM:
|
||||
//case LEFT_BASE_LINE:
|
||||
//case LEFT_BOTTOM_BASE_LINE:
|
||||
// break;
|
||||
case CENTER_TOP:
|
||||
case CENTER_CENTER:
|
||||
case CENTER_BOTTOM:
|
||||
case CENTER_BASE_LINE:
|
||||
case CENTER_BOTTOM_BASE_LINE:
|
||||
cursor.x() = (cursor.x() - endOfLine_coords.x()) * 0.5f;
|
||||
break;
|
||||
case RIGHT_TOP:
|
||||
case RIGHT_CENTER:
|
||||
case RIGHT_BOTTOM:
|
||||
case RIGHT_BASE_LINE:
|
||||
case RIGHT_BOTTOM_BASE_LINE:
|
||||
cursor.x() = cursor.x() - endOfLine_coords.x();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RIGHT_TO_LEFT:
|
||||
{
|
||||
switch( _alignment )
|
||||
{
|
||||
case LEFT_TOP:
|
||||
case LEFT_CENTER:
|
||||
case LEFT_BOTTOM:
|
||||
case LEFT_BASE_LINE:
|
||||
case LEFT_BOTTOM_BASE_LINE:
|
||||
cursor.x() = 2 * cursor.x() - endOfLine_coords.x();
|
||||
break;
|
||||
case CENTER_TOP:
|
||||
case CENTER_CENTER:
|
||||
case CENTER_BOTTOM:
|
||||
case CENTER_BASE_LINE:
|
||||
case CENTER_BOTTOM_BASE_LINE:
|
||||
cursor.x() = cursor.x()
|
||||
+ (cursor.x() - endOfLine_coords.x()) * 0.5f;
|
||||
break;
|
||||
// nothing to be done for these
|
||||
//case RIGHT_TOP:
|
||||
//case RIGHT_CENTER:
|
||||
//case RIGHT_BOTTOM:
|
||||
//case RIGHT_BASE_LINE:
|
||||
//case RIGHT_BOTTOM_BASE_LINE:
|
||||
// break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VERTICAL:
|
||||
{
|
||||
switch( _alignment )
|
||||
{
|
||||
// TODO: current behaviour top baselines lined up in both cases - need to implement
|
||||
// top of characters alignment - Question is this necessary?
|
||||
// ... otherwise, nothing to be done for these 6 cases
|
||||
//case LEFT_TOP:
|
||||
//case CENTER_TOP:
|
||||
//case RIGHT_TOP:
|
||||
// break;
|
||||
//case LEFT_BASE_LINE:
|
||||
//case CENTER_BASE_LINE:
|
||||
//case RIGHT_BASE_LINE:
|
||||
// break;
|
||||
case LEFT_CENTER:
|
||||
case CENTER_CENTER:
|
||||
case RIGHT_CENTER:
|
||||
cursor.y() = cursor.y()
|
||||
+ (cursor.y() - endOfLine_coords.y()) * 0.5f;
|
||||
break;
|
||||
case LEFT_BOTTOM_BASE_LINE:
|
||||
case CENTER_BOTTOM_BASE_LINE:
|
||||
case RIGHT_BOTTOM_BASE_LINE:
|
||||
cursor.y() = cursor.y() - (line_length * _characterHeight);
|
||||
break;
|
||||
case LEFT_BOTTOM:
|
||||
case CENTER_BOTTOM:
|
||||
case RIGHT_BOTTOM:
|
||||
cursor.y() = 2 * cursor.y() - endOfLine_coords.y();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( itr != endOfLine_itr )
|
||||
{
|
||||
|
||||
for(;itr != endOfLine_itr;++itr)
|
||||
{
|
||||
unsigned int charcode = *itr;
|
||||
|
||||
osgText::Glyph* glyph = activefont->getGlyph(_fontSize, charcode);
|
||||
if( glyph )
|
||||
{
|
||||
float width = (float) (glyph->getWidth()) * wr;
|
||||
float height = (float) (glyph->getHeight()) * hr;
|
||||
|
||||
if( _layout == RIGHT_TO_LEFT )
|
||||
{
|
||||
cursor.x() -= glyph->getHorizontalAdvance() * wr;
|
||||
}
|
||||
|
||||
// adjust cursor position w.r.t any kerning.
|
||||
if( kerning && previous_charcode )
|
||||
{
|
||||
switch( _layout )
|
||||
{
|
||||
case LEFT_TO_RIGHT:
|
||||
{
|
||||
osg::Vec2 delta( activefont->getKerning( previous_charcode,
|
||||
charcode,
|
||||
_kerningType ) );
|
||||
cursor.x() += delta.x() * wr;
|
||||
cursor.y() += delta.y() * hr;
|
||||
break;
|
||||
}
|
||||
case RIGHT_TO_LEFT:
|
||||
{
|
||||
osg::Vec2 delta( activefont->getKerning( charcode,
|
||||
previous_charcode,
|
||||
_kerningType ) );
|
||||
cursor.x() -= delta.x() * wr;
|
||||
cursor.y() -= delta.y() * hr;
|
||||
break;
|
||||
}
|
||||
case VERTICAL:
|
||||
break; // no kerning when vertical.
|
||||
}
|
||||
}
|
||||
|
||||
local = cursor;
|
||||
osg::Vec2 bearing( horizontal ? glyph->getHorizontalBearing()
|
||||
: glyph->getVerticalBearing() );
|
||||
local.x() += bearing.x() * wr;
|
||||
local.y() += bearing.y() * hr;
|
||||
|
||||
// set up the coords of the quad
|
||||
osg::Vec2 upLeft = local + osg::Vec2(0.f, height);
|
||||
osg::Vec2 lowLeft = local;
|
||||
osg::Vec2 lowRight = local + osg::Vec2(width, 0.f);
|
||||
osg::Vec2 upRight = local + osg::Vec2(width, height);
|
||||
|
||||
// move the cursor onto the next character.
|
||||
// also expand bounding box
|
||||
switch( _layout )
|
||||
{
|
||||
case LEFT_TO_RIGHT:
|
||||
cursor.x() += glyph->getHorizontalAdvance() * wr;
|
||||
bb.expandBy(lowLeft.x(), lowLeft.y());
|
||||
bb.expandBy(upRight.x(), upRight.y());
|
||||
break;
|
||||
case VERTICAL:
|
||||
cursor.y() -= glyph->getVerticalAdvance() * hr;
|
||||
bb.expandBy(upLeft.x(), upLeft.y());
|
||||
bb.expandBy(lowRight.x(), lowRight.y());
|
||||
break;
|
||||
case RIGHT_TO_LEFT:
|
||||
bb.expandBy(lowRight.x(), lowRight.y());
|
||||
bb.expandBy(upLeft.x(), upLeft.y());
|
||||
break;
|
||||
}
|
||||
previous_charcode = charcode;
|
||||
}
|
||||
}
|
||||
|
||||
// skip over spaces and return.
|
||||
while( itr != text.end() && *itr == ' ' )
|
||||
++itr;
|
||||
if( itr != text.end() && *itr == '\n' )
|
||||
++itr;
|
||||
}
|
||||
else
|
||||
{
|
||||
++itr;
|
||||
}
|
||||
|
||||
// move to new line.
|
||||
switch( _layout )
|
||||
{
|
||||
case LEFT_TO_RIGHT:
|
||||
{
|
||||
startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
|
||||
cursor = startOfLine_coords;
|
||||
previous_charcode = 0;
|
||||
break;
|
||||
}
|
||||
case RIGHT_TO_LEFT:
|
||||
{
|
||||
startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
|
||||
cursor = startOfLine_coords;
|
||||
previous_charcode = 0;
|
||||
break;
|
||||
}
|
||||
case VERTICAL:
|
||||
{
|
||||
startOfLine_coords.x() += _characterHeight * (1.0 + _lineSpacing)
|
||||
/ getCharacterAspectRatio();
|
||||
cursor = startOfLine_coords;
|
||||
previous_charcode = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const_cast<TextOSG*>(this)->_maximumWidth = max_width_safe;
|
||||
|
||||
return bb.size();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Vec2 Text::TextOSG::handleHit(const osg::Vec2f& pos)
|
||||
{
|
||||
@@ -455,17 +179,18 @@ namespace canvas
|
||||
osg::BoundingBox Text::TextOSG::computeBound() const
|
||||
{
|
||||
osg::BoundingBox bb = osgText::Text::computeBound();
|
||||
if( !bb.valid() )
|
||||
return bb;
|
||||
|
||||
#if OSG_VERSION_LESS_THAN(3,1,0)
|
||||
if( bb.valid() )
|
||||
{
|
||||
// TODO bounding box still doesn't seem always right (eg. with center
|
||||
// horizontal alignment not completely accurate)
|
||||
bb._min.y() += _offset.y();
|
||||
bb._max.y() += _offset.y();
|
||||
}
|
||||
// TODO bounding box still doesn't seem always right (eg. with center
|
||||
// horizontal alignment not completely accurate)
|
||||
bb._min.y() += _offset.y();
|
||||
bb._max.y() += _offset.y();
|
||||
#endif
|
||||
|
||||
_text_element->setBoundingBox(bb);
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
@@ -645,18 +370,6 @@ namespace canvas
|
||||
}
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Text::heightForWidth(int w) const
|
||||
{
|
||||
return _text->sizeForWidth(w).y();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Text::maxWidth() const
|
||||
{
|
||||
return _text->sizeForWidth(INT_MAX).x();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Vec2 Text::getNearestCursor(const osg::Vec2& pos) const
|
||||
{
|
||||
|
||||
@@ -46,8 +46,6 @@ namespace canvas
|
||||
void setFont(const char* name);
|
||||
void setAlignment(const char* align);
|
||||
|
||||
int heightForWidth(int w) const;
|
||||
int maxWidth() const;
|
||||
osg::Vec2 getNearestCursor(const osg::Vec2& pos) const;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/// Unit tests for canvas::Element
|
||||
#define BOOST_TEST_MODULE canvas
|
||||
#include <BoostTestTargetConfig.h>
|
||||
|
||||
#include "CanvasElement.hxx"
|
||||
#include "CanvasGroup.hxx"
|
||||
|
||||
namespace sc = simgear::canvas;
|
||||
|
||||
BOOST_AUTO_TEST_CASE( attr_data )
|
||||
{
|
||||
// http://www.w3.org/TR/html5/dom.html#attr-data-*
|
||||
|
||||
#define SG_CHECK_ATTR2PROP(attr, prop)\
|
||||
BOOST_CHECK_EQUAL(sc::Element::attrToDataPropName(attr), prop)
|
||||
|
||||
// If name starts with "data-", for each "-" (U+002D) character in the name
|
||||
// that is followed by a lowercase ASCII letter, remove the "-" (U+002D)
|
||||
// character and replace the character that followed it by the same character
|
||||
// converted to ASCII uppercase.
|
||||
|
||||
SG_CHECK_ATTR2PROP("no-data", "");
|
||||
SG_CHECK_ATTR2PROP("data-blub", "blub");
|
||||
SG_CHECK_ATTR2PROP("data-blub-x-y", "blubXY");
|
||||
SG_CHECK_ATTR2PROP("data-blub-x-y-", "blubXY-");
|
||||
|
||||
#undef SG_CHECK_ATTR2PROP
|
||||
|
||||
#define SG_CHECK_PROP2ATTR(prop, attr)\
|
||||
BOOST_CHECK_EQUAL(sc::Element::dataPropToAttrName(prop), attr)
|
||||
|
||||
// If name contains a "-" (U+002D) character followed by a lowercase ASCII
|
||||
// letter, throw a SyntaxError exception (empty string) and abort these steps.
|
||||
// For each uppercase ASCII letter in name, insert a "-" (U+002D) character
|
||||
// before the character and replace the character with the same character
|
||||
// converted to ASCII lowercase.
|
||||
// Insert the string "data-" at the front of name.
|
||||
|
||||
SG_CHECK_PROP2ATTR("test", "data-test");
|
||||
SG_CHECK_PROP2ATTR("testIt", "data-test-it");
|
||||
SG_CHECK_PROP2ATTR("testIt-Hyphen", "data-test-it--hyphen");
|
||||
SG_CHECK_PROP2ATTR("-test", "");
|
||||
SG_CHECK_PROP2ATTR("test-", "data-test-");
|
||||
|
||||
#undef SG_CHECK_PROP2ATTR
|
||||
|
||||
SGPropertyNode_ptr node = new SGPropertyNode;
|
||||
sc::ElementPtr el =
|
||||
sc::Element::create<sc::Group>(sc::CanvasWeakPtr(), node);
|
||||
|
||||
el->setDataProp("myData", 3);
|
||||
BOOST_CHECK_EQUAL( el->getDataProp<int>("myData"), 3 );
|
||||
BOOST_CHECK_EQUAL( node->getIntValue("data-my-data"), 3 );
|
||||
|
||||
SGPropertyNode* prop = el->getDataProp<SGPropertyNode*>("notExistingProp");
|
||||
BOOST_CHECK( !prop );
|
||||
prop = el->getDataProp<SGPropertyNode*>("myData");
|
||||
BOOST_CHECK( prop );
|
||||
BOOST_CHECK_EQUAL( prop->getParent(), node );
|
||||
BOOST_CHECK_EQUAL( prop->getIntValue(), 3 );
|
||||
|
||||
BOOST_CHECK( el->hasDataProp("myData") );
|
||||
el->removeDataProp("myData");
|
||||
BOOST_CHECK( !el->hasDataProp("myData") );
|
||||
BOOST_CHECK_EQUAL( el->getDataProp("myData", 5), 5 );
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
include (SimGearComponent)
|
||||
|
||||
set(HEADERS
|
||||
CustomEvent.hxx
|
||||
MouseEvent.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
CustomEvent.cxx
|
||||
MouseEvent.cxx
|
||||
)
|
||||
|
||||
simgear_scene_component(canvas-events canvas/events "${SOURCES}" "${HEADERS}")
|
||||
|
||||
add_boost_test(canvas_event
|
||||
SOURCES event_test.cpp
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
)
|
||||
@@ -1,73 +0,0 @@
|
||||
///@file Canvas user defined event
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef CANVAS_CUSTOM_EVENT_HXX_
|
||||
#define CANVAS_CUSTOM_EVENT_HXX_
|
||||
|
||||
#include <simgear/canvas/CanvasEvent.hxx>
|
||||
#include <simgear/structure/map.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
class CustomEvent:
|
||||
public Event
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
*
|
||||
* @param type_str Event type name (if name does not exist yet it will
|
||||
* be registered as new event type)
|
||||
* @param data Optional user data stored in event
|
||||
*/
|
||||
CustomEvent( std::string const& type_str,
|
||||
bool bubbles = false,
|
||||
StringMap const& data = StringMap() );
|
||||
|
||||
/**
|
||||
*
|
||||
* @param type_id Event type id
|
||||
* @param data Optional user data stored in event
|
||||
*/
|
||||
CustomEvent( int type_id,
|
||||
bool bubbles = false,
|
||||
StringMap const& data = StringMap() );
|
||||
|
||||
/**
|
||||
* Set user data
|
||||
*/
|
||||
void setDetail(StringMap const& data);
|
||||
|
||||
/**
|
||||
* Get user data
|
||||
*/
|
||||
StringMap const& getDetail() const { return detail; }
|
||||
|
||||
virtual bool canBubble() const { return bubbles; }
|
||||
|
||||
StringMap detail; //<! user data map
|
||||
bool bubbles;
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* CANVAS_CUSTOM_EVENT_HXX_ */
|
||||
@@ -1,70 +0,0 @@
|
||||
// Mouse event
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "MouseEvent.hxx"
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
MouseEvent::MouseEvent():
|
||||
button(0),
|
||||
buttons(0),
|
||||
modifiers(0),
|
||||
click_count(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
MouseEvent::MouseEvent(const osgGA::GUIEventAdapter& ea):
|
||||
button(0),
|
||||
buttons(ea.getButtonMask()),
|
||||
modifiers(ea.getModKeyMask()),
|
||||
click_count(0)
|
||||
{
|
||||
time = ea.getTime();
|
||||
|
||||
// Convert button mask to index
|
||||
int button_mask = ea.getButton();
|
||||
while( (button_mask >>= 1) > 0 )
|
||||
button += 1;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool MouseEvent::canBubble() const
|
||||
{
|
||||
// Check if event supports bubbling
|
||||
const Event::Type types_no_bubbling[] = {
|
||||
Event::MOUSE_ENTER,
|
||||
Event::MOUSE_LEAVE,
|
||||
};
|
||||
const size_t num_types_no_bubbling = sizeof(types_no_bubbling)
|
||||
/ sizeof(types_no_bubbling[0]);
|
||||
|
||||
for( size_t i = 0; i < num_types_no_bubbling; ++i )
|
||||
if( type == types_no_bubbling[i] )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,36 +0,0 @@
|
||||
/// Unit tests for reference counting and smart pointer classes
|
||||
#define BOOST_TEST_MODULE structure
|
||||
#include <BoostTestTargetConfig.h>
|
||||
|
||||
#include "MouseEvent.hxx"
|
||||
#include "CustomEvent.hxx"
|
||||
|
||||
namespace sc = simgear::canvas;
|
||||
|
||||
BOOST_AUTO_TEST_CASE( canvas_event_types )
|
||||
{
|
||||
// Register type
|
||||
BOOST_REQUIRE_EQUAL( sc::Event::strToType("test"),
|
||||
sc::Event::UNKNOWN );
|
||||
BOOST_REQUIRE_EQUAL( sc::Event::getOrRegisterType("test"),
|
||||
sc::Event::CUSTOM_EVENT );
|
||||
BOOST_REQUIRE_EQUAL( sc::Event::strToType("test"),
|
||||
sc::Event::CUSTOM_EVENT );
|
||||
BOOST_REQUIRE_EQUAL( sc::Event::typeToStr(sc::Event::CUSTOM_EVENT),
|
||||
"test" );
|
||||
|
||||
// Basic internal type
|
||||
BOOST_REQUIRE_EQUAL( sc::Event::typeToStr(sc::Event::MOUSE_DOWN),
|
||||
"mousedown" );
|
||||
BOOST_REQUIRE_EQUAL( sc::Event::strToType("mousedown"),
|
||||
sc::Event::MOUSE_DOWN );
|
||||
|
||||
// Unknown type
|
||||
BOOST_REQUIRE_EQUAL( sc::Event::typeToStr(123),
|
||||
"unknown" );
|
||||
|
||||
// Register type through custom event instance
|
||||
sc::CustomEvent e("blub");
|
||||
BOOST_REQUIRE_EQUAL( e.getTypeString(), "blub" );
|
||||
BOOST_REQUIRE_NE( e.getType(), sc::Event::UNKNOWN );
|
||||
}
|
||||
@@ -1,488 +0,0 @@
|
||||
// Align items horizontally or vertically in a box
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "BoxLayout.hxx"
|
||||
#include "SpacerItem.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
BoxLayout::BoxLayout(Direction dir):
|
||||
_padding(5)
|
||||
{
|
||||
setDirection(dir);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
BoxLayout::~BoxLayout()
|
||||
{
|
||||
_parent.reset(); // No need to invalidate parent again...
|
||||
clear();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void BoxLayout::addItem(const LayoutItemRef& item)
|
||||
{
|
||||
return addItem(item, 0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void BoxLayout::addItem(const LayoutItemRef& item, int stretch)
|
||||
{
|
||||
insertItem(-1, item, stretch);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void BoxLayout::addStretch(int stretch)
|
||||
{
|
||||
insertStretch(-1, stretch);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void BoxLayout::addSpacing(int size)
|
||||
{
|
||||
insertSpacing(-1, size);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void BoxLayout::insertItem(int index, const LayoutItemRef& item, int stretch)
|
||||
{
|
||||
ItemData item_data = {0};
|
||||
item_data.layout_item = item;
|
||||
item_data.stretch = std::max(0, stretch);
|
||||
|
||||
item->setCanvas(_canvas);
|
||||
item->setParent(this);
|
||||
|
||||
if( index < 0 )
|
||||
_layout_items.push_back(item_data);
|
||||
else
|
||||
_layout_items.insert(_layout_items.begin() + index, item_data);
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void BoxLayout::insertStretch(int index, int stretch)
|
||||
{
|
||||
insertItem(index, LayoutItemRef(new SpacerItem()), stretch);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void BoxLayout::insertSpacing(int index, int size)
|
||||
{
|
||||
SGVec2i size_hint = horiz()
|
||||
? SGVec2i(size, 0)
|
||||
: SGVec2i(0, size),
|
||||
max_size = size_hint;
|
||||
|
||||
insertItem(index, LayoutItemRef(new SpacerItem(size_hint, max_size)));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
size_t BoxLayout::count() const
|
||||
{
|
||||
return _layout_items.size();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
LayoutItemRef BoxLayout::itemAt(size_t index)
|
||||
{
|
||||
if( index >= _layout_items.size() )
|
||||
return LayoutItemRef();
|
||||
|
||||
return _layout_items[index].layout_item;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
LayoutItemRef BoxLayout::takeAt(size_t index)
|
||||
{
|
||||
if( index >= _layout_items.size() )
|
||||
return LayoutItemRef();
|
||||
|
||||
LayoutItems::iterator it = _layout_items.begin() + index;
|
||||
LayoutItemRef item = it->layout_item;
|
||||
item->onRemove();
|
||||
_layout_items.erase(it);
|
||||
|
||||
invalidate();
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void BoxLayout::clear()
|
||||
{
|
||||
for( LayoutItems::iterator it = _layout_items.begin();
|
||||
it != _layout_items.end();
|
||||
++it )
|
||||
{
|
||||
it->layout_item->onRemove();
|
||||
}
|
||||
_layout_items.clear();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void BoxLayout::setStretch(size_t index, int stretch)
|
||||
{
|
||||
if( index >= _layout_items.size() )
|
||||
return;
|
||||
|
||||
_layout_items.at(index).stretch = std::max(0, stretch);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool BoxLayout::setStretchFactor(const LayoutItemRef& item, int stretch)
|
||||
{
|
||||
for( LayoutItems::iterator it = _layout_items.begin();
|
||||
it != _layout_items.end();
|
||||
++it )
|
||||
{
|
||||
if( item == it->layout_item )
|
||||
{
|
||||
it->stretch = std::max(0, stretch);
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int BoxLayout::stretch(size_t index) const
|
||||
{
|
||||
if( index >= _layout_items.size() )
|
||||
return 0;
|
||||
|
||||
return _layout_items.at(index).stretch;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void BoxLayout::setSpacing(int spacing)
|
||||
{
|
||||
if( spacing == _padding )
|
||||
return;
|
||||
|
||||
_padding = spacing;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int BoxLayout::spacing() const
|
||||
{
|
||||
return _padding;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void BoxLayout::setDirection(Direction dir)
|
||||
{
|
||||
_direction = dir;
|
||||
_get_layout_coord = &SGVec2i::x;
|
||||
_get_fixed_coord = &SGVec2i::y;
|
||||
|
||||
if( !horiz() )
|
||||
std::swap(_get_layout_coord, _get_fixed_coord);
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
BoxLayout::Direction BoxLayout::direction() const
|
||||
{
|
||||
return _direction;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool BoxLayout::hasHeightForWidth() const
|
||||
{
|
||||
if( _flags & SIZE_INFO_DIRTY )
|
||||
updateSizeHints();
|
||||
|
||||
return _layout_data.has_hfw;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int BoxLayout::heightForWidth(int w) const
|
||||
{
|
||||
if( !hasHeightForWidth() )
|
||||
return -1;
|
||||
|
||||
updateWFHCache(w);
|
||||
return _hfw_height;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int BoxLayout::minimumHeightForWidth(int w) const
|
||||
{
|
||||
if( !hasHeightForWidth() )
|
||||
return -1;
|
||||
|
||||
updateWFHCache(w);
|
||||
return _hfw_min_height;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void BoxLayout::setCanvas(const CanvasWeakPtr& canvas)
|
||||
{
|
||||
_canvas = canvas;
|
||||
|
||||
for(size_t i = 0; i < _layout_items.size(); ++i)
|
||||
_layout_items[i].layout_item->setCanvas(canvas);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool BoxLayout::horiz() const
|
||||
{
|
||||
return (_direction == LeftToRight) || (_direction == RightToLeft);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void BoxLayout::updateSizeHints() const
|
||||
{
|
||||
SGVec2i min_size(0, 0),
|
||||
max_size(0, 0),
|
||||
size_hint(0, 0);
|
||||
|
||||
_layout_data.reset();
|
||||
_hfw_width = _hfw_height = _hfw_min_height = -1;
|
||||
|
||||
bool is_first = true;
|
||||
|
||||
for(size_t i = 0; i < _layout_items.size(); ++i)
|
||||
{
|
||||
// TODO check visible
|
||||
|
||||
ItemData& item_data = _layout_items[i];
|
||||
LayoutItem const& item = *item_data.layout_item;
|
||||
|
||||
item_data.min_size = (item.minimumSize().*_get_layout_coord)();
|
||||
item_data.max_size = (item.maximumSize().*_get_layout_coord)();
|
||||
item_data.size_hint = (item.sizeHint().*_get_layout_coord)();
|
||||
item_data.has_hfw = item.hasHeightForWidth();
|
||||
|
||||
if( !dynamic_cast<SpacerItem*>(item_data.layout_item.get()) )
|
||||
{
|
||||
if( is_first )
|
||||
{
|
||||
item_data.padding_orig = 0;
|
||||
is_first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
item_data.padding_orig = _padding;
|
||||
_layout_data.padding += item_data.padding_orig;
|
||||
}
|
||||
}
|
||||
|
||||
// Add sizes of all children in layout direction
|
||||
safeAdd(min_size.x(), item_data.min_size);
|
||||
safeAdd(max_size.x(), item_data.max_size);
|
||||
safeAdd(size_hint.x(), item_data.size_hint);
|
||||
|
||||
// Take maximum in fixed (non-layouted) direction
|
||||
min_size.y() = std::max( min_size.y(),
|
||||
(item.minimumSize().*_get_fixed_coord)() );
|
||||
max_size.y() = std::max( max_size.y(),
|
||||
(item.maximumSize().*_get_fixed_coord)() );
|
||||
size_hint.y() = std::max( size_hint.y(),
|
||||
(item.sizeHint().*_get_fixed_coord)() );
|
||||
|
||||
_layout_data.has_hfw = _layout_data.has_hfw || item.hasHeightForWidth();
|
||||
}
|
||||
|
||||
safeAdd(min_size.x(), _layout_data.padding);
|
||||
safeAdd(max_size.x(), _layout_data.padding);
|
||||
safeAdd(size_hint.x(), _layout_data.padding);
|
||||
|
||||
_layout_data.min_size = min_size.x();
|
||||
_layout_data.max_size = max_size.x();
|
||||
_layout_data.size_hint = size_hint.x();
|
||||
|
||||
_min_size.x() = (min_size.*_get_layout_coord)();
|
||||
_max_size.x() = (max_size.*_get_layout_coord)();
|
||||
_size_hint.x() = (size_hint.*_get_layout_coord)();
|
||||
|
||||
_min_size.y() = (min_size.*_get_fixed_coord)();
|
||||
_max_size.y() = (max_size.*_get_fixed_coord)();
|
||||
_size_hint.y() = (size_hint.*_get_fixed_coord)();
|
||||
|
||||
_flags &= ~SIZE_INFO_DIRTY;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void BoxLayout::updateWFHCache(int w) const
|
||||
{
|
||||
if( w == _hfw_width )
|
||||
return;
|
||||
|
||||
_hfw_height = 0;
|
||||
_hfw_min_height = 0;
|
||||
|
||||
if( horiz() )
|
||||
{
|
||||
_layout_data.size = w;
|
||||
const_cast<BoxLayout*>(this)->distribute(_layout_items, _layout_data);
|
||||
|
||||
for(size_t i = 0; i < _layout_items.size(); ++i)
|
||||
{
|
||||
ItemData const& data = _layout_items[i];
|
||||
_hfw_height = std::max(_hfw_height, data.hfw(data.size));
|
||||
_hfw_min_height = std::max(_hfw_min_height, data.mhfw(data.size));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(size_t i = 0; i < _layout_items.size(); ++i)
|
||||
{
|
||||
ItemData const& data = _layout_items[i];
|
||||
_hfw_height += data.hfw(w) + data.padding_orig;
|
||||
_hfw_min_height += data.mhfw(w) + data.padding_orig;
|
||||
}
|
||||
}
|
||||
|
||||
_hfw_width = w;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SGVec2i BoxLayout::sizeHintImpl() const
|
||||
{
|
||||
updateSizeHints();
|
||||
return _size_hint;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SGVec2i BoxLayout::minimumSizeImpl() const
|
||||
{
|
||||
updateSizeHints();
|
||||
return _min_size;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SGVec2i BoxLayout::maximumSizeImpl() const
|
||||
{
|
||||
updateSizeHints();
|
||||
return _max_size;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void BoxLayout::doLayout(const SGRecti& geom)
|
||||
{
|
||||
if( _flags & SIZE_INFO_DIRTY )
|
||||
updateSizeHints();
|
||||
|
||||
// Store current size hints because vertical layouts containing
|
||||
// height-for-width items the size hints are update for the actual width of
|
||||
// the layout
|
||||
int min_size_save = _layout_data.min_size,
|
||||
size_hint_save = _layout_data.size_hint;
|
||||
|
||||
_layout_data.size = (geom.size().*_get_layout_coord)();
|
||||
|
||||
// update width dependent data for layouting of vertical layouts
|
||||
if( _layout_data.has_hfw && !horiz() )
|
||||
{
|
||||
for(size_t i = 0; i < _layout_items.size(); ++i)
|
||||
{
|
||||
ItemData& data = _layout_items[i];
|
||||
if( data.has_hfw )
|
||||
{
|
||||
int w = SGMisc<int>::clip( geom.width(),
|
||||
data.layout_item->minimumSize().x(),
|
||||
data.layout_item->maximumSize().x() );
|
||||
|
||||
data.min_size = data.mhfw(w);
|
||||
data.size_hint = data.hfw(w);
|
||||
|
||||
// Update size hints for layouting with difference to size hints
|
||||
// calculated by using the size hints provided (without trading
|
||||
// height for width)
|
||||
_layout_data.min_size += data.min_size
|
||||
- data.layout_item->minimumSize().y();
|
||||
_layout_data.size_hint += data.size_hint
|
||||
- data.layout_item->sizeHint().y();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now do the actual layouting
|
||||
distribute(_layout_items, _layout_data);
|
||||
|
||||
// Restore size hints possibly changed by vertical layouting
|
||||
_layout_data.min_size = min_size_save;
|
||||
_layout_data.size_hint = size_hint_save;
|
||||
|
||||
// and finally set the layouted geometry for each item
|
||||
int fixed_size = (geom.size().*_get_fixed_coord)();
|
||||
SGVec2i cur_pos( (geom.pos().*_get_layout_coord)(),
|
||||
(geom.pos().*_get_fixed_coord)() );
|
||||
|
||||
bool reverse = (_direction == RightToLeft) || (_direction == BottomToTop);
|
||||
if( reverse )
|
||||
cur_pos.x() += (geom.size().*_get_layout_coord)();
|
||||
|
||||
for(size_t i = 0; i < _layout_items.size(); ++i)
|
||||
{
|
||||
ItemData const& data = _layout_items[i];
|
||||
cur_pos.x() += reverse ? -data.padding - data.size : data.padding;
|
||||
|
||||
SGVec2i size(
|
||||
data.size,
|
||||
std::min( (data.layout_item->maximumSize().*_get_fixed_coord)(),
|
||||
fixed_size )
|
||||
);
|
||||
|
||||
// Center in fixed direction (TODO allow specifying alignment)
|
||||
int offset_fixed = (fixed_size - size.y()) / 2;
|
||||
cur_pos.y() += offset_fixed;
|
||||
|
||||
data.layout_item->setGeometry(SGRecti(
|
||||
(cur_pos.*_get_layout_coord)(),
|
||||
(cur_pos.*_get_fixed_coord)(),
|
||||
(size.*_get_layout_coord)(),
|
||||
(size.*_get_fixed_coord)()
|
||||
));
|
||||
|
||||
if( !reverse )
|
||||
cur_pos.x() += data.size;
|
||||
cur_pos.y() -= offset_fixed;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
HBoxLayout::HBoxLayout():
|
||||
BoxLayout(LeftToRight)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
VBoxLayout::VBoxLayout():
|
||||
BoxLayout(TopToBottom)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,154 +0,0 @@
|
||||
// Align items horizontally or vertically in a box
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef SG_CANVAS_BOX_LAYOUT_HXX_
|
||||
#define SG_CANVAS_BOX_LAYOUT_HXX_
|
||||
|
||||
#include "Layout.hxx"
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
class BoxLayout:
|
||||
public Layout
|
||||
{
|
||||
public:
|
||||
|
||||
enum Direction
|
||||
{
|
||||
LeftToRight,
|
||||
RightToLeft,
|
||||
TopToBottom,
|
||||
BottomToTop
|
||||
};
|
||||
|
||||
BoxLayout(Direction dir);
|
||||
~BoxLayout();
|
||||
|
||||
virtual void addItem(const LayoutItemRef& item);
|
||||
|
||||
void addItem(const LayoutItemRef& item, int stretch);
|
||||
|
||||
void addStretch(int stretch = 0);
|
||||
|
||||
void addSpacing(int size);
|
||||
|
||||
void insertItem(int index, const LayoutItemRef& item, int stretch = 0);
|
||||
|
||||
void insertStretch(int index, int stretch = 0);
|
||||
|
||||
void insertSpacing(int index, int size);
|
||||
|
||||
virtual size_t count() const;
|
||||
virtual LayoutItemRef itemAt(size_t index);
|
||||
virtual LayoutItemRef takeAt(size_t index);
|
||||
virtual void clear();
|
||||
|
||||
/**
|
||||
* Set the stretch factor of the item at position @a index to @a stretch.
|
||||
*/
|
||||
void setStretch(size_t index, int stretch);
|
||||
|
||||
/**
|
||||
* Set the stretch factor of the given @a item to @a stretch, if it exists
|
||||
* in this layout.
|
||||
*
|
||||
* @return true, if the @a item was found in the layout
|
||||
*/
|
||||
bool setStretchFactor(const LayoutItemRef& item, int stretch);
|
||||
|
||||
/**
|
||||
* Get the stretch factor of the item at position @a index
|
||||
*/
|
||||
int stretch(size_t index) const;
|
||||
|
||||
virtual void setSpacing(int spacing);
|
||||
virtual int spacing() const;
|
||||
|
||||
void setDirection(Direction dir);
|
||||
Direction direction() const;
|
||||
|
||||
virtual bool hasHeightForWidth() const;
|
||||
virtual int heightForWidth(int w) const;
|
||||
virtual int minimumHeightForWidth(int w) const;
|
||||
|
||||
virtual void setCanvas(const CanvasWeakPtr& canvas);
|
||||
|
||||
bool horiz() const;
|
||||
|
||||
protected:
|
||||
|
||||
typedef const int& (SGVec2i::*CoordGetter)() const;
|
||||
CoordGetter _get_layout_coord, //<! getter for coordinate in layout
|
||||
// direction
|
||||
_get_fixed_coord; //<! getter for coordinate in secondary
|
||||
// (fixed) direction
|
||||
|
||||
int _padding;
|
||||
Direction _direction;
|
||||
|
||||
typedef std::vector<ItemData> LayoutItems;
|
||||
|
||||
mutable LayoutItems _layout_items;
|
||||
mutable ItemData _layout_data;
|
||||
|
||||
// Cache for last height-for-width query
|
||||
mutable int _hfw_width,
|
||||
_hfw_height,
|
||||
_hfw_min_height;
|
||||
|
||||
void updateSizeHints() const;
|
||||
void updateWFHCache(int w) const;
|
||||
|
||||
virtual SGVec2i sizeHintImpl() const;
|
||||
virtual SGVec2i minimumSizeImpl() const;
|
||||
virtual SGVec2i maximumSizeImpl() const;
|
||||
|
||||
virtual void doLayout(const SGRecti& geom);
|
||||
};
|
||||
|
||||
/**
|
||||
* Shortcut for creating a horizontal box layout
|
||||
*/
|
||||
class HBoxLayout:
|
||||
public BoxLayout
|
||||
{
|
||||
public:
|
||||
HBoxLayout();
|
||||
};
|
||||
|
||||
/**
|
||||
* Shortcut for creating a vertical box layout
|
||||
*/
|
||||
class VBoxLayout:
|
||||
public BoxLayout
|
||||
{
|
||||
public:
|
||||
VBoxLayout();
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<BoxLayout> BoxLayoutRef;
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
|
||||
#endif /* SG_CANVAS_BOX_LAYOUT_HXX_ */
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
include (SimGearComponent)
|
||||
|
||||
set(HEADERS
|
||||
BoxLayout.hxx
|
||||
Layout.hxx
|
||||
LayoutItem.hxx
|
||||
NasalWidget.hxx
|
||||
SpacerItem.hxx
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
BoxLayout.cxx
|
||||
Layout.cxx
|
||||
LayoutItem.cxx
|
||||
NasalWidget.cxx
|
||||
SpacerItem.cxx
|
||||
)
|
||||
|
||||
simgear_scene_component(canvas-layout canvas/layout "${SOURCES}" "${HEADERS}")
|
||||
|
||||
add_boost_test(canvas_layout
|
||||
SOURCES canvas_layout_test.cxx
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
)
|
||||
@@ -1,295 +0,0 @@
|
||||
// Basic class for canvas layouts
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "Layout.hxx"
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Layout::update()
|
||||
{
|
||||
if( !(_flags & (LAYOUT_DIRTY | SIZE_INFO_DIRTY)) )
|
||||
return;
|
||||
|
||||
doLayout(_geometry);
|
||||
|
||||
_flags &= ~LAYOUT_DIRTY;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Layout::invalidate()
|
||||
{
|
||||
LayoutItem::invalidate();
|
||||
_flags |= LAYOUT_DIRTY;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Layout::setGeometry(const SGRecti& geom)
|
||||
{
|
||||
if( geom != _geometry )
|
||||
{
|
||||
_geometry = geom;
|
||||
_flags |= LAYOUT_DIRTY;
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Layout::removeItem(const LayoutItemRef& item)
|
||||
{
|
||||
size_t i = 0;
|
||||
while( LayoutItemRef child = itemAt(i) )
|
||||
{
|
||||
if( item == child )
|
||||
return (void)takeAt(i);
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Layout::clear()
|
||||
{
|
||||
while( itemAt(0) )
|
||||
takeAt(0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Layout::ItemData::reset()
|
||||
{
|
||||
layout_item = 0;
|
||||
size_hint = 0;
|
||||
min_size = 0;
|
||||
max_size = 0;
|
||||
padding_orig= 0;
|
||||
padding = 0;
|
||||
size = 0;
|
||||
stretch = 0;
|
||||
has_hfw = false;
|
||||
done = false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Layout::ItemData::hfw(int w) const
|
||||
{
|
||||
if( has_hfw )
|
||||
return layout_item->heightForWidth(w);
|
||||
else
|
||||
return layout_item->sizeHint().y();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int Layout::ItemData::mhfw(int w) const
|
||||
{
|
||||
if( has_hfw )
|
||||
return layout_item->minimumHeightForWidth(w);
|
||||
else
|
||||
return layout_item->minimumSize().y();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Layout::safeAdd(int& a, int b)
|
||||
{
|
||||
if( SGLimits<int>::max() - b < a )
|
||||
a = SGLimits<int>::max();
|
||||
else
|
||||
a += b;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Layout::distribute(std::vector<ItemData>& items, const ItemData& space)
|
||||
{
|
||||
const int num_children = static_cast<int>(items.size());
|
||||
_num_not_done = num_children;
|
||||
|
||||
SG_LOG( SG_GUI,
|
||||
SG_DEBUG,
|
||||
"Layout::distribute(" << space.size << "px for "
|
||||
<< num_children << " items, s.t."
|
||||
<< " min=" << space.min_size
|
||||
<< ", hint=" << space.size_hint
|
||||
<< ", max=" << space.max_size << ")" );
|
||||
|
||||
if( space.size < space.min_size )
|
||||
{
|
||||
// TODO
|
||||
SG_LOG( SG_GUI, SG_WARN, "Layout: not enough size (not implemented)");
|
||||
}
|
||||
else if( space.size < space.max_size )
|
||||
{
|
||||
_sum_stretch = 0;
|
||||
_space_stretch = 0;
|
||||
|
||||
bool less_then_hint = space.size < space.size_hint;
|
||||
|
||||
// Give min_size/size_hint to all items
|
||||
_space_left = space.size
|
||||
- (less_then_hint ? space.min_size : space.size_hint);
|
||||
for(int i = 0; i < num_children; ++i)
|
||||
{
|
||||
ItemData& d = items[i];
|
||||
d.size = less_then_hint ? d.min_size : d.size_hint;
|
||||
d.padding = d.padding_orig;
|
||||
d.done = d.size >= (less_then_hint ? d.size_hint : d.max_size);
|
||||
|
||||
SG_LOG(
|
||||
SG_GUI,
|
||||
SG_DEBUG,
|
||||
i << ") initial=" << d.size
|
||||
<< ", min=" << d.min_size
|
||||
<< ", hint=" << d.size_hint
|
||||
<< ", max=" << d.max_size
|
||||
);
|
||||
|
||||
if( d.done )
|
||||
{
|
||||
_num_not_done -= 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( d.stretch > 0 )
|
||||
{
|
||||
_sum_stretch += d.stretch;
|
||||
_space_stretch += d.size;
|
||||
}
|
||||
}
|
||||
|
||||
// Distribute remaining space to increase the size of each item up to its
|
||||
// size_hint/max_size
|
||||
while( _space_left > 0 )
|
||||
{
|
||||
if( _num_not_done <= 0 )
|
||||
{
|
||||
SG_LOG(SG_GUI, SG_WARN, "space left, but no more items?");
|
||||
break;
|
||||
}
|
||||
|
||||
int space_per_element = std::max(1, _space_left / _num_not_done);
|
||||
|
||||
SG_LOG(SG_GUI, SG_DEBUG, "space/element=" << space_per_element);
|
||||
|
||||
for(int i = 0; i < num_children; ++i)
|
||||
{
|
||||
ItemData& d = items[i];
|
||||
|
||||
SG_LOG(
|
||||
SG_GUI,
|
||||
SG_DEBUG,
|
||||
i << ") left=" << _space_left
|
||||
<< ", not_done=" << _num_not_done
|
||||
<< ", sum=" << _sum_stretch
|
||||
<< ", stretch=" << _space_stretch
|
||||
<< ", stretch/unit=" << _space_stretch / std::max(1, _sum_stretch)
|
||||
);
|
||||
|
||||
if( d.done )
|
||||
continue;
|
||||
|
||||
if( _sum_stretch > 0 && d.stretch <= 0 )
|
||||
d.done = true;
|
||||
else
|
||||
{
|
||||
int target_size = 0;
|
||||
int max_size = less_then_hint ? d.size_hint : d.max_size;
|
||||
|
||||
if( _sum_stretch > 0 )
|
||||
{
|
||||
target_size = (d.stretch * (_space_left + _space_stretch))
|
||||
/ _sum_stretch;
|
||||
|
||||
// Item would be smaller than minimum size or larger than maximum
|
||||
// size, so just keep bounded size and ignore stretch factor
|
||||
if( target_size <= d.size || target_size >= max_size )
|
||||
{
|
||||
d.done = true;
|
||||
_sum_stretch -= d.stretch;
|
||||
_space_stretch -= d.size;
|
||||
|
||||
if( target_size >= max_size )
|
||||
target_size = max_size;
|
||||
else
|
||||
target_size = d.size;
|
||||
}
|
||||
else
|
||||
_space_stretch += target_size - d.size;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Give space evenly to all remaining elements in this round
|
||||
target_size = d.size + std::min(_space_left, space_per_element);
|
||||
|
||||
if( target_size >= max_size )
|
||||
{
|
||||
d.done = true;
|
||||
target_size = max_size;
|
||||
}
|
||||
}
|
||||
|
||||
int old_size = d.size;
|
||||
d.size = target_size;
|
||||
_space_left -= d.size - old_size;
|
||||
}
|
||||
|
||||
if( d.done )
|
||||
{
|
||||
_num_not_done -= 1;
|
||||
|
||||
if( _sum_stretch <= 0 && d.stretch > 0 )
|
||||
// Distribute remaining space evenly to all non-stretchable items
|
||||
// in a new round
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_space_left = space.size - space.max_size;
|
||||
for(int i = 0; i < num_children; ++i)
|
||||
{
|
||||
ItemData& d = items[i];
|
||||
d.size = d.max_size;
|
||||
|
||||
// Add superfluous space as padding
|
||||
d.padding = d.padding_orig + _space_left
|
||||
// Padding after last child...
|
||||
/ (_num_not_done + 1);
|
||||
|
||||
_space_left -= d.padding - d.padding_orig;
|
||||
_num_not_done -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
SG_LOG(SG_GUI, SG_DEBUG, "distribute:");
|
||||
for(int i = 0; i < num_children; ++i)
|
||||
{
|
||||
ItemData const& d = items[i];
|
||||
SG_LOG( SG_GUI,
|
||||
SG_DEBUG,
|
||||
i << ") pad=" << d.padding
|
||||
<< ", size = " << d.size );
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,133 +0,0 @@
|
||||
// Basic class for canvas layouts
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef SG_CANVAS_LAYOUT_HXX_
|
||||
#define SG_CANVAS_LAYOUT_HXX_
|
||||
|
||||
#include "LayoutItem.hxx"
|
||||
#include <vector>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
class Layout:
|
||||
public LayoutItem
|
||||
{
|
||||
public:
|
||||
void update();
|
||||
|
||||
virtual void invalidate();
|
||||
virtual void setGeometry(const SGRecti& geom);
|
||||
|
||||
virtual void addItem(const LayoutItemRef& item) = 0;
|
||||
virtual void setSpacing(int spacing) = 0;
|
||||
virtual int spacing() const = 0;
|
||||
|
||||
/**
|
||||
* Get the number of items.
|
||||
*/
|
||||
virtual size_t count() const = 0;
|
||||
|
||||
/**
|
||||
* Get the item at position @a index.
|
||||
*
|
||||
* If there is no such item the function must do nothing and return an
|
||||
* empty reference.
|
||||
*/
|
||||
virtual LayoutItemRef itemAt(size_t index) = 0;
|
||||
|
||||
/**
|
||||
* Remove and get the item at position @a index.
|
||||
*
|
||||
* If there is no such item the function must do nothing and return an
|
||||
* empty reference.
|
||||
*/
|
||||
virtual LayoutItemRef takeAt(size_t index) = 0;
|
||||
|
||||
/**
|
||||
* Remove the given @a item from the layout.
|
||||
*/
|
||||
void removeItem(const LayoutItemRef& item);
|
||||
|
||||
/**
|
||||
* Remove all items.
|
||||
*/
|
||||
virtual void clear();
|
||||
|
||||
protected:
|
||||
enum LayoutFlags
|
||||
{
|
||||
LAYOUT_DIRTY = LayoutItem::LAST_FLAG << 1,
|
||||
LAST_FLAG = LAYOUT_DIRTY
|
||||
};
|
||||
|
||||
struct ItemData
|
||||
{
|
||||
LayoutItemRef layout_item;
|
||||
int size_hint,
|
||||
min_size,
|
||||
max_size,
|
||||
padding_orig, //<! original padding as specified by the user
|
||||
padding, //<! padding before element (layouted)
|
||||
size, //<! layouted size
|
||||
stretch; //<! stretch factor
|
||||
bool has_hfw : 1, //<! height for width
|
||||
done : 1; //<! layouting done
|
||||
|
||||
/** Clear values (reset to default/empty state) */
|
||||
void reset();
|
||||
|
||||
int hfw(int w) const;
|
||||
int mhfw(int w) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Override to implement the actual layouting
|
||||
*/
|
||||
virtual void doLayout(const SGRecti& geom) = 0;
|
||||
|
||||
/**
|
||||
* Add two integers taking care of overflow (limit to INT_MAX)
|
||||
*/
|
||||
static void safeAdd(int& a, int b);
|
||||
|
||||
/**
|
||||
* Distribute the available @a space to all @a items
|
||||
*/
|
||||
void distribute(std::vector<ItemData>& items, const ItemData& space);
|
||||
|
||||
private:
|
||||
|
||||
int _num_not_done, //<! number of children not layouted yet
|
||||
_sum_stretch, //<! sum of stretch factors of all not yet layouted
|
||||
// children
|
||||
_space_stretch,//<! space currently assigned to all not yet layouted
|
||||
// stretchable children
|
||||
_space_left; //<! remaining space not used by any child yet
|
||||
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<Layout> LayoutRef;
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
|
||||
#endif /* SG_CANVAS_LAYOUT_HXX_ */
|
||||
@@ -1,171 +0,0 @@
|
||||
// Basic element for layouting canvas elements
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "LayoutItem.hxx"
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
const SGVec2i LayoutItem::MAX_SIZE( SGLimits<int>::max(),
|
||||
SGLimits<int>::max() );
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
LayoutItem::LayoutItem():
|
||||
_flags(0),
|
||||
_size_hint(0, 0),
|
||||
_min_size(0, 0),
|
||||
_max_size(MAX_SIZE)
|
||||
{
|
||||
invalidate();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
LayoutItem::~LayoutItem()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SGVec2i LayoutItem::sizeHint() const
|
||||
{
|
||||
if( _flags & SIZE_HINT_DIRTY )
|
||||
{
|
||||
_size_hint = sizeHintImpl();
|
||||
_flags &= ~SIZE_HINT_DIRTY;
|
||||
}
|
||||
|
||||
return _size_hint;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SGVec2i LayoutItem::minimumSize() const
|
||||
{
|
||||
if( _flags & MINIMUM_SIZE_DIRTY )
|
||||
{
|
||||
_min_size = minimumSizeImpl();
|
||||
_flags &= ~MINIMUM_SIZE_DIRTY;
|
||||
}
|
||||
|
||||
return _min_size;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SGVec2i LayoutItem::maximumSize() const
|
||||
{
|
||||
if( _flags & MAXIMUM_SIZE_DIRTY )
|
||||
{
|
||||
_max_size = maximumSizeImpl();
|
||||
_flags &= ~MAXIMUM_SIZE_DIRTY;
|
||||
}
|
||||
|
||||
return _max_size;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool LayoutItem::hasHeightForWidth() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int LayoutItem::heightForWidth(int w) const
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int LayoutItem::minimumHeightForWidth(int w) const
|
||||
{
|
||||
return heightForWidth(w);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void LayoutItem::invalidate()
|
||||
{
|
||||
_flags |= SIZE_INFO_DIRTY;
|
||||
invalidateParent();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void LayoutItem::invalidateParent()
|
||||
{
|
||||
LayoutItemRef parent = _parent.lock();
|
||||
if( parent )
|
||||
parent->invalidate();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void LayoutItem::setGeometry(const SGRecti& geom)
|
||||
{
|
||||
_geometry = geom;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SGRecti LayoutItem::geometry() const
|
||||
{
|
||||
return _geometry;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void LayoutItem::setCanvas(const CanvasWeakPtr& canvas)
|
||||
{
|
||||
_canvas = canvas;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
CanvasPtr LayoutItem::getCanvas() const
|
||||
{
|
||||
return _canvas.lock();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void LayoutItem::setParent(const LayoutItemWeakRef& parent)
|
||||
{
|
||||
_parent = parent;
|
||||
LayoutItemRef parent_ref = parent.lock();
|
||||
setCanvas(parent_ref ? parent_ref->_canvas : CanvasWeakPtr());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
LayoutItemRef LayoutItem::getParent() const
|
||||
{
|
||||
return _parent.lock();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SGVec2i LayoutItem::sizeHintImpl() const
|
||||
{
|
||||
return _size_hint;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SGVec2i LayoutItem::minimumSizeImpl() const
|
||||
{
|
||||
return _min_size;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SGVec2i LayoutItem::maximumSizeImpl() const
|
||||
{
|
||||
return _max_size;
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,149 +0,0 @@
|
||||
///@file Basic element for layouting canvas elements
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef SG_CANVAS_LAYOUT_ITEM_HXX_
|
||||
#define SG_CANVAS_LAYOUT_ITEM_HXX_
|
||||
|
||||
#include <simgear/canvas/canvas_fwd.hxx>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
#include <simgear/math/SGRect.hxx>
|
||||
#include <simgear/misc/stdint.hxx>
|
||||
#include <simgear/structure/SGWeakReferenced.hxx>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
class LayoutItem;
|
||||
typedef SGSharedPtr<LayoutItem> LayoutItemRef;
|
||||
typedef SGWeakPtr<LayoutItem> LayoutItemWeakRef;
|
||||
|
||||
/**
|
||||
* Base class for all layouting elements. Specializations either implement a
|
||||
* layouting algorithm or a widget.
|
||||
*/
|
||||
class LayoutItem:
|
||||
public virtual SGVirtualWeakReferenced
|
||||
{
|
||||
public:
|
||||
|
||||
/** Maximum item size (indicating no limit) */
|
||||
static const SGVec2i MAX_SIZE;
|
||||
|
||||
LayoutItem();
|
||||
virtual ~LayoutItem();
|
||||
|
||||
/**
|
||||
* Get the preferred size of this item.
|
||||
*/
|
||||
SGVec2i sizeHint() const;
|
||||
|
||||
/**
|
||||
* Get the minimum amount of the space this item requires.
|
||||
*/
|
||||
SGVec2i minimumSize() const;
|
||||
|
||||
/**
|
||||
* Get the maximum amount of space this item can use.
|
||||
*/
|
||||
SGVec2i maximumSize() const;
|
||||
|
||||
virtual bool hasHeightForWidth() const;
|
||||
virtual int heightForWidth(int w) const;
|
||||
virtual int minimumHeightForWidth(int w) const;
|
||||
|
||||
/**
|
||||
* Mark all cached data as invalid and require it to be recalculated.
|
||||
*/
|
||||
virtual void invalidate();
|
||||
|
||||
/**
|
||||
* Mark all cached data of parent item as invalid (if it is known)
|
||||
*/
|
||||
void invalidateParent();
|
||||
|
||||
/**
|
||||
* Set position and size of this element. For layouts this triggers a
|
||||
* recalculation of the layout.
|
||||
*/
|
||||
virtual void setGeometry(const SGRecti& geom);
|
||||
|
||||
/**
|
||||
* Get position and size of this element.
|
||||
*/
|
||||
virtual SGRecti geometry() const;
|
||||
|
||||
/**
|
||||
* Set the canvas this item is attached to.
|
||||
*/
|
||||
virtual void setCanvas(const CanvasWeakPtr& canvas);
|
||||
|
||||
/**
|
||||
* Get the canvas this item is attached to.
|
||||
*/
|
||||
CanvasPtr getCanvas() const;
|
||||
|
||||
/**
|
||||
* Set the parent layout item (usually this is a layout).
|
||||
*/
|
||||
void setParent(const LayoutItemWeakRef& parent);
|
||||
|
||||
/**
|
||||
* Get the parent layout.
|
||||
*/
|
||||
LayoutItemRef getParent() const;
|
||||
|
||||
/// Called before item is removed from a layout
|
||||
virtual void onRemove() {}
|
||||
|
||||
protected:
|
||||
|
||||
friend class Canvas;
|
||||
|
||||
enum Flags
|
||||
{
|
||||
SIZE_HINT_DIRTY = 1,
|
||||
MINIMUM_SIZE_DIRTY = SIZE_HINT_DIRTY << 1,
|
||||
MAXIMUM_SIZE_DIRTY = MINIMUM_SIZE_DIRTY << 1,
|
||||
SIZE_INFO_DIRTY = SIZE_HINT_DIRTY
|
||||
| MINIMUM_SIZE_DIRTY
|
||||
| MAXIMUM_SIZE_DIRTY,
|
||||
LAST_FLAG = MAXIMUM_SIZE_DIRTY
|
||||
};
|
||||
|
||||
CanvasWeakPtr _canvas;
|
||||
LayoutItemWeakRef _parent;
|
||||
|
||||
SGRecti _geometry;
|
||||
|
||||
mutable uint32_t _flags;
|
||||
mutable SGVec2i _size_hint,
|
||||
_min_size,
|
||||
_max_size;
|
||||
|
||||
virtual SGVec2i sizeHintImpl() const;
|
||||
virtual SGVec2i minimumSizeImpl() const;
|
||||
virtual SGVec2i maximumSizeImpl() const;
|
||||
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* SG_CANVAS_LAYOUT_ITEM_HXX_ */
|
||||
@@ -1,295 +0,0 @@
|
||||
// Glue for GUI layout items implemented in Nasal space
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "NasalWidget.hxx"
|
||||
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
NasalWidget::NasalWidget(naRef impl):
|
||||
Object(impl),
|
||||
_layout_size_hint(32, 32),
|
||||
_layout_min_size(16, 16),
|
||||
_layout_max_size(MAX_SIZE),
|
||||
_user_size_hint(0, 0),
|
||||
_user_min_size(0, 0),
|
||||
_user_max_size(MAX_SIZE)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
NasalWidget::~NasalWidget()
|
||||
{
|
||||
onRemove();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void NasalWidget::invalidate()
|
||||
{
|
||||
LayoutItem::invalidate();
|
||||
_flags |= LAYOUT_DIRTY;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void NasalWidget::setGeometry(const SGRect<int>& geom)
|
||||
{
|
||||
if( _geometry != geom )
|
||||
_geometry = geom;
|
||||
else if( !(_flags & LAYOUT_DIRTY) || !_set_geometry )
|
||||
return;
|
||||
|
||||
naContext c = naNewContext();
|
||||
try
|
||||
{
|
||||
_set_geometry(nasal::to_nasal(c, this), geom);
|
||||
_flags &= ~LAYOUT_DIRTY;
|
||||
}
|
||||
catch( std::exception const& ex )
|
||||
{
|
||||
SG_LOG(
|
||||
SG_GUI,
|
||||
SG_WARN,
|
||||
"NasalWidget::setGeometry: callback error: '" << ex.what() << "'"
|
||||
);
|
||||
}
|
||||
naFreeContext(c);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void NasalWidget::onRemove()
|
||||
{
|
||||
if( !_nasal_impl.valid() )
|
||||
return;
|
||||
|
||||
typedef boost::function<void(nasal::Me)> Deleter;
|
||||
|
||||
naContext c = naNewContext();
|
||||
try
|
||||
{
|
||||
Deleter del =
|
||||
nasal::get_member<Deleter>(c, _nasal_impl.get_naRef(), "onRemove");
|
||||
if( del )
|
||||
del(nasal::to_nasal(c, this));
|
||||
}
|
||||
catch( std::exception const& ex )
|
||||
{
|
||||
SG_LOG(
|
||||
SG_GUI,
|
||||
SG_WARN,
|
||||
"NasalWidget::onRemove: callback error: '" << ex.what() << "'"
|
||||
);
|
||||
}
|
||||
naFreeContext(c);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void NasalWidget::setSetGeometryFunc(const SetGeometryFunc& func)
|
||||
{
|
||||
_set_geometry = func;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void NasalWidget::setHeightForWidthFunc(const HeightForWidthFunc& func)
|
||||
{
|
||||
_height_for_width = func;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void NasalWidget::setMinimumHeightForWidthFunc(const HeightForWidthFunc& func)
|
||||
{
|
||||
_min_height_for_width = func;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void NasalWidget::setSizeHint(const SGVec2i& s)
|
||||
{
|
||||
if( _user_size_hint == s )
|
||||
return;
|
||||
|
||||
_user_size_hint = s;
|
||||
|
||||
// TODO just invalidate size_hint? Probably not a performance issue...
|
||||
invalidate();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void NasalWidget::setMinimumSize(const SGVec2i& s)
|
||||
{
|
||||
if( _user_min_size == s )
|
||||
return;
|
||||
|
||||
_user_min_size = s;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void NasalWidget::setMaximumSize(const SGVec2i& s)
|
||||
{
|
||||
if( _user_max_size == s )
|
||||
return;
|
||||
|
||||
_user_max_size = s;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void NasalWidget::setLayoutSizeHint(const SGVec2i& s)
|
||||
{
|
||||
if( _layout_size_hint == s )
|
||||
return;
|
||||
|
||||
_layout_size_hint = s;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void NasalWidget::setLayoutMinimumSize(const SGVec2i& s)
|
||||
{
|
||||
if( _layout_min_size == s )
|
||||
return;
|
||||
|
||||
_layout_min_size = s;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void NasalWidget::setLayoutMaximumSize(const SGVec2i& s)
|
||||
{
|
||||
if( _layout_max_size == s )
|
||||
return;
|
||||
|
||||
_layout_max_size = s;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool NasalWidget::hasHeightForWidth() const
|
||||
{
|
||||
return !_height_for_width.empty() || !_min_height_for_width.empty();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int NasalWidget::heightForWidth(int w) const
|
||||
{
|
||||
return callHeightForWidthFunc( _height_for_width.empty()
|
||||
? _min_height_for_width
|
||||
: _height_for_width, w );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int NasalWidget::minimumHeightForWidth(int w) const
|
||||
{
|
||||
return callHeightForWidthFunc( _min_height_for_width.empty()
|
||||
? _height_for_width
|
||||
: _min_height_for_width, w );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
static naRef f_makeNasalWidget(const nasal::CallContext& ctx)
|
||||
{
|
||||
return ctx.to_nasal(NasalWidgetRef(
|
||||
new NasalWidget( ctx.requireArg<naRef>(0) )
|
||||
));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void NasalWidget::setupGhost(nasal::Hash& ns)
|
||||
{
|
||||
nasal::Ghost<NasalWidgetRef>::init("canvas.Widget")
|
||||
.bases<LayoutItemRef>()
|
||||
.bases<nasal::ObjectRef>()
|
||||
.method("setSetGeometryFunc", &NasalWidget::setSetGeometryFunc)
|
||||
.method("setMinimumHeightForWidthFunc",
|
||||
&NasalWidget::setMinimumHeightForWidthFunc)
|
||||
.method("setHeightForWidthFunc", &NasalWidget::setHeightForWidthFunc)
|
||||
.method("setSizeHint", &NasalWidget::setSizeHint)
|
||||
.method("setMinimumSize", &NasalWidget::setMinimumSize)
|
||||
.method("setMaximumSize", &NasalWidget::setMaximumSize)
|
||||
.method("setLayoutSizeHint", &NasalWidget::setLayoutSizeHint)
|
||||
.method("setLayoutMinimumSize", &NasalWidget::setLayoutMinimumSize)
|
||||
.method("setLayoutMaximumSize", &NasalWidget::setLayoutMaximumSize);
|
||||
|
||||
nasal::Hash widget_hash = ns.createHash("Widget");
|
||||
widget_hash.set("new", &f_makeNasalWidget);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
int NasalWidget::callHeightForWidthFunc( const HeightForWidthFunc& hfw,
|
||||
int w ) const
|
||||
{
|
||||
if( hfw.empty() )
|
||||
return -1;
|
||||
|
||||
naContext c = naNewContext();
|
||||
try
|
||||
{
|
||||
return hfw(nasal::to_nasal(c, const_cast<NasalWidget*>(this)), w);
|
||||
}
|
||||
catch( std::exception const& ex )
|
||||
{
|
||||
SG_LOG(
|
||||
SG_GUI,
|
||||
SG_WARN,
|
||||
"NasalWidget.heightForWidth: callback error: '" << ex.what() << "'"
|
||||
);
|
||||
}
|
||||
naFreeContext(c);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SGVec2i NasalWidget::sizeHintImpl() const
|
||||
{
|
||||
return SGVec2i(
|
||||
_user_size_hint.x() > 0 ? _user_size_hint.x() : _layout_size_hint.x(),
|
||||
_user_size_hint.y() > 0 ? _user_size_hint.y() : _layout_size_hint.y()
|
||||
);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SGVec2i NasalWidget::minimumSizeImpl() const
|
||||
{
|
||||
return SGVec2i(
|
||||
_user_min_size.x() > 0 ? _user_min_size.x() : _layout_min_size.x(),
|
||||
_user_min_size.y() > 0 ? _user_min_size.y() : _layout_min_size.y()
|
||||
);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SGVec2i NasalWidget::maximumSizeImpl() const
|
||||
{
|
||||
return SGVec2i(
|
||||
_user_max_size.x() < MAX_SIZE.x() ? _user_max_size.x()
|
||||
: _layout_max_size.x(),
|
||||
_user_max_size.y() < MAX_SIZE.y() ? _user_max_size.y()
|
||||
: _layout_max_size.y()
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,129 +0,0 @@
|
||||
///@file Glue for GUI widgets implemented in Nasal space
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef SG_CANVAS_NASAL_WIDGET_HXX_
|
||||
#define SG_CANVAS_NASAL_WIDGET_HXX_
|
||||
|
||||
#include "LayoutItem.hxx"
|
||||
|
||||
#include <simgear/nasal/cppbind/from_nasal.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalHash.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalObject.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
/**
|
||||
* Baseclass/ghost to create widgets with Nasal.
|
||||
*/
|
||||
class NasalWidget:
|
||||
public LayoutItem,
|
||||
public nasal::Object
|
||||
{
|
||||
public:
|
||||
|
||||
typedef boost::function<void (nasal::Me, const SGRecti&)> SetGeometryFunc;
|
||||
typedef boost::function<int (nasal::Me, int)> HeightForWidthFunc;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param impl Initial implementation hash (nasal part of
|
||||
* implementation)
|
||||
*/
|
||||
NasalWidget(naRef impl);
|
||||
|
||||
~NasalWidget();
|
||||
|
||||
virtual void invalidate();
|
||||
virtual void setGeometry(const SGRecti& geom);
|
||||
virtual void onRemove();
|
||||
|
||||
void setSetGeometryFunc(const SetGeometryFunc& func);
|
||||
void setHeightForWidthFunc(const HeightForWidthFunc& func);
|
||||
void setMinimumHeightForWidthFunc(const HeightForWidthFunc& func);
|
||||
|
||||
/** Set size hint.
|
||||
*
|
||||
* Overrides default size hint. Set to (0, 0) to fall back to default size
|
||||
* hint.
|
||||
*/
|
||||
void setSizeHint(const SGVec2i& s);
|
||||
|
||||
/** Set minimum size.
|
||||
*
|
||||
* Overrides default minimum size. Set to (0, 0) to fall back to default
|
||||
* minimum size.
|
||||
*/
|
||||
void setMinimumSize(const SGVec2i& s);
|
||||
|
||||
/** Set maximum size.
|
||||
*
|
||||
* Overrides default maximum size hint. Set to LayoutItem::MAX_SIZE to
|
||||
* fall back to default maximum size.
|
||||
*/
|
||||
void setMaximumSize(const SGVec2i& s);
|
||||
|
||||
void setLayoutSizeHint(const SGVec2i& s);
|
||||
void setLayoutMinimumSize(const SGVec2i& s);
|
||||
void setLayoutMaximumSize(const SGVec2i& s);
|
||||
|
||||
virtual bool hasHeightForWidth() const;
|
||||
virtual int heightForWidth(int w) const;
|
||||
virtual int minimumHeightForWidth(int w) const;
|
||||
|
||||
/**
|
||||
* @param ns Namespace to register the class interface
|
||||
*/
|
||||
static void setupGhost(nasal::Hash& ns);
|
||||
|
||||
protected:
|
||||
enum WidgetFlags
|
||||
{
|
||||
LAYOUT_DIRTY = LayoutItem::LAST_FLAG << 1,
|
||||
LAST_FLAG = LAYOUT_DIRTY
|
||||
};
|
||||
|
||||
SetGeometryFunc _set_geometry;
|
||||
HeightForWidthFunc _height_for_width,
|
||||
_min_height_for_width;
|
||||
|
||||
SGVec2i _layout_size_hint,
|
||||
_layout_min_size,
|
||||
_layout_max_size,
|
||||
_user_size_hint,
|
||||
_user_min_size,
|
||||
_user_max_size;
|
||||
|
||||
int callHeightForWidthFunc( const HeightForWidthFunc& hfw,
|
||||
int w ) const;
|
||||
|
||||
virtual SGVec2i sizeHintImpl() const;
|
||||
virtual SGVec2i minimumSizeImpl() const;
|
||||
virtual SGVec2i maximumSizeImpl() const;
|
||||
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<NasalWidget> NasalWidgetRef;
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
|
||||
#endif /* SG_CANVAS_NASAL_WIDGET_HXX_ */
|
||||
@@ -1,36 +0,0 @@
|
||||
// Element providing blank space in a layout.
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "SpacerItem.hxx"
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SpacerItem::SpacerItem( const SGVec2i& size,
|
||||
const SGVec2i& max_size )
|
||||
{
|
||||
_size_hint = size;
|
||||
_min_size = size;
|
||||
_max_size = max_size;
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
@@ -1,43 +0,0 @@
|
||||
///@file Element providing blank space in a layout.
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef SG_CANVAS_SPACER_ITEM_HXX_
|
||||
#define SG_CANVAS_SPACER_ITEM_HXX_
|
||||
|
||||
#include "LayoutItem.hxx"
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
namespace canvas
|
||||
{
|
||||
/**
|
||||
* Element for providing blank space in a layout.
|
||||
*/
|
||||
class SpacerItem:
|
||||
public LayoutItem
|
||||
{
|
||||
public:
|
||||
SpacerItem( const SGVec2i& size = SGVec2i(0, 0),
|
||||
const SGVec2i& max_size = MAX_SIZE );
|
||||
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* SG_CANVAS_SPACER_ITEM_HXX_ */
|
||||
@@ -1,461 +0,0 @@
|
||||
// Testing canvas layouting system
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#define BOOST_TEST_MODULE canvas_layout
|
||||
#include <BoostTestTargetConfig.h>
|
||||
|
||||
#include "BoxLayout.hxx"
|
||||
#include "NasalWidget.hxx"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <cstdlib>
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
struct SetLogLevelFixture
|
||||
{
|
||||
SetLogLevelFixture()
|
||||
{
|
||||
sglog().set_log_priority(SG_DEBUG);
|
||||
}
|
||||
};
|
||||
BOOST_GLOBAL_FIXTURE(SetLogLevelFixture);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
namespace sc = simgear::canvas;
|
||||
|
||||
class TestWidget:
|
||||
public sc::LayoutItem
|
||||
{
|
||||
public:
|
||||
TestWidget( const SGVec2i& min_size,
|
||||
const SGVec2i& size_hint,
|
||||
const SGVec2i& max_size = MAX_SIZE )
|
||||
{
|
||||
_size_hint = size_hint;
|
||||
_min_size = min_size;
|
||||
_max_size = max_size;
|
||||
}
|
||||
|
||||
TestWidget(const TestWidget& rhs)
|
||||
{
|
||||
_size_hint = rhs._size_hint;
|
||||
_min_size = rhs._min_size;
|
||||
_max_size = rhs._max_size;
|
||||
}
|
||||
|
||||
void setMinSize(const SGVec2i& size) { _min_size = size; }
|
||||
void setMaxSize(const SGVec2i& size) { _max_size = size; }
|
||||
void setSizeHint(const SGVec2i& size) { _size_hint = size; }
|
||||
|
||||
virtual void setGeometry(const SGRecti& geom) { _geom = geom; }
|
||||
virtual SGRecti geometry() const { return _geom; }
|
||||
|
||||
protected:
|
||||
|
||||
SGRecti _geom;
|
||||
|
||||
virtual SGVec2i sizeHintImpl() const { return _size_hint; }
|
||||
virtual SGVec2i minimumSizeImpl() const { return _min_size; }
|
||||
virtual SGVec2i maximumSizeImpl() const { return _max_size; }
|
||||
};
|
||||
|
||||
class TestWidgetHFW:
|
||||
public TestWidget
|
||||
{
|
||||
public:
|
||||
TestWidgetHFW( const SGVec2i& min_size,
|
||||
const SGVec2i& size_hint,
|
||||
const SGVec2i& max_size = MAX_SIZE ):
|
||||
TestWidget(min_size, size_hint, max_size)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual bool hasHeightForWidth() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual int heightForWidth(int w) const
|
||||
{
|
||||
return _size_hint.x() * _size_hint.y() / w;
|
||||
}
|
||||
|
||||
virtual int minimumHeightForWidth(int w) const
|
||||
{
|
||||
return _min_size.x() * _min_size.y() / w;
|
||||
}
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<TestWidget> TestWidgetRef;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
BOOST_AUTO_TEST_CASE( horizontal_layout )
|
||||
{
|
||||
sc::BoxLayout box_layout(sc::BoxLayout::BottomToTop);
|
||||
box_layout.setSpacing(5);
|
||||
|
||||
BOOST_CHECK_EQUAL(box_layout.direction(), sc::BoxLayout::BottomToTop);
|
||||
BOOST_CHECK_EQUAL(box_layout.spacing(), 5);
|
||||
|
||||
box_layout.setDirection(sc::BoxLayout::LeftToRight);
|
||||
box_layout.setSpacing(9);
|
||||
|
||||
BOOST_CHECK_EQUAL(box_layout.direction(), sc::BoxLayout::LeftToRight);
|
||||
BOOST_CHECK_EQUAL(box_layout.spacing(), 9);
|
||||
|
||||
TestWidgetRef fixed_size_widget( new TestWidget( SGVec2i(16, 16),
|
||||
SGVec2i(16, 16),
|
||||
SGVec2i(16, 16) ) );
|
||||
box_layout.addItem(fixed_size_widget);
|
||||
|
||||
BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(16, 16));
|
||||
BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(16, 16));
|
||||
BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(16, 16));
|
||||
|
||||
TestWidgetRef limited_resize_widget( new TestWidget( SGVec2i(16, 16),
|
||||
SGVec2i(32, 32),
|
||||
SGVec2i(256, 64) ) );
|
||||
box_layout.addItem(limited_resize_widget);
|
||||
|
||||
// Combined sizes of both widget plus the padding between them
|
||||
BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(41, 16));
|
||||
BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(57, 32));
|
||||
BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(281, 64));
|
||||
|
||||
// Test with different spacing/padding
|
||||
box_layout.setSpacing(5);
|
||||
|
||||
BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(37, 16));
|
||||
BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(53, 32));
|
||||
BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(277, 64));
|
||||
|
||||
box_layout.setGeometry(SGRecti(0, 0, 128, 32));
|
||||
|
||||
// Fixed size for first widget and remaining space goes to second widget
|
||||
BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(0, 8, 16, 16));
|
||||
BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(21, 0, 107, 32));
|
||||
|
||||
TestWidgetRef stretch_widget( new TestWidget( SGVec2i(16, 16),
|
||||
SGVec2i(32, 32),
|
||||
SGVec2i(128, 32) ) );
|
||||
box_layout.addItem(stretch_widget, 1);
|
||||
box_layout.update();
|
||||
|
||||
BOOST_CHECK_EQUAL(box_layout.minimumSize(), SGVec2i(58, 16));
|
||||
BOOST_CHECK_EQUAL(box_layout.sizeHint(), SGVec2i(90, 32));
|
||||
BOOST_CHECK_EQUAL(box_layout.maximumSize(), SGVec2i(410, 64));
|
||||
|
||||
// Due to the stretch factor only the last widget gets additional space. All
|
||||
// other widgets get the preferred size.
|
||||
BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(0, 8, 16, 16));
|
||||
BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(21, 0, 32, 32));
|
||||
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(58, 0, 70, 32));
|
||||
|
||||
// Test stretch factor
|
||||
TestWidgetRef fast_stretch( new TestWidget(*stretch_widget) );
|
||||
sc::BoxLayout box_layout_stretch(sc::BoxLayout::LeftToRight);
|
||||
|
||||
box_layout_stretch.addItem(stretch_widget, 1);
|
||||
box_layout_stretch.addItem(fast_stretch, 2);
|
||||
|
||||
box_layout_stretch.setGeometry(SGRecti(0,0,128,32));
|
||||
|
||||
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 41, 32));
|
||||
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(46, 0, 82, 32));
|
||||
|
||||
box_layout_stretch.setGeometry(SGRecti(0,0,256,32));
|
||||
|
||||
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(0, 0, 123, 32));
|
||||
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(128, 0, 128, 32));
|
||||
|
||||
// Test superflous space to padding
|
||||
box_layout_stretch.setGeometry(SGRecti(0,0,512,32));
|
||||
|
||||
BOOST_CHECK_EQUAL(stretch_widget->geometry(), SGRecti(83, 0, 128, 32));
|
||||
BOOST_CHECK_EQUAL(fast_stretch->geometry(), SGRecti(300, 0, 128, 32));
|
||||
|
||||
// Test more space then preferred, but less than maximum
|
||||
{
|
||||
sc::HBoxLayout hbox;
|
||||
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
|
||||
SGVec2i(32, 32),
|
||||
SGVec2i(9999, 32) ) ),
|
||||
w2( new TestWidget(*w1) );
|
||||
|
||||
hbox.addItem(w1);
|
||||
hbox.addItem(w2);
|
||||
|
||||
hbox.setGeometry( SGRecti(0, 0, 256, 32) );
|
||||
|
||||
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 126, 32));
|
||||
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(131, 0, 125, 32));
|
||||
|
||||
hbox.setStretch(0, 1);
|
||||
hbox.setStretch(1, 1);
|
||||
|
||||
BOOST_CHECK_EQUAL(hbox.stretch(0), 1);
|
||||
BOOST_CHECK_EQUAL(hbox.stretch(1), 1);
|
||||
|
||||
hbox.update();
|
||||
|
||||
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 125, 32));
|
||||
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(130, 0, 126, 32));
|
||||
|
||||
BOOST_REQUIRE( hbox.setStretchFactor(w1, 2) );
|
||||
BOOST_REQUIRE( hbox.setStretchFactor(w2, 3) );
|
||||
BOOST_CHECK_EQUAL(hbox.stretch(0), 2);
|
||||
BOOST_CHECK_EQUAL(hbox.stretch(1), 3);
|
||||
|
||||
hbox.removeItem(w1);
|
||||
|
||||
BOOST_CHECK( !hbox.setStretchFactor(w1, 0) );
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
BOOST_AUTO_TEST_CASE( spacer_layouting )
|
||||
{
|
||||
sc::HBoxLayout hbox;
|
||||
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
|
||||
SGVec2i(32, 32),
|
||||
SGVec2i(9999, 9999) ) ),
|
||||
w2( new TestWidget(*w1) );
|
||||
|
||||
hbox.addItem(w1);
|
||||
hbox.addItem(w2);
|
||||
hbox.addStretch(1);
|
||||
|
||||
BOOST_CHECK_EQUAL(hbox.minimumSize(), SGVec2i(37, 16));
|
||||
BOOST_CHECK_EQUAL(hbox.sizeHint(), SGVec2i(69, 32));
|
||||
BOOST_CHECK_EQUAL(hbox.maximumSize(), sc::LayoutItem::MAX_SIZE);
|
||||
|
||||
hbox.setGeometry(SGRecti(0, 0, 256, 40));
|
||||
|
||||
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 32, 40));
|
||||
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(37, 0, 32, 40));
|
||||
|
||||
// now center with increased spacing between both widgets
|
||||
hbox.insertStretch(0, 1);
|
||||
hbox.insertSpacing(2, 10);
|
||||
|
||||
BOOST_CHECK_EQUAL(hbox.minimumSize(), SGVec2i(47, 16));
|
||||
BOOST_CHECK_EQUAL(hbox.sizeHint(), SGVec2i(79, 32));
|
||||
BOOST_CHECK_EQUAL(hbox.maximumSize(), sc::LayoutItem::MAX_SIZE);
|
||||
|
||||
hbox.update();
|
||||
|
||||
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(88, 0, 32, 40));
|
||||
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(135, 0, 32, 40));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
BOOST_AUTO_TEST_CASE( vertical_layout)
|
||||
{
|
||||
sc::BoxLayout vbox(sc::BoxLayout::TopToBottom);
|
||||
vbox.setSpacing(7);
|
||||
|
||||
TestWidgetRef fixed_size_widget( new TestWidget( SGVec2i(16, 16),
|
||||
SGVec2i(16, 16),
|
||||
SGVec2i(16, 16) ) );
|
||||
TestWidgetRef limited_resize_widget( new TestWidget( SGVec2i(16, 16),
|
||||
SGVec2i(32, 32),
|
||||
SGVec2i(256, 64) ) );
|
||||
|
||||
vbox.addItem(fixed_size_widget);
|
||||
vbox.addItem(limited_resize_widget);
|
||||
|
||||
BOOST_CHECK_EQUAL(vbox.minimumSize(), SGVec2i(16, 39));
|
||||
BOOST_CHECK_EQUAL(vbox.sizeHint(), SGVec2i(32, 55));
|
||||
BOOST_CHECK_EQUAL(vbox.maximumSize(), SGVec2i(256, 87));
|
||||
|
||||
vbox.setGeometry(SGRecti(10, 20, 16, 55));
|
||||
|
||||
BOOST_CHECK_EQUAL(fixed_size_widget->geometry(), SGRecti(10, 20, 16, 16));
|
||||
BOOST_CHECK_EQUAL(limited_resize_widget->geometry(), SGRecti(10, 43, 16, 32));
|
||||
|
||||
vbox.setDirection(sc::BoxLayout::BottomToTop);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
BOOST_AUTO_TEST_CASE( boxlayout_insert_remove )
|
||||
{
|
||||
sc::HBoxLayout hbox;
|
||||
|
||||
BOOST_CHECK_EQUAL(hbox.count(), 0);
|
||||
BOOST_CHECK(!hbox.itemAt(0));
|
||||
BOOST_CHECK(!hbox.takeAt(0));
|
||||
|
||||
TestWidgetRef w1( new TestWidget( SGVec2i(16, 16),
|
||||
SGVec2i(32, 32),
|
||||
SGVec2i(9999, 32) ) ),
|
||||
w2( new TestWidget(*w1) );
|
||||
|
||||
hbox.addItem(w1);
|
||||
BOOST_CHECK_EQUAL(hbox.count(), 1);
|
||||
BOOST_CHECK_EQUAL(hbox.itemAt(0), w1);
|
||||
|
||||
hbox.insertItem(0, w2);
|
||||
BOOST_CHECK_EQUAL(hbox.count(), 2);
|
||||
BOOST_CHECK_EQUAL(hbox.itemAt(0), w2);
|
||||
BOOST_CHECK_EQUAL(hbox.itemAt(1), w1);
|
||||
|
||||
hbox.removeItem(w2);
|
||||
BOOST_CHECK_EQUAL(hbox.count(), 1);
|
||||
BOOST_CHECK_EQUAL(hbox.itemAt(0), w1);
|
||||
|
||||
hbox.addItem(w2);
|
||||
BOOST_CHECK_EQUAL(hbox.count(), 2);
|
||||
|
||||
hbox.clear();
|
||||
BOOST_CHECK_EQUAL(hbox.count(), 0);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
BOOST_AUTO_TEST_CASE( boxlayout_hfw )
|
||||
{
|
||||
TestWidgetRef w1( new TestWidgetHFW( SGVec2i(16, 16),
|
||||
SGVec2i(32, 32) ) ),
|
||||
w2( new TestWidgetHFW( SGVec2i(24, 24),
|
||||
SGVec2i(48, 48) ) );
|
||||
|
||||
BOOST_CHECK_EQUAL(w1->heightForWidth(16), 64);
|
||||
BOOST_CHECK_EQUAL(w1->minimumHeightForWidth(16), 16);
|
||||
BOOST_CHECK_EQUAL(w2->heightForWidth(24), 96);
|
||||
BOOST_CHECK_EQUAL(w2->minimumHeightForWidth(24), 24);
|
||||
|
||||
TestWidgetRef w_no_hfw( new TestWidget( SGVec2i(16, 16),
|
||||
SGVec2i(32, 32) ) );
|
||||
BOOST_CHECK(!w_no_hfw->hasHeightForWidth());
|
||||
BOOST_CHECK_EQUAL(w_no_hfw->heightForWidth(16), -1);
|
||||
BOOST_CHECK_EQUAL(w_no_hfw->minimumHeightForWidth(16), -1);
|
||||
|
||||
// horizontal
|
||||
sc::HBoxLayout hbox;
|
||||
hbox.setSpacing(5);
|
||||
hbox.addItem(w1);
|
||||
hbox.addItem(w2);
|
||||
|
||||
BOOST_CHECK_EQUAL(hbox.heightForWidth(45), w2->heightForWidth(24));
|
||||
BOOST_CHECK_EQUAL(hbox.heightForWidth(85), w2->heightForWidth(48));
|
||||
|
||||
hbox.addItem(w_no_hfw);
|
||||
|
||||
BOOST_CHECK_EQUAL(hbox.heightForWidth(66), 96);
|
||||
BOOST_CHECK_EQUAL(hbox.heightForWidth(122), 48);
|
||||
BOOST_CHECK_EQUAL(hbox.minimumHeightForWidth(66), 24);
|
||||
BOOST_CHECK_EQUAL(hbox.minimumHeightForWidth(122), 16);
|
||||
|
||||
hbox.setGeometry(SGRecti(0, 0, 66, 24));
|
||||
|
||||
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 16, 24));
|
||||
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(21, 0, 24, 24));
|
||||
BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(50, 0, 16, 24));
|
||||
|
||||
// vertical
|
||||
sc::VBoxLayout vbox;
|
||||
vbox.setSpacing(5);
|
||||
vbox.addItem(w1);
|
||||
vbox.addItem(w2);
|
||||
|
||||
BOOST_CHECK_EQUAL(vbox.heightForWidth(24), 143);
|
||||
BOOST_CHECK_EQUAL(vbox.heightForWidth(48), 74);
|
||||
BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(24), 39);
|
||||
BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(48), 22);
|
||||
|
||||
vbox.addItem(w_no_hfw);
|
||||
|
||||
BOOST_CHECK_EQUAL(vbox.heightForWidth(24), 180);
|
||||
BOOST_CHECK_EQUAL(vbox.heightForWidth(48), 111);
|
||||
BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(24), 60);
|
||||
BOOST_CHECK_EQUAL(vbox.minimumHeightForWidth(48), 43);
|
||||
|
||||
SGVec2i min_size = vbox.minimumSize(),
|
||||
size_hint = vbox.sizeHint();
|
||||
|
||||
BOOST_CHECK_EQUAL(min_size, SGVec2i(24, 66));
|
||||
BOOST_CHECK_EQUAL(size_hint, SGVec2i(48, 122));
|
||||
|
||||
vbox.setGeometry(SGRecti(0, 0, 24, 122));
|
||||
|
||||
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 24, 33));
|
||||
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 38, 24, 47));
|
||||
BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(0, 90, 24, 32));
|
||||
|
||||
// Vertical layouting modifies size hints, so check if they are correctly
|
||||
// restored
|
||||
BOOST_CHECK_EQUAL(min_size, vbox.minimumSize());
|
||||
BOOST_CHECK_EQUAL(size_hint, vbox.sizeHint());
|
||||
|
||||
vbox.setGeometry(SGRecti(0, 0, 50, 122));
|
||||
|
||||
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 50, 25));
|
||||
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 30, 50, 51));
|
||||
BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(0, 86, 50, 36));
|
||||
|
||||
// Same geometry as before -> should get same widget geometry
|
||||
// (check internal size hint cache updates correctly)
|
||||
vbox.setGeometry(SGRecti(0, 0, 24, 122));
|
||||
|
||||
BOOST_CHECK_EQUAL(w1->geometry(), SGRecti(0, 0, 24, 33));
|
||||
BOOST_CHECK_EQUAL(w2->geometry(), SGRecti(0, 38, 24, 47));
|
||||
BOOST_CHECK_EQUAL(w_no_hfw->geometry(), SGRecti(0, 90, 24, 32));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
BOOST_AUTO_TEST_CASE( nasal_widget )
|
||||
{
|
||||
naContext c = naNewContext();
|
||||
naRef me = naNewHash(c);
|
||||
|
||||
sc::NasalWidgetRef w( new sc::NasalWidget(me) );
|
||||
|
||||
// Default layout sizes (no user set values)
|
||||
BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(16, 16));
|
||||
BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(32, 32));
|
||||
BOOST_CHECK_EQUAL(w->maximumSize(), sc::LayoutItem::MAX_SIZE);
|
||||
|
||||
// Changed layout sizes
|
||||
w->setLayoutMinimumSize( SGVec2i(2, 12) );
|
||||
w->setLayoutSizeHint( SGVec2i(3, 13) );
|
||||
w->setLayoutMaximumSize( SGVec2i(4, 14) );
|
||||
|
||||
BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(2, 12));
|
||||
BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(3, 13));
|
||||
BOOST_CHECK_EQUAL(w->maximumSize(), SGVec2i(4, 14));
|
||||
|
||||
// User set values (overwrite layout sizes)
|
||||
w->setMinimumSize( SGVec2i(15, 16) );
|
||||
w->setSizeHint( SGVec2i(17, 18) );
|
||||
w->setMaximumSize( SGVec2i(19, 20) );
|
||||
|
||||
BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(15, 16));
|
||||
BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(17, 18));
|
||||
BOOST_CHECK_EQUAL(w->maximumSize(), SGVec2i(19, 20));
|
||||
|
||||
// Only vertical user set values (layout/default for horizontal hints)
|
||||
w->setMinimumSize( SGVec2i(0, 21) );
|
||||
w->setSizeHint( SGVec2i(0, 22) );
|
||||
w->setMaximumSize( SGVec2i(SGLimits<int>::max(), 23) );
|
||||
|
||||
BOOST_CHECK_EQUAL(w->minimumSize(), SGVec2i(2, 21));
|
||||
BOOST_CHECK_EQUAL(w->sizeHint(), SGVec2i(3, 22));
|
||||
BOOST_CHECK_EQUAL(w->maximumSize(), SGVec2i(4, 23));
|
||||
|
||||
naFreeContext(c);
|
||||
}
|
||||
@@ -31,8 +31,7 @@ typedef enum {
|
||||
SG_ENVIRONMENT = 0x00100000,
|
||||
SG_SOUND = 0x00200000,
|
||||
SG_NAVAID = 0x00400000,
|
||||
SG_GUI = 0x00800000,
|
||||
SG_UNDEFD = 0x01000000, // For range checking
|
||||
SG_UNDEFD = 0x00800000, // For range checking
|
||||
|
||||
SG_ALL = 0xFFFFFFFF
|
||||
} sgDebugClass;
|
||||
|
||||
@@ -70,7 +70,6 @@ const char* debugClassToString(sgDebugClass c)
|
||||
case SG_ENVIRONMENT:return "environment";
|
||||
case SG_SOUND: return "sound";
|
||||
case SG_NAVAID: return "navaid";
|
||||
case SG_GUI: return "gui";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
@@ -439,4 +438,4 @@ void requestConsole()
|
||||
global_privateLogstream->requestConsole();
|
||||
}
|
||||
|
||||
} // of namespace simgear
|
||||
} // of namespace simgear
|
||||
@@ -122,28 +122,27 @@ public:
|
||||
// socket-level errors
|
||||
virtual void handleError(int error)
|
||||
{
|
||||
const char* errStr = strerror(error);
|
||||
if (!activeRequest)
|
||||
{
|
||||
// connection level failure, eg name lookup or routing
|
||||
// we won't have an active request yet, so let's fail all of the
|
||||
// requests since we presume it's a systematic failure for
|
||||
// the host in question
|
||||
if (error == ENOENT) {
|
||||
// name lookup failure
|
||||
// we won't have an active request yet, so the logic below won't
|
||||
// fire to actually call setFailure. Let's fail all of the requests
|
||||
BOOST_FOREACH(Request_ptr req, sentRequests) {
|
||||
req->setFailure(error, errStr);
|
||||
req->setFailure(error, "hostname lookup failure");
|
||||
}
|
||||
|
||||
BOOST_FOREACH(Request_ptr req, queuedRequests) {
|
||||
req->setFailure(error, errStr);
|
||||
req->setFailure(error, "hostname lookup failure");
|
||||
}
|
||||
|
||||
// name lookup failure, abandon all requests on this connection
|
||||
sentRequests.clear();
|
||||
queuedRequests.clear();
|
||||
}
|
||||
|
||||
NetChat::handleError(error);
|
||||
if (activeRequest) {
|
||||
activeRequest->setFailure(error, errStr);
|
||||
SG_LOG(SG_IO, SG_INFO, "HTTP socket error");
|
||||
activeRequest->setFailure(error, "socket error");
|
||||
activeRequest = NULL;
|
||||
_contentDecoder.reset();
|
||||
}
|
||||
@@ -253,7 +252,6 @@ public:
|
||||
|
||||
if (state == STATE_CLOSED) {
|
||||
if (!connectToHost()) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ Request* Request::done(const Callback& cb)
|
||||
if( _ready_state == DONE )
|
||||
cb(this);
|
||||
else
|
||||
_cb_done.push_back(cb);
|
||||
_cb_done = cb;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -50,7 +50,7 @@ Request* Request::fail(const Callback& cb)
|
||||
if( _ready_state == FAILED )
|
||||
cb(this);
|
||||
else
|
||||
_cb_fail.push_back(cb);
|
||||
_cb_fail = cb;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -61,7 +61,7 @@ Request* Request::always(const Callback& cb)
|
||||
if( isComplete() )
|
||||
cb(this);
|
||||
else
|
||||
_cb_always.push_back(cb);
|
||||
_cb_always = cb;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -272,16 +272,6 @@ std::string Request::hostAndPort() const
|
||||
return u.substr(schemeEnd + 3, hostEnd - (schemeEnd + 3));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
std::string Request::responseMime() const
|
||||
{
|
||||
std::string content_type = _responseHeaders.get("content-type");
|
||||
if( content_type.empty() )
|
||||
return "application/octet-stream";
|
||||
|
||||
return content_type.substr(0, content_type.find(';'));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Request::setResponseLength(unsigned int l)
|
||||
{
|
||||
@@ -304,9 +294,7 @@ void Request::setFailure(int code, const std::string& reason)
|
||||
{
|
||||
_responseStatus = code;
|
||||
_responseReason = reason;
|
||||
|
||||
if( !isComplete() )
|
||||
setReadyState(FAILED);
|
||||
setReadyState(FAILED);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -321,19 +309,22 @@ void Request::setReadyState(ReadyState state)
|
||||
onDone();
|
||||
onAlways();
|
||||
|
||||
_cb_done(this);
|
||||
if( _cb_done )
|
||||
_cb_done(this);
|
||||
}
|
||||
else if( state == FAILED )
|
||||
{
|
||||
onFail();
|
||||
onAlways();
|
||||
|
||||
_cb_fail(this);
|
||||
if( _cb_fail )
|
||||
_cb_fail(this);
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
_cb_always(this);
|
||||
if( _cb_always )
|
||||
_cb_always(this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -345,6 +336,9 @@ void Request::abort()
|
||||
//----------------------------------------------------------------------------
|
||||
void Request::abort(const std::string& reason)
|
||||
{
|
||||
if( isComplete() )
|
||||
return;
|
||||
|
||||
setFailure(-1, reason);
|
||||
_willClose = true;
|
||||
}
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <simgear/structure/function_list.hxx>
|
||||
#include <simgear/structure/map.hxx>
|
||||
#include <simgear/structure/SGReferenced.hxx>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
#include <simgear/math/sg_types.hxx>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
class SGPropertyNode;
|
||||
|
||||
@@ -47,21 +46,15 @@ public:
|
||||
{ return _request_headers.get(key); }
|
||||
|
||||
/**
|
||||
* Add a handler to be called when the request successfully completes.
|
||||
* Set the handler to be called when the request successfully completes.
|
||||
*
|
||||
* @note If the request is already complete, the handler is called
|
||||
* immediately.
|
||||
*/
|
||||
Request* done(const Callback& cb);
|
||||
|
||||
template<class C>
|
||||
Request* done(C* instance, void (C::*mem_func)(Request*))
|
||||
{
|
||||
return done(boost::bind(mem_func, instance, _1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a handler to be called when the request completes or aborts with an
|
||||
* Set the handler to be called when the request completes or aborts with an
|
||||
* error.
|
||||
*
|
||||
* @note If the request has already failed, the handler is called
|
||||
@@ -69,27 +62,15 @@ public:
|
||||
*/
|
||||
Request* fail(const Callback& cb);
|
||||
|
||||
template<class C>
|
||||
Request* fail(C* instance, void (C::*mem_func)(Request*))
|
||||
{
|
||||
return fail(boost::bind(mem_func, instance, _1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a handler to be called when the request either successfully completes
|
||||
* or fails.
|
||||
* Set the handler to be called when the request either successfully
|
||||
* completes or fails.
|
||||
*
|
||||
* @note If the request is already complete or has already failed, the
|
||||
* handler is called immediately.
|
||||
*/
|
||||
Request* always(const Callback& cb);
|
||||
|
||||
template<class C>
|
||||
Request* always(C* instance, void (C::*mem_func)(Request*))
|
||||
{
|
||||
return always(boost::bind(mem_func, instance, _1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the data for the body of the request. The request is automatically
|
||||
* send using the POST method.
|
||||
@@ -118,8 +99,6 @@ public:
|
||||
StringMap const& responseHeaders() const
|
||||
{ return _responseHeaders; }
|
||||
|
||||
std::string responseMime() const;
|
||||
|
||||
virtual int responseCode() const
|
||||
{ return _responseStatus; }
|
||||
|
||||
@@ -224,9 +203,9 @@ private:
|
||||
unsigned int _responseLength;
|
||||
unsigned int _receivedBodyBytes;
|
||||
|
||||
function_list<Callback> _cb_done,
|
||||
_cb_fail,
|
||||
_cb_always;
|
||||
Callback _cb_done,
|
||||
_cb_fail,
|
||||
_cb_always;
|
||||
|
||||
ReadyState _ready_state;
|
||||
bool _willClose;
|
||||
|
||||
@@ -82,9 +82,9 @@ int main( int argc, char **argv ) {
|
||||
group_list pts_v = obj.get_pts_v();
|
||||
group_list pts_n = obj.get_pts_n();
|
||||
for ( i = 0; i < (int)pts_v.size(); ++i ) {
|
||||
material = pt_materials[i];
|
||||
vertex_index = pts_v[i];
|
||||
normal_index = pts_n[i];
|
||||
material = pt_materials[i];
|
||||
vertex_index = pts_v[i];
|
||||
normal_index = pts_n[i];
|
||||
cout << "# usemtl " << material << endl;
|
||||
cout << "pt ";
|
||||
for ( j = 0; j < (int)vertex_index.size(); ++j ) {
|
||||
@@ -97,12 +97,12 @@ int main( int argc, char **argv ) {
|
||||
string_list tri_materials = obj.get_tri_materials();
|
||||
group_list tris_v = obj.get_tris_v();
|
||||
group_list tris_n = obj.get_tris_n();
|
||||
group_tci_list tris_tc = obj.get_tris_tcs();
|
||||
group_list tris_tc = obj.get_tris_tc();
|
||||
for ( i = 0; i < (int)tris_v.size(); ++i ) {
|
||||
material = tri_materials[i];
|
||||
vertex_index = tris_v[i];
|
||||
normal_index = tris_n[i];
|
||||
tex_index = tris_tc[0][i];
|
||||
material = tri_materials[i];
|
||||
vertex_index = tris_v[i];
|
||||
normal_index = tris_n[i];
|
||||
tex_index = tris_tc[i];
|
||||
cout << "# usemtl " << material << endl;
|
||||
cout << "f ";
|
||||
for ( j = 0; j < (int)vertex_index.size(); ++j ) {
|
||||
@@ -120,12 +120,12 @@ int main( int argc, char **argv ) {
|
||||
string_list strip_materials = obj.get_strip_materials();
|
||||
group_list strips_v = obj.get_strips_v();
|
||||
group_list strips_n = obj.get_strips_n();
|
||||
group_tci_list strips_tc = obj.get_strips_tcs();
|
||||
group_list strips_tc = obj.get_strips_tc();
|
||||
for ( i = 0; i < (int)strips_v.size(); ++i ) {
|
||||
material = strip_materials[i];
|
||||
vertex_index = strips_v[i];
|
||||
normal_index = strips_n[i];
|
||||
tex_index = strips_tc[0][i];
|
||||
material = strip_materials[i];
|
||||
vertex_index = strips_v[i];
|
||||
normal_index = strips_n[i];
|
||||
tex_index = strips_tc[i];
|
||||
cout << "# usemtl " << material << endl;
|
||||
cout << "ts ";
|
||||
for ( j = 0; j < (int)vertex_index.size(); ++j ) {
|
||||
@@ -142,12 +142,12 @@ int main( int argc, char **argv ) {
|
||||
string_list fan_materials = obj.get_fan_materials();
|
||||
group_list fans_v = obj.get_fans_v();
|
||||
group_list fans_n = obj.get_fans_n();
|
||||
group_tci_list fans_tc = obj.get_fans_tcs();
|
||||
group_list fans_tc = obj.get_fans_tc();
|
||||
for ( i = 0; i < (int)fans_v.size(); ++i ) {
|
||||
material = fan_materials[i];
|
||||
vertex_index = fans_v[i];
|
||||
normal_index = fans_n[i];
|
||||
tex_index = fans_tc[0][i];
|
||||
material = fan_materials[i];
|
||||
vertex_index = fans_v[i];
|
||||
normal_index = fans_n[i];
|
||||
tex_index = fans_tc[i];
|
||||
cout << "# usemtl " << material << endl;
|
||||
cout << "tf ";
|
||||
for ( j = 0; j < (int)vertex_index.size(); ++j ) {
|
||||
|
||||
@@ -58,11 +58,9 @@ enum sgObjectTypes {
|
||||
SG_BOUNDING_SPHERE = 0,
|
||||
|
||||
SG_VERTEX_LIST = 1,
|
||||
SG_COLOR_LIST = 4,
|
||||
SG_NORMAL_LIST = 2,
|
||||
SG_TEXCOORD_LIST = 3,
|
||||
SG_COLOR_LIST = 4,
|
||||
SG_VA_FLOAT_LIST = 5,
|
||||
SG_VA_INTEGER_LIST = 6,
|
||||
|
||||
SG_POINTS = 9,
|
||||
|
||||
@@ -72,32 +70,15 @@ enum sgObjectTypes {
|
||||
};
|
||||
|
||||
enum sgIndexTypes {
|
||||
SG_IDX_VERTICES = 0x01,
|
||||
SG_IDX_NORMALS = 0x02,
|
||||
SG_IDX_COLORS = 0x04,
|
||||
SG_IDX_TEXCOORDS_0 = 0x08,
|
||||
SG_IDX_TEXCOORDS_1 = 0x10,
|
||||
SG_IDX_TEXCOORDS_2 = 0x20,
|
||||
SG_IDX_TEXCOORDS_3 = 0x40,
|
||||
};
|
||||
|
||||
enum sgVertexAttributeTypes {
|
||||
// vertex attributes
|
||||
SG_VA_INTEGER_0 = 0x00000001,
|
||||
SG_VA_INTEGER_1 = 0x00000002,
|
||||
SG_VA_INTEGER_2 = 0x00000004,
|
||||
SG_VA_INTEGER_3 = 0x00000008,
|
||||
|
||||
SG_VA_FLOAT_0 = 0x00000100,
|
||||
SG_VA_FLOAT_1 = 0x00000200,
|
||||
SG_VA_FLOAT_2 = 0x00000400,
|
||||
SG_VA_FLOAT_3 = 0x00000800,
|
||||
SG_IDX_VERTICES = 0x01,
|
||||
SG_IDX_NORMALS = 0x02,
|
||||
SG_IDX_COLORS = 0x04,
|
||||
SG_IDX_TEXCOORDS = 0x08
|
||||
};
|
||||
|
||||
enum sgPropertyTypes {
|
||||
SG_MATERIAL = 0,
|
||||
SG_INDEX_TYPES = 1,
|
||||
SG_VERT_ATTRIBS = 2
|
||||
SG_INDEX_TYPES = 1
|
||||
};
|
||||
|
||||
|
||||
@@ -149,17 +130,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
float readInt()
|
||||
{
|
||||
unsigned int* p = reinterpret_cast<unsigned int*>(ptr + offset);
|
||||
if ( sgIsBigEndian() ) {
|
||||
sgEndianSwap((uint32_t *) p);
|
||||
}
|
||||
|
||||
offset += sizeof(unsigned int);
|
||||
return *p;
|
||||
}
|
||||
|
||||
SGVec3d readVec3d()
|
||||
{
|
||||
double* p = reinterpret_cast<double*>(ptr + offset);
|
||||
@@ -232,19 +202,15 @@ template <class T>
|
||||
static void read_indices(char* buffer,
|
||||
size_t bytes,
|
||||
int indexMask,
|
||||
int vaMask,
|
||||
int_list& vertices,
|
||||
int_list& normals,
|
||||
int_list& colors,
|
||||
tci_list& texCoords,
|
||||
vai_list& vas
|
||||
)
|
||||
int_list& texCoords)
|
||||
{
|
||||
const int indexSize = sizeof(T) * std::bitset<32>((int)indexMask).count();
|
||||
const int vaSize = sizeof(T) * std::bitset<32>((int)vaMask).count();
|
||||
const int count = bytes / (indexSize + vaSize);
|
||||
const int indexSize = sizeof(T) * std::bitset<32>(indexMask).count();
|
||||
const int count = bytes / indexSize;
|
||||
|
||||
// fix endian-ness of the whole lot, if required
|
||||
// fix endian-ness of the whole lot, if required
|
||||
if (sgIsBigEndian()) {
|
||||
int indices = bytes / sizeof(T);
|
||||
T* src = reinterpret_cast<T*>(buffer);
|
||||
@@ -258,21 +224,7 @@ static void read_indices(char* buffer,
|
||||
if (indexMask & SG_IDX_VERTICES) vertices.push_back(*src++);
|
||||
if (indexMask & SG_IDX_NORMALS) normals.push_back(*src++);
|
||||
if (indexMask & SG_IDX_COLORS) colors.push_back(*src++);
|
||||
if (indexMask & SG_IDX_TEXCOORDS_0) texCoords[0].push_back(*src++);
|
||||
if (indexMask & SG_IDX_TEXCOORDS_1) texCoords[1].push_back(*src++);
|
||||
if (indexMask & SG_IDX_TEXCOORDS_2) texCoords[2].push_back(*src++);
|
||||
if (indexMask & SG_IDX_TEXCOORDS_3) texCoords[3].push_back(*src++);
|
||||
|
||||
if ( vaMask ) {
|
||||
if (vaMask & SG_VA_INTEGER_0) vas[0].push_back(*src++);
|
||||
if (vaMask & SG_VA_INTEGER_1) vas[1].push_back(*src++);
|
||||
if (vaMask & SG_VA_INTEGER_2) vas[2].push_back(*src++);
|
||||
if (vaMask & SG_VA_INTEGER_3) vas[3].push_back(*src++);
|
||||
if (vaMask & SG_VA_FLOAT_0) vas[4].push_back(*src++);
|
||||
if (vaMask & SG_VA_FLOAT_1) vas[5].push_back(*src++);
|
||||
if (vaMask & SG_VA_FLOAT_2) vas[6].push_back(*src++);
|
||||
if (vaMask & SG_VA_FLOAT_3) vas[7].push_back(*src++);
|
||||
}
|
||||
if (indexMask & SG_IDX_TEXCOORDS) texCoords.push_back(*src++);
|
||||
} // of elements in the index
|
||||
}
|
||||
|
||||
@@ -297,19 +249,15 @@ void write_indice(gzFile fp, uint32_t value)
|
||||
|
||||
|
||||
template <class T>
|
||||
void write_indices(gzFile fp,
|
||||
unsigned char indexMask,
|
||||
unsigned int vaMask,
|
||||
void write_indices(gzFile fp, unsigned char indexMask,
|
||||
const int_list& vertices,
|
||||
const int_list& normals,
|
||||
const int_list& colors,
|
||||
const tci_list& texCoords,
|
||||
const vai_list& vas )
|
||||
const int_list& texCoords)
|
||||
{
|
||||
unsigned int count = vertices.size();
|
||||
const int indexSize = sizeof(T) * std::bitset<32>((int)indexMask).count();
|
||||
const int vaSize = sizeof(T) * std::bitset<32>((int)vaMask).count();
|
||||
sgWriteUInt(fp, (indexSize + vaSize) * count);
|
||||
const int indexSize = sizeof(T) * std::bitset<32>(indexMask).count();
|
||||
sgWriteUInt(fp, indexSize * count);
|
||||
|
||||
for (unsigned int i=0; i < count; ++i) {
|
||||
write_indice(fp, static_cast<T>(vertices[i]));
|
||||
@@ -320,45 +268,8 @@ void write_indices(gzFile fp,
|
||||
if (indexMask & SG_IDX_COLORS) {
|
||||
write_indice(fp, static_cast<T>(colors[i]));
|
||||
}
|
||||
if (indexMask & SG_IDX_TEXCOORDS_0) {
|
||||
write_indice(fp, static_cast<T>(texCoords[0][i]));
|
||||
}
|
||||
if (indexMask & SG_IDX_TEXCOORDS_1) {
|
||||
write_indice(fp, static_cast<T>(texCoords[1][i]));
|
||||
}
|
||||
if (indexMask & SG_IDX_TEXCOORDS_2) {
|
||||
write_indice(fp, static_cast<T>(texCoords[2][i]));
|
||||
}
|
||||
if (indexMask & SG_IDX_TEXCOORDS_3) {
|
||||
write_indice(fp, static_cast<T>(texCoords[3][i]));
|
||||
}
|
||||
|
||||
if (vaMask) {
|
||||
if (vaMask & SG_VA_INTEGER_0) {
|
||||
write_indice(fp, static_cast<T>(vas[0][i]));
|
||||
}
|
||||
if (vaMask & SG_VA_INTEGER_1) {
|
||||
write_indice(fp, static_cast<T>(vas[1][i]));
|
||||
}
|
||||
if (vaMask & SG_VA_INTEGER_2) {
|
||||
write_indice(fp, static_cast<T>(vas[2][i]));
|
||||
}
|
||||
if (vaMask & SG_VA_INTEGER_3) {
|
||||
write_indice(fp, static_cast<T>(vas[3][i]));
|
||||
}
|
||||
|
||||
if (vaMask & SG_VA_FLOAT_0) {
|
||||
write_indice(fp, static_cast<T>(vas[4][i]));
|
||||
}
|
||||
if (vaMask & SG_VA_FLOAT_1) {
|
||||
write_indice(fp, static_cast<T>(vas[5][i]));
|
||||
}
|
||||
if (vaMask & SG_VA_FLOAT_2) {
|
||||
write_indice(fp, static_cast<T>(vas[6][i]));
|
||||
}
|
||||
if (vaMask & SG_VA_FLOAT_3) {
|
||||
write_indice(fp, static_cast<T>(vas[7][i]));
|
||||
}
|
||||
if (indexMask & SG_IDX_TEXCOORDS) {
|
||||
write_indice(fp, static_cast<T>(texCoords[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -370,15 +281,13 @@ void SGBinObject::read_object( gzFile fp,
|
||||
int nproperties,
|
||||
int nelements,
|
||||
group_list& vertices,
|
||||
group_list& normals,
|
||||
group_list& colors,
|
||||
group_tci_list& texCoords,
|
||||
group_vai_list& vertexAttribs,
|
||||
string_list& materials)
|
||||
group_list& normals,
|
||||
group_list& colors,
|
||||
group_list& texCoords,
|
||||
string_list& materials)
|
||||
{
|
||||
unsigned int nbytes;
|
||||
unsigned int nbytes;
|
||||
unsigned char idx_mask;
|
||||
unsigned int vertex_attrib_mask;
|
||||
int j;
|
||||
sgSimpleBuffer buf( 32768 ); // 32 Kb
|
||||
char material[256];
|
||||
@@ -387,49 +296,26 @@ void SGBinObject::read_object( gzFile fp,
|
||||
if ( obj_type == SG_POINTS ) {
|
||||
idx_mask = SG_IDX_VERTICES;
|
||||
} else {
|
||||
idx_mask = (char)(SG_IDX_VERTICES | SG_IDX_TEXCOORDS_0);
|
||||
idx_mask = (char)(SG_IDX_VERTICES | SG_IDX_TEXCOORDS);
|
||||
}
|
||||
vertex_attrib_mask = 0;
|
||||
|
||||
|
||||
for ( j = 0; j < nproperties; ++j ) {
|
||||
char prop_type;
|
||||
sgReadChar( fp, &prop_type );
|
||||
sgReadUInt( fp, &nbytes );
|
||||
|
||||
buf.resize(nbytes);
|
||||
char *ptr = buf.get_ptr();
|
||||
|
||||
switch( prop_type )
|
||||
{
|
||||
case SG_MATERIAL:
|
||||
sgReadBytes( fp, nbytes, ptr );
|
||||
if (nbytes > 255) {
|
||||
nbytes = 255;
|
||||
}
|
||||
strncpy( material, ptr, nbytes );
|
||||
material[nbytes] = '\0';
|
||||
break;
|
||||
|
||||
case SG_INDEX_TYPES:
|
||||
if (nbytes == 1) {
|
||||
sgReadChar( fp, (char *)&idx_mask );
|
||||
} else {
|
||||
sgReadBytes( fp, nbytes, ptr );
|
||||
}
|
||||
break;
|
||||
|
||||
case SG_VERT_ATTRIBS:
|
||||
if (nbytes == 4) {
|
||||
sgReadUInt( fp, &vertex_attrib_mask );
|
||||
} else {
|
||||
sgReadBytes( fp, nbytes, ptr );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
sgReadBytes( fp, nbytes, ptr );
|
||||
SG_LOG(SG_IO, SG_ALERT, "Found UNKNOWN property type with nbytes == " << nbytes << " mask is " << (int)idx_mask );
|
||||
break;
|
||||
sgReadBytes( fp, nbytes, ptr );
|
||||
if ( prop_type == SG_MATERIAL ) {
|
||||
if (nbytes > 255) {
|
||||
nbytes = 255;
|
||||
}
|
||||
strncpy( material, ptr, nbytes );
|
||||
material[nbytes] = '\0';
|
||||
// cout << "material type = " << material << endl;
|
||||
} else if ( prop_type == SG_INDEX_TYPES ) {
|
||||
idx_mask = ptr[0];
|
||||
//cout << std::hex << "index mask:" << idx_mask << std::dec << endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,7 +323,7 @@ void SGBinObject::read_object( gzFile fp,
|
||||
throw sg_exception("Error reading object properties");
|
||||
}
|
||||
|
||||
size_t indexCount = std::bitset<32>((int)idx_mask).count();
|
||||
size_t indexCount = std::bitset<32>(idx_mask).count();
|
||||
if (indexCount == 0) {
|
||||
throw sg_exception("object index mask has no bits set");
|
||||
}
|
||||
@@ -459,20 +345,17 @@ void SGBinObject::read_object( gzFile fp,
|
||||
int_list vs;
|
||||
int_list ns;
|
||||
int_list cs;
|
||||
tci_list tcs;
|
||||
vai_list vas;
|
||||
|
||||
int_list tcs;
|
||||
if (version >= 10) {
|
||||
read_indices<uint32_t>(ptr, nbytes, idx_mask, vertex_attrib_mask, vs, ns, cs, tcs, vas );
|
||||
read_indices<uint32_t>(ptr, nbytes, idx_mask, vs, ns, cs, tcs);
|
||||
} else {
|
||||
read_indices<uint16_t>(ptr, nbytes, idx_mask, vertex_attrib_mask, vs, ns, cs, tcs, vas );
|
||||
read_indices<uint16_t>(ptr, nbytes, idx_mask, vs, ns, cs, tcs);
|
||||
}
|
||||
|
||||
vertices.push_back( vs );
|
||||
normals.push_back( ns );
|
||||
colors.push_back( cs );
|
||||
texCoords.push_back( tcs );
|
||||
vertexAttribs.push_back( vas );
|
||||
materials.push_back( material );
|
||||
} // of element iteration
|
||||
}
|
||||
@@ -497,29 +380,25 @@ bool SGBinObject::read_bin( const string& file ) {
|
||||
pts_v.clear();
|
||||
pts_n.clear();
|
||||
pts_c.clear();
|
||||
pts_tcs.clear();
|
||||
pts_vas.clear();
|
||||
pts_tc.clear();
|
||||
pt_materials.clear();
|
||||
|
||||
tris_v.clear();
|
||||
tris_n.clear();
|
||||
tris_c.clear();
|
||||
tris_tcs.clear();
|
||||
tris_vas.clear();
|
||||
tris_tc.clear();
|
||||
tri_materials.clear();
|
||||
|
||||
strips_v.clear();
|
||||
strips_n.clear();
|
||||
strips_c.clear();
|
||||
strips_tcs.clear();
|
||||
strips_vas.clear();
|
||||
strips_tc.clear();
|
||||
strip_materials.clear();
|
||||
|
||||
fans_v.clear();
|
||||
fans_n.clear();
|
||||
fans_c.clear();
|
||||
fans_tcs.clear();
|
||||
fans_vas.clear();
|
||||
fans_tc.clear();
|
||||
fan_materials.clear();
|
||||
|
||||
gzFile fp;
|
||||
@@ -540,9 +419,10 @@ bool SGBinObject::read_bin( const string& file ) {
|
||||
sgReadUInt( fp, &header );
|
||||
if ( ((header & 0xFF000000) >> 24) == 'S' &&
|
||||
((header & 0x00FF0000) >> 16) == 'G' ) {
|
||||
|
||||
// cout << "Good header" << endl;
|
||||
// read file version
|
||||
version = (header & 0x0000FFFF);
|
||||
// cout << "File version = " << version << endl;
|
||||
} else {
|
||||
// close the file before we return
|
||||
gzclose(fp);
|
||||
@@ -580,7 +460,7 @@ bool SGBinObject::read_bin( const string& file ) {
|
||||
nobjects = v;
|
||||
}
|
||||
|
||||
SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin Total objects to read = " << nobjects);
|
||||
//cout << "Total objects to read = " << nobjects << endl;
|
||||
|
||||
if ( sgReadError() ) {
|
||||
throw sg_io_exception("Error reading BTG file header", sg_location(file));
|
||||
@@ -609,10 +489,9 @@ bool SGBinObject::read_bin( const string& file ) {
|
||||
nelements = v;
|
||||
}
|
||||
|
||||
SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin object " << i <<
|
||||
" = " << (int)obj_type << " props = " << nproperties <<
|
||||
" elements = " << nelements);
|
||||
|
||||
//cout << "object " << i << " = " << (int)obj_type << " props = "
|
||||
// << nproperties << " elements = " << nelements << endl;
|
||||
|
||||
if ( obj_type == SG_BOUNDING_SPHERE ) {
|
||||
// read bounding sphere properties
|
||||
read_properties( fp, nproperties );
|
||||
@@ -642,7 +521,7 @@ bool SGBinObject::read_bin( const string& file ) {
|
||||
wgs84_nodes.reserve( count );
|
||||
for ( k = 0; k < count; ++k ) {
|
||||
SGVec3f v = buf.readVec3f();
|
||||
// extend from float to double, hmmm
|
||||
// extend from float to double, hmmm
|
||||
wgs84_nodes.push_back( SGVec3d(v[0], v[1], v[2]) );
|
||||
}
|
||||
}
|
||||
@@ -702,60 +581,23 @@ bool SGBinObject::read_bin( const string& file ) {
|
||||
texcoords.push_back( buf.readVec2f() );
|
||||
}
|
||||
}
|
||||
} else if ( obj_type == SG_VA_FLOAT_LIST ) {
|
||||
// read vertex attribute (float) properties
|
||||
read_properties( fp, nproperties );
|
||||
|
||||
// read vertex attribute list elements
|
||||
for ( j = 0; j < nelements; ++j ) {
|
||||
sgReadUInt( fp, &nbytes );
|
||||
buf.resize( nbytes );
|
||||
buf.reset();
|
||||
char *ptr = buf.get_ptr();
|
||||
sgReadBytes( fp, nbytes, ptr );
|
||||
int count = nbytes / (sizeof(float));
|
||||
va_flt.reserve(count);
|
||||
for ( k = 0; k < count; ++k ) {
|
||||
va_flt.push_back( buf.readFloat() );
|
||||
}
|
||||
}
|
||||
} else if ( obj_type == SG_VA_INTEGER_LIST ) {
|
||||
// read vertex attribute (integer) properties
|
||||
read_properties( fp, nproperties );
|
||||
|
||||
// read vertex attribute list elements
|
||||
for ( j = 0; j < nelements; ++j ) {
|
||||
sgReadUInt( fp, &nbytes );
|
||||
buf.resize( nbytes );
|
||||
buf.reset();
|
||||
char *ptr = buf.get_ptr();
|
||||
sgReadBytes( fp, nbytes, ptr );
|
||||
int count = nbytes / (sizeof(unsigned int));
|
||||
va_int.reserve(count);
|
||||
for ( k = 0; k < count; ++k ) {
|
||||
va_int.push_back( buf.readInt() );
|
||||
}
|
||||
}
|
||||
} else if ( obj_type == SG_POINTS ) {
|
||||
// read point elements
|
||||
read_object( fp, SG_POINTS, nproperties, nelements,
|
||||
pts_v, pts_n, pts_c, pts_tcs,
|
||||
pts_vas, pt_materials );
|
||||
pts_v, pts_n, pts_c, pts_tc, pt_materials );
|
||||
} else if ( obj_type == SG_TRIANGLE_FACES ) {
|
||||
// read triangle face properties
|
||||
read_object( fp, SG_TRIANGLE_FACES, nproperties, nelements,
|
||||
tris_v, tris_n, tris_c, tris_tcs,
|
||||
tris_vas, tri_materials );
|
||||
tris_v, tris_n, tris_c, tris_tc, tri_materials );
|
||||
} else if ( obj_type == SG_TRIANGLE_STRIPS ) {
|
||||
// read triangle strip properties
|
||||
read_object( fp, SG_TRIANGLE_STRIPS, nproperties, nelements,
|
||||
strips_v, strips_n, strips_c, strips_tcs,
|
||||
strips_vas, strip_materials );
|
||||
strips_v, strips_n, strips_c, strips_tc,
|
||||
strip_materials );
|
||||
} else if ( obj_type == SG_TRIANGLE_FANS ) {
|
||||
// read triangle fan properties
|
||||
read_object( fp, SG_TRIANGLE_FANS, nproperties, nelements,
|
||||
fans_v, fans_n, fans_c, fans_tcs,
|
||||
fans_vas, fan_materials );
|
||||
fans_v, fans_n, fans_c, fans_tc, fan_materials );
|
||||
} else {
|
||||
// unknown object type, just skip
|
||||
read_properties( fp, nproperties );
|
||||
@@ -810,13 +652,9 @@ unsigned int SGBinObject::count_objects(const string_list& materials)
|
||||
return result;
|
||||
}
|
||||
|
||||
void SGBinObject::write_objects(gzFile fp, int type,
|
||||
const group_list& verts,
|
||||
const group_list& normals,
|
||||
const group_list& colors,
|
||||
const group_tci_list& texCoords,
|
||||
const group_vai_list& vertexAttribs,
|
||||
const string_list& materials)
|
||||
void SGBinObject::write_objects(gzFile fp, int type, const group_list& verts,
|
||||
const group_list& normals, const group_list& colors,
|
||||
const group_list& texCoords, const string_list& materials)
|
||||
{
|
||||
if (verts.empty()) {
|
||||
return;
|
||||
@@ -828,77 +666,45 @@ void SGBinObject::write_objects(gzFile fp, int type,
|
||||
|
||||
while (start < materials.size()) {
|
||||
m = materials[start];
|
||||
// find range of objects with identical material, write out as a single object
|
||||
// find range of objects with identical material, write out as a single object
|
||||
for (end = start+1; (end < materials.size()) && (m == materials[end]); ++end) {}
|
||||
|
||||
// calc the number of elements
|
||||
const int count = end - start;
|
||||
|
||||
// calc the number of properties
|
||||
unsigned int va_mask = 0;
|
||||
unsigned int va_count = vertexAttribs.size();
|
||||
for ( unsigned int va=0; va<va_count; va++ ) {
|
||||
if ( !vertexAttribs[va].empty() && !vertexAttribs[va].front().empty() ) {
|
||||
va_mask |= ( 1 << va );
|
||||
}
|
||||
}
|
||||
|
||||
if ( va_mask ) {
|
||||
write_header(fp, type, 3, count);
|
||||
} else {
|
||||
write_header(fp, type, 2, count);
|
||||
}
|
||||
write_header(fp, type, 2, count);
|
||||
|
||||
// properties
|
||||
// material property
|
||||
sgWriteChar( fp, (char)SG_MATERIAL ); // property
|
||||
sgWriteUInt( fp, m.length() ); // nbytes
|
||||
sgWriteBytes( fp, m.length(), m.c_str() );
|
||||
|
||||
// index mask property
|
||||
unsigned char idx_mask = 0;
|
||||
if ( !verts.empty() && !verts[start].empty()) idx_mask |= SG_IDX_VERTICES;
|
||||
if ( !normals.empty() && !normals[start].empty()) idx_mask |= SG_IDX_NORMALS;
|
||||
if ( !colors.empty() && !colors[start].empty()) idx_mask |= SG_IDX_COLORS;
|
||||
if ( !texCoords.empty() && !texCoords[start][0].empty()) idx_mask |= SG_IDX_TEXCOORDS_0;
|
||||
if ( !texCoords.empty() && !texCoords[start][1].empty()) idx_mask |= SG_IDX_TEXCOORDS_1;
|
||||
if ( !texCoords.empty() && !texCoords[start][2].empty()) idx_mask |= SG_IDX_TEXCOORDS_2;
|
||||
if ( !texCoords.empty() && !texCoords[start][3].empty()) idx_mask |= SG_IDX_TEXCOORDS_3;
|
||||
if ( !verts.empty() && !verts.front().empty()) idx_mask |= SG_IDX_VERTICES;
|
||||
if ( !normals.empty() && !normals.front().empty()) idx_mask |= SG_IDX_NORMALS;
|
||||
if ( !colors.empty() && !colors.front().empty()) idx_mask |= SG_IDX_COLORS;
|
||||
if ( !texCoords.empty() && !texCoords.front().empty()) idx_mask |= SG_IDX_TEXCOORDS;
|
||||
|
||||
if (idx_mask == 0) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "SGBinObject::write_objects: object with material:"
|
||||
<< m << "has no indices set");
|
||||
}
|
||||
|
||||
|
||||
|
||||
sgWriteChar( fp, (char)SG_INDEX_TYPES ); // property
|
||||
sgWriteUInt( fp, 1 ); // nbytes
|
||||
sgWriteChar( fp, idx_mask );
|
||||
|
||||
// vertex attribute property
|
||||
if (va_mask != 0) {
|
||||
sgWriteChar( fp, (char)SG_VERT_ATTRIBS ); // property
|
||||
sgWriteUInt( fp, 4 ); // nbytes
|
||||
sgWriteChar( fp, va_mask );
|
||||
}
|
||||
|
||||
|
||||
// cout << "material:" << m << ", count =" << count << endl;
|
||||
// elements
|
||||
for (unsigned int i=start; i < end; ++i) {
|
||||
const int_list& va(verts[i]);
|
||||
const int_list& na((idx_mask & SG_IDX_NORMALS) ? normals[i] : emptyList);
|
||||
const int_list& ca((idx_mask & SG_IDX_COLORS) ? colors[i] : emptyList);
|
||||
|
||||
// pass the whole texcoord array - we'll figure out which indicies to write
|
||||
// in write_indices
|
||||
const tci_list& tca( texCoords[i] );
|
||||
|
||||
// pass the whole vertex array - we'll figure out which indicies to write
|
||||
// in write_indices
|
||||
const vai_list& vaa( vertexAttribs[i] );
|
||||
const int_list& tca((idx_mask & SG_IDX_TEXCOORDS) ? texCoords[i] : emptyList);
|
||||
|
||||
if (version == 7) {
|
||||
write_indices<uint16_t>(fp, idx_mask, va_mask, va, na, ca, tca, vaa);
|
||||
write_indices<uint16_t>(fp, idx_mask, va, na, ca, tca);
|
||||
} else {
|
||||
write_indices<uint32_t>(fp, idx_mask, va_mask, va, na, ca, tca, vaa);
|
||||
write_indices<uint32_t>(fp, idx_mask, va, na, ca, tca);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -943,6 +749,7 @@ bool SGBinObject::write_bin_file(const SGPath& file)
|
||||
|
||||
SGPath file2(file);
|
||||
file2.create_dir( 0755 );
|
||||
cout << "Output file = " << file.str() << endl;
|
||||
|
||||
gzFile fp;
|
||||
if ( (fp = gzopen( file.c_str(), "wb9" )) == NULL ) {
|
||||
@@ -952,19 +759,19 @@ bool SGBinObject::write_bin_file(const SGPath& file)
|
||||
|
||||
sgClearWriteError();
|
||||
|
||||
SG_LOG(SG_IO, SG_DEBUG, "points size = " << pts_v.size()
|
||||
<< " pt_materials = " << pt_materials.size() );
|
||||
SG_LOG(SG_IO, SG_DEBUG, "triangles size = " << tris_v.size()
|
||||
<< " tri_materials = " << tri_materials.size() );
|
||||
SG_LOG(SG_IO, SG_DEBUG, "strips size = " << strips_v.size()
|
||||
<< " strip_materials = " << strip_materials.size() );
|
||||
SG_LOG(SG_IO, SG_DEBUG, "fans size = " << fans_v.size()
|
||||
<< " fan_materials = " << fan_materials.size() );
|
||||
cout << "points size = " << pts_v.size() << " pt_materials = "
|
||||
<< pt_materials.size() << endl;
|
||||
cout << "triangles size = " << tris_v.size() << " tri_materials = "
|
||||
<< tri_materials.size() << endl;
|
||||
cout << "strips size = " << strips_v.size() << " strip_materials = "
|
||||
<< strip_materials.size() << endl;
|
||||
cout << "fans size = " << fans_v.size() << " fan_materials = "
|
||||
<< fan_materials.size() << endl;
|
||||
|
||||
SG_LOG(SG_IO, SG_DEBUG, "nodes = " << wgs84_nodes.size() );
|
||||
SG_LOG(SG_IO, SG_DEBUG, "colors = " << colors.size() );
|
||||
SG_LOG(SG_IO, SG_DEBUG, "normals = " << normals.size() );
|
||||
SG_LOG(SG_IO, SG_DEBUG, "tex coords = " << texcoords.size() );
|
||||
cout << "nodes = " << wgs84_nodes.size() << endl;
|
||||
cout << "colors = " << colors.size() << endl;
|
||||
cout << "normals = " << normals.size() << endl;
|
||||
cout << "tex coords = " << texcoords.size() << endl;
|
||||
|
||||
version = 10;
|
||||
bool shortMaterialsRanges =
|
||||
@@ -996,8 +803,7 @@ bool SGBinObject::write_bin_file(const SGPath& file)
|
||||
nobjects += count_objects(strip_materials);
|
||||
nobjects += count_objects(fan_materials);
|
||||
|
||||
SG_LOG(SG_IO, SG_DEBUG, "total top level objects = " << nobjects);
|
||||
|
||||
cout << "total top level objects = " << nobjects << endl;
|
||||
if (version == 7) {
|
||||
sgWriteUShort( fp, (uint16_t) nobjects );
|
||||
} else {
|
||||
@@ -1043,10 +849,10 @@ bool SGBinObject::write_bin_file(const SGPath& file)
|
||||
sgWriteVec2( fp, texcoords[i]);
|
||||
}
|
||||
|
||||
write_objects(fp, SG_POINTS, pts_v, pts_n, pts_c, pts_tcs, pts_vas, pt_materials);
|
||||
write_objects(fp, SG_TRIANGLE_FACES, tris_v, tris_n, tris_c, tris_tcs, tris_vas, tri_materials);
|
||||
write_objects(fp, SG_TRIANGLE_STRIPS, strips_v, strips_n, strips_c, strips_tcs, strips_vas, strip_materials);
|
||||
write_objects(fp, SG_TRIANGLE_FANS, fans_v, fans_n, fans_c, fans_tcs, fans_vas, fan_materials);
|
||||
write_objects(fp, SG_POINTS, pts_v, pts_n, pts_c, pts_tc, pt_materials);
|
||||
write_objects(fp, SG_TRIANGLE_FACES, tris_v, tris_n, tris_c, tris_tc, tri_materials);
|
||||
write_objects(fp, SG_TRIANGLE_STRIPS, strips_v, strips_n, strips_c, strips_tc, strip_materials);
|
||||
write_objects(fp, SG_TRIANGLE_FANS, fans_v, fans_n, fans_c, fans_tc, fan_materials);
|
||||
|
||||
// close the file
|
||||
gzclose(fp);
|
||||
@@ -1146,7 +952,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
|
||||
}
|
||||
// cout << "group = " << start << " to " << end - 1 << endl;
|
||||
|
||||
SGSphered d( SGVec3d(0.0, 0.0, 0.0), -1.0 );
|
||||
SGSphered d;
|
||||
for ( i = start; i < end; ++i ) {
|
||||
for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
|
||||
d.expandBy(wgs84_nodes[ tris_v[i][j] ]);
|
||||
@@ -1166,7 +972,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
|
||||
for ( i = start; i < end; ++i ) {
|
||||
fprintf(fp, "f");
|
||||
for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
|
||||
fprintf(fp, " %d/%d", tris_v[i][j], tris_tcs[i][0][j] );
|
||||
fprintf(fp, " %d/%d", tris_v[i][j], tris_tc[i][j] );
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
@@ -1195,7 +1001,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
|
||||
// cout << "group = " << start << " to " << end - 1 << endl;
|
||||
|
||||
|
||||
SGSphered d( SGVec3d(0.0, 0.0, 0.0), -1.0 );
|
||||
SGSphered d;
|
||||
for ( i = start; i < end; ++i ) {
|
||||
for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
|
||||
d.expandBy(wgs84_nodes[ tris_v[i][j] ]);
|
||||
@@ -1215,7 +1021,7 @@ bool SGBinObject::write_ascii( const string& base, const string& name,
|
||||
for ( i = start; i < end; ++i ) {
|
||||
fprintf(fp, "ts");
|
||||
for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
|
||||
fprintf(fp, " %d/%d", strips_v[i][j], strips_tcs[i][0][j] );
|
||||
fprintf(fp, " %d/%d", strips_v[i][j], strips_tc[i][j] );
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
@@ -1255,27 +1061,3 @@ void SGBinObject::read_properties(gzFile fp, int nproperties)
|
||||
}
|
||||
}
|
||||
|
||||
bool SGBinObject::add_point( const SGBinObjectPoint& pt )
|
||||
{
|
||||
// add the point info
|
||||
pt_materials.push_back( pt.material );
|
||||
|
||||
pts_v.push_back( pt.v_list );
|
||||
pts_n.push_back( pt.n_list );
|
||||
pts_c.push_back( pt.c_list );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SGBinObject::add_triangle( const SGBinObjectTriangle& tri )
|
||||
{
|
||||
// add the triangle info and keep lists aligned
|
||||
tri_materials.push_back( tri.material );
|
||||
tris_v.push_back( tri.v_list );
|
||||
tris_n.push_back( tri.n_list );
|
||||
tris_c.push_back( tri.c_list );
|
||||
tris_tcs.push_back( tri.tc_list );
|
||||
tris_vas.push_back( tri.va_list );
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -36,79 +36,16 @@
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <boost/array.hpp>
|
||||
|
||||
#define MAX_TC_SETS (4)
|
||||
#define MAX_VAS (8)
|
||||
|
||||
// I really want to pass around fixed length arrays, as the size
|
||||
// has to be hardcoded
|
||||
// but it's a C++0x feature use boost in its absence
|
||||
typedef boost::array<int_list, MAX_TC_SETS> tci_list;
|
||||
typedef boost::array<int_list, MAX_VAS> vai_list;
|
||||
|
||||
/** STL Structure used to store (integer index) object information */
|
||||
/** STL Structure used to store object information */
|
||||
typedef std::vector < int_list > group_list;
|
||||
typedef group_list::iterator group_list_iterator;
|
||||
typedef group_list::const_iterator const_group_list_iterator;
|
||||
|
||||
/** STL Structure used to store (tc index) object information */
|
||||
typedef std::vector < tci_list > group_tci_list;
|
||||
typedef group_tci_list::iterator group_tci_list_iterator;
|
||||
typedef group_tci_list::const_iterator const_group_tci_list_iterator;
|
||||
|
||||
/** STL Structure used to store (va index) object information */
|
||||
typedef std::vector < vai_list > group_vai_list;
|
||||
typedef group_vai_list::iterator group_vai_list_iterator;
|
||||
typedef group_vai_list::const_iterator const_group_vai_list_iterator;
|
||||
|
||||
|
||||
// forward decls
|
||||
class SGBucket;
|
||||
class SGPath;
|
||||
|
||||
class SGBinObjectPoint {
|
||||
public:
|
||||
std::string material;
|
||||
int_list v_list;
|
||||
int_list n_list;
|
||||
int_list c_list;
|
||||
|
||||
void clear( void ) {
|
||||
material = "";
|
||||
v_list.clear();
|
||||
n_list.clear();
|
||||
c_list.clear();
|
||||
};
|
||||
};
|
||||
|
||||
class SGBinObjectTriangle {
|
||||
public:
|
||||
std::string material;
|
||||
int_list v_list;
|
||||
int_list n_list;
|
||||
int_list c_list;
|
||||
|
||||
tci_list tc_list;
|
||||
vai_list va_list;
|
||||
|
||||
void clear( void ) {
|
||||
material = "";
|
||||
v_list.clear();
|
||||
n_list.clear();
|
||||
c_list.clear();
|
||||
for ( unsigned int i=0; i<MAX_TC_SETS; i++ ) {
|
||||
tc_list[i].clear();
|
||||
}
|
||||
for ( unsigned int i=0; i<MAX_VAS; i++ ) {
|
||||
va_list[i].clear();
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A class to manipulate the simgear 3d object format.
|
||||
* This class provides functionality to both read and write the binary format.
|
||||
@@ -149,40 +86,34 @@ private:
|
||||
SGVec3d gbs_center;
|
||||
float gbs_radius;
|
||||
|
||||
std::vector<SGVec3d> wgs84_nodes; // vertex list
|
||||
std::vector<SGVec4f> colors; // color list
|
||||
std::vector<SGVec3f> normals; // normal list
|
||||
std::vector<SGVec2f> texcoords; // texture coordinate list
|
||||
std::vector<float> va_flt; // vertex attribute list (floats)
|
||||
std::vector<int> va_int; // vertex attribute list (ints)
|
||||
|
||||
group_list pts_v; // points vertex index
|
||||
group_list pts_n; // points normal index
|
||||
group_list pts_c; // points color index
|
||||
group_tci_list pts_tcs; // points texture coordinates ( up to 4 sets )
|
||||
group_vai_list pts_vas; // points vertex attributes ( up to 8 sets )
|
||||
string_list pt_materials; // points materials
|
||||
std::vector<SGVec3d> wgs84_nodes; // vertex list
|
||||
std::vector<SGVec4f> colors; // color list
|
||||
std::vector<SGVec3f> normals; // normal list
|
||||
std::vector<SGVec2f> texcoords; // texture coordinate list
|
||||
|
||||
group_list tris_v; // triangles vertex index
|
||||
group_list tris_n; // triangles normal index
|
||||
group_list tris_c; // triangles color index
|
||||
group_tci_list tris_tcs; // triangles texture coordinates ( up to 4 sets )
|
||||
group_vai_list tris_vas; // triangles vertex attributes ( up to 8 sets )
|
||||
string_list tri_materials; // triangles materials
|
||||
group_list pts_v; // points vertex index
|
||||
group_list pts_n; // points normal index
|
||||
group_list pts_c; // points color index
|
||||
group_list pts_tc; // points texture coordinate index
|
||||
string_list pt_materials; // points materials
|
||||
|
||||
group_list strips_v; // tristrips vertex index
|
||||
group_list strips_n; // tristrips normal index
|
||||
group_list strips_c; // tristrips color index
|
||||
group_tci_list strips_tcs; // tristrips texture coordinates ( up to 4 sets )
|
||||
group_vai_list strips_vas; // tristrips vertex attributes ( up to 8 sets )
|
||||
string_list strip_materials; // tristrips materials
|
||||
group_list tris_v; // triangles vertex index
|
||||
group_list tris_n; // triangles normal index
|
||||
group_list tris_c; // triangles color index
|
||||
group_list tris_tc; // triangles texture coordinate index
|
||||
string_list tri_materials; // triangles materials
|
||||
|
||||
group_list fans_v; // fans vertex index
|
||||
group_list fans_n; // fans normal index
|
||||
group_list fans_c; // fans color index
|
||||
group_tci_list fans_tcs; // fanss texture coordinates ( up to 4 sets )
|
||||
group_vai_list fans_vas; // fans vertex attributes ( up to 8 sets )
|
||||
string_list fan_materials; // fans materials
|
||||
group_list strips_v; // tristrips vertex index
|
||||
group_list strips_n; // tristrips normal index
|
||||
group_list strips_c; // tristrips color index
|
||||
group_list strips_tc; // tristrips texture coordinate index
|
||||
string_list strip_materials;// tristrips materials
|
||||
|
||||
group_list fans_v; // fans vertex index
|
||||
group_list fans_n; // fans normal index
|
||||
group_list fans_c; // fans color index
|
||||
group_list fans_tc; // fans texture coordinate index
|
||||
string_list fan_materials; // fans materials
|
||||
|
||||
void read_properties(gzFile fp, int nproperties);
|
||||
|
||||
@@ -193,23 +124,17 @@ private:
|
||||
group_list& vertices,
|
||||
group_list& normals,
|
||||
group_list& colors,
|
||||
group_tci_list& texCoords,
|
||||
group_vai_list& vertexAttribs,
|
||||
group_list& texCoords,
|
||||
string_list& materials);
|
||||
|
||||
void write_header(gzFile fp, int type, int nProps, int nElements);
|
||||
void write_objects(gzFile fp,
|
||||
int type,
|
||||
const group_list& verts,
|
||||
const group_list& normals,
|
||||
const group_list& colors,
|
||||
const group_tci_list& texCoords,
|
||||
const group_vai_list& vertexAttribs,
|
||||
const string_list& materials);
|
||||
void write_objects(gzFile fp, int type, const group_list& verts,
|
||||
const group_list& normals, const group_list& colors,
|
||||
const group_list& texCoords, const string_list& materials);
|
||||
|
||||
unsigned int count_objects(const string_list& materials);
|
||||
|
||||
public:
|
||||
public:
|
||||
|
||||
inline unsigned short get_version() const { return version; }
|
||||
|
||||
inline const SGVec3d& get_gbs_center() const { return gbs_center; }
|
||||
@@ -218,8 +143,10 @@ public:
|
||||
inline float get_gbs_radius() const { return gbs_radius; }
|
||||
inline void set_gbs_radius( float r ) { gbs_radius = r; }
|
||||
|
||||
inline const std::vector<SGVec3d>& get_wgs84_nodes() const { return wgs84_nodes; }
|
||||
inline void set_wgs84_nodes( const std::vector<SGVec3d>& n ) { wgs84_nodes = n; }
|
||||
inline const std::vector<SGVec3d>& get_wgs84_nodes() const
|
||||
{ return wgs84_nodes; }
|
||||
inline void set_wgs84_nodes( const std::vector<SGVec3d>& n )
|
||||
{ wgs84_nodes = n; }
|
||||
|
||||
inline const std::vector<SGVec4f>& get_colors() const { return colors; }
|
||||
inline void set_colors( const std::vector<SGVec4f>& c ) { colors = c; }
|
||||
@@ -230,38 +157,51 @@ public:
|
||||
inline const std::vector<SGVec2f>& get_texcoords() const { return texcoords; }
|
||||
inline void set_texcoords( const std::vector<SGVec2f>& t ) { texcoords = t; }
|
||||
|
||||
// Points API
|
||||
bool add_point( const SGBinObjectPoint& pt );
|
||||
inline const group_list& get_pts_v() const { return pts_v; }
|
||||
inline const group_list& get_pts_n() const { return pts_n; }
|
||||
inline const group_tci_list& get_pts_tcs() const { return pts_tcs; }
|
||||
inline const group_vai_list& get_pts_vas() const { return pts_vas; }
|
||||
inline void set_pts_v( const group_list& g ) { pts_v = g; }
|
||||
inline const group_list& get_pts_n() const { return pts_n; }
|
||||
inline void set_pts_n( const group_list& g ) { pts_n = g; }
|
||||
inline const group_list& get_pts_c() const { return pts_c; }
|
||||
inline void set_pts_c( const group_list& g ) { pts_c = g; }
|
||||
inline const group_list& get_pts_tc() const { return pts_tc; }
|
||||
inline void set_pts_tc( const group_list& g ) { pts_tc = g; }
|
||||
inline const string_list& get_pt_materials() const { return pt_materials; }
|
||||
inline void set_pt_materials( const string_list& s ) { pt_materials = s; }
|
||||
|
||||
// Triangles API
|
||||
bool add_triangle( const SGBinObjectTriangle& tri );
|
||||
inline const group_list& get_tris_v() const { return tris_v; }
|
||||
inline void set_tris_v( const group_list& g ) { tris_v = g; }
|
||||
inline const group_list& get_tris_n() const { return tris_n; }
|
||||
inline void set_tris_n( const group_list& g ) { tris_n = g; }
|
||||
inline const group_list& get_tris_c() const { return tris_c; }
|
||||
inline const group_tci_list& get_tris_tcs() const { return tris_tcs; }
|
||||
inline const group_vai_list& get_tris_vas() const { return tris_vas; }
|
||||
inline void set_tris_c( const group_list& g ) { tris_c = g; }
|
||||
inline const group_list& get_tris_tc() const { return tris_tc; }
|
||||
inline void set_tris_tc( const group_list& g ) { tris_tc = g; }
|
||||
inline const string_list& get_tri_materials() const { return tri_materials; }
|
||||
inline void set_tri_materials( const string_list& s ) { tri_materials = s; }
|
||||
|
||||
// Strips API (deprecated - read only)
|
||||
inline const group_list& get_strips_v() const { return strips_v; }
|
||||
inline void set_strips_v( const group_list& g ) { strips_v = g; }
|
||||
inline const group_list& get_strips_n() const { return strips_n; }
|
||||
inline void set_strips_n( const group_list& g ) { strips_n = g; }
|
||||
inline const group_list& get_strips_c() const { return strips_c; }
|
||||
inline const group_tci_list& get_strips_tcs() const { return strips_tcs; }
|
||||
inline const group_vai_list& get_strips_vas() const { return strips_vas; }
|
||||
inline const string_list& get_strip_materials() const { return strip_materials; }
|
||||
inline void set_strips_c( const group_list& g ) { strips_c = g; }
|
||||
|
||||
// Fans API (deprecated - read only )
|
||||
inline const group_list& get_strips_tc() const { return strips_tc; }
|
||||
inline void set_strips_tc( const group_list& g ) { strips_tc = g; }
|
||||
inline const string_list& get_strip_materials() const { return strip_materials; }
|
||||
inline void set_strip_materials( const string_list& s ) { strip_materials = s; }
|
||||
|
||||
inline const group_list& get_fans_v() const { return fans_v; }
|
||||
inline void set_fans_v( const group_list& g ) { fans_v = g; }
|
||||
inline const group_list& get_fans_n() const { return fans_n; }
|
||||
inline void set_fans_n( const group_list& g ) { fans_n = g; }
|
||||
inline const group_list& get_fans_c() const { return fans_c; }
|
||||
inline const group_tci_list& get_fans_tcs() const { return fans_tcs; }
|
||||
inline const group_vai_list& get_fans_vas() const { return fans_vas; }
|
||||
inline void set_fans_c( const group_list& g ) { fans_c = g; }
|
||||
|
||||
inline const group_list& get_fans_tc() const { return fans_tc; }
|
||||
inline void set_fans_tc( const group_list& g ) { fans_tc = g; }
|
||||
inline const string_list& get_fan_materials() const { return fan_materials; }
|
||||
inline void set_fan_materials( const string_list& s ) { fan_materials = s; }
|
||||
|
||||
/**
|
||||
* Read a binary file object and populate the provided structures.
|
||||
|
||||
@@ -103,15 +103,6 @@ int_list make_tri(int maxIndex)
|
||||
return r;
|
||||
}
|
||||
|
||||
tci_list make_tri_tcs(int maxIndex)
|
||||
{
|
||||
tci_list tci;
|
||||
tci[0].push_back(random() % maxIndex);
|
||||
tci[0].push_back(random() % maxIndex);
|
||||
tci[0].push_back(random() % maxIndex);
|
||||
return tci;
|
||||
}
|
||||
|
||||
void compareTris(const SGBinObject& a, const SGBinObject& b)
|
||||
{
|
||||
unsigned int count = a.get_tri_materials().size();
|
||||
@@ -122,31 +113,32 @@ void compareTris(const SGBinObject& a, const SGBinObject& b)
|
||||
|
||||
COMPARE(a.get_tri_materials()[i], b.get_tri_materials()[i]);
|
||||
|
||||
const int_list& tA(a.get_tris_tcs()[i][0]);
|
||||
const int_list& tB(b.get_tris_tcs()[i][0]);
|
||||
const int_list& tA(a.get_tris_tc()[i]);
|
||||
const int_list& tB(b.get_tris_tc()[i]);
|
||||
VERIFY(tA == tB);
|
||||
}
|
||||
}
|
||||
|
||||
void generate_tris(SGBinObject& b, int count)
|
||||
{
|
||||
group_list v, n;
|
||||
group_tci_list tc;
|
||||
group_list v, n, tc;
|
||||
string_list materials;
|
||||
|
||||
int maxVertices = b.get_wgs84_nodes().size();
|
||||
int maxNormals = b.get_normals().size();
|
||||
int maxTCs = b.get_texcoords().size();
|
||||
|
||||
SGBinObjectTriangle sgboTri;
|
||||
for (int t=0; t<count; ++t) {
|
||||
sgboTri.material = "material1";
|
||||
sgboTri.v_list = make_tri(maxVertices);
|
||||
sgboTri.n_list = make_tri(maxNormals);
|
||||
sgboTri.tc_list[0] = make_tri(maxTCs);
|
||||
|
||||
b.add_triangle( sgboTri );
|
||||
v.push_back(make_tri(maxVertices));
|
||||
n.push_back(make_tri(maxNormals));
|
||||
tc.push_back(make_tri(maxTCs));
|
||||
materials.push_back("material1");
|
||||
}
|
||||
|
||||
b.set_tris_v(v);
|
||||
b.set_tris_n(n);
|
||||
b.set_tris_tc(tc);
|
||||
b.set_tri_materials(materials);
|
||||
}
|
||||
|
||||
void test_basic()
|
||||
|
||||
@@ -19,13 +19,10 @@
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
#include "SGMath.hxx"
|
||||
#include "SGRect.hxx"
|
||||
#include "sg_random.h"
|
||||
|
||||
template<typename T>
|
||||
@@ -271,33 +268,6 @@ MatrixTest(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void doRectTest()
|
||||
{
|
||||
SGRect<T> rect(10, 15, 20, 25);
|
||||
|
||||
COMPARE(rect.x(), 10)
|
||||
COMPARE(rect.y(), 15)
|
||||
COMPARE(rect.width(), 20)
|
||||
COMPARE(rect.height(), 25)
|
||||
|
||||
COMPARE(rect.pos(), SGVec2<T>(10, 15))
|
||||
COMPARE(rect.size(), SGVec2<T>(20, 25))
|
||||
|
||||
COMPARE(rect.l(), 10)
|
||||
COMPARE(rect.t(), 15)
|
||||
COMPARE(rect.r(), 30)
|
||||
COMPARE(rect.b(), 40)
|
||||
|
||||
VERIFY(rect == rect)
|
||||
VERIFY(rect == SGRect<T>(10, 15, 20, 25))
|
||||
VERIFY(rect != SGRect<T>(11, 15, 20, 25))
|
||||
|
||||
VERIFY(rect.contains(10, 15))
|
||||
VERIFY(!rect.contains(9, 15))
|
||||
VERIFY(rect.contains(9, 15, 1))
|
||||
}
|
||||
|
||||
bool
|
||||
GeodesyTest(void)
|
||||
{
|
||||
@@ -381,10 +351,6 @@ main(void)
|
||||
if (!MatrixTest<double>())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
// Do rect tests
|
||||
doRectTest<int>();
|
||||
doRectTest<double>();
|
||||
|
||||
// Check geodetic/geocentric/cartesian conversions
|
||||
if (!GeodesyTest())
|
||||
return EXIT_FAILURE;
|
||||
|
||||
@@ -34,7 +34,7 @@ class SGRect
|
||||
|
||||
}
|
||||
|
||||
explicit SGRect(const SGVec2<T>& pt):
|
||||
SGRect(const SGVec2<T>& pt):
|
||||
_min(pt),
|
||||
_max(pt)
|
||||
{
|
||||
@@ -88,15 +88,11 @@ class SGRect
|
||||
T y() const { return _min.y(); }
|
||||
T width() const { return _max.x() - _min.x(); }
|
||||
T height() const { return _max.y() - _min.y(); }
|
||||
SGVec2<T> const& pos() const { return _min; }
|
||||
SGVec2<T> size() const { return SGVec2<T>(width(), height()); }
|
||||
|
||||
void setX(T x) { T w = width(); _min.x() = x; _max.x() = x + w; }
|
||||
void setY(T y) { T h = height(); _min.y() = y; _max.y() = y + h; }
|
||||
void setWidth(T w) { _max.x() = _min.x() + w; }
|
||||
void setHeight(T h) { _max.y() = _min.y() + h; }
|
||||
void setPos(const SGVec2<T>& p) { setX(p.x()); setY(p.y()); }
|
||||
void setSize(const SGVec2<T>& s) { setWidth(s.x()); setHeight(s.y()); }
|
||||
|
||||
T l() const { return _min.x(); }
|
||||
T r() const { return _max.x(); }
|
||||
@@ -113,18 +109,6 @@ class SGRect
|
||||
void setTop(T t) { _min.y() = t; }
|
||||
void setBottom(T b) { _max.y() = b; }
|
||||
|
||||
/**
|
||||
* Expand rectangle to include the given position
|
||||
*/
|
||||
void expandBy(T x, T y)
|
||||
{
|
||||
if( x < _min.x() ) _min.x() = x;
|
||||
if( x > _max.x() ) _max.x() = x;
|
||||
|
||||
if( y < _min.y() ) _min.y() = y;
|
||||
if( y > _max.y() ) _max.y() = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move rect by vector
|
||||
*/
|
||||
@@ -145,17 +129,6 @@ class SGRect
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const SGRect<T>& rhs) const
|
||||
{
|
||||
return _min == rhs._min
|
||||
&& _max == rhs._max;
|
||||
}
|
||||
|
||||
bool operator!=(const SGRect<T>& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
bool contains(T x, T y) const
|
||||
{
|
||||
return _min.x() <= x && x <= _max.x()
|
||||
@@ -203,8 +176,4 @@ std::basic_ostream<char_type, traits_type>&
|
||||
operator<<(std::basic_ostream<char_type, traits_type>& s, const SGRect<T>& rect)
|
||||
{ return s << "min = " << rect.getMin() << ", max = " << rect.getMax(); }
|
||||
|
||||
typedef SGRect<int> SGRecti;
|
||||
typedef SGRect<float> SGRectf;
|
||||
typedef SGRect<double> SGRectd;
|
||||
|
||||
#endif /* SG_RECT_HXX_ */
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
//
|
||||
|
||||
#ifndef SGTriangle_H
|
||||
#define SGTriangle_H
|
||||
#define SGTrianlge_H
|
||||
|
||||
template<typename T>
|
||||
class SGTriangle {
|
||||
|
||||
@@ -5,8 +5,6 @@ set(HEADERS
|
||||
CSSBorder.hxx
|
||||
ListDiff.hxx
|
||||
ResourceManager.hxx
|
||||
SimpleMarkdown.hxx
|
||||
SVGpreserveAspectRatio.hxx
|
||||
interpolator.hxx
|
||||
make_new.hxx
|
||||
sg_dir.hxx
|
||||
@@ -24,8 +22,6 @@ set(HEADERS
|
||||
set(SOURCES
|
||||
CSSBorder.cxx
|
||||
ResourceManager.cxx
|
||||
SimpleMarkdown.cxx
|
||||
SVGpreserveAspectRatio.cxx
|
||||
interpolator.cxx
|
||||
sg_dir.cxx
|
||||
sg_path.cxx
|
||||
@@ -37,10 +33,6 @@ set(SOURCES
|
||||
gzcontainerfile.cxx
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
list(APPEND SOURCES CocoaHelpers.mm)
|
||||
endif()
|
||||
|
||||
simgear_component(misc misc "${SOURCES}" "${HEADERS}")
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
@@ -53,6 +45,10 @@ add_executable(test_tabbed_values tabbed_values_test.cxx)
|
||||
add_test(tabbed_values ${EXECUTABLE_OUTPUT_PATH}/test_tabbed_values)
|
||||
target_link_libraries(test_tabbed_values ${TEST_LIBS})
|
||||
|
||||
add_executable(test_strings strutils_test.cxx )
|
||||
add_test(strings ${EXECUTABLE_OUTPUT_PATH}/test_strings)
|
||||
target_link_libraries(test_strings ${TEST_LIBS})
|
||||
|
||||
add_executable(test_streams sgstream_test.cxx )
|
||||
add_test(streams ${EXECUTABLE_OUTPUT_PATH}/test_streams)
|
||||
target_link_libraries(test_streams ${TEST_LIBS})
|
||||
@@ -62,23 +58,3 @@ add_test(path ${EXECUTABLE_OUTPUT_PATH}/test_path)
|
||||
target_link_libraries(test_path ${TEST_LIBS})
|
||||
|
||||
endif(ENABLE_TESTS)
|
||||
|
||||
add_boost_test(SimpleMarkdown
|
||||
SOURCES SimpleMarkdown_test.cxx
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
)
|
||||
|
||||
add_boost_test(SVGpreserveAspectRatio
|
||||
SOURCES SVGpreserveAspectRatio_test.cxx
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
)
|
||||
|
||||
add_boost_test(strutils
|
||||
SOURCES strutils_test.cxx
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
)
|
||||
|
||||
add_boost_test(utf8tolatin1
|
||||
SOURCES utf8tolatin1_test.cxx
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
)
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <Foundation/NSAutoreleasePool.h>
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
namespace {
|
||||
|
||||
class CocoaAutoreleasePool
|
||||
{
|
||||
public:
|
||||
CocoaAutoreleasePool()
|
||||
{
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
}
|
||||
|
||||
~CocoaAutoreleasePool()
|
||||
{
|
||||
[pool release];
|
||||
}
|
||||
|
||||
private:
|
||||
NSAutoreleasePool* pool;
|
||||
};
|
||||
|
||||
} // of anonyous namespace
|
||||
|
||||
SGPath appleSpecialFolder(int dirType, int domainMask, const SGPath& def)
|
||||
{
|
||||
CocoaAutoreleasePool ap;
|
||||
NSFileManager* fm = [NSFileManager defaultManager];
|
||||
NSURL* pathUrl = [fm URLForDirectory:dirType
|
||||
inDomain:domainMask
|
||||
appropriateForURL:Nil
|
||||
create:YES
|
||||
error:nil];
|
||||
if (!pathUrl) {
|
||||
return def;;
|
||||
}
|
||||
|
||||
return SGPath([[pathUrl path] UTF8String], def.getPermissionChecker());
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
// Parse and represent SVG preserveAspectRatio attribute
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "SVGpreserveAspectRatio.hxx"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
#include <boost/tokenizer.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SVGpreserveAspectRatio::SVGpreserveAspectRatio():
|
||||
_align_x(ALIGN_NONE),
|
||||
_align_y(ALIGN_NONE),
|
||||
_meet(true)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SVGpreserveAspectRatio::Align SVGpreserveAspectRatio::alignX() const
|
||||
{
|
||||
return _align_x;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SVGpreserveAspectRatio::Align SVGpreserveAspectRatio::alignY() const
|
||||
{
|
||||
return _align_y;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool SVGpreserveAspectRatio::scaleToFill() const
|
||||
{
|
||||
return (_align_x == ALIGN_NONE) && (_align_y == ALIGN_NONE);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool SVGpreserveAspectRatio::scaleToFit() const
|
||||
{
|
||||
return !scaleToFill() && _meet;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool SVGpreserveAspectRatio::scaleToCrop() const
|
||||
{
|
||||
return !scaleToFill() && !_meet;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool SVGpreserveAspectRatio::meet() const
|
||||
{
|
||||
return _meet;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
SVGpreserveAspectRatio::operator==(const SVGpreserveAspectRatio& rhs) const
|
||||
{
|
||||
return (_align_x == rhs._align_x)
|
||||
&& (_align_y == rhs._align_y)
|
||||
&& (_meet == rhs._meet || scaleToFill());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
SVGpreserveAspectRatio SVGpreserveAspectRatio::parse(const std::string& str)
|
||||
{
|
||||
SVGpreserveAspectRatio ret;
|
||||
enum
|
||||
{
|
||||
PARSE_defer,
|
||||
PARSE_align,
|
||||
PARSE_meetOrSlice,
|
||||
PARSE_done,
|
||||
PARSE_error
|
||||
} parse_state = PARSE_defer;
|
||||
|
||||
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
|
||||
const boost::char_separator<char> del(" \t\n");
|
||||
|
||||
tokenizer tokens(str.begin(), str.end(), del);
|
||||
for( tokenizer::const_iterator tok = tokens.begin();
|
||||
tok != tokens.end()
|
||||
&& parse_state != PARSE_error;
|
||||
++tok )
|
||||
{
|
||||
const std::string& cur_tok = tok.current_token();
|
||||
|
||||
switch( parse_state )
|
||||
{
|
||||
case PARSE_defer:
|
||||
if( cur_tok == "defer" )
|
||||
{
|
||||
SG_LOG( SG_GENERAL,
|
||||
SG_INFO,
|
||||
"SVGpreserveAspectRatio: 'defer' is ignored." );
|
||||
parse_state = PARSE_align;
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
case PARSE_align:
|
||||
if( cur_tok == "none" )
|
||||
{
|
||||
ret._align_x = ALIGN_NONE;
|
||||
ret._align_y = ALIGN_NONE;
|
||||
}
|
||||
else if( cur_tok.length() == 8 )
|
||||
{
|
||||
if( strutils::starts_with(cur_tok, "xMin") )
|
||||
ret._align_x = ALIGN_MIN;
|
||||
else if( strutils::starts_with(cur_tok, "xMid") )
|
||||
ret._align_x = ALIGN_MID;
|
||||
else if( strutils::starts_with(cur_tok, "xMax") )
|
||||
ret._align_x = ALIGN_MAX;
|
||||
else
|
||||
{
|
||||
parse_state = PARSE_error;
|
||||
break;
|
||||
}
|
||||
|
||||
if( strutils::ends_with(cur_tok, "YMin") )
|
||||
ret._align_y = ALIGN_MIN;
|
||||
else if( strutils::ends_with(cur_tok, "YMid") )
|
||||
ret._align_y = ALIGN_MID;
|
||||
else if( strutils::ends_with(cur_tok, "YMax") )
|
||||
ret._align_y = ALIGN_MAX;
|
||||
else
|
||||
{
|
||||
parse_state = PARSE_error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parse_state = PARSE_error;
|
||||
break;
|
||||
}
|
||||
parse_state = PARSE_meetOrSlice;
|
||||
break;
|
||||
case PARSE_meetOrSlice:
|
||||
if( cur_tok == "meet" )
|
||||
ret._meet = true;
|
||||
else if( cur_tok == "slice" )
|
||||
ret._meet = false;
|
||||
else
|
||||
{
|
||||
parse_state = PARSE_error;
|
||||
break;
|
||||
}
|
||||
parse_state = PARSE_done;
|
||||
break;
|
||||
case PARSE_done:
|
||||
SG_LOG( SG_GENERAL,
|
||||
SG_WARN,
|
||||
"SVGpreserveAspectRatio: Ignoring superfluous token"
|
||||
" '" << cur_tok << "'" );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( parse_state == PARSE_error )
|
||||
{
|
||||
SG_LOG( SG_GENERAL,
|
||||
SG_WARN,
|
||||
"SVGpreserveAspectRatio: Failed to parse: '" << str << "'" );
|
||||
return SVGpreserveAspectRatio();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace simgear
|
||||
@@ -1,67 +0,0 @@
|
||||
///@file Parse and represent SVG preserveAspectRatio attribute
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef SG_SVG_PRESERVE_ASPECT_RATIO_HXX_
|
||||
#define SG_SVG_PRESERVE_ASPECT_RATIO_HXX_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
/**
|
||||
* SVG preserveAspectRatio attribute
|
||||
*
|
||||
* @see http://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
|
||||
*/
|
||||
class SVGpreserveAspectRatio
|
||||
{
|
||||
public:
|
||||
enum Align
|
||||
{
|
||||
ALIGN_NONE,
|
||||
ALIGN_MIN,
|
||||
ALIGN_MID,
|
||||
ALIGN_MAX
|
||||
};
|
||||
|
||||
SVGpreserveAspectRatio();
|
||||
|
||||
Align alignX() const;
|
||||
Align alignY() const;
|
||||
|
||||
bool scaleToFill() const;
|
||||
bool scaleToFit() const;
|
||||
bool scaleToCrop() const;
|
||||
|
||||
bool meet() const;
|
||||
|
||||
bool operator==(const SVGpreserveAspectRatio& rhs) const;
|
||||
|
||||
static SVGpreserveAspectRatio parse(const std::string& str);
|
||||
|
||||
private:
|
||||
Align _align_x,
|
||||
_align_y;
|
||||
bool _meet; //!< uniform scale to fit, if true
|
||||
// uniform scale to fill+crop, if false
|
||||
};
|
||||
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* SG_SVG_PRESERVE_ASPECT_RATIO_HXX_ */
|
||||
@@ -1,60 +0,0 @@
|
||||
/// Unit tests for SVGpreserveAspectRatio
|
||||
#define BOOST_TEST_MODULE misc
|
||||
#include <BoostTestTargetConfig.h>
|
||||
|
||||
#include "SVGpreserveAspectRatio.hxx"
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
std::ostream& operator<<( std::ostream& strm,
|
||||
const SVGpreserveAspectRatio& ar )
|
||||
{
|
||||
strm << "[ align_x=" << ar.alignX() <<
|
||||
", align_y=" << ar.alignY() <<
|
||||
", meet=" << ar.meet() <<
|
||||
"]";
|
||||
return strm;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( parse_attribute )
|
||||
{
|
||||
using simgear::SVGpreserveAspectRatio;
|
||||
|
||||
SVGpreserveAspectRatio ar = SVGpreserveAspectRatio::parse("none");
|
||||
BOOST_CHECK( ar.scaleToFill() );
|
||||
BOOST_CHECK( !ar.scaleToFit() );
|
||||
BOOST_CHECK( !ar.scaleToCrop() );
|
||||
BOOST_CHECK_EQUAL( ar.alignX(), SVGpreserveAspectRatio::ALIGN_NONE );
|
||||
BOOST_CHECK_EQUAL( ar.alignY(), SVGpreserveAspectRatio::ALIGN_NONE );
|
||||
|
||||
SVGpreserveAspectRatio ar_meet = SVGpreserveAspectRatio::parse("none meet");
|
||||
SVGpreserveAspectRatio ar_slice = SVGpreserveAspectRatio::parse("none slice");
|
||||
|
||||
BOOST_CHECK_EQUAL( ar, ar_meet );
|
||||
BOOST_CHECK_EQUAL( ar, ar_slice );
|
||||
|
||||
ar_meet = SVGpreserveAspectRatio::parse("xMidYMid meet");
|
||||
BOOST_CHECK( !ar_meet.scaleToFill() );
|
||||
BOOST_CHECK( ar_meet.scaleToFit() );
|
||||
BOOST_CHECK( !ar_meet.scaleToCrop() );
|
||||
BOOST_CHECK_EQUAL( ar_meet.alignX(), SVGpreserveAspectRatio::ALIGN_MID );
|
||||
BOOST_CHECK_EQUAL( ar_meet.alignY(), SVGpreserveAspectRatio::ALIGN_MID );
|
||||
|
||||
ar_slice = SVGpreserveAspectRatio::parse("xMidYMid slice");
|
||||
BOOST_CHECK( !ar_slice.scaleToFill() );
|
||||
BOOST_CHECK( !ar_slice.scaleToFit() );
|
||||
BOOST_CHECK( ar_slice.scaleToCrop() );
|
||||
BOOST_CHECK_EQUAL( ar_slice.alignX(), SVGpreserveAspectRatio::ALIGN_MID );
|
||||
BOOST_CHECK_EQUAL( ar_slice.alignY(), SVGpreserveAspectRatio::ALIGN_MID );
|
||||
|
||||
BOOST_CHECK_NE(ar_meet, ar_slice);
|
||||
|
||||
// defer is ignored, meet is default
|
||||
ar_meet = SVGpreserveAspectRatio::parse("defer xMinYMin");
|
||||
BOOST_CHECK( !ar_meet.scaleToFill() );
|
||||
BOOST_CHECK( ar_meet.scaleToFit() );
|
||||
BOOST_CHECK( !ar_meet.scaleToCrop() );
|
||||
BOOST_CHECK_EQUAL( ar_meet.alignX(), SVGpreserveAspectRatio::ALIGN_MIN );
|
||||
BOOST_CHECK_EQUAL( ar_meet.alignY(), SVGpreserveAspectRatio::ALIGN_MIN );
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
// Really simple markdown parser
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "SimpleMarkdown.hxx"
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
// White space
|
||||
static bool isSpace(const char c)
|
||||
{
|
||||
return c == ' ' || c == '\t';
|
||||
}
|
||||
|
||||
// Unordered list item
|
||||
static bool isULItem(const char c)
|
||||
{
|
||||
return c == '*' || c == '+' || c == '-';
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
std::string SimpleMarkdown::parse(const std::string& src)
|
||||
{
|
||||
std::string out;
|
||||
|
||||
int num_space = 0,
|
||||
num_newline = 0;
|
||||
bool line_empty = true;
|
||||
|
||||
for( std::string::const_iterator it = src.begin();
|
||||
it != src.end();
|
||||
++it )
|
||||
{
|
||||
if( isSpace(*it) )
|
||||
{
|
||||
++num_space;
|
||||
}
|
||||
else if( *it == '\n' )
|
||||
{
|
||||
// Two or more whitespace at end of line -> line break
|
||||
if( !line_empty && num_space >= 2 )
|
||||
{
|
||||
out.push_back('\n');
|
||||
line_empty = true;
|
||||
}
|
||||
|
||||
++num_newline;
|
||||
num_space = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove all new lines before first printable character
|
||||
if( out.empty() )
|
||||
num_newline = 0;
|
||||
|
||||
// Two or more new lines (aka. at least one empty line) -> new paragraph
|
||||
if( num_newline >= 2 )
|
||||
{
|
||||
out.append("\n\n");
|
||||
|
||||
line_empty = true;
|
||||
num_newline = 0;
|
||||
}
|
||||
|
||||
// Replace unordered list item markup at begin of line with bullet
|
||||
// (TODO multilevel lists, indent multiple lines, etc.)
|
||||
if( (line_empty || num_newline) && isULItem(*it) )
|
||||
{
|
||||
if( num_newline < 2 )
|
||||
out.push_back('\n');
|
||||
out.append("\xE2\x80\xA2");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Collapse multiple whitespace
|
||||
if( !line_empty && (num_space || num_newline) )
|
||||
out.push_back(' ');
|
||||
out.push_back(*it);
|
||||
}
|
||||
|
||||
line_empty = false;
|
||||
num_space = 0;
|
||||
num_newline = 0;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace simgear
|
||||
@@ -1,39 +0,0 @@
|
||||
///@file Really simple markdown parser
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef SIMPLE_MARKDOWN_HXX_
|
||||
#define SIMPLE_MARKDOWN_HXX_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
/**
|
||||
* Really simple mardown parser. Currently just paragraphs, new lines and
|
||||
* one level of unordered lists are supported.
|
||||
*
|
||||
* @see http://en.wikipedia.org/wiki/Markdown
|
||||
*/
|
||||
class SimpleMarkdown
|
||||
{
|
||||
public:
|
||||
static std::string parse(const std::string& src);
|
||||
};
|
||||
} // namespace simgear
|
||||
|
||||
#endif /* SIMPLE_MARKDOWN_HXX_ */
|
||||
@@ -1,34 +0,0 @@
|
||||
/// Unit tests for simple markdown parser
|
||||
#define BOOST_TEST_MODULE misc
|
||||
#include <BoostTestTargetConfig.h>
|
||||
|
||||
#include "SimpleMarkdown.hxx"
|
||||
|
||||
BOOST_AUTO_TEST_CASE( basic_markdown )
|
||||
{
|
||||
std::string const src(
|
||||
" \n"
|
||||
"\n"
|
||||
"blub\n"
|
||||
"* \tlist item\n"
|
||||
"* another\t item\n"
|
||||
"\n"
|
||||
"test blubt\n"
|
||||
" aha \n"
|
||||
"asd\n"
|
||||
" * 2nd list, another item\n"
|
||||
" * 2nd list, one more..."
|
||||
);
|
||||
std::string const out(
|
||||
"blub\n"
|
||||
"\xE2\x80\xA2 list item\n"
|
||||
"\xE2\x80\xA2 another item\n"
|
||||
"\n"
|
||||
"test blubt aha\n"
|
||||
"asd\n"
|
||||
"\xE2\x80\xA2 2nd list, another item\n"
|
||||
"\xE2\x80\xA2 2nd list, one more..."
|
||||
);
|
||||
|
||||
BOOST_CHECK_EQUAL(simgear::SimpleMarkdown::parse(src), out);
|
||||
}
|
||||
@@ -41,20 +41,6 @@ void test_dir()
|
||||
}
|
||||
|
||||
cout << temp.path().modTime() << endl;
|
||||
|
||||
std::cout << "Standard Locations:"
|
||||
<< "\n - Home: " << SGPath::standardLocation(SGPath::HOME)
|
||||
<< "\n - Desktop: " << SGPath::standardLocation(SGPath::DESKTOP)
|
||||
<< "\n - Downloads: " << SGPath::standardLocation(SGPath::DOWNLOADS)
|
||||
<< "\n - Documents: " << SGPath::standardLocation(SGPath::DOCUMENTS)
|
||||
<< "\n - Pictures: " << SGPath::standardLocation(SGPath::PICTURES)
|
||||
<< std::endl;
|
||||
|
||||
VERIFY( !SGPath::standardLocation(SGPath::HOME ).isNull() );
|
||||
VERIFY( !SGPath::standardLocation(SGPath::DESKTOP ).isNull() );
|
||||
VERIFY( !SGPath::standardLocation(SGPath::DOWNLOADS).isNull() );
|
||||
VERIFY( !SGPath::standardLocation(SGPath::DOCUMENTS).isNull() );
|
||||
VERIFY( !SGPath::standardLocation(SGPath::PICTURES ).isNull() );
|
||||
}
|
||||
|
||||
SGPath::Permissions validateNone(const SGPath&)
|
||||
@@ -192,12 +178,12 @@ int main(int argc, char* argv[])
|
||||
pp.append("./test-dir/file.txt");
|
||||
COMPARE(pp.create_dir(0700), -3);
|
||||
|
||||
pp.setPermissionChecker(&validateRead);
|
||||
pp.setPermissonChecker(&validateRead);
|
||||
COMPARE(pp.canRead(), true);
|
||||
COMPARE(pp.canWrite(), false);
|
||||
COMPARE(pp.create_dir(0700), -3);
|
||||
|
||||
pp.setPermissionChecker(&validateWrite);
|
||||
pp.setPermissonChecker(&validateWrite);
|
||||
COMPARE(pp.canRead(), false);
|
||||
COMPARE(pp.canWrite(), true);
|
||||
|
||||
|
||||
@@ -56,9 +56,10 @@ static const char sgSearchPathSep = ':';
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <ShlObj.h> // for CSIDL
|
||||
|
||||
static SGPath pathForCSIDL(int csidl, const SGPath& def)
|
||||
static SGPath pathForCSIDL(int csidl, const SGPath::PermissonChecker& checker)
|
||||
{
|
||||
typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPSTR, int, BOOL);
|
||||
static GetSpecialFolderPath SHGetSpecialFolderPath = NULL;
|
||||
@@ -70,63 +71,17 @@ static SGPath pathForCSIDL(int csidl, const SGPath& def)
|
||||
}
|
||||
|
||||
if (!SHGetSpecialFolderPath){
|
||||
return def;
|
||||
return SGPath();
|
||||
}
|
||||
|
||||
char path[MAX_PATH];
|
||||
if (SHGetSpecialFolderPath(0, path, csidl, false)) {
|
||||
return SGPath(path, def.getPermissionChecker());
|
||||
return SGPath(path, checker);
|
||||
}
|
||||
|
||||
return def;
|
||||
return SGPath();
|
||||
}
|
||||
#elif __APPLE__
|
||||
|
||||
// defined in CocoaHelpers.mm
|
||||
SGPath appleSpecialFolder(int dirType, int domainMask, const SGPath& def);
|
||||
|
||||
#else
|
||||
static SGPath getXDGDir( const std::string& name,
|
||||
const SGPath& def,
|
||||
const std::string& fallback )
|
||||
{
|
||||
// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
|
||||
// $XDG_CONFIG_HOME defines the base directory relative to which user specific
|
||||
// configuration files should be stored. If $XDG_CONFIG_HOME is either not set
|
||||
// or empty, a default equal to $HOME/.config should be used.
|
||||
const SGPath user_dirs = SGPath::fromEnv( "XDG_CONFIG_HOME",
|
||||
SGPath::home() / ".config")
|
||||
/ "user-dirs.dirs";
|
||||
|
||||
// Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
|
||||
// homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an absolute
|
||||
// path. No other format is supported.
|
||||
const std::string XDG_ID = "XDG_" + name + "_DIR=\"";
|
||||
|
||||
std::ifstream user_dirs_file( user_dirs.c_str() );
|
||||
std::string line;
|
||||
while( std::getline(user_dirs_file, line).good() )
|
||||
{
|
||||
if( !starts_with(line, XDG_ID) || *line.rbegin() != '"' )
|
||||
continue;
|
||||
|
||||
// Extract dir from XDG_<name>_DIR="<dir>"
|
||||
line = line.substr(XDG_ID.length(), line.length() - XDG_ID.length() - 1 );
|
||||
|
||||
const std::string HOME = "$HOME";
|
||||
if( starts_with(line, HOME) )
|
||||
return SGPath::home(def)
|
||||
/ simgear::strutils::unescape(line.substr(HOME.length()));
|
||||
|
||||
return SGPath(line, def.getPermissionChecker());
|
||||
}
|
||||
|
||||
if( def.isNull() )
|
||||
return SGPath::home(def) / fallback;
|
||||
|
||||
return def;
|
||||
}
|
||||
#endif
|
||||
|
||||
// For windows, replace "\" by "/".
|
||||
@@ -148,7 +103,7 @@ SGPath::fix()
|
||||
|
||||
|
||||
// default constructor
|
||||
SGPath::SGPath(PermissionChecker validator)
|
||||
SGPath::SGPath(PermissonChecker validator)
|
||||
: path(""),
|
||||
_permission_checker(validator),
|
||||
_cached(false),
|
||||
@@ -159,7 +114,7 @@ SGPath::SGPath(PermissionChecker validator)
|
||||
|
||||
|
||||
// create a path based on "path"
|
||||
SGPath::SGPath( const std::string& p, PermissionChecker validator )
|
||||
SGPath::SGPath( const std::string& p, PermissonChecker validator )
|
||||
: path(p),
|
||||
_permission_checker(validator),
|
||||
_cached(false),
|
||||
@@ -172,7 +127,7 @@ SGPath::SGPath( const std::string& p, PermissionChecker validator )
|
||||
// create a path based on "path" and a "subpath"
|
||||
SGPath::SGPath( const SGPath& p,
|
||||
const std::string& r,
|
||||
PermissionChecker validator )
|
||||
PermissonChecker validator )
|
||||
: path(p.path),
|
||||
_permission_checker(validator),
|
||||
_cached(false),
|
||||
@@ -228,14 +183,14 @@ void SGPath::set( const string& p ) {
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void SGPath::setPermissionChecker(PermissionChecker validator)
|
||||
void SGPath::setPermissonChecker(PermissonChecker validator)
|
||||
{
|
||||
_permission_checker = validator;
|
||||
_rwCached = false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
SGPath::PermissionChecker SGPath::getPermissionChecker() const
|
||||
SGPath::PermissonChecker SGPath::getPermissonChecker() const
|
||||
{
|
||||
return _permission_checker;
|
||||
}
|
||||
@@ -379,7 +334,6 @@ string SGPath::complete_lower_extension() const
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void SGPath::validate() const
|
||||
{
|
||||
if (_cached && _cacheEnabled) {
|
||||
@@ -424,7 +378,6 @@ void SGPath::validate() const
|
||||
_cached = true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void SGPath::checkAccess() const
|
||||
{
|
||||
if( _rwCached && _cacheEnabled )
|
||||
@@ -477,23 +430,14 @@ bool SGPath::isFile() const
|
||||
return _exists && _isFile;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#ifdef _WIN32
|
||||
# define sgMkDir(d,m) _mkdir(d)
|
||||
#else
|
||||
# define sgMkDir(d,m) mkdir(d,m)
|
||||
#endif
|
||||
|
||||
int SGPath::create_dir(mode_t mode)
|
||||
{
|
||||
if( !canWrite() )
|
||||
{
|
||||
SG_LOG( SG_IO,
|
||||
SG_WARN, "Error creating directory for '" << str() << "'"
|
||||
" reason: access denied" );
|
||||
return -3;
|
||||
}
|
||||
|
||||
int SGPath::create_dir( mode_t mode ) {
|
||||
string_list dirlist = sgPathSplit(dir());
|
||||
if ( dirlist.empty() )
|
||||
return -1;
|
||||
@@ -510,31 +454,37 @@ int SGPath::create_dir(mode_t mode)
|
||||
i = 2;
|
||||
}
|
||||
#endif
|
||||
struct stat info;
|
||||
int r;
|
||||
for(; (r = stat(dir.c_str(), &info)) == 0 && i < path_elements.size(); ++i)
|
||||
dir.append(path_elements[i]);
|
||||
if( r == 0 )
|
||||
return 0; // Directory already exists
|
||||
|
||||
for(;;)
|
||||
{
|
||||
if( sgMkDir(dir.c_str(), mode) )
|
||||
{
|
||||
SG_LOG( SG_IO,
|
||||
SG_ALERT, "Error creating directory: (" << dir.str() << ")" );
|
||||
return -2;
|
||||
struct stat info;
|
||||
int r;
|
||||
for(; ( r = stat( dir.c_str(), &info ) ) == 0 && i < path_elements.size(); i++) {
|
||||
dir.append(path_elements[i]);
|
||||
}
|
||||
else
|
||||
SG_LOG(SG_IO, SG_DEBUG, "Directory created: " << dir.str());
|
||||
if ( r == 0 ) {
|
||||
return 0; // Directory already exists
|
||||
}
|
||||
for(;;)
|
||||
{
|
||||
if( !dir.canWrite() )
|
||||
{
|
||||
SG_LOG( SG_IO,
|
||||
SG_WARN, "Error creating directory: (" << dir.str() << ")" <<
|
||||
" reason: access denied" );
|
||||
return -3;
|
||||
}
|
||||
else if( sgMkDir(dir.c_str(), mode) )
|
||||
{
|
||||
SG_LOG( SG_IO,
|
||||
SG_ALERT, "Error creating directory: (" << dir.str() << ")" );
|
||||
return -2;
|
||||
}
|
||||
|
||||
if( i >= path_elements.size() )
|
||||
return 0;
|
||||
if( i >= path_elements.size() )
|
||||
return 0;
|
||||
|
||||
dir.append(path_elements[i++]);
|
||||
}
|
||||
dir.append(path_elements[i++]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
string_list sgPathBranchSplit( const string &dirpath ) {
|
||||
@@ -697,56 +647,6 @@ bool SGPath::rename(const SGPath& newName)
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
SGPath SGPath::standardLocation(StandardLocation type, const SGPath& def)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case HOME:
|
||||
return home(def);
|
||||
#ifdef _WIN32
|
||||
case DESKTOP:
|
||||
return pathForCSIDL(CSIDL_DESKTOPDIRECTORY, def);
|
||||
case DOWNLOADS:
|
||||
// TODO use KnownFolders
|
||||
// http://msdn.microsoft.com/en-us/library/bb776911%28v=vs.85%29.aspx
|
||||
if( !def.isNull() )
|
||||
return def;
|
||||
|
||||
return pathForCSIDL(CSIDL_DESKTOPDIRECTORY, def);
|
||||
case DOCUMENTS:
|
||||
return pathForCSIDL(CSIDL_MYDOCUMENTS, def);
|
||||
case PICTURES:
|
||||
return pathForCSIDL(CSIDL_MYPICTURES, def);
|
||||
#elif __APPLE__
|
||||
// since this is C++, we can't include NSPathUtilities.h to access the enum
|
||||
// values, so hard-coding them here (they are stable, don't worry)
|
||||
case DOWNLOADS:
|
||||
return appleSpecialFolder(15, 1, def);
|
||||
case DESKTOP:
|
||||
return appleSpecialFolder(12, 1, def);
|
||||
case DOCUMENTS:
|
||||
return appleSpecialFolder(9, 1, def);
|
||||
case PICTURES:
|
||||
return appleSpecialFolder(19, 1, def);
|
||||
#else
|
||||
case DESKTOP:
|
||||
return getXDGDir("DESKTOP", def, "Desktop");
|
||||
case DOWNLOADS:
|
||||
return getXDGDir("DOWNLOADS", def, "Downloads");
|
||||
case DOCUMENTS:
|
||||
return getXDGDir("DOCUMENTS", def, "Documents");
|
||||
case PICTURES:
|
||||
return getXDGDir("PICTURES", def, "Pictures");
|
||||
#endif
|
||||
default:
|
||||
SG_LOG( SG_GENERAL,
|
||||
SG_WARN,
|
||||
"SGPath::standardLocation() unhandled type: " << type );
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
SGPath SGPath::fromEnv(const char* name, const SGPath& def)
|
||||
{
|
||||
@@ -771,19 +671,84 @@ SGPath SGPath::home(const SGPath& def)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
//------------------------------------------------------------------------------
|
||||
SGPath SGPath::desktop(const SGPath& def)
|
||||
{
|
||||
return standardLocation(DESKTOP, def);
|
||||
SGPath r = pathForCSIDL(CSIDL_DESKTOPDIRECTORY, def._permission_checker);
|
||||
if (!r.isNull()) {
|
||||
return r;
|
||||
}
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "SGPath::desktop() failed, bad" );
|
||||
return def;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
SGPath SGPath::documents(const SGPath& def)
|
||||
{
|
||||
return standardLocation(DOCUMENTS, def);
|
||||
SGPath r = pathForCSIDL(CSIDL_MYDOCUMENTS, def._permission_checker);
|
||||
if (!r.isNull()) {
|
||||
return r;
|
||||
}
|
||||
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "SGPath::documents() failed, bad" );
|
||||
return def;
|
||||
}
|
||||
#elif __APPLE__
|
||||
#include <CoreServices/CoreServices.h>
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
SGPath SGPath::desktop(const SGPath& def)
|
||||
{
|
||||
FSRef ref;
|
||||
OSErr err = FSFindFolder(kUserDomain, kDesktopFolderType, false, &ref);
|
||||
if (err)
|
||||
return def;
|
||||
|
||||
unsigned char path[1024];
|
||||
if (FSRefMakePath(&ref, path, 1024) != noErr)
|
||||
return def;
|
||||
|
||||
return SGPath((const char*) path, def._permission_checker);
|
||||
}
|
||||
#else
|
||||
//------------------------------------------------------------------------------
|
||||
SGPath SGPath::desktop(const SGPath& def)
|
||||
{
|
||||
// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
|
||||
// $XDG_CONFIG_HOME defines the base directory relative to which user specific
|
||||
// configuration files should be stored. If $XDG_CONFIG_HOME is either not set
|
||||
// or empty, a default equal to $HOME/.config should be used.
|
||||
const SGPath user_dirs = fromEnv("XDG_CONFIG_HOME", home() / ".config")
|
||||
/ "user-dirs.dirs";
|
||||
|
||||
// Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
|
||||
// homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an absolute
|
||||
// path. No other format is supported.
|
||||
const std::string DESKTOP = "XDG_DESKTOP_DIR=\"";
|
||||
|
||||
std::ifstream user_dirs_file( user_dirs.c_str() );
|
||||
std::string line;
|
||||
while( std::getline(user_dirs_file, line).good() )
|
||||
{
|
||||
if( !starts_with(line, DESKTOP) || *line.rbegin() != '"' )
|
||||
continue;
|
||||
|
||||
// Extract dir from XDG_DESKTOP_DIR="<dir>"
|
||||
line = line.substr(DESKTOP.length(), line.length() - DESKTOP.length() - 1 );
|
||||
|
||||
const std::string HOME = "$HOME";
|
||||
if( starts_with(line, HOME) )
|
||||
return home(def) / simgear::strutils::unescape(line.substr(HOME.length()));
|
||||
|
||||
return SGPath(line, def._permission_checker);
|
||||
}
|
||||
|
||||
return home(def) / "Desktop";
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string SGPath::realpath() const
|
||||
{
|
||||
#if (defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED <= 1050)
|
||||
|
||||
@@ -57,10 +57,10 @@ public:
|
||||
bool read : 1;
|
||||
bool write : 1;
|
||||
};
|
||||
typedef Permissions (*PermissionChecker)(const SGPath&);
|
||||
typedef Permissions (*PermissonChecker)(const SGPath&);
|
||||
|
||||
/** Default constructor */
|
||||
explicit SGPath(PermissionChecker validator = NULL);
|
||||
explicit SGPath(PermissonChecker validator = NULL);
|
||||
|
||||
/** Copy contructor */
|
||||
SGPath(const SGPath& p);
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
* Construct a path based on the starting path provided.
|
||||
* @param p initial path
|
||||
*/
|
||||
SGPath( const std::string& p, PermissionChecker validator = NULL );
|
||||
SGPath( const std::string& p, PermissonChecker validator = NULL );
|
||||
|
||||
/**
|
||||
* Construct a path based on the starting path provided and a relative subpath
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
*/
|
||||
SGPath( const SGPath& p,
|
||||
const std::string& r,
|
||||
PermissionChecker validator = NULL );
|
||||
PermissonChecker validator = NULL );
|
||||
|
||||
/** Destructor */
|
||||
~SGPath();
|
||||
@@ -95,8 +95,8 @@ public:
|
||||
bool operator==(const SGPath& other) const;
|
||||
bool operator!=(const SGPath& other) const;
|
||||
|
||||
void setPermissionChecker(PermissionChecker validator);
|
||||
PermissionChecker getPermissionChecker() const;
|
||||
void setPermissonChecker(PermissonChecker validator);
|
||||
PermissonChecker getPermissonChecker() const;
|
||||
|
||||
/**
|
||||
* Set if file information (exists, type, mod-time) is cached or
|
||||
@@ -206,12 +206,9 @@ public:
|
||||
|
||||
/**
|
||||
* Create the designated directory.
|
||||
*
|
||||
* @param mode Permissions. See:
|
||||
* http://en.wikipedia.org/wiki/File_system_permissions#Numeric_notation
|
||||
* @return 0 on success, or <0 on failure.
|
||||
*/
|
||||
int create_dir(mode_t mode = 0755);
|
||||
int create_dir(mode_t mode);
|
||||
|
||||
/**
|
||||
* Check if reading file is allowed. Readabilty does not imply the existance
|
||||
@@ -260,18 +257,6 @@ public:
|
||||
*/
|
||||
bool rename(const SGPath& newName);
|
||||
|
||||
enum StandardLocation
|
||||
{
|
||||
HOME,
|
||||
DESKTOP,
|
||||
DOWNLOADS,
|
||||
DOCUMENTS,
|
||||
PICTURES
|
||||
};
|
||||
|
||||
static SGPath standardLocation( StandardLocation type,
|
||||
const SGPath& def = SGPath() );
|
||||
|
||||
/**
|
||||
* Get a path stored in the environment variable with the given \a name.
|
||||
*
|
||||
@@ -295,7 +280,6 @@ public:
|
||||
* Get path to the user's documents directory
|
||||
*/
|
||||
static SGPath documents(const SGPath& def = SGPath());
|
||||
|
||||
private:
|
||||
|
||||
void fix();
|
||||
@@ -304,7 +288,7 @@ private:
|
||||
void checkAccess() const;
|
||||
|
||||
std::string path;
|
||||
PermissionChecker _permission_checker;
|
||||
PermissonChecker _permission_checker;
|
||||
|
||||
mutable bool _cached : 1;
|
||||
mutable bool _rwCached : 1;
|
||||
|
||||
@@ -20,7 +20,7 @@ int main()
|
||||
"third line ends with both\r\n"
|
||||
"fourth line as well\r\n"
|
||||
"fifth line is another CR/LF line\r\n"
|
||||
"end of test\r\n", 158);
|
||||
"end of test\r\n", 1024);
|
||||
f.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "strutils.hxx"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/package/md5.h>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
@@ -54,11 +53,8 @@ namespace simgear {
|
||||
size_t len = get_length (p);
|
||||
if (len == 1) return *p;
|
||||
value_type res = static_cast<unsigned char> ( *p & (0xff >> (len + 1))) << ((len - 1) * 6 );
|
||||
for (--len; len; --len) {
|
||||
value_type next_byte = static_cast<unsigned char> (*(++p)) - 0x80;
|
||||
if (next_byte & 0xC0) return 0x00ffffff; // invalid UTF-8
|
||||
res |= next_byte << ((len - 1) * 6);
|
||||
}
|
||||
for (--len; len; --len)
|
||||
res |= (static_cast<unsigned char> (*(++p)) - 0x80) << ((len - 1) * 6);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -66,7 +62,6 @@ namespace simgear {
|
||||
string s_latin1;
|
||||
for (string::iterator p = s_utf8.begin(); p != s_utf8.end(); ++p) {
|
||||
value_type value = get_value<string::iterator&>(p);
|
||||
if (value > 0x10ffff) return s_utf8; // invalid UTF-8: guess that the input was already Latin-1
|
||||
if (value > 0xff) SG_LOG(SG_IO, SG_WARN, "utf8ToLatin1: wrong char value: " << value);
|
||||
s_latin1 += static_cast<char>(value);
|
||||
}
|
||||
@@ -402,32 +397,8 @@ std::string convertWindowsLocal8BitToUtf8(const std::string& a)
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
std::string md5(const unsigned char* data, size_t num)
|
||||
{
|
||||
SG_MD5_CTX md5_ctx;
|
||||
SG_MD5Init(&md5_ctx);
|
||||
SG_MD5Update(&md5_ctx, data, num);
|
||||
|
||||
unsigned char digest[MD5_DIGEST_LENGTH];
|
||||
SG_MD5Final(digest, &md5_ctx);
|
||||
|
||||
return encodeHex(digest, MD5_DIGEST_LENGTH);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
std::string md5(const char* data, size_t num)
|
||||
{
|
||||
return md5(reinterpret_cast<const unsigned char*>(data), num);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
std::string md5(const std::string& str)
|
||||
{
|
||||
return md5(reinterpret_cast<const unsigned char*>(str.c_str()), str.size());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static const std::string base64_chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
@@ -507,24 +478,28 @@ void decodeBase64(const std::string& encoded_string, std::vector<unsigned char>&
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
const char hexChar[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
|
||||
std::string encodeHex(const std::string& bytes)
|
||||
{
|
||||
return encodeHex(
|
||||
reinterpret_cast<const unsigned char*>(bytes.c_str()),
|
||||
bytes.size()
|
||||
);
|
||||
std::string hex;
|
||||
size_t count = bytes.size();
|
||||
for (unsigned int i=0; i<count;++i) {
|
||||
unsigned char c = bytes[i];
|
||||
hex.push_back(hexChar[c >> 4]);
|
||||
hex.push_back(hexChar[c & 0x0f]);
|
||||
}
|
||||
|
||||
return hex;
|
||||
}
|
||||
|
||||
std::string encodeHex(const unsigned char* rawBytes, unsigned int length)
|
||||
{
|
||||
std::string hex(length * 2, '\0');
|
||||
std::string hex;
|
||||
for (unsigned int i=0; i<length;++i) {
|
||||
unsigned char c = *rawBytes++;
|
||||
hex[i * 2] = hexChar[c >> 4];
|
||||
hex[i * 2 + 1] = hexChar[c & 0x0f];
|
||||
hex.push_back(hexChar[c >> 4]);
|
||||
hex.push_back(hexChar[c & 0x0f]);
|
||||
}
|
||||
|
||||
return hex;
|
||||
|
||||
@@ -176,13 +176,6 @@ namespace simgear {
|
||||
WCharVec convertUtf8ToWString(const std::string& a);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Get md5 hash of raw data.
|
||||
*/
|
||||
std::string md5(const unsigned char* data, size_t num);
|
||||
std::string md5(const char* data, size_t num);
|
||||
std::string md5(const std::string& str);
|
||||
|
||||
/**
|
||||
* convert base-64 encoded data to raw bytes (possibly with embedded
|
||||
* NULs). Throws an exception if input data is not base64, or is
|
||||
|
||||
@@ -1,80 +1,84 @@
|
||||
/// Unit tests for function inside strutils package
|
||||
#define BOOST_TEST_MODULE misc
|
||||
#include <BoostTestTargetConfig.h>
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Test harness.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <iostream>
|
||||
#include "strutils.hxx"
|
||||
|
||||
namespace strutils = simgear::strutils;
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
BOOST_AUTO_TEST_CASE( strutils_functions )
|
||||
using namespace simgear::strutils;
|
||||
|
||||
#define COMPARE(a, b) \
|
||||
if ((a) != (b)) { \
|
||||
cerr << "failed:" << #a << " != " << #b << endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define VERIFY(a) \
|
||||
if (!(a)) { \
|
||||
cerr << "failed:" << #a << endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
int main (int ac, char ** av)
|
||||
{
|
||||
std::string a("abcd");
|
||||
BOOST_CHECK_EQUAL(strutils::strip(a), a);
|
||||
BOOST_CHECK_EQUAL(strutils::strip(" a "), "a");
|
||||
BOOST_CHECK_EQUAL(strutils::lstrip(" a "), "a ");
|
||||
BOOST_CHECK_EQUAL(strutils::rstrip("\ta "), "\ta");
|
||||
std::string a("abcd");
|
||||
COMPARE(strip(a), a);
|
||||
COMPARE(strip(" a "), "a");
|
||||
COMPARE(lstrip(" a "), "a ");
|
||||
COMPARE(rstrip("\ta "), "\ta");
|
||||
// check internal spacing is preserved
|
||||
COMPARE(strip("\t \na \t b\r \n "), "a \t b");
|
||||
|
||||
|
||||
VERIFY(starts_with("banana", "ban"));
|
||||
VERIFY(!starts_with("abanana", "ban"));
|
||||
VERIFY(starts_with("banana", "banana")); // pass - string starts with itself
|
||||
VERIFY(!starts_with("ban", "banana")); // fail - original string is prefix of
|
||||
|
||||
VERIFY(ends_with("banana", "ana"));
|
||||
VERIFY(ends_with("foo.text", ".text"));
|
||||
VERIFY(!ends_with("foo.text", ".html"));
|
||||
|
||||
COMPARE(simplify("\ta\t b \nc\n\r \r\n"), "a b c");
|
||||
COMPARE(simplify("The quick - brown dog!"), "The quick - brown dog!");
|
||||
COMPARE(simplify("\r\n \r\n \t \r"), "");
|
||||
|
||||
COMPARE(to_int("999"), 999);
|
||||
COMPARE(to_int("0000000"), 0);
|
||||
COMPARE(to_int("-10000"), -10000);
|
||||
|
||||
VERIFY(compare_versions("1.0.12", "1.1") < 0);
|
||||
VERIFY(compare_versions("1.1", "1.0.12") > 0);
|
||||
VERIFY(compare_versions("10.6.7", "10.6.7") == 0);
|
||||
VERIFY(compare_versions("2.0", "2.0.99") < 0);
|
||||
VERIFY(compare_versions("99", "99") == 0);
|
||||
VERIFY(compare_versions("99", "98") > 0);
|
||||
|
||||
// since we compare numerically, leasing zeros shouldn't matter
|
||||
VERIFY(compare_versions("0.06.7", "0.6.07") == 0);
|
||||
|
||||
string_list la = split("zero one two three four five");
|
||||
COMPARE(la[2], "two");
|
||||
COMPARE(la[5], "five");
|
||||
COMPARE(la.size(), 6);
|
||||
|
||||
string_list lb = split("alpha:beta:gamma:delta", ":", 2);
|
||||
COMPARE(lb.size(), 3);
|
||||
COMPARE(lb[0], "alpha");
|
||||
COMPARE(lb[1], "beta");
|
||||
COMPARE(lb[2], "gamma:delta");
|
||||
|
||||
std::string j = join(la, "&");
|
||||
COMPARE(j, "zero&one&two&three&four&five");
|
||||
|
||||
// check internal spacing is preserved
|
||||
BOOST_CHECK_EQUAL(strutils::strip("\t \na \t b\r \n "), "a \t b");
|
||||
COMPARE(unescape("\\ \\n\\t\\x41\\117a"), " \n\tAOa");
|
||||
|
||||
|
||||
BOOST_CHECK(strutils::starts_with("banana", "ban"));
|
||||
BOOST_CHECK(!strutils::starts_with("abanana", "ban"));
|
||||
BOOST_CHECK(strutils::starts_with("banana", "banana")); // pass - string starts with itself
|
||||
BOOST_CHECK(!strutils::starts_with("ban", "banana")); // fail - original string is prefix of
|
||||
|
||||
BOOST_CHECK(strutils::ends_with("banana", "ana"));
|
||||
BOOST_CHECK(strutils::ends_with("foo.text", ".text"));
|
||||
BOOST_CHECK(!strutils::ends_with("foo.text", ".html"));
|
||||
|
||||
BOOST_CHECK_EQUAL(strutils::simplify("\ta\t b \nc\n\r \r\n"), "a b c");
|
||||
BOOST_CHECK_EQUAL(strutils::simplify("The quick - brown dog!"), "The quick - brown dog!");
|
||||
BOOST_CHECK_EQUAL(strutils::simplify("\r\n \r\n \t \r"), "");
|
||||
|
||||
BOOST_CHECK_EQUAL(strutils::to_int("999"), 999);
|
||||
BOOST_CHECK_EQUAL(strutils::to_int("0000000"), 0);
|
||||
BOOST_CHECK_EQUAL(strutils::to_int("-10000"), -10000);
|
||||
|
||||
string_list la = strutils::split("zero one two three four five");
|
||||
BOOST_CHECK_EQUAL(la[2], "two");
|
||||
BOOST_CHECK_EQUAL(la[5], "five");
|
||||
BOOST_CHECK_EQUAL(la.size(), 6);
|
||||
|
||||
string_list lb = strutils::split("alpha:beta:gamma:delta", ":", 2);
|
||||
BOOST_CHECK_EQUAL(lb.size(), 3);
|
||||
BOOST_CHECK_EQUAL(lb[0], "alpha");
|
||||
BOOST_CHECK_EQUAL(lb[1], "beta");
|
||||
BOOST_CHECK_EQUAL(lb[2], "gamma:delta");
|
||||
|
||||
std::string j = strutils::join(la, "&");
|
||||
BOOST_CHECK_EQUAL(j, "zero&one&two&three&four&five");
|
||||
|
||||
BOOST_CHECK_EQUAL(strutils::unescape("\\ \\n\\t\\x41\\117a"), " \n\tAOa");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( compare_versions )
|
||||
{
|
||||
BOOST_CHECK_LT(strutils::compare_versions("1.0.12", "1.1"), 0);
|
||||
BOOST_CHECK_GT(strutils::compare_versions("1.1", "1.0.12"), 0);
|
||||
BOOST_CHECK_EQUAL(strutils::compare_versions("10.6.7", "10.6.7"), 0);
|
||||
BOOST_CHECK_LT(strutils::compare_versions("2.0", "2.0.99"), 0);
|
||||
BOOST_CHECK_EQUAL(strutils::compare_versions("99", "99"), 0);
|
||||
BOOST_CHECK_GT(strutils::compare_versions("99", "98"), 0);
|
||||
|
||||
// since we compare numerically, leasing zeros shouldn't matter
|
||||
BOOST_CHECK_EQUAL(strutils::compare_versions("0.06.7", "0.6.07"), 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( md5_hex )
|
||||
{
|
||||
// hex encoding
|
||||
unsigned char raw_data[] = {0x0f, 0x1a, 0xbc, 0xd2, 0xe3, 0x45, 0x67, 0x89};
|
||||
const std::string& hex_data =
|
||||
strutils::encodeHex(raw_data, sizeof(raw_data)/sizeof(raw_data[0]));
|
||||
BOOST_REQUIRE_EQUAL(hex_data, "0f1abcd2e3456789");
|
||||
BOOST_REQUIRE_EQUAL(strutils::encodeHex("abcde"), "6162636465");
|
||||
|
||||
// md5
|
||||
BOOST_CHECK_EQUAL(strutils::md5("test"), "098f6bcd4621d373cade4e832627b4f6");
|
||||
cout << "all tests passed successfully!" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2,37 +2,18 @@
|
||||
#ifndef SG_MISC_TEST_MACROS_HXX
|
||||
#define SG_MISC_TEST_MACROS_HXX
|
||||
|
||||
#include <cmath> // for fabs()
|
||||
|
||||
#define COMPARE(a, b) \
|
||||
if ((a) != (b)) { \
|
||||
std::cerr << "failed:" << #a << " != " << #b << std::endl; \
|
||||
std::cerr << "\tgot:'" << a << "'" << std::endl; \
|
||||
std::cerr << "\tat:" << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define VERIFY(a) \
|
||||
if (!(a)) { \
|
||||
std::cerr << "failed:" << #a << std::endl; \
|
||||
std::cerr << "\tat:" << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define COMPARE_EP(a, b) \
|
||||
if (fabs(a - b) > SG_EPSILON) { \
|
||||
std::cerr << "failed with epsilon:" << #a << " != " << #b << std::endl; \
|
||||
std::cerr << "\tgot:'" << a << "'" << std::endl; \
|
||||
std::cerr << "\tat:" << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
#define COMPARE_EP2(a, b, ep) \
|
||||
if (fabs(a - b) > ep) { \
|
||||
std::cerr << "failed with epsilon:" << #a << " != " << #b << std::endl; \
|
||||
std::cerr << "\tgot:'" << a << "'" << std::endl; \
|
||||
std::cerr << "\tat:" << __FILE__ << ":" << __LINE__ << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // of SG_MISC_TEST_MACROS_HXX
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
/// Unit tests for utf8ToLatin1 conversion function
|
||||
#define BOOST_TEST_MODULE misc
|
||||
#include <BoostTestTargetConfig.h>
|
||||
|
||||
#include "strutils.hxx"
|
||||
#include <string>
|
||||
|
||||
BOOST_AUTO_TEST_CASE( utf8_latin1_conversion )
|
||||
{
|
||||
std::string utf8_string1 = "Zweibr\u00FCcken";
|
||||
//valid UTF-8, convertible to Latin-1
|
||||
std::string latin1_string1 = "Zweibr\374cken";
|
||||
//Latin-1, not valid UTF-8
|
||||
std::string utf8_string2 = "\u600f\U00010143";
|
||||
//valid UTF-8, out of range for Latin-1
|
||||
|
||||
std::string output_string1u = simgear::strutils::utf8ToLatin1(utf8_string1);
|
||||
BOOST_CHECK_EQUAL(output_string1u, latin1_string1);
|
||||
|
||||
std::string output_string1l = simgear::strutils::utf8ToLatin1(latin1_string1);
|
||||
BOOST_CHECK_EQUAL(output_string1l, latin1_string1);
|
||||
|
||||
std::string output_string3 = simgear::strutils::utf8ToLatin1(utf8_string2);
|
||||
//we don't check the result of this one as there is no right answer,
|
||||
//just make sure it doesn't crash/hang
|
||||
}
|
||||
@@ -71,9 +71,7 @@ static double numify(naContext ctx, naRef o)
|
||||
else if(IS_NIL(o)) ERR(ctx, "nil used in numeric context");
|
||||
else if(!IS_STR(o)) ERR(ctx, "non-scalar in numeric context");
|
||||
else if(naStr_tonum(o, &n)) return n;
|
||||
else naRuntimeError( ctx,
|
||||
"non-numeric string in numeric context: '%s'",
|
||||
naStr_data(o) );
|
||||
else ERR(ctx, "non-numeric string in numeric context");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -594,9 +592,6 @@ static naRef run(naContext ctx)
|
||||
case OP_LTE: BINOP(l <= r ? 1 : 0); break;
|
||||
case OP_GT: BINOP(l > r ? 1 : 0); break;
|
||||
case OP_GTE: BINOP(l >= r ? 1 : 0); break;
|
||||
case OP_BIT_AND: BINOP((int)l & (int)r); break;
|
||||
case OP_BIT_OR: BINOP((int)l | (int)r); break;
|
||||
case OP_BIT_XOR: BINOP((int)l ^ (int)r); break;
|
||||
#undef BINOP
|
||||
|
||||
case OP_EQ: case OP_NEQ:
|
||||
@@ -610,9 +605,6 @@ static naRef run(naContext ctx)
|
||||
case OP_NEG:
|
||||
STK(1) = naNum(-numify(ctx, STK(1)));
|
||||
break;
|
||||
case OP_BIT_NEG:
|
||||
STK(1) = naNum(~(int)numify(ctx, STK(1)));
|
||||
break;
|
||||
case OP_NOT:
|
||||
STK(1) = naNum(boolify(ctx, STK(1)) ? 0 : 1);
|
||||
break;
|
||||
|
||||
@@ -26,8 +26,7 @@ enum {
|
||||
OP_MEMBER, OP_SETMEMBER, OP_LOCAL, OP_SETLOCAL, OP_NEWVEC, OP_VAPPEND,
|
||||
OP_NEWHASH, OP_HAPPEND, OP_MARK, OP_UNMARK, OP_BREAK, OP_SETSYM, OP_DUP2,
|
||||
OP_INDEX, OP_BREAK2, OP_PUSHEND, OP_JIFTRUE, OP_JIFNOT, OP_FCALLH,
|
||||
OP_MCALLH, OP_XCHG2, OP_UNPACK, OP_SLICE, OP_SLICE2, OP_BIT_AND, OP_BIT_OR,
|
||||
OP_BIT_XOR, OP_BIT_NEG
|
||||
OP_MCALLH, OP_XCHG2, OP_UNPACK, OP_SLICE, OP_SLICE2
|
||||
};
|
||||
|
||||
struct Frame {
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
// These are more sensical predicate names in most contexts in this file
|
||||
#define LEFT(tok) ((tok)->children)
|
||||
#define RIGHT(tok) ((tok)->lastChild)
|
||||
#define UNARY(tok) (LEFT(tok) && LEFT(tok) == RIGHT(tok))
|
||||
#define BINARY(tok) (LEFT(tok) && RIGHT(tok) && LEFT(tok)->next == RIGHT(tok))
|
||||
|
||||
// Forward references for recursion
|
||||
@@ -166,15 +165,6 @@ static int defArg(struct Parser* p, struct Token* t)
|
||||
RIGHT(t)->num *= -1;
|
||||
return defArg(p, RIGHT(t));
|
||||
}
|
||||
if(t->type == TOK_CAT && RIGHT(t) &&
|
||||
RIGHT(t)->type == TOK_LITERAL && !RIGHT(t)->str)
|
||||
{
|
||||
/* default arguments are constants, but "~1" parses as two
|
||||
* tokens, so we have to subset the expression generator for that
|
||||
* case */
|
||||
RIGHT(t)->num = ~(int)RIGHT(t)->num;
|
||||
return defArg(p, RIGHT(t));
|
||||
}
|
||||
return findConstantIndex(p, t);
|
||||
}
|
||||
|
||||
@@ -644,15 +634,11 @@ static void genExpr(struct Parser* p, struct Token* t)
|
||||
else genExpr(p, LEFT(t));
|
||||
break;
|
||||
case TOK_LBRA:
|
||||
if(UNARY(t)) {
|
||||
emit(p, OP_NEWVEC);
|
||||
genList(p, LEFT(t), 1);
|
||||
}
|
||||
else if(BINARY(t)) {
|
||||
if(BINARY(t)) {
|
||||
genExtract(p, t);
|
||||
} else {
|
||||
// forbid usage as 'vec[]'
|
||||
naParseError(p, "missing index or slice expression(s)", t->line);
|
||||
emit(p, OP_NEWVEC);
|
||||
genList(p, LEFT(t), 1);
|
||||
}
|
||||
break;
|
||||
case TOK_LCURL:
|
||||
@@ -687,21 +673,6 @@ static void genExpr(struct Parser* p, struct Token* t)
|
||||
genExpr(p, RIGHT(t)); // unary negation (see also TOK_MINUS!)
|
||||
emit(p, OP_NEG);
|
||||
break;
|
||||
case TOK_CAT:
|
||||
if(BINARY(t)) {
|
||||
genBinOp(OP_CAT, p, t); // string concatenation
|
||||
} else if(RIGHT(t) && RIGHT(t)->type == TOK_LITERAL && !RIGHT(t)->str) {
|
||||
RIGHT(t)->num = ~(int)RIGHT(t)->num; // Pre-negate constants
|
||||
genScalarConstant(p, RIGHT(t));
|
||||
} else {
|
||||
genExpr(p, RIGHT(t)); // unary, bitwise negation
|
||||
emit(p, OP_BIT_NEG);
|
||||
}
|
||||
break;
|
||||
case TOK_BIT_NEG:
|
||||
genExpr(p, RIGHT(t)); // unary, bitwise negation (see also TOK_CAT!)
|
||||
emit(p, OP_BIT_NEG);
|
||||
break;
|
||||
case TOK_DOT:
|
||||
genExpr(p, LEFT(t));
|
||||
if(!RIGHT(t) || RIGHT(t)->type != TOK_SYMBOL)
|
||||
@@ -714,12 +685,10 @@ static void genExpr(struct Parser* p, struct Token* t)
|
||||
case TOK_AND: case TOK_OR:
|
||||
genShortCircuit(p, t);
|
||||
break;
|
||||
case TOK_BIT_AND:genBinOp(OP_BIT_AND, p, t); break;
|
||||
case TOK_BIT_OR: genBinOp(OP_BIT_OR, p, t); break;
|
||||
case TOK_BIT_XOR:genBinOp(OP_BIT_XOR, p, t); break;
|
||||
case TOK_MUL: genBinOp(OP_MUL, p, t); break;
|
||||
case TOK_PLUS: genBinOp(OP_PLUS, p, t); break;
|
||||
case TOK_DIV: genBinOp(OP_DIV, p, t); break;
|
||||
case TOK_CAT: genBinOp(OP_CAT, p, t); break;
|
||||
case TOK_LT: genBinOp(OP_LT, p, t); break;
|
||||
case TOK_LTE: genBinOp(OP_LTE, p, t); break;
|
||||
case TOK_EQ: genBinOp(OP_EQ, p, t); break;
|
||||
@@ -731,9 +700,6 @@ static void genExpr(struct Parser* p, struct Token* t)
|
||||
case TOK_MULEQ: genEqOp(OP_MUL, p, t); break;
|
||||
case TOK_DIVEQ: genEqOp(OP_DIV, p, t); break;
|
||||
case TOK_CATEQ: genEqOp(OP_CAT, p, t); break;
|
||||
case TOK_BIT_ANDEQ: genEqOp(OP_BIT_AND, p, t); break;
|
||||
case TOK_BIT_OREQ: genEqOp(OP_BIT_OR, p, t); break;
|
||||
case TOK_BIT_XOREQ: genEqOp(OP_BIT_XOR, p, t); break;
|
||||
default:
|
||||
naParseError(p, "parse error", t->line);
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@ set(HEADERS
|
||||
Ghost.hxx
|
||||
NasalCallContext.hxx
|
||||
NasalHash.hxx
|
||||
NasalObject.hxx
|
||||
NasalObjectHolder.hxx
|
||||
NasalString.hxx
|
||||
from_nasal.hxx
|
||||
@@ -20,10 +19,9 @@ set(DETAIL_HEADERS
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
Ghost.cxx
|
||||
NasalHash.cxx
|
||||
NasalObjectHolder.cxx
|
||||
NasalString.cxx
|
||||
NasalObject.cxx
|
||||
detail/from_nasal_helper.cxx
|
||||
detail/to_nasal_helper.cxx
|
||||
)
|
||||
@@ -35,14 +33,4 @@ if(ENABLE_TESTS)
|
||||
add_executable(test_cppbind cppbind_test.cxx)
|
||||
add_test(cppbind ${EXECUTABLE_OUTPUT_PATH}/test_cppbind)
|
||||
target_link_libraries(test_cppbind ${TEST_LIBS})
|
||||
endif(ENABLE_TESTS)
|
||||
|
||||
add_boost_test(cppbind_ghost
|
||||
SOURCES cppbind_test_ghost.cxx
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
)
|
||||
|
||||
add_boost_test(nasal_num
|
||||
SOURCES nasal_num_test.cxx
|
||||
LIBRARIES ${TEST_LIBS}
|
||||
)
|
||||
endif(ENABLE_TESTS)
|
||||
@@ -1,108 +0,0 @@
|
||||
// Expose C++ objects to Nasal as ghosts
|
||||
//
|
||||
// Copyright (C) 2014 Thomas Geymayer <tomgey@gmail.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include "Ghost.hxx"
|
||||
|
||||
namespace nasal
|
||||
{
|
||||
namespace internal
|
||||
{
|
||||
//--------------------------------------------------------------------------
|
||||
GhostMetadata::DestroyList GhostMetadata::_destroy_list;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void GhostMetadata::addNasalBase(const naRef& parent)
|
||||
{
|
||||
assert( naIsHash(parent) );
|
||||
_parents.push_back(parent);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool GhostMetadata::isBaseOf(naGhostType* ghost_type, bool& is_weak) const
|
||||
{
|
||||
if( ghost_type == _ghost_type_strong_ptr )
|
||||
{
|
||||
is_weak = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if( ghost_type == _ghost_type_weak_ptr )
|
||||
{
|
||||
is_weak = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
for( DerivedList::const_iterator derived = _derived_classes.begin();
|
||||
derived != _derived_classes.end();
|
||||
++derived )
|
||||
{
|
||||
if( (*derived)->isBaseOf(ghost_type, is_weak) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
GhostMetadata::GhostMetadata( const std::string& name,
|
||||
const naGhostType* ghost_type_strong,
|
||||
const naGhostType* ghost_type_weak ):
|
||||
_name_strong(name),
|
||||
_name_weak(name + " (weak ref)"),
|
||||
_ghost_type_strong_ptr(ghost_type_strong),
|
||||
_ghost_type_weak_ptr(ghost_type_weak)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void GhostMetadata::addDerived(const GhostMetadata* derived)
|
||||
{
|
||||
assert(derived);
|
||||
_derived_classes.push_back(derived);
|
||||
|
||||
SG_LOG
|
||||
(
|
||||
SG_NASAL,
|
||||
SG_INFO,
|
||||
"Ghost::addDerived: " << _name_strong << " -> "
|
||||
<< derived->_name_strong
|
||||
);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
naRef GhostMetadata::getParents(naContext c)
|
||||
{
|
||||
return nasal::to_nasal(c, _parents);
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ghostProcessDestroyList()
|
||||
{
|
||||
using internal::GhostMetadata;
|
||||
typedef GhostMetadata::DestroyList::const_iterator destroy_iterator;
|
||||
for( destroy_iterator it = GhostMetadata::_destroy_list.begin();
|
||||
it != GhostMetadata::_destroy_list.end();
|
||||
++it )
|
||||
it->first(it->second);
|
||||
GhostMetadata::_destroy_list.clear();
|
||||
}
|
||||
|
||||
} // namespace nasal
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user