Merge remote-tracking branch 'hg/develop/hg/develop' into ryzomcore

This commit is contained in:
deed 2019-04-16 21:28:45 +02:00
commit 46af0c090e
107 changed files with 8410 additions and 1199 deletions

34
azure-pipelines.yml Normal file
View file

@ -0,0 +1,34 @@
jobs:
- job: ubuntu16
pool:
vmImage: 'Ubuntu-16.04'
steps:
- script: |
sudo apt-get update
sudo apt-get install -y software-properties-common
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install cmake build-essential -y
sudo apt-get install gcc-8 g++-8 -y
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 60
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 60
sudo apt-get install libmysqlclient-dev -y
sudo apt-get install bison autoconf automake -y
sudo apt-get install libpng12-dev libjpeg62-dev -y
sudo apt-get install liblua5.1-dev libluabind-dev libcpptest-dev -y
sudo apt-get install libogg-dev libvorbis-dev libopenal-dev -y
sudo apt-get install libgif-dev libfreetype6-dev -y
sudo apt-get install libxml2-dev -y
sudo apt-get install libcurl4-openssl-dev -y
displayName: 'Dependencies'
- script: |
mkdir build
cmake --version
cd build
cmake -DWITH_NEL_TESTS=OFF -DWITH_NEL_SAMPLES=ON -DWITH_LUA51=ON -DWITH_RYZOM_SERVER=ON -DWITH_RYZOM_TOOLS=OFF -DWITH_NEL_TOOLS=OFF ../code
cat CMakeCache.txt
displayName: 'CMake'
- script: |
cd build
make -j`nproc`
displayName: 'Make'

View file

@ -0,0 +1,173 @@
# vim: ts=2 sw=2
# - Try to find the required ffmpeg components(default: AVFORMAT, AVUTIL, AVCODEC)
#
# Once done this will define
# FFMPEG_FOUND - System has the all required components.
# FFMPEG_INCLUDE_DIRS - Include directory necessary for using the required components headers.
# FFMPEG_LIBRARIES - Link these to use the required ffmpeg components.
# FFMPEG_DEFINITIONS - Compiler switches required for using the required ffmpeg components.
#
# For each of the components it will additionaly set.
# - AVCODEC
# - AVDEVICE
# - AVFORMAT
# - AVUTIL
# - POSTPROC
# - SWSCALE
# - SWRESAMPLE
# the following variables will be defined
# <component>_FOUND - System has <component>
# <component>_INCLUDE_DIRS - Include directory necessary for using the <component> headers
# <component>_LIBRARIES - Link these to use <component>
# <component>_DEFINITIONS - Compiler switches required for using <component>
# <component>_VERSION - The components version
#
# Copyright (c) 2006, Matthias Kretz, <kretz@kde.org>
# Copyright (c) 2008, Alexander Neundorf, <neundorf@kde.org>
# Copyright (c) 2011, Michael Jansen, <kde@michael-jansen.biz>
#
# Redistribution and use is allowed according to the terms of the BSD license.
include(FindPackageHandleStandardArgs)
if(NOT FFmpeg_FIND_COMPONENTS)
set(FFmpeg_FIND_COMPONENTS AVFORMAT AVCODEC AVUTIL)
endif()
#
### Macro: set_component_found
#
# Marks the given component as found if both *_LIBRARIES AND *_INCLUDE_DIRS is present.
#
macro(set_component_found _component)
if(${_component}_LIBRARIES AND ${_component}_INCLUDE_DIRS)
# message(STATUS " - ${_component} found.")
set(${_component}_FOUND TRUE)
else()
# message(STATUS " - ${_component} not found.")
endif()
endmacro()
#
### Macro: find_component
#
# Checks for the given component by invoking pkgconfig and then looking up the libraries and
# include directories.
#
macro(find_component _component _pkgconfig _library _header)
if(NOT WIN32)
# use pkg-config to get the directories and then use these values
# in the FIND_PATH() and FIND_LIBRARY() calls
find_package(PkgConfig)
if(PKG_CONFIG_FOUND)
pkg_check_modules(PC_${_component} ${_pkgconfig})
endif()
endif()
find_path(${_component}_INCLUDE_DIRS ${_header}
HINTS
${FFMPEGSDK_INC}
${PC_LIB${_component}_INCLUDEDIR}
${PC_LIB${_component}_INCLUDE_DIRS}
PATH_SUFFIXES
ffmpeg
)
find_library(${_component}_LIBRARIES NAMES ${_library}
HINTS
${FFMPEGSDK_LIB}
${PC_LIB${_component}_LIBDIR}
${PC_LIB${_component}_LIBRARY_DIRS}
)
STRING(REGEX REPLACE "/.*" "/version.h" _ver_header ${_header})
if(EXISTS "${${_component}_INCLUDE_DIRS}/${_ver_header}")
file(STRINGS "${${_component}_INCLUDE_DIRS}/${_ver_header}" version_str REGEX "^#define[\t ]+LIB${_component}_VERSION_M.*")
foreach(_str "${version_str}")
if(NOT version_maj)
string(REGEX REPLACE "^.*LIB${_component}_VERSION_MAJOR[\t ]+([0-9]*).*$" "\\1" version_maj "${_str}")
endif()
if(NOT version_min)
string(REGEX REPLACE "^.*LIB${_component}_VERSION_MINOR[\t ]+([0-9]*).*$" "\\1" version_min "${_str}")
endif()
if(NOT version_mic)
string(REGEX REPLACE "^.*LIB${_component}_VERSION_MICRO[\t ]+([0-9]*).*$" "\\1" version_mic "${_str}")
endif()
endforeach()
unset(version_str)
set(${_component}_VERSION "${version_maj}.${version_min}.${version_mic}" CACHE STRING "The ${_component} version number.")
unset(version_maj)
unset(version_min)
unset(version_mic)
endif(EXISTS "${${_component}_INCLUDE_DIRS}/${_ver_header}")
set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING "The ${_component} version number.")
set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS.")
set_component_found(${_component})
mark_as_advanced(
${_component}_INCLUDE_DIRS
${_component}_LIBRARIES
${_component}_DEFINITIONS
${_component}_VERSION)
endmacro()
set(FFMPEGSDK $ENV{FFMPEG_HOME})
if(FFMPEGSDK)
set(FFMPEGSDK_INC "${FFMPEGSDK}/include")
set(FFMPEGSDK_LIB "${FFMPEGSDK}/lib")
endif()
# Check for all possible components.
find_component(AVCODEC libavcodec avcodec libavcodec/avcodec.h)
find_component(AVFORMAT libavformat avformat libavformat/avformat.h)
find_component(AVDEVICE libavdevice avdevice libavdevice/avdevice.h)
find_component(AVUTIL libavutil avutil libavutil/avutil.h)
find_component(SWSCALE libswscale swscale libswscale/swscale.h)
find_component(SWRESAMPLE libswresample swresample libswresample/swresample.h)
find_component(POSTPROC libpostproc postproc libpostproc/postprocess.h)
# Check if the required components were found and add their stuff to the FFMPEG_* vars.
foreach(_component ${FFmpeg_FIND_COMPONENTS})
if(${_component}_FOUND)
# message(STATUS "Required component ${_component} present.")
set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${${_component}_LIBRARIES})
set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} ${${_component}_DEFINITIONS})
list(APPEND FFMPEG_INCLUDE_DIRS ${${_component}_INCLUDE_DIRS})
else()
# message(STATUS "Required component ${_component} missing.")
endif()
endforeach()
# Build the include path and library list with duplicates removed.
if(FFMPEG_INCLUDE_DIRS)
list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS)
endif()
if(FFMPEG_LIBRARIES)
list(REMOVE_DUPLICATES FFMPEG_LIBRARIES)
endif()
# cache the vars.
set(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} CACHE STRING "The FFmpeg include directories." FORCE)
set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} CACHE STRING "The FFmpeg libraries." FORCE)
set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} CACHE STRING "The FFmpeg cflags." FORCE)
mark_as_advanced(FFMPEG_INCLUDE_DIRS FFMPEG_LIBRARIES FFMPEG_DEFINITIONS)
# Now set the noncached _FOUND vars for the components.
foreach(_component AVCODEC AVDEVICE AVFORMAT AVUTIL POSTPROCESS SWRESAMPLE SWSCALE)
set_component_found(${_component})
endforeach ()
# Compile the list of required vars
set(_FFmpeg_REQUIRED_VARS FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIRS)
foreach(_component ${FFmpeg_FIND_COMPONENTS})
list(APPEND _FFmpeg_REQUIRED_VARS ${_component}_LIBRARIES ${_component}_INCLUDE_DIRS)
endforeach()
# Give a nice error message if some of the required vars are missing.
find_package_handle_standard_args(FFmpeg DEFAULT_MSG ${_FFmpeg_REQUIRED_VARS})

View file

@ -41,31 +41,49 @@ ENDMACRO()
MACRO(PCH_SET_COMPILE_FLAGS _target)
SET(PCH_FLAGS)
SET(PCH_ARCHS)
SET(PCH_INCLUDES)
SET(_FLAGS)
# Append target for clang if defined
IF(CMAKE_CXX_COMPILER_TARGET)
LIST(APPEND PCH_FLAGS "--target=${CMAKE_CXX_COMPILER_TARGET}")
ENDIF()
IF(CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN)
LIST(APPEND PCH_FLAGS "--gcc-toolchain=${CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN}")
ENDIF()
IF(CMAKE_SYSROOT)
LIST(APPEND PCH_FLAGS "--sysroot=${CMAKE_SYSROOT}")
ENDIF()
IF(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES)
FOREACH(item ${CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES})
LIST(APPEND PCH_FLAGS "-isystem ${item}")
ENDFOREACH()
ENDIF()
# C++ flags
SET(_FLAG ${CMAKE_CXX_FLAGS})
SEPARATE_ARGUMENTS(_FLAG)
SET(_FLAGS ${CMAKE_CXX_FLAGS})
SEPARATE_ARGUMENTS(_FLAGS)
LIST(APPEND _FLAGS ${_FLAG})
LIST(APPEND PCH_FLAGS ${_FLAGS})
# C++ config flags
STRING(TOUPPER "${CMAKE_BUILD_TYPE}" _UPPER_BUILD)
SET(_FLAG ${CMAKE_CXX_FLAGS_${_UPPER_BUILD}})
SEPARATE_ARGUMENTS(_FLAG)
SET(_FLAGS ${CMAKE_CXX_FLAGS_${_UPPER_BUILD}})
SEPARATE_ARGUMENTS(_FLAGS)
LIST(APPEND _FLAGS ${_FLAG})
LIST(APPEND PCH_FLAGS ${_FLAGS})
GET_TARGET_PROPERTY(_targetType ${_target} TYPE)
SET(_USE_PIC OFF)
IF(${_targetType} STREQUAL "SHARED_LIBRARY" OR ${_targetType} STREQUAL "MODULE_LIBRARY")
SET(_FLAG ${CMAKE_SHARED_LIBRARY_CXX_FLAGS})
SEPARATE_ARGUMENTS(_FLAG)
LIST(APPEND _FLAGS ${_FLAG})
SET(_FLAGS ${CMAKE_SHARED_LIBRARY_CXX_FLAGS})
SEPARATE_ARGUMENTS(_FLAGS)
LIST(APPEND PCH_FLAGS ${_FLAGS})
ELSE()
GET_TARGET_PROPERTY(_pic ${_target} POSITION_INDEPENDENT_CODE)
IF(_pic)
@ -75,7 +93,7 @@ MACRO(PCH_SET_COMPILE_FLAGS _target)
GET_DIRECTORY_PROPERTY(DIRINC INCLUDE_DIRECTORIES)
FOREACH(item ${DIRINC})
LIST(APPEND _FLAGS -I"${item}")
LIST(APPEND PCH_INCLUDES "${item}")
ENDFOREACH()
# NOTE: As cmake files (eg FindQT4) may now use generator expressions around their defines that evaluate
@ -98,14 +116,14 @@ MACRO(PCH_SET_COMPILE_FLAGS _target)
ENDFOREACH()
ENDIF()
GET_DIRECTORY_PROPERTY(DEFINITIONS DIRECTORY ${CMAKE_SOURCE_DIR} COMPILE_DEFINITIONS)
GET_DIRECTORY_PROPERTY(DEFINITIONS DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_DEFINITIONS)
IF(DEFINITIONS)
FOREACH(item ${DEFINITIONS})
APPEND_DEFINITION(GLOBAL_DEFINITIONS ${item})
ENDFOREACH()
ENDIF()
GET_DIRECTORY_PROPERTY(DEFINITIONS DIRECTORY ${CMAKE_SOURCE_DIR} COMPILE_DEFINITIONS_${_UPPER_BUILD})
GET_DIRECTORY_PROPERTY(DEFINITIONS DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_DEFINITIONS_${_UPPER_BUILD})
IF(DEFINITIONS)
FOREACH(item ${DEFINITIONS})
APPEND_DEFINITION(GLOBAL_DEFINITIONS ${item})
@ -114,22 +132,22 @@ MACRO(PCH_SET_COMPILE_FLAGS _target)
GET_TARGET_PROPERTY(oldProps ${_target} COMPILE_FLAGS)
IF(oldProps)
SET(_FLAG ${oldProps})
SEPARATE_ARGUMENTS(_FLAG)
LIST(APPEND _FLAGS ${_FLAG})
SET(_FLAGS ${oldProps})
SEPARATE_ARGUMENTS(_FLAGS)
LIST(APPEND PCH_FLAGS ${_FLAGS})
ENDIF()
GET_TARGET_PROPERTY(oldPropsBuild ${_target} COMPILE_FLAGS_${_UPPER_BUILD})
IF(oldPropsBuild)
SET(_FLAG ${oldPropsBuild})
SEPARATE_ARGUMENTS(_FLAG)
LIST(APPEND _FLAGS ${_FLAG})
SET(_FLAGS ${oldPropsBuild})
SEPARATE_ARGUMENTS(_FLAGS)
LIST(APPEND PCH_FLAGS ${_FLAGS})
ENDIF()
GET_TARGET_PROPERTY(DIRINC ${_target} INCLUDE_DIRECTORIES)
IF(DIRINC)
FOREACH(item ${DIRINC})
LIST(APPEND _FLAGS -I"${item}")
LIST(APPEND PCH_INCLUDES "${item}")
ENDFOREACH()
ENDIF()
@ -147,6 +165,18 @@ MACRO(PCH_SET_COMPILE_FLAGS _target)
ENDFOREACH()
ENDIF()
GET_TARGET_PROPERTY(OPTIONS ${_target} COMPILE_OPTIONS)
IF(OPTIONS)
SEPARATE_ARGUMENTS(OPTIONS)
LIST(APPEND PCH_FLAGS ${OPTIONS})
ENDIF()
GET_TARGET_PROPERTY(OPTIONS ${_target} COMPILE_OPTIONS_${_UPPER_BUILD})
IF(OPTIONS)
SEPARATE_ARGUMENTS(OPTIONS)
LIST(APPEND PCH_FLAGS ${OPTIONS})
ENDIF()
GET_TARGET_PROPERTY(_LIBS ${_target} INTERFACE_LINK_LIBRARIES)
IF(_LIBS)
FOREACH(_LIB ${_LIBS})
@ -156,7 +186,7 @@ MACRO(PCH_SET_COMPILE_FLAGS _target)
IF(_DIRS)
FOREACH(item ${_DIRS})
LIST(APPEND GLOBAL_DEFINITIONS -I"${item}")
LIST(APPEND PCH_INCLUDES "${item}")
ENDFOREACH()
ENDIF()
@ -185,7 +215,7 @@ MACRO(PCH_SET_COMPILE_FLAGS _target)
ENDIF()
IF(_USE_PIC)
LIST(APPEND _FLAGS ${CMAKE_CXX_COMPILE_OPTIONS_PIC})
LIST(APPEND PCH_FLAGS ${CMAKE_CXX_COMPILE_OPTIONS_PIC})
ENDIF()
ENDIF()
@ -195,116 +225,81 @@ MACRO(PCH_SET_COMPILE_FLAGS _target)
IF(_DIRECTORY_FLAGS)
SEPARATE_ARGUMENTS(_DIRECTORY_FLAGS)
FOREACH(item ${_DIRECTORY_FLAGS})
LIST(APPEND _FLAGS "${item}")
LIST(APPEND PCH_FLAGS "${item}")
ENDFOREACH()
ENDIF()
GET_DIRECTORY_PROPERTY(_DIRECTORY_DEFINITIONS DIRECTORY ${CMAKE_SOURCE_DIR} DEFINITIONS)
GET_DIRECTORY_PROPERTY(_DIRECTORY_DEFINITIONS DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEFINITIONS)
IF(_DIRECTORY_DEFINITIONS)
SEPARATE_ARGUMENTS(_DIRECTORY_DEFINITIONS)
FOREACH(item ${_DIRECTORY_DEFINITIONS})
LIST(APPEND _FLAGS "${item}")
LIST(APPEND PCH_FLAGS "${item}")
ENDFOREACH()
ENDIF()
ENDIF()
IF(CMAKE_CXX11_EXTENSION_COMPILE_OPTION)
LIST(APPEND _FLAGS ${CMAKE_CXX11_EXTENSION_COMPILE_OPTION})
ENDIF()
# Format definitions
IF(MSVC)
# Fix path with space
SEPARATE_ARGUMENTS(_FLAGS UNIX_COMMAND "${_FLAGS}")
LIST(APPEND PCH_FLAGS "${CMAKE_CXX11_EXTENSION_COMPILE_OPTION}")
ENDIF()
# Already in list form and items may contain non-leading spaces that should not be split on
LIST(INSERT _FLAGS 0 "${GLOBAL_DEFINITIONS}")
LIST(APPEND PCH_FLAGS "${GLOBAL_DEFINITIONS}")
IF(CLANG)
# Determining all architectures and get common flags
SET(_ARCH_NEXT)
SET(_XARCH_NEXT)
FOREACH(item ${_FLAGS})
IF(_ARCH_NEXT)
LIST(FIND PCH_ARCHS ${item} ITEM_FOUND)
IF(ITEM_FOUND EQUAL -1)
LIST(APPEND PCH_ARCHS ${item})
STRING(TOUPPER "${item}" _UPPER_ARCH)
SET(PCH_ARCH_${_UPPER_ARCH}_FLAGS "-arch" ${item})
ENDIF()
SET(_ARCH_NEXT OFF)
ELSEIF(_XARCH_NEXT)
SET(_XARCH_NEXT OFF)
IF(WIN32)
SET(SYSTEM_FLAG "[-/$]")
ELSE()
IF(item MATCHES "^-arch")
SET(_ARCH_NEXT ON)
ELSEIF(item MATCHES "^-Xarch_")
STRING(REGEX REPLACE "-Xarch_([a-z0-9_]+)" "\\1" item ${item})
LIST(FIND PCH_ARCHS ${item} ITEM_FOUND)
IF(ITEM_FOUND EQUAL -1)
LIST(APPEND PCH_ARCHS ${item})
STRING(TOUPPER "${item}" _UPPER_ARCH)
SET(PCH_ARCH_${_UPPER_ARCH}_FLAGS "-arch" ${item})
SET(SYSTEM_FLAG "[-$]")
ENDIF()
SET(_XARCH_NEXT ON)
SET(_FINAL_FLAGS)
SET(_PREVIOUS_FLAG)
FOREACH(_FLAG ${PCH_FLAGS})
# If parameter is really a flag (starts with -)
IF(_FLAG MATCHES "^${SYSTEM_FLAG}")
IF(_PREVIOUS_FLAG)
# Append previous flag
LIST(APPEND _FINAL_FLAGS ${_PREVIOUS_FLAG})
ENDIF()
SET(_PREVIOUS_FLAG ${_FLAG})
ELSE()
LIST(APPEND PCH_FLAGS ${item})
IF(_PREVIOUS_FLAG)
# Append previous flag and its parameter
# TODO: escape them only if there is an space
LIST(APPEND _FINAL_FLAGS "${_PREVIOUS_FLAG} \"${_FLAG}\"")
SET(_PREVIOUS_FLAG)
ELSE()
# Shouldn't happen
MESSAGE(FATAL_ERROR "No previous flag before ${_FLAG}")
ENDIF()
ENDIF()
ENDFOREACH()
# Get architcture specific flags
SET(_XARCH_NEXT)
FOREACH(item ${_FLAGS})
IF(_XARCH_NEXT)
STRING(TOUPPER "${_XARCH_NEXT}" _UPPER_XARCH)
LIST(APPEND PCH_ARCH_${_UPPER_XARCH}_FLAGS ${item})
SET(_XARCH_NEXT OFF)
ELSE()
IF(item MATCHES "^-Xarch_")
STRING(SUBSTRING "${item}" 7 -1 _XARCH_NEXT)
ENDIF()
ENDIF()
ENDFOREACH()
# Remove duplicated architectures
IF(_ARCHS AND PCH_ARCHS)
LIST(REMOVE_DUPLICATES PCH_ARCHS)
ENDIF()
ELSE()
SET(PCH_FLAGS ${_FLAGS})
IF(_PREVIOUS_FLAG)
LIST(APPEND _FINAL_FLAGS ${_PREVIOUS_FLAG})
ENDIF()
IF(PCH_FLAGS)
SET(PCH_FLAGS ${_FINAL_FLAGS})
# Remove flags that don't work with PCH
LIST(REMOVE_ITEM PCH_FLAGS "-Wa,--noexecstack")
# Remove all empty parameters
LIST(REMOVE_ITEM PCH_FLAGS "")
# Remove duplicate parameters
LIST(REMOVE_DUPLICATES PCH_FLAGS)
ENDIF()
# create a command-line string
STRING(REGEX REPLACE ";" " " PCH_FLAGS "${PCH_FLAGS}")
# and separate arguments
SEPARATE_ARGUMENTS(PCH_FLAGS)
ENDMACRO()
MACRO(GET_PDB_FILENAME _out_filename _target)
# determine output directory based on target type
GET_TARGET_PROPERTY(_targetType ${_target} TYPE)
IF(${_targetType} STREQUAL EXECUTABLE)
SET(_targetOutput ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
ELSEIF(${_targetType} STREQUAL STATIC_LIBRARY)
SET(_targetOutput ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY})
ELSE()
SET(_targetOutput ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
ENDIF()
# determine target postfix
STRING(TOUPPER "${CMAKE_BUILD_TYPE}_POSTFIX" _postfix_var_name)
GET_TARGET_PROPERTY(_targetPostfix ${_target} ${_postfix_var_name})
IF(${_targetPostfix} MATCHES NOTFOUND)
SET(_targetPostfix "")
ENDIF()
SET(${_out_filename} "${_targetOutput}/${_target}${_targetPostfix}.pdb")
ENDMACRO(GET_PDB_FILENAME)
MACRO(PCH_SET_COMPILE_COMMAND _inputcpp _compile_FLAGS)
MACRO(PCH_SET_COMPILE_COMMAND _inputcpp _compile_FLAGS _includes)
IF(CMAKE_CXX_COMPILER_ARG1)
# remove leading space in compiler argument
STRING(REGEX REPLACE "^ +" "" pchsupport_compiler_cxx_arg1 ${CMAKE_CXX_COMPILER_ARG1})
@ -313,19 +308,34 @@ MACRO(PCH_SET_COMPILE_COMMAND _inputcpp _compile_FLAGS)
ENDIF()
IF(MSVC)
GET_PDB_FILENAME(_PDB_FILE ${_PCH_current_target})
SET(PCH_COMMAND ${CMAKE_CXX_COMPILER} ${pchsupport_compiler_cxx_arg1} ${_compile_FLAGS} /Yc /Fp"${PCH_OUTPUT}" ${_inputcpp} /Fd"${_PDB_FILE}" /c /Fo"${PCH_OUTPUT}.obj")
GET_INTERMEDIATE_PDB_FULLPATH(${_PCH_current_target} _PDB_FILE)
SET(PCH_TEMP_CONTENT)
FOREACH(_include ${_includes})
SET(PCH_TEMP_CONTENT "${PCH_TEMP_CONTENT} -I \"${_include}\"")
ENDFOREACH()
SET(PCH_TEMP_FILE ${CMAKE_CURRENT_BINARY_DIR}/pch_command.txt)
FILE(WRITE ${PCH_TEMP_FILE} "${PCH_TEMP_CONTENT}")
SET(PCH_COMMAND ${CMAKE_CXX_COMPILER} /nologo @${PCH_TEMP_FILE} ${pchsupport_compiler_cxx_arg1} ${_compile_FLAGS} /Yc /Fp"${PCH_OUTPUT}" ${_inputcpp} /Fd"${_PDB_FILE}" /c /Fo"${PCH_OUTPUT}.obj")
# Ninja PCH Support
# http://public.kitware.com/pipermail/cmake-developers/2012-March/003653.html
SET_SOURCE_FILES_PROPERTIES(${_inputcpp} PROPERTIES OBJECT_OUTPUTS "${PCH_OUTPUT}.obj")
ELSE()
SET(HEADER_FORMAT "c++-header")
SET(_FLAGS "")
SET(_FLAGS)
IF(APPLE)
SET(HEADER_FORMAT "objective-${HEADER_FORMAT}")
SET(_FLAGS ${OBJC_FLAGS})
LIST(APPEND _FLAGS ${OBJC_FLAGS})
ENDIF()
FOREACH(_include ${_includes})
LIST(APPEND _FLAGS -I "${_include}")
ENDFOREACH()
SET(PCH_COMMAND ${CMAKE_CXX_COMPILER} ${pchsupport_compiler_cxx_arg1} ${_compile_FLAGS} ${_FLAGS} -x ${HEADER_FORMAT} -o ${PCH_OUTPUT} -c ${PCH_INPUT})
ENDIF()
ENDMACRO()
@ -467,7 +477,7 @@ MACRO(ADD_PRECOMPILED_HEADER _targetName _inputh _inputcpp)
PCH_SET_PRECOMPILED_HEADER_OUTPUT(${_targetName} ${_inputh} ${_ARCH} "")
LIST(APPEND PCH_OUTPUTS ${PCH_OUTPUT})
PCH_SET_COMPILE_COMMAND(${_inputcpp} "${PCH_ARCH_${_UPPER_ARCH}_FLAGS};${PCH_FLAGS}")
PCH_SET_COMPILE_COMMAND(${_inputcpp} "${PCH_ARCH_${_UPPER_ARCH}_FLAGS};${PCH_FLAGS}" "${PCH_INCLUDES}")
PCH_CREATE_TARGET(${_targetName} ${_targetName}_pch_${_ARCH})
ADD_PRECOMPILED_HEADER_TO_TARGET_ARCH(${_targetName} ${_ARCH})
@ -476,7 +486,7 @@ MACRO(ADD_PRECOMPILED_HEADER _targetName _inputh _inputcpp)
PCH_SET_PRECOMPILED_HEADER_OUTPUT(${_targetName} ${_inputh} "" "")
LIST(APPEND PCH_OUTPUTS ${PCH_OUTPUT})
PCH_SET_COMPILE_COMMAND(${_inputcpp} "${PCH_FLAGS}")
PCH_SET_COMPILE_COMMAND(${_inputcpp} "${PCH_FLAGS}" "${PCH_INCLUDES}")
PCH_CREATE_TARGET(${_targetName} ${_targetName}_pch)
ENDIF()
@ -525,8 +535,14 @@ MACRO(ADD_NATIVE_PRECOMPILED_HEADER _targetName _inputh _inputcpp)
SET_TARGET_PROPERTIES(${_targetName} PROPERTIES XCODE_ATTRIBUTE_GCC_PRECOMPILE_PREFIX_HEADER "YES")
ELSE()
#Fallback to the "old" precompiled suppport
IF(CMAKE_OSX_ARCHITECTURES AND TARGETS_COUNT GREATER 1)
FOREACH(_ARCH ${CMAKE_OSX_ARCHITECTURES})
ADD_PRECOMPILED_HEADER(${_targetName}_${_ARCH} ${_inputh} ${_inputcpp})
ENDFOREACH()
ELSE()
ADD_PRECOMPILED_HEADER(${_targetName} ${_inputh} ${_inputcpp})
ENDIF()
ENDIF()
IF(TARGET ${_targetName}_static)
ADD_NATIVE_PRECOMPILED_HEADER(${_targetName}_static ${_inputh} ${_inputcpp})

View file

@ -20,6 +20,7 @@ ENDIF()
IF(WITH_SOUND)
FIND_PACKAGE(Ogg)
FIND_PACKAGE(Vorbis)
FIND_PACKAGE(FFmpeg COMPONENTS AVCODEC AVFORMAT AVUTIL SWRESAMPLE)
IF(WITH_DRIVER_OPENAL)
FIND_PACKAGE(OpenAL)

View file

@ -178,6 +178,10 @@ public:
CVertexBuffer Vertices;
CMaterial *Material;
CRGBA Color;
ucstring Text;
uint32 CacheVersion;
/// The width of the string, in pixels (eg: 30)
float StringWidth;
/// The height of the string, in pixels (eg: 10)
@ -223,6 +227,7 @@ public:
*/
CComputedString (bool bSetupVB=true)
{
CacheVersion = 0;
StringWidth = 0;
StringHeight = 0;
if (bSetupVB)

View file

@ -74,6 +74,8 @@ public:
uint32 getUID() { return _UID; }
std::string getFontFileName() const;
private:
static uint32 _FontGeneratorCounterUID;

View file

@ -59,6 +59,9 @@ class CFontManager
CSmartPtr<CMaterial> _MatFont;
CSmartPtr<CTextureFont> _TexFont;
// Keep track number of textures created to properly report cache version
uint32 _TexCacheNr;
public:
/**
@ -71,6 +74,7 @@ public:
_NbChar = 0;
_MatFont = NULL;
_TexFont = NULL;
_TexCacheNr = 0;
}
@ -94,7 +98,6 @@ public:
*/
CMaterial* getFontMaterial();
/**
* Compute primitive blocks and materials of each character of
* the string.
@ -152,6 +155,7 @@ public:
void dumpCache (const char *filename)
{
if (_TexFont)
_TexFont->dumpTextureFont (filename);
}
@ -160,6 +164,15 @@ public:
*/
void invalidate();
// get font atlas rebuild count
uint32 getCacheVersion() const
{
if (_TexFont)
return (_TexFont->getCacheVersion() << 16) + _TexCacheNr;
return 0;
}
};

View file

@ -150,6 +150,10 @@ public:
{
nlassert (index < _CacheStrings.size());
CComputedString &rCS = _CacheStrings[index];
if (rCS.CacheVersion != _FontManager->getCacheVersion())
{
computeString(rCS.Text, rCS);
}
if (_Shaded)
{
CRGBA bkup = rCS.Color;
@ -184,6 +188,10 @@ public:
{
nlassert (index < _CacheStrings.size());
CComputedString &rCS = _CacheStrings[index];
if (rCS.CacheVersion != _FontManager->getCacheVersion())
{
computeString(rCS.Text, rCS);
}
if(_Shaded)
{
CRGBA bkup = rCS.Color;
@ -218,6 +226,11 @@ public:
{
nlassert (index < _CacheStrings.size());
CComputedString &rCS = _CacheStrings[index];
if (rCS.CacheVersion != _FontManager->getCacheVersion())
{
computeString(rCS.Text, rCS);
}
if (_Shaded)
{
CRGBA bkup = rCS.Color;

View file

@ -18,6 +18,7 @@
#define NL_TEXTURE_FONT_H
#include "nel/misc/types_nl.h"
#include "nel/misc/rect.h"
#include "nel/3d/texture.h"
namespace NL3D
@ -25,9 +26,6 @@ namespace NL3D
class CFontGenerator;
#define TEXTUREFONT_NBCATEGORY 5 // Config 1
//#define TEXTUREFONT_NBCATEGORY 4
// ****************************************************************************
/**
* CTextureFont
@ -37,32 +35,59 @@ class CTextureFont : public ITexture
public:
struct SLetterInfo
// Holds info for glyphs rendered on atlas
struct SGlyphInfo
{
// To generate the letter
ucchar Char;
CFontGenerator *FontGenerator;
// font atlas info
uint32 CacheVersion;
// atlas region with padding
uint32 X, Y, W, H;
// rendered glyph size without padding
uint32 CharWidth;
uint32 CharHeight;
// UV coords for rendered glyph without padding
float U0, V0, U1, V1;
uint32 GlyphIndex;
sint Size;
bool Embolden;
bool Oblique;
CFontGenerator *FontGenerator;
SGlyphInfo()
: CacheVersion(0),
U0(0.f), V0(0.f), U1(0.f), V1(0.f),
X(0), Y(0), W(0), H(0), CharWidth(0), CharHeight(0),
GlyphIndex(0), Size(0), Embolden(false), Oblique(false), FontGenerator(NULL)
{
}
};
// The less recently used infos
SLetterInfo *Next, *Prev;
// Holds info for glyphs displayed on screen
struct SLetterInfo
{
ucchar Char;
sint Size;
bool Embolden;
bool Oblique;
CFontGenerator *FontGenerator;
uint Cat; // 8x8, 16x16, 24x24, 32x32
//////////////////////////////////////////////////////////////////////
float U ,V;
uint32 CharWidth;
uint32 CharHeight;
uint32 GlyphIndex; // number of the character in the this font
uint32 GlyphIndex;
uint32 CharWidth; // Displayed glyph height
uint32 CharHeight; // Displayed glyph height
sint32 Top; // Distance between origin and top of the texture
sint32 Left; // Distance between origin and left of the texture
sint32 AdvX; // Advance to the next caracter
SLetterInfo():Char(0), FontGenerator(NULL), Size(0), Embolden(false), Oblique(false), Next(NULL), Prev(NULL), Cat(0), CharWidth(0), CharHeight(0), GlyphIndex(0), Top(0), Left(0), AdvX(0)
SGlyphInfo* glyph;
SLetterInfo()
: Char(0), Size(0), Embolden(false), Oblique(false), FontGenerator(NULL),
GlyphIndex(0), CharWidth(0), CharHeight(0), Top(0), Left(0), AdvX(0),
glyph(NULL)
{
}
};
@ -70,14 +95,13 @@ public:
struct SLetterKey
{
ucchar Char;
CFontGenerator *FontGenerator;
sint Size;
bool Embolden;
bool Oblique;
CFontGenerator *FontGenerator;
// Does not use FontGenerator in return value
uint32 getVal();
//bool operator < (const SLetterKey&k) const;
//bool operator == (const SLetterKey&k) const;
SLetterKey():Char(0), FontGenerator(NULL), Size(0), Embolden(false), Oblique(false)
{
@ -96,19 +120,76 @@ public:
void doGenerate (bool async = false);
// This function manage the cache if the letter wanted does not exist
SLetterInfo* getLetterInfo (SLetterKey& k);
// \param render Set to true if letter is currently visible on screen
SLetterInfo* getLetterInfo (SLetterKey& k, bool render);
void dumpTextureFont (const char *filename);
// Version is increased with each rebuild of font atlas
uint32 getCacheVersion() const { return _CacheVersion; }
private:
uint32 _CacheVersion;
// current texture size
uint32 _TextureSizeX;
uint32 _TextureSizeY;
// maximum texture size allowed
uint32 _TextureMaxW;
uint32 _TextureMaxH;
// padding around glyphs
uint8 _PaddingL, _PaddingT;
uint8 _PaddingR, _PaddingB;
// To find a letter in the texture
std::map<uint32, SLetterInfo*> Accel;
std::vector<SLetterInfo> Letters[TEXTUREFONT_NBCATEGORY];
SLetterInfo *Front[TEXTUREFONT_NBCATEGORY], *Back[TEXTUREFONT_NBCATEGORY];
// Keep track of available space in main texture
std::vector<NLMISC::CRect> _AtlasNodes;
void rebuildLetter (sint cat, sint x, sint y);
std::vector <SLetterInfo> _Letters;
// lookup letter from letter cache or create new
SLetterInfo* findLetter(SLetterKey& k, bool insert);
// lower/upper bound of glyphs to render, sizes outside are scaled bitmaps
uint _MinGlyphSize;
uint _MaxGlyphSize;
// start using size stem from this font size
uint _GlyphSizeStepMin;
// every n'th font size is rendered, intermediates are using bitmap scaling
uint _GlyphSizeStep;
// rendered glyph cache
std::list<SGlyphInfo> _GlyphCache;
SGlyphInfo* findLetterGlyph(SLetterInfo *letter, bool insert);
// render letter glyph into glyph cache
SGlyphInfo* renderLetterGlyph(SLetterInfo *letter, uint32 bitmapFontSize);
// copy glyph bitmap into texture and invalidate that region
void copyGlyphBitmap(uint8* bitmap, uint32 bitmapW, uint32 bitmapH, uint32 atlasX, uint32 atlasY);
// Find best fit for WxH rect in atlas
uint fitRegion(uint index, uint width, uint height);
// Return top/left from font texture or false if there is no more room
bool reserveAtlas(const uint32 width, const uint32 height, uint32 &x, uint32 &y);
// repack glyphs, resize texture, and invalidate unused glyphs.
void repackAtlas();
void repackAtlas(uint32 width, uint32 height);
// resize texture,
bool resizeAtlas();
// remove all glyphs from atlas, clear glyph cache, letter info is kept
void clearAtlas();
// if return true: newW, newH contains next size font atlas should be resized
// if return false: _TextureMaxW and _TextureMaxH is reached
bool getNextTextureSize(uint32 &newW, uint32 &newH) const;
/// Todo: serialize a font texture.
public:

View file

@ -175,6 +175,7 @@ namespace NLGUI
bool _CallingAH : 1;
bool _Cancelable : 1; // true if the slider may be cancelled when pressed on the mouse right button
bool _Frozen : 1;
bool _Scale : 1;
// For Target Scroller only: the target offset step in pixel.
sint32 _TargetStepX;

View file

@ -111,9 +111,19 @@ namespace NLGUI
sint32 getWMin() const { return _WMin; }
void setWMin( sint32 wmin ) { _WMin = wmin; }
sint32 getHMin() const { return _HMin; }
void setHMin( sint32 hmin ) { _HMin = hmin; }
// Compute Size according to bitmap and Text (Ensure as big as possible button)
sint32 getWMax() const;
// Set texture directly without _l.tga, _m.tga, _r.tga convention
// Texture size is only read from normal textures
// If updateHeight == false, then _BmpH will keep its value
void setTexture(const std::string &l, const std::string &m, const std::string &r, bool updateHeight = true);
void setTexturePushed(const std::string &l, const std::string &m, const std::string &r);
void setTextureOver(const std::string &l, const std::string &m, const std::string &r);
int luaGetViewText(CLuaState &ls);
REFLECT_EXPORT_START(CCtrlTextButton, CCtrlBaseButton)
@ -122,6 +132,7 @@ namespace NLGUI
REFLECT_SINT32("text_x", getTextX, setTextX)
REFLECT_SINT32("wmargin", getWMargin, setWMargin)
REFLECT_SINT32("wmin", getWMin, setWMin)
REFLECT_SINT32("hmin", getHMin, setHMin)
REFLECT_LUA_METHOD("getViewText", luaGetViewText)
REFLECT_EXPORT_END
@ -151,8 +162,8 @@ namespace NLGUI
sint32 _BmpLeftW, _BmpMiddleW, _BmpRightW, _BmpH;
// Value to add to TextW to get button W.
sint32 _WMargin;
// Min W Value
sint32 _WMin;
// Min W, H Value
sint32 _WMin, _HMin;
sint32 _TextY;
sint32 _TextX;
THotSpot _TextPosRef;

View file

@ -85,6 +85,7 @@ namespace NLGUI
// view text
void setViewText(const ucstring & text);
ucstring getViewText() const;
CViewText *getViewText();
void setTexture(uint i, const ucstring &texture);

View file

@ -33,6 +33,7 @@ typedef std::map<std::string, std::string> TStyle;
namespace NLGUI
{
class CCtrlButton;
class CCtrlTextButton;
class CCtrlScroll;
class CGroupList;
class CGroupMenu;
@ -105,6 +106,9 @@ namespace NLGUI
Height=-1;
MaxWidth=-1;
MaxHeight=-1;
BorderWidth=1;
BackgroundColor=NLMISC::CRGBA::Black;
BackgroundColorOver=NLMISC::CRGBA::Black;
}
uint FontSize;
uint FontWeight;
@ -119,6 +123,9 @@ namespace NLGUI
sint32 Height;
sint32 MaxWidth;
sint32 MaxHeight;
sint32 BorderWidth;
NLMISC::CRGBA BackgroundColor;
NLMISC::CRGBA BackgroundColorOver;
};
// ImageDownload system
@ -262,6 +269,8 @@ namespace NLGUI
void browseUndo ();
// Redo browse: Browse the precedent url undoed. no op if none
void browseRedo ();
// disable refresh button
void clearRefresh();
// clear undo/redo
void clearUndoRedo();
@ -270,6 +279,8 @@ namespace NLGUI
void setURL(const std::string &url);
int luaClearRefresh(CLuaState &ls);
int luaClearUndoRedo(CLuaState &ls);
int luaBrowse(CLuaState &ls);
int luaRefresh(CLuaState &ls);
int luaRemoveContent(CLuaState &ls);
@ -285,6 +296,8 @@ namespace NLGUI
REFLECT_EXPORT_START(CGroupHTML, CGroupScrollText)
REFLECT_LUA_METHOD("browse", luaBrowse)
REFLECT_LUA_METHOD("refresh", luaRefresh)
REFLECT_LUA_METHOD("clearUndoRedo", luaClearUndoRedo)
REFLECT_LUA_METHOD("clearRefresh", luaClearRefresh)
REFLECT_LUA_METHOD("removeContent", luaRemoveContent)
REFLECT_LUA_METHOD("insertText", luaInsertText)
REFLECT_LUA_METHOD("addString", luaAddString)
@ -296,6 +309,7 @@ namespace NLGUI
REFLECT_LUA_METHOD("renderHtml", luaRenderHtml)
REFLECT_STRING("url", getURL, setURL)
REFLECT_FLOAT("timeout", getTimeout, setTimeout)
REFLECT_STRING("title", getTitle, setTitle)
REFLECT_EXPORT_END
protected :
@ -339,6 +353,9 @@ namespace NLGUI
// Get Home URL
virtual std::string home();
// Clear style stack and restore default style
void resetCssStyle();
// Parse style html tag
TStyle parseStyle(const std::string &str_styles);
@ -366,7 +383,7 @@ namespace NLGUI
void addString(const ucstring &str);
// Add an image in the current paragraph
void addImage(const char *image, bool reloadImg=false, const CStyleParams &style = CStyleParams());
void addImage(const std::string &id, const char *image, bool reloadImg=false, const CStyleParams &style = CStyleParams());
// Add a text area in the current paragraph
CInterfaceGroup *addTextArea (const std::string &templateName, const char *name, uint rows, uint cols, bool multiLine, const ucstring &content, uint maxlength);
@ -391,6 +408,8 @@ namespace NLGUI
// Set the title
void setTitle (const ucstring &title);
void setTitle (const std::string &title);
std::string getTitle() const;
// Lookup a url in local file system
bool lookupLocalFile (std::string &result, const char *url, bool isUrl);
@ -780,7 +799,7 @@ namespace NLGUI
static TGroupHtmlByUIDMap _GroupHtmlByUID;
// read style attribute
void getStyleParams(const std::string &styleString, CStyleParams &style, bool inherit = true);
void getStyleParams(const std::string &styleString, CStyleParams &style, const CStyleParams &current);
void applyCssMinMax(sint32 &width, sint32 &height, sint32 minw=0, sint32 minh=0, sint32 maxw=0, sint32 maxh=0);
// load and render local html file (from bnp for example)
@ -846,6 +865,9 @@ namespace NLGUI
void setImage(CViewBase *view, const std::string &file, const TImageType type);
void setImageSize(CViewBase *view, const CStyleParams &style = CStyleParams());
void setTextButtonStyle(CCtrlTextButton *ctrlButton, const CStyleParams &style);
void setTextStyle(CViewText *pVT, const CStyleParams &style);
// BnpDownload system
void initBnpDownload();
void checkBnpDownload();

View file

@ -351,6 +351,9 @@ namespace NLGUI
void setMinW(sint32 minW);
void setMinH(sint32 minH);
// change fontsize for new menu items
void setFontSize(uint32 fontSize);
// Gray a line on the RootMenu
void setGrayedLine(uint line, bool g);

View file

@ -206,6 +206,11 @@ namespace NLGUI
invalidateContent();
}
/// temporarily enable mouse over effect
// will be automatically disabled when mouse leaves element
void enableTempOver() { _TempOver = true; }
void disableTempOver() { _TempOver = false; }
/// \from CInterfaceElement
void onInvalidateContent();
sint32 getMaxUsedW() const;
@ -233,6 +238,8 @@ namespace NLGUI
// Do we have a color under the element pointed by the mouse
bool _Over;
// Temporarily force mouse over effect. Deactivated when mouse moves away
bool _TempOver;
// If over is true so we have a color
NLMISC::CRGBA _OverColor;

View file

@ -38,6 +38,16 @@ namespace NLGUI
class IActionHandler;
class CGroupParagraph;
/**
* Interface for UI scale change event
*/
class IInterfaceScaleWatcher
{
public:
virtual ~IInterfaceScaleWatcher(){}
virtual void onInterfaceScaleChanged()=0;
};
/**
* A visitor to walk a tree of interface elements and apply a teartment on them.
*
@ -66,7 +76,7 @@ namespace NLGUI
* \author Nevrax France
* \date 2002
*/
class CInterfaceElement : public CReflectableRefPtrTarget, public NLMISC::IStreamable
class CInterfaceElement : public IInterfaceScaleWatcher, public CReflectableRefPtrTarget, public NLMISC::IStreamable
{
public:
@ -409,6 +419,10 @@ namespace NLGUI
*/
virtual void onInvalidateContent() {}
/* Element UI scale change event callback
*/
virtual void onInterfaceScaleChanged() {}
// called by interfaceManager for master window only
void resetInvalidCoords();

View file

@ -49,6 +49,14 @@ namespace NLGUI
#undef HTML_ATTR
#define HTML_ATTR(t,a) MY_HTML_##t##_##a
enum
{
HTML_ATTR(HTML,DIR) = 0,
HTML_ATTR(HTML,LANG),
HTML_ATTR(HTML,VERSION),
HTML_ATTR(HTML,STYLE),
};
enum
{
HTML_ATTR(A,ACCESSKEY) = 0,
@ -208,6 +216,7 @@ namespace NLGUI
HTML_ATTR(P,QUICK_HELP_EVENTS),
HTML_ATTR(P,QUICK_HELP_LINK),
HTML_ATTR(P,NAME),
HTML_ATTR(P,STYLE),
};
enum
@ -271,6 +280,9 @@ namespace NLGUI
#undef HTML_ATTR
// ***************************************************************************
// Read a CSS length value, return true if one of supported units '%, rem, em, px, pt'
// On failure: 'value' and 'unit' values are undefined
bool getCssLength (float &value, std::string &unit, const std::string &str);
// Read a width HTML parameter. "100" or "100%". Returns true if percent (0 ~ 1) else false
bool getPercentage (sint32 &width, float &percent, const char *str);

View file

@ -176,6 +176,13 @@ namespace NLGUI
*/
void getScreenOOSize (float &oow, float &ooh);
/*
* UI scaling
*/
void setInterfaceScale(float scale, sint32 width = 0, sint32 height = 0);
float getInterfaceScale() const { return _InterfaceScale; }
void setBilinearFiltering(bool b) { _Bilinear = b; }
/*
* is the Screen minimized?
*/
@ -185,7 +192,7 @@ namespace NLGUI
* drawBitmap : this is the interface with all the views
*
*/
void drawRotFlipBitmap (sint layerId, sint32 x, sint32 y, sint32 width, sint32 height, uint8 rot, bool flipv,
void drawRotFlipBitmap (sint layerId, float x, float y, float width, float height, uint8 rot, bool flipv,
sint32 nTxId, const NLMISC::CRGBA &col = NLMISC::CRGBA(255,255,255,255));
/*
@ -526,6 +533,14 @@ namespace NLGUI
float _OneOverScreenW, _OneOverScreenH;
bool _IsMinimized;
// UI scaling
float _InterfaceScale;
float _InterfaceUserScale;
sint32 _InterfaceBaseW, _InterfaceBaseH;
sint32 _EffectiveScreenW, _EffectiveScreenH;
bool _Bilinear;
void updateInterfaceScale();
//map linking a uint to a bitmap. Used to display figurs
std::vector<sint32> _IndexesToTextureIds;
@ -596,7 +611,6 @@ namespace NLGUI
/// Set of hw cursor images
static std::set< std::string > *hwCursors;
static float hwCursorScale;
};

View file

@ -37,7 +37,7 @@ namespace NLGUI
class CViewText : public CViewBase
{
public:
enum TTextMode { ClipWord, DontClipWord, Justified };
enum TTextMode { ClipWord, DontClipWord, Justified, Centered };
public:
DECLARE_UI_CLASS(CViewText)
@ -70,6 +70,7 @@ namespace NLGUI
virtual void checkCoords();
virtual void updateCoords();
virtual void onAddToGroup();
virtual void onInterfaceScaleChanged();
/// From CInterfaceElement
sint32 getMaxUsedW() const;
@ -81,7 +82,7 @@ namespace NLGUI
void setText (const ucstring &text);
void setFontName (const std::string &name);
void setFontSize (sint nFontSize);
void setFontSize (sint nFontSize, bool coef = true);
void setEmbolden (bool nEmbolden);
void setOblique (bool nOblique);
void setColor (const NLMISC::CRGBA &color);
@ -90,6 +91,7 @@ namespace NLGUI
void setShadowColor (const NLMISC::CRGBA &color);
void setShadowOffset (sint x, sint y);
void setLineMaxW (sint nMaxW, bool invalidate=true);
void setOverflowText(const ucstring &text) { _OverflowText = text; }
void setMultiLine (bool bMultiLine);
void setMultiLineSpace (sint nMultiLineSpace);
void setMultiLineMaxWOnly (bool state);
@ -98,6 +100,9 @@ namespace NLGUI
void setMultiMaxLine(uint l) { _MultiMaxLine = l; }
void setMultiMinLine(uint l) { _MultiMinLine = l; }
// Override chars used to compute font size
void setFontSizing(const std::string &chars, const std::string &fallback);
// Force only a subset of letter to be displayed. Default is 0/0xFFFFFFFF
void enableStringSelection(uint start, uint end);
void disableStringSelection();
@ -115,6 +120,7 @@ namespace NLGUI
NLMISC::CRGBA getShadowColor() { return _ShadowColor; }
void getShadowOffset(sint &x, sint &y) { x = _ShadowX; y = _ShadowY; }
sint getLineMaxW() const { return _LineMaxW; }
ucstring getOverflowText() const { return _OverflowText; }
bool getMultiLine() const { return _MultiLine; }
sint getMultiLineSpace() const { return _MultiLineSpace; }
bool getMultiLineMaxWOnly() const { return _MultiLineMaxWOnly; }
@ -128,6 +134,8 @@ namespace NLGUI
uint getFontHeight() const;
// get current font leg height, in pixels
uint getFontLegHeight() const;
// get current line height, in pixels
float getLineHeight() const;
// Set the display mode (supported with multiline only for now)
void setTextMode(TTextMode mode);
TTextMode getTextMode() const { return _TextMode; }
@ -148,11 +156,11 @@ namespace NLGUI
* When looking at standard edit box, we see that if a line is split accross to line with no
* This also returns the height of the line
*/
void getCharacterPositionFromIndex(sint index, bool lineEnd, sint &x, sint &y, sint &height) const;
void getCharacterPositionFromIndex(sint index, bool lineEnd, float &x, float &y, float &height) const;
/** From a coordinate relative to the BR BR corner of the text, return the index of a character.
* If no character is found at the given position, the closest character is returned (first or last character, for the line or the whole text)
*/
void getCharacterIndexFromPosition(sint x, sint y, uint &index, bool &lineEnd) const;
void getCharacterIndexFromPosition(float x, float y, uint &index, bool &lineEnd) const;
/** From a character index, get the index of the line it belongs to, or -1 if the index is invalid
* \param cursorDisplayedAtEndOfPreviousLine true if the cursor is displayed at the end of the previous line that match its index
*/
@ -235,15 +243,21 @@ namespace NLGUI
std::string _FontName;
/// the font size
sint _FontSize;
bool _FontSizeCoef;
bool _Embolden;
bool _Oblique;
// width of the font in pixel. Just a Hint for tabing format (computed with '_')
uint _FontWidth;
float _FontWidth;
// strings to use when computing font size
ucstring _FontSizingChars;
ucstring _FontSizingFallback;
// height of the font in pixel.
// use getFontHeight
uint _FontHeight;
uint _FontLegHeight;
float _FontHeight;
float _FontLegHeight;
float _SpaceWidth;
/// last UI scale used to calculate font size
float _Scale;
/// the text color
NLMISC::CRGBA _Color;
/// the shadow mode
@ -260,6 +274,7 @@ namespace NLGUI
sint32 _LineMaxW;
/// For single line, true if the text is clamped (ie displayed with "...")
bool _SingleLineTextClamped;
ucstring _OverflowText;
/// Multiple lines handling
bool _MultiLine;
@ -341,8 +356,8 @@ namespace NLGUI
// Clear the line & remove text contexts
void clear(NL3D::UTextContext &textContext);
// Add a new word (and its context) in the line + a number of spaces to append at the end of the line
void addWord(const ucstring &word, uint numSpaces, const CFormatInfo &wordFormat, uint fontWidth, NL3D::UTextContext &textContext);
void addWord(const CWord &word, uint fontWidth);
void addWord(const ucstring &word, uint numSpaces, const CFormatInfo &wordFormat, float fontWidth, NL3D::UTextContext &textContext);
void addWord(const CWord &word, float fontWidth);
uint getNumWords() const { return (uint)_Words.size(); }
CWord &getWord(uint index) { return _Words[index]; }
float getSpaceWidth() const { return _SpaceWidth; }
@ -402,7 +417,7 @@ namespace NLGUI
uint _TextSelectionEnd;
// First line X coordinate
sint _FirstLineX;
float _FirstLineX;
/// Dynamic tooltips
std::vector<CCtrlToolTip*> _Tooltips;
@ -427,9 +442,9 @@ namespace NLGUI
// Clear all the lines and free their datas
void clearLines();
// Update in the case of a multiline text
void updateTextContextMultiLine(uint nMaxWidth);
void updateTextContextMultiLine(float nMaxWidth);
// Update in the case of a multiline text with justification
void updateTextContextMultiLineJustified(uint nMaxWidth, bool expandSpaces);
void updateTextContextMultiLineJustified(float nMaxWidth, bool expandSpaces);
// Recompute font size info
void computeFontSize ();

View file

@ -49,6 +49,7 @@ namespace NLGUI
class CProcedure;
class IEditorSelectionWatcher;
class IWidgetAdditionWatcher;
class IInterfaceScaleWatcher;
/**
GUI Widget Manager
@ -530,6 +531,11 @@ namespace NLGUI
bool unGroupSelection();
void setMultiSelection( bool b ){ multiSelection = b; }
float getInterfaceScale() const { return _InterfaceScale; }
void notifyInterfaceScaleWatchers();
void registerInterfaceScaleWatcher(IInterfaceScaleWatcher *watcher);
void unregisterInterfaceScaleWatcher(IInterfaceScaleWatcher *watcher);
bool createNewGUI( const std::string &project, const std::string &window );
private:
@ -615,6 +621,7 @@ namespace NLGUI
uint32 _ScreenH;
uint32 _ScreenW;
float _InterfaceScale;
std::vector< CInterfaceAnim* > activeAnims;
@ -622,6 +629,7 @@ namespace NLGUI
std::vector< IOnWidgetsDrawnHandler* > onWidgetsDrawnHandlers;
std::vector< IEditorSelectionWatcher* > selectionWatchers;
std::vector< IWidgetWatcher* > widgetWatchers;
std::vector< IInterfaceScaleWatcher* > scaleWatchers;
std::vector< std::string > editorSelection;
bool _GroupSelection;

View file

@ -222,6 +222,7 @@ inline double isValidDouble (double v)
* \param str a string to transform to lower case
*/
std::string toLower ( const char *str );
std::string toLower ( const std::string &str );
void toLower ( char *str );
char toLower ( const char ch ); // convert only one character

View file

@ -0,0 +1,108 @@
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2018 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef NLSOUND_AUDIO_DECODER_FFMPEG_H
#define NLSOUND_AUDIO_DECODER_FFMPEG_H
#include <nel/misc/types_nl.h>
#include <nel/sound/audio_decoder.h>
struct AVCodecContext;
struct AVFormatContext;
struct AVIOContext;
struct AVPacket;
struct SwrContext;
namespace NLSOUND {
/**
* \brief CAudioDecoderFfmpeg
* \date 2018-10-21 08:08GMT
* \author Meelis Mägi (Nimetu)
* CAudioDecoderFfmpeg
* Create trough IAudioDecoder
*/
class CAudioDecoderFfmpeg : public IAudioDecoder
{
protected:
NLMISC::IStream *_Stream;
bool _IsSupported;
bool _Loop;
bool _IsMusicEnded;
sint32 _StreamOffset;
sint32 _StreamSize;
AVIOContext *_AvioContext;
AVFormatContext *_FormatContext;
AVCodecContext *_AudioContext;
SwrContext *_SwrContext;
// selected stream
sint32 _AudioStreamIndex;
// output buffer for decoded frame
SwrContext *_ConvertContext;
private:
// called from constructor if ffmpeg fails to initialize
// or from destructor to cleanup ffmpeg pointers
void release();
public:
CAudioDecoderFfmpeg(NLMISC::IStream *stream, bool loop);
virtual ~CAudioDecoderFfmpeg();
inline NLMISC::IStream *getStream() { return _Stream; }
inline sint32 getStreamSize() { return _StreamSize; }
inline sint32 getStreamOffset() { return _StreamOffset; }
// Return true if ffmpeg is able to decode the stream
bool isFormatSupported() const;
/// Get information on a music file (only artist and title at the moment).
static bool getInfo(NLMISC::IStream *stream, std::string &artist, std::string &title, float &length);
/// Get how many bytes the music buffer requires for output minimum.
virtual uint32 getRequiredBytes();
/// Get an amount of bytes between minimum and maximum (can be lower than minimum if at end).
virtual uint32 getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum);
/// Get the amount of channels (2 is stereo) in output.
virtual uint8 getChannels();
/// Get the samples per second (often 44100) in output.
virtual uint getSamplesPerSec();
/// Get the bits per sample (often 16) in output.
virtual uint8 getBitsPerSample();
/// Get if the music has ended playing (never true if loop).
virtual bool isMusicEnded();
/// Get the total time in seconds.
virtual float getLength();
/// Set looping
virtual void setLooping(bool loop);
}; /* class CAudioDecoderFfmpeg */
} /* namespace NLSOUND */
#endif // NLSOUND_AUDIO_DECODER_FFMPEG_H
/* end of file */

View file

@ -0,0 +1,96 @@
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2018 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef NLSOUND_AUDIO_DECODER_MP3_H
#define NLSOUND_AUDIO_DECODER_MP3_H
#include <nel/misc/types_nl.h>
#include <nel/sound/audio_decoder.h>
// disable drmp3_init_file()
#define DR_MP3_NO_STDIO
#include <nel/sound/decoder/dr_mp3.h>
namespace NLSOUND {
/**
* \brief CAudioDecoderMP3
* \date 2019-01-13 12:39GMT
* \author Meelis Mägi (Nimetu)
* CAudioDecoderMP3
* Create trough IAudioDecoder, type "mp3"
*/
class CAudioDecoderMP3 : public IAudioDecoder
{
protected:
NLMISC::IStream *_Stream;
bool _IsSupported;
bool _Loop;
bool _IsMusicEnded;
sint32 _StreamOffset;
sint32 _StreamSize;
drmp3 _Decoder;
// set to total pcm frames after getLength() is called
uint64 _PCMFrameCount;
public:
CAudioDecoderMP3(NLMISC::IStream *stream, bool loop);
virtual ~CAudioDecoderMP3();
inline NLMISC::IStream *getStream() { return _Stream; }
inline sint32 getStreamSize() { return _StreamSize; }
inline sint32 getStreamOffset() { return _StreamOffset; }
// Return true if mp3 is valid
bool isFormatSupported() const;
/// Get information on a music file (only ID3v1 tag is read.
static bool getInfo(NLMISC::IStream *stream, std::string &artist, std::string &title, float &length);
/// Get how many bytes the music buffer requires for output minimum.
virtual uint32 getRequiredBytes();
/// Get an amount of bytes between minimum and maximum (can be lower than minimum if at end).
virtual uint32 getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum);
/// Get the amount of channels (2 is stereo) in output.
virtual uint8 getChannels();
/// Get the samples per second (often 44100) in output.
virtual uint getSamplesPerSec();
/// Get the bits per sample (often 16) in output.
virtual uint8 getBitsPerSample();
/// Get if the music has ended playing (never true if loop).
virtual bool isMusicEnded();
/// Get the total time in seconds.
virtual float getLength();
/// Set looping
virtual void setLooping(bool loop);
}; /* class CAudioDecoderMP3 */
} /* namespace NLSOUND */
#endif // NLSOUND_AUDIO_DECODER_MP3_H
/* end of file */

File diff suppressed because it is too large Load diff

View file

@ -100,6 +100,7 @@ private:
IAudioDecoder *m_AudioDecoder;
bool m_Paused;
bool m_DecodingEnded;
}; /* class CStreamFileSource */

View file

@ -81,6 +81,11 @@ const char *CFontGenerator::getFT2Error(FT_Error fte)
return ukn;
}
std::string CFontGenerator::getFontFileName() const
{
return _FontFileName;
}
CFontGenerator *newCFontGenerator(const std::string &fontFileName)
{
return new CFontGenerator(fontFileName);

View file

@ -46,6 +46,7 @@ CMaterial* CFontManager::getFontMaterial()
if (_TexFont == NULL)
{
_TexFont = new CTextureFont;
_TexCacheNr++;
}
if (_MatFont == NULL)
@ -142,11 +143,17 @@ void CFontManager::computeString (const ucstring &s,
sint32 nMaxZ = -1000000, nMinZ = 1000000;
output.StringHeight = 0;
// save string info for later rebuild as needed
output.Text = s;
output.CacheVersion = getCacheVersion();
uint j = 0;
{
CVertexBufferReadWrite vba;
output.Vertices.lock (vba);
hlfPixScrW = 0.f;
hlfPixScrH = 0.f;
// For all chars
for (uint i = 0; i < s.size(); i++)
@ -157,18 +164,23 @@ void CFontManager::computeString (const ucstring &s,
k.Size = fontSize;
k.Embolden = embolden;
k.Oblique = oblique;
CTextureFont::SLetterInfo *pLI = pTexFont->getLetterInfo (k);
// render letter
CTextureFont::SLetterInfo *pLI = pTexFont->getLetterInfo (k, true);
if(pLI != NULL)
{
if ((pLI->CharWidth > 0) && (pLI->CharHeight > 0))
if (pLI->glyph)
{
// If letter is heavily upscaled, then there is noticeable clipping on edges
// fixing UV will make it bit better
if ((pLI->Size >> 1) > pLI->glyph->Size)
{
hlfPixTexW = 0.5f * TexRatioW;
hlfPixTexH = 0.5f * TexRatioH;
}
// Creating vertices
dx = pLI->Left;
dz = -((sint32)pLI->CharHeight-(sint32)(pLI->Top));
u1 = pLI->U - hlfPixTexW;
v1 = pLI->V - hlfPixTexH;
u2 = pLI->U + ((float)pLI->CharWidth) * TexRatioW + hlfPixTexW;
v2 = pLI->V + ((float)pLI->CharHeight) * TexRatioH + hlfPixTexH;
dz = -((sint32)pLI->CharHeight - (sint32)(pLI->Top));
x1 = (penx + dx) - hlfPixScrW;
z1 = (penz + dz) - hlfPixScrH;
@ -176,19 +188,19 @@ void CFontManager::computeString (const ucstring &s,
z2 = (penz + dz + (sint32)pLI->CharHeight) + hlfPixScrH;
vba.setVertexCoord (j, x1, 0, z1);
vba.setTexCoord (j, 0, u1, v2);
vba.setTexCoord (j, 0, pLI->glyph->U0-hlfPixTexW, pLI->glyph->V1+hlfPixTexH);
++j;
vba.setVertexCoord (j, x2, 0, z1);
vba.setTexCoord (j, 0, u2, v2);
vba.setTexCoord (j, 0, pLI->glyph->U1+hlfPixTexW, pLI->glyph->V1+hlfPixTexH);
++j;
vba.setVertexCoord (j, x2, 0, z2);
vba.setTexCoord (j, 0, u2, v1);
vba.setTexCoord (j, 0, pLI->glyph->U1+hlfPixTexW, pLI->glyph->V0-hlfPixTexH);
++j;
vba.setVertexCoord (j, x1, 0, z2);
vba.setTexCoord (j, 0, u1, v1);
vba.setTexCoord (j, 0, pLI->glyph->U0-hlfPixTexW, pLI->glyph->V0-hlfPixTexH);
++j;
// String Bound
@ -245,6 +257,19 @@ void CFontManager::computeStringInfo ( const ucstring &s,
{
output.Color = color;
// save string info for later rebuild as needed
output.Text = s;
output.CacheVersion = 0;
if (s.empty())
{
output.StringWidth = 0.f;
output.StringHeight = 0;
output.StringLine = 0;
return;
}
// resize fontSize if window not of 800x600.
if (keep800x600Ratio)
{
@ -273,7 +298,7 @@ void CFontManager::computeStringInfo ( const ucstring &s,
k.Size = fontSize;
k.Embolden = embolden;
k.Oblique = oblique;
pLI = pTexFont->getLetterInfo (k);
pLI = pTexFont->getLetterInfo (k, false);
if(pLI != NULL)
{
if ((pLI->CharWidth > 0) && (pLI->CharHeight > 0))
@ -318,7 +343,11 @@ void CFontManager::invalidate()
{
if (_TexFont)
_TexFont = NULL;
_TexFont = new CTextureFont;
_TexCacheNr++;
getFontMaterial()->setTexture(0, _TexFont);
}

View file

@ -74,25 +74,9 @@ uint32 CTextContext::textPush (const char *format, ...)
char *str;
NLMISC_CONVERT_VARGS (str, format, NLMISC::MaxCStringSize);
if (_CacheNbFreePlaces == 0)
{
CComputedString csTmp;
_CacheStrings.push_back (csTmp);
if (_CacheFreePlaces.empty())
_CacheFreePlaces.resize (1);
_CacheFreePlaces[0] = (uint32)_CacheStrings.size()-1;
_CacheNbFreePlaces = 1;
}
// compute the string.
uint32 index = _CacheFreePlaces[_CacheNbFreePlaces-1];
CComputedString &strToFill = _CacheStrings[index];
_FontManager->computeString (str, _FontGen, _Color, _FontSize, _Embolden, _Oblique, _Driver, strToFill, _Keep800x600Ratio);
_CacheNbFreePlaces--;
return index;
ucstring uc;
uc.fromUtf8((const char *)str);
return textPush(uc);
}
// ------------------------------------------------------------------------------------------------
@ -115,8 +99,10 @@ uint32 CTextContext::textPush (const ucstring &str)
uint32 index = _CacheFreePlaces[_CacheNbFreePlaces-1];
nlassert (index < _CacheStrings.size());
CComputedString &strToFill = _CacheStrings[index];
_FontManager->computeString (str, _FontGen, _Color
, _FontSize, _Embolden, _Oblique, _Driver, strToFill, _Keep800x600Ratio);
_FontManager->computeString (str, _FontGen, _Color, _FontSize, _Embolden, _Oblique, _Driver, strToFill, _Keep800x600Ratio);
// just compute letters, glyphs are rendered on demand before first draw
//_FontManager->computeStringInfo(str, _FontGen, _Color, _FontSize, _Embolden, _Oblique, _Driver, strToFill, _Keep800x600Ratio);
_CacheNbFreePlaces--;

View file

@ -23,7 +23,7 @@
#include "nel/misc/common.h"
#include "nel/misc/rect.h"
#include "nel/misc/file.h"
#include "nel/misc/path.h"
using namespace std;
using namespace NLMISC;
@ -35,37 +35,14 @@ using namespace NLMISC;
namespace NL3D
{
// Config 1
const int TextureSizeX = 1024;
const int TextureSizeY = 1024; // If change this value -> change NbLine too
const int Categories[TEXTUREFONT_NBCATEGORY] = { 8, 16, 24, 32, 64 };
const int NbLine[TEXTUREFONT_NBCATEGORY] = { 8, 24, 16, 4, 1 }; // Based on textsize
/*
const int TextureSizeX = 256;
const int TextureSizeY = 256;
const int Categories[TEXTUREFONT_NBCATEGORY] = { 8, 16, 24, 32 };
const int NbLine[TEXTUREFONT_NBCATEGORY] = { 4, 6, 4, 1 }; // Based on textsize
*/
// ---------------------------------------------------------------------------
inline uint32 CTextureFont::SLetterKey::getVal()
{
// this limits Size to 6bits
// Large sizes already render wrong when many
// different glyphs are used due to limited texture atlas
uint8 eb = ((uint)Embolden) + ((uint)Oblique << 1);
if (FontGenerator == NULL)
return Char + ((Size&255)<<16) + (eb << 22);
else
return Char + ((Size&255)<<16) + (eb << 22) + ((FontGenerator->getUID()&0xFF)<<24);
}
// ---------------------------------------------------------------------------
CTextureFont::CTextureFont()
: _CacheVersion(1),
_TextureSizeX(512), _TextureSizeY(512), _TextureMaxW(4096), _TextureMaxH(4096),
_PaddingL(0), _PaddingT(0), _PaddingR(1), _PaddingB(1),
_MinGlyphSize(5), _MaxGlyphSize(200),
_GlyphSizeStepMin(50), _GlyphSizeStep(5)
{
uint i;
setFilterMode (ITexture::Linear, ITexture::LinearMipMapOff);
setWrapS (ITexture::Repeat);
@ -75,53 +52,9 @@ CTextureFont::CTextureFont()
setReleasable (false);
resize (TextureSizeX, TextureSizeY, CBitmap::Alpha);
for(i = 0; i < TextureSizeX*TextureSizeY; ++i)
getPixels()[i] = 0;
// convertToType (CBitmap::Alpha);
resize (_TextureSizeX, _TextureSizeY, CBitmap::Alpha, true);
sint posY = 0;
for(i = 0; i < TEXTUREFONT_NBCATEGORY; ++i)
{
// Number of chars per cache
Letters[i].resize ((TextureSizeX/Categories[i])*NbLine[i]);
for(uint32 j = 0; j < Letters[i].size(); ++j)
{
SLetterInfo &rLetter = Letters[i][j];
rLetter.Char = 0xffff;
rLetter.FontGenerator = NULL;
rLetter.Size= 0;
rLetter.Embolden = false;
rLetter.Oblique = false;
// The less recently used infos
if (j < Letters[i].size()-1)
rLetter.Next = &Letters[i][j+1];
else
rLetter.Next = NULL;
if (j > 0)
rLetter.Prev = &Letters[i][j-1];
else
rLetter.Prev = NULL;
rLetter.Cat = i;
sint sizeX = TextureSizeX/Categories[i];
rLetter.U = (Categories[i]*(j%sizeX)) / ((float)TextureSizeX);
rLetter.V = (posY + Categories[i]*((sint)(j/sizeX))) / ((float)TextureSizeY);
/////////////////////////////////////////////////
rLetter.CharWidth = rLetter.CharHeight = 0;
rLetter.GlyphIndex = rLetter.Top = rLetter.Left = rLetter.AdvX = 0;
}
Front[i] = &Letters[i][0];
Back[i] = &Letters[i][Letters[i].size()-1];
posY += NbLine[i] * Categories[i];
}
_AtlasNodes.push_back(CRect(0, 0, _TextureSizeX, _TextureSizeY));
}
@ -129,17 +62,16 @@ CTextureFont::~CTextureFont()
{
}
// ---------------------------------------------------------------------------
void CTextureFont::dumpTextureFont(const char *filename)
{
CBitmap b;
COFile f( filename );
b.resize (TextureSizeX, TextureSizeY, CBitmap::RGBA);
b.resize (_TextureSizeX, _TextureSizeY, CBitmap::RGBA);
CObjectVector<uint8>&bits = b.getPixels();
CObjectVector<uint8>&src = getPixels();
for (uint i = 0; i < (TextureSizeX*TextureSizeY); ++i)
for (uint i = 0; i < (_TextureSizeX*_TextureSizeY); ++i)
{
bits[i*4+0] = bits[i*4+1] = bits[i*4+2] = bits[i*4+3] = src[i];
}
@ -147,242 +79,471 @@ void CTextureFont::dumpTextureFont(const char *filename)
b.writeTGA (f, 32);
}
// ---------------------------------------------------------------------------
bool CTextureFont::getNextTextureSize(uint32 &newW, uint32 &newH) const
{
// width will be resized first (256x256 -> 512x256)
if (_TextureSizeX <= _TextureSizeY)
{
newW = _TextureSizeX * 2;
newH = _TextureSizeY;
}
else
{
newW = _TextureSizeX;
newH = _TextureSizeY * 2;
}
// no more room
return newW <= _TextureMaxW && newH <= _TextureMaxH;
}
// ---------------------------------------------------------------------------
// cat : categories where the letter is
// x : pos x of the letter
// y : pos y of the letter
void CTextureFont::rebuildLetter (sint cat, sint x, sint y)
// out of room, clear everything and rebuild glyphs on demand
// note: text will display wrong until glyphs get rendered again
void CTextureFont::clearAtlas()
{
sint sizex = TextureSizeX / Categories[cat];
sint index = x + y*sizex;
SLetterInfo &rLetter = Letters[cat][index];
nlwarning("Glyph cache will be cleared.");
if (rLetter.FontGenerator == NULL)
return;
_AtlasNodes.clear();
_AtlasNodes.push_back(CRect(0, 0, _TextureSizeX, _TextureSizeY));
sint catTopY = 0;
sint c = 0;
while (c < cat)
// clear texture
_Data[0].fill(0);
// clear glyph cache
for(uint i = 0; i< _Letters.size(); ++i)
{
catTopY += NbLine[c] * Categories[c];
++c;
_Letters[i].glyph = NULL;
}
// Destination position in pixel of the letter
sint posx = x * Categories[cat];
sint posy = catTopY + y * Categories[cat];
_GlyphCache.clear();
uint32 pitch = 0;
uint8 *bitmap = rLetter.FontGenerator->getBitmap ( rLetter.Char, rLetter.Size, rLetter.Embolden, rLetter.Oblique,
rLetter.CharWidth, rLetter.CharHeight,
pitch, rLetter.Left, rLetter.Top,
rLetter.AdvX, rLetter.GlyphIndex );
_CacheVersion++;
// Copy FreeType buffer
uint i;
for (i = 0; i < rLetter.CharHeight; ++i)
touch();
}
// ---------------------------------------------------------------------------
void CTextureFont::repackAtlas()
{
repackAtlas(_TextureSizeX, _TextureSizeY);
}
// ---------------------------------------------------------------------------
// backup old glyphs and move them to newly resized texture
// new atlas will be sorted if _GlyphCache is
void CTextureFont::repackAtlas(uint32 newW, uint32 newH)
{
uint32 newCacheVersion = _CacheVersion+1;
CBitmap btm;
uint32 oldW, oldH;
oldW = _TextureSizeX;
oldH = _TextureSizeY;
btm.resize(oldW, oldH, CBitmap::Alpha, true);
btm.blit(this, 0, 0);
// resize texture
if (_TextureSizeX != newW || _TextureSizeY != newH)
{
uint8 *pDst = &_Data[0][posx + (posy+i)*TextureSizeY];
uint8 *pSrc = &bitmap[i*pitch];
for (uint j = 0; j < rLetter.CharWidth; ++j)
_TextureSizeX = newW;
_TextureSizeY = newH;
resize (_TextureSizeX, _TextureSizeY, CBitmap::Alpha, true);
}
else
{
*pDst = *pSrc;
_Data[0].fill(0);
}
// release atlas and rebuild
_AtlasNodes.clear();
_AtlasNodes.push_back(CRect(0, 0, _TextureSizeX, _TextureSizeY));
CObjectVector<uint8>&src = btm.getPixels();
for(std::list<SGlyphInfo>::iterator it = _GlyphCache.begin(); it != _GlyphCache.end(); ++it)
{
if (it->CacheVersion != _CacheVersion)
{
// TODO: must remove glyph from all letters before removing glyph from cache
//continue;
}
SGlyphInfo &glyph = *it;
glyph.CacheVersion = newCacheVersion;
uint32 atlasX, atlasY;
if (reserveAtlas(glyph.W, glyph.H, atlasX, atlasY))
{
for (uint y = 0; y < glyph.H; ++y)
{
uint8 *pDst = &_Data[0][(atlasY + y) * _TextureSizeX + atlasX];
for (uint x = 0; x < glyph.W; ++x)
{
*pDst = src[(glyph.Y + y) * oldW + glyph.X + x];
++pDst;
++pSrc;
}
}
// Black border bottom and right
for (i = 0; i < rLetter.CharHeight+1; ++i)
// TODO: dup code with renderGlyph
glyph.U0 = (atlasX+_PaddingL) / (float)_TextureSizeX;
glyph.V0 = (atlasY+_PaddingT) / (float)_TextureSizeY;
glyph.U1 = (atlasX+_PaddingL+glyph.CharWidth) / (float)_TextureSizeX;
glyph.V1 = (atlasY+_PaddingT+glyph.CharHeight) / (float)_TextureSizeY;
glyph.X = atlasX;
glyph.Y = atlasY;
}
}
_CacheVersion = newCacheVersion;
// invalidate full texture
touch();
}
// ---------------------------------------------------------------------------
bool CTextureFont::resizeAtlas()
{
uint32 newW, newH;
if (!getNextTextureSize(newW, newH))
{
_Data[0][posx + rLetter.CharWidth + (posy+i)*TextureSizeY] = 0;
nlwarning("Font texture at maximum (%d,%d). Resize failed.", _TextureSizeX, _TextureSizeY);
return false;
}
for (i = 0; i < rLetter.CharWidth+1; ++i)
{
_Data[0][posx + i + (posy+rLetter.CharHeight)*TextureSizeY] = 0;
}
/*
dumpTextureFont (this);
int a = 5;
a++;
*/
// resize and redraw
repackAtlas(newW, newH);
return true;
}
// ---------------------------------------------------------------------------
void CTextureFont::doGenerate(bool async)
{
// Rectangle invalidate ?
if (_ListInvalidRect.begin()!=_ListInvalidRect.end())
{
// Yes, rebuild only those rectangles.
// For each rectangle to compute
std::list<NLMISC::CRect>::iterator ite=_ListInvalidRect.begin();
while (ite!=_ListInvalidRect.end())
{
// Compute rectangle coordinates
sint x = ite->left();
sint y = ite->bottom();
// Look in which category is the rectangle
sint cat = 0;
sint catTopY = 0;
sint catBotY = NbLine[cat] * Categories[cat];
while (y > catBotY)
{
if (y < catBotY)
break;
++cat;
nlassert (cat < TEXTUREFONT_NBCATEGORY);
catTopY = catBotY;
catBotY += NbLine[cat] * Categories[cat];
}
x = x / Categories[cat];
y = ite->top();
y = y - catTopY;
y = y / Categories[cat];
rebuildLetter (cat, x, y);
// Next rectangle
ite++;
}
}
else
{
for(int cat = 0; cat < TEXTUREFONT_NBCATEGORY; ++cat)
{
sint sizex = TextureSizeX / Categories[cat];
sint sizey = NbLine[cat];
for (sint y = 0; y < sizey; y++)
for (sint x = 0; x < sizex; x++)
{
rebuildLetter (cat, x, y);
}
}
}
/*
dumpTextureFont (this);
int a = 5;
*/
/*
nlinfo("doGenerate: Letters(%d/%d), Glyphs(%d/%d)\n", _Letters.size(), _Letters.size() * sizeof(SLetterInfo),
_GlyphCache.size(), _GlyphCache.size() * sizeof(SGlyphInfo));
//std::string fname = CFile::findNewFile("/tmp/font-texture.tga");
std::string fname = toString("/tmp/font-texture-%p-%03d.tga", this, _CacheVersion);
dumpTextureFont (fname.c_str());
*/
}
// ---------------------------------------------------------------------------
CTextureFont::SLetterInfo* CTextureFont::getLetterInfo (SLetterKey& k)
uint CTextureFont::fitRegion(uint index, uint width, uint height)
{
sint cat;
uint32 nTmp = k.getVal();
map<uint32, SLetterInfo*>::iterator itAccel = Accel.find (nTmp);
if (itAccel != Accel.end())
if (_AtlasNodes[index].X + width > _TextureSizeX - 1)
{
// Put it in the first place
SLetterInfo *pLetterToMove = itAccel->second;
cat = pLetterToMove->Cat;
if (pLetterToMove != Front[cat])
{
// unlink
nlassert(pLetterToMove->Prev);
pLetterToMove->Prev->Next = pLetterToMove->Next;
if (pLetterToMove == Back[cat])
{
Back[cat] = pLetterToMove->Prev;
}
else
{
pLetterToMove->Next->Prev = pLetterToMove->Prev;
return -1;
}
// link to front
pLetterToMove->Prev = NULL;
pLetterToMove->Next = Front[cat];
Front[cat]->Prev = pLetterToMove;
Front[cat] = pLetterToMove;
}
return pLetterToMove;
uint x = _AtlasNodes[index].X;
uint y = _AtlasNodes[index].Y;
sint widthLeft = width;
while(widthLeft > 0)
{
if (_AtlasNodes[index].Y > y)
{
y = _AtlasNodes[index].Y;
}
// The letter is not already present
// Found the category of the new letter
uint32 width, height;
// _AtlasNodes[0] for margin is not used here
if (_AtlasNodes[index].Y + height > _TextureSizeY - 1)
{
return -1;
}
//k.FontGenerator->getSizes (k.Char, k.Size, width, height);
// \todo mat : Temp !!! Try to use freetype cache
uint32 nPitch, nGlyphIndex;
sint32 nLeft, nTop, nAdvX;
k.FontGenerator->getBitmap (k.Char, k.Size, k.Embolden, k.Oblique, width, height, nPitch, nLeft, nTop,
nAdvX, nGlyphIndex );
widthLeft -= _AtlasNodes[index].Width;
index++;
}
// Add 1 pixel space for black border to get correct category
cat = 0;
if (((sint)width+1 > Categories[TEXTUREFONT_NBCATEGORY-1]) ||
((sint)height+1 > Categories[TEXTUREFONT_NBCATEGORY-1]))
return y;
}
bool CTextureFont::reserveAtlas(const uint32 width, const uint32 height, uint32 &x, uint32 &y)
{
if (_AtlasNodes.empty())
{
nlwarning("No available space in texture atlas (_AtlasNodes.empty() == true)");
return false;
}
x = 0;
y = 0;
sint bestIndex = -1;
sint bestWidth = _TextureSizeX;
sint bestHeight = _TextureSizeY;
sint selY=0;
for (uint i = 0; i < _AtlasNodes.size(); ++i)
{
selY = fitRegion(i, width, height);
if (selY >=0)
{
if (((selY + height) < bestHeight) || ((selY + height) == bestHeight && _AtlasNodes[i].Width > 0 && _AtlasNodes[i].Width < bestWidth))
{
bestHeight = selY + height;
bestIndex = i;
bestWidth = _AtlasNodes[i].Width;
x = _AtlasNodes[i].X;
y = selY;
}
}
}
if (bestIndex == -1)
{
x = 0;
y = 0;
return false;
}
CRect r(x, y + height, width, 0);
_AtlasNodes.insert(_AtlasNodes.begin() + bestIndex, r);
// shrink or remove nodes overlaping with newly inserted node
for(uint i = bestIndex+1; i< _AtlasNodes.size(); i++)
{
if (_AtlasNodes[i].X < (_AtlasNodes[i-1].X + _AtlasNodes[i-1].Width))
{
sint shrink = _AtlasNodes[i-1].X + _AtlasNodes[i-1].Width - _AtlasNodes[i].X;
_AtlasNodes[i].X += shrink;
if (_AtlasNodes[i].Width > shrink)
{
_AtlasNodes[i].Width -= shrink;
break;
}
_AtlasNodes.erase(_AtlasNodes.begin() + i);
i--;
}
else break;
}
// merge nearby nodes from same row
for(uint i = 0; i < _AtlasNodes.size() - 1; i++)
{
if (_AtlasNodes[i].Y == _AtlasNodes[i+1].Y)
{
_AtlasNodes[i].Width += _AtlasNodes[i+1].Width;
_AtlasNodes.erase(_AtlasNodes.begin() + i + 1);
i--;
}
}
return true;
}
// ---------------------------------------------------------------------------
// bitmap : texture data
// bitmapW : bitmap width
// bitmapH : bitmap height
// atlasX : pos x in font texture
// atlasY : pos y in font texture
void CTextureFont::copyGlyphBitmap(uint8* bitmap, uint32 bitmapW, uint32 bitmapH, uint32 atlasX, uint32 atlasY)
{
for (uint bY = 0; bY < bitmapH; ++bY)
{
uint8 *pDst = &_Data[0][(atlasY+_PaddingT+bY) * _TextureSizeX+atlasX+_PaddingL];
for (uint bX = 0; bX < bitmapW; ++bX)
{
*pDst = bitmap[bY * bitmapW+bX];
++pDst;
}
}
if (_PaddingR > 0 || _PaddingB > 0 || _PaddingL > 0 || _PaddingT > 0)
{
for(uint i = 0; i<(bitmapH+_PaddingT+_PaddingB); ++i)
{
if (_PaddingT > 0) _Data[0][(atlasY + i) * _TextureSizeX + atlasX ] = 0;
if (_PaddingB > 0) _Data[0][(atlasY + i) * _TextureSizeX + atlasX + _PaddingL + bitmapW] = 0;
}
for (uint i = 0; i<(bitmapW+_PaddingL+_PaddingR); ++i)
{
if (_PaddingL > 0) _Data[0][atlasY * _TextureSizeX + atlasX + i] = 0;
if (_PaddingB > 0) _Data[0][(atlasY + _PaddingT + bitmapH) * _TextureSizeX + atlasX + i] = 0;
}
}
CRect r(atlasX, atlasY, bitmapW + _PaddingL + _PaddingR, bitmapH + _PaddingT + _PaddingB);
touchRect(r);
}
// ---------------------------------------------------------------------------
CTextureFont::SGlyphInfo* CTextureFont::renderLetterGlyph(SLetterInfo *letter, uint bitmapFontSize)
{
uint32 nPitch;
sint32 left;
sint32 top;
sint32 advx;
uint32 charWidth;
uint32 charHeight;
uint32 glyphIndex;
uint8 *bitmap = letter->FontGenerator->getBitmap (letter->Char, bitmapFontSize, letter->Embolden, letter->Oblique,
charWidth, charHeight,
nPitch, left, top,
advx, glyphIndex );
uint32 atlasX, atlasY;
uint32 rectW, rectH;
rectW = charWidth + _PaddingL + _PaddingR;
rectH = charHeight + _PaddingT + _PaddingB;
if (!reserveAtlas(rectW, rectH, atlasX, atlasY))
{
// no room
return NULL;
}
copyGlyphBitmap(bitmap, charWidth, charHeight, atlasX, atlasY);
while (((sint)width+1 > Categories[cat]) || ((sint)height+1 > Categories[cat]))
SGlyphInfo* glyphInfo = NULL;
{
++cat;
nlassert (cat != TEXTUREFONT_NBCATEGORY);
// keep cache sorted by height (smaller first)
std::list<SGlyphInfo>::iterator it = _GlyphCache.begin();
while(it != _GlyphCache.end() && it->CharHeight < charHeight)
{
++it;
}
// And replace the less recently used letter
SLetterKey k2;
k2.Char = Back[cat]->Char;
k2.FontGenerator = Back[cat]->FontGenerator;
k2.Size = Back[cat]->Size;
k2.Embolden = Back[cat]->Embolden;
k2.Oblique = Back[cat]->Oblique;
itAccel = Accel.find (k2.getVal());
if (itAccel != Accel.end())
{
Accel.erase (itAccel);
it = _GlyphCache.insert(it, SGlyphInfo());
glyphInfo = &(*it);
}
SLetterInfo *NewBack = Back[cat]->Prev;
NewBack->Next = NULL;
Back[cat]->Cat = cat;
Back[cat]->Char = k.Char;
Back[cat]->FontGenerator = k.FontGenerator;
Back[cat]->Size = k.Size;
Back[cat]->Embolden = k.Embolden;
Back[cat]->Oblique = k.Oblique;
Back[cat]->CharWidth = width;
Back[cat]->CharHeight = height;
Back[cat]->Top = nTop;
Back[cat]->Left = nLeft;
Back[cat]->AdvX = nAdvX;
Back[cat]->Prev = NULL;
Back[cat]->Next = Front[cat];
Front[cat]->Prev = Back[cat];
Front[cat] = Back[cat];
Back[cat] = NewBack;
glyphInfo->GlyphIndex = glyphIndex;
glyphInfo->Size = bitmapFontSize;
glyphInfo->Embolden = letter->Embolden;
glyphInfo->Oblique = letter->Oblique;
glyphInfo->FontGenerator = letter->FontGenerator;
glyphInfo->CacheVersion = _CacheVersion;
Accel.insert (map<uint32, SLetterInfo*>::value_type(k.getVal(),Front[cat]));
glyphInfo->U0 = (atlasX+_PaddingL) / (float)_TextureSizeX;
glyphInfo->V0 = (atlasY+_PaddingT) / (float)_TextureSizeY;
glyphInfo->U1 = (atlasX+_PaddingL+charWidth) / (float)_TextureSizeX;
glyphInfo->V1 = (atlasY+_PaddingT+charHeight) / (float)_TextureSizeY;
// Invalidate the zone
sint index = (sint)(Front[cat] - &Letters[cat][0]);// / sizeof (SLetterInfo);
sint sizex = TextureSizeX / Categories[cat];
sint x = index % sizex;
sint y = index / sizex;
x = x * Categories[cat];
y = y * Categories[cat];
glyphInfo->CharWidth = charWidth;
glyphInfo->CharHeight = charHeight;
sint c = 0;
while (c < cat)
glyphInfo->X = atlasX;
glyphInfo->Y = atlasY;
glyphInfo->W = rectW;
glyphInfo->H = rectH;
return glyphInfo;
}
// ---------------------------------------------------------------------------
CTextureFont::SGlyphInfo* CTextureFont::findLetterGlyph(SLetterInfo *letter, bool insert)
{
uint bitmapFontSize = max((sint)_MinGlyphSize, min((sint)_MaxGlyphSize, letter->Size));
if (_GlyphSizeStep > 1 && bitmapFontSize > _GlyphSizeStepMin)
{
y = y + NbLine[c] * Categories[c];
++c;
bitmapFontSize = (bitmapFontSize / _GlyphSizeStep) * _GlyphSizeStep;
}
// must update the char, WITH the black borders
CRect r (x, y, width+1, height+1);
// CacheVersion not checked, all glyphs in cache must be rendered on texture
for(std::list<SGlyphInfo>::iterator it = _GlyphCache.begin(); it != _GlyphCache.end(); ++it)
{
if (it->GlyphIndex == letter->GlyphIndex &&
it->Size == bitmapFontSize &&
it->Embolden == letter->Embolden &&
it->Oblique == letter->Oblique &&
it->FontGenerator == letter->FontGenerator)
{
return &(*it);
}
}
touchRect (r);
if (insert)
{
return renderLetterGlyph(letter, bitmapFontSize);
}
return Front[cat];
return NULL;
}
// ---------------------------------------------------------------------------
CTextureFont::SLetterInfo* CTextureFont::findLetter(SLetterKey &k, bool insert)
{
// TODO: use std::map<uint64>
for(uint i = 0; i < _Letters.size(); ++i)
{
if (_Letters[i].Char == k.Char && _Letters[i].Size == k.Size &&
_Letters[i].Embolden == k.Embolden && _Letters[i].Oblique == k.Oblique &&
_Letters[i].FontGenerator == k.FontGenerator)
{
return &_Letters[i];
}
}
if (insert)
{
_Letters.push_back(SLetterInfo());
SLetterInfo* letter = &_Letters.back();
// get metrics for requested size
letter->Char = k.Char;
letter->Size = k.Size;
letter->Embolden = k.Embolden;
letter->Oblique = k.Oblique;
letter->FontGenerator = k.FontGenerator;
uint32 nPitch;
letter->FontGenerator->getBitmap(letter->Char, letter->Size, letter->Embolden, letter->Oblique,
letter->CharWidth, letter->CharHeight,
nPitch, letter->Left, letter->Top,
letter->AdvX, letter->GlyphIndex );
return letter;
}
return NULL;
}
// ---------------------------------------------------------------------------
CTextureFont::SLetterInfo* CTextureFont::getLetterInfo (SLetterKey& k, bool render)
{
// find already cached letter or create new one
SLetterInfo* letter = findLetter(k, true);
// letter not found (=NULL) or render not requested
if (!letter || !render) return letter;
// nothing to render, ie space char
if (letter->CharWidth == 0 || letter->CharHeight == 0) return letter;
if (!letter->glyph || letter->glyph->CacheVersion != _CacheVersion)
{
// render glyph
letter->glyph = findLetterGlyph(letter, true);
if (letter->glyph == NULL)
{
// resize/repack and try again
if (!resizeAtlas()) repackAtlas();
letter->glyph = findLetterGlyph(letter, true);
if (letter->glyph == NULL)
{
// make room by clearing all glyphs and reduce max size for glyphs
clearAtlas();
if (_MaxGlyphSize > _MinGlyphSize)
{
_MaxGlyphSize = max(_MinGlyphSize, _MaxGlyphSize - 10);
}
letter->glyph = findLetterGlyph(letter, true);
}
}
}
return letter;
}
} // NL3D

View file

@ -748,9 +748,13 @@ namespace NLGUI
{
virtual void execute (CCtrlBase *pCaller, const std::string &params)
{
if (!CViewRenderer::getInstance()->getDriver()->copyTextToClipboard(params))
ucstring s;
s.fromUtf8(params);
if (!CViewRenderer::getInstance()->getDriver()->copyTextToClipboard(s))
{
nlwarning("Copy to clipboard failed: '%s'", params.c_str());
}
}
};
REGISTER_ACTION_HANDLER(CAHCopyToClipboard, "copy_to_clipboard");
}

View file

@ -68,6 +68,7 @@ namespace NLGUI
_StepValue = 0;
_TileM = false;
_Frozen = false;
_Scale = false;
}
// ------------------------------------------------------------------------------------------------
@ -108,6 +109,11 @@ namespace NLGUI
return getTextureTopOrRight();
}
else
if( name == "scale" )
{
return toString( _Scale );
}
else
if( name == "vertical" )
{
return toString( _Vertical );
@ -244,6 +250,14 @@ namespace NLGUI
return;
}
else
if( name =="scale" )
{
bool b;
if (fromString( value, b ) )
_Scale = b;
return;
}
else
if( name == "vertical" )
{
bool b;
@ -408,6 +422,7 @@ namespace NLGUI
xmlSetProp( node, BAD_CAST "tx_bottomleft", BAD_CAST getTextureBottomOrLeft().c_str() );
xmlSetProp( node, BAD_CAST "tx_middle", BAD_CAST getTextureMiddle().c_str() );
xmlSetProp( node, BAD_CAST "tx_topright", BAD_CAST getTextureTopOrRight().c_str() );
xmlSetProp( node, BAD_CAST "scale", BAD_CAST toString( _Scale ).c_str() );
xmlSetProp( node, BAD_CAST "vertical", BAD_CAST toString( _Vertical ).c_str() );
std::string align;
@ -480,6 +495,10 @@ namespace NLGUI
if(prop) setTextureTopOrRight(string((const char*)prop));
else setTextureTopOrRight ("w_scroll_l0_t.tga");
// Override texture size (w for vertical, h for horizontal)
prop = (char*) xmlGetProp( node, (xmlChar*)"scale" );
if (prop) _Scale = convertBool((const char*)prop);
// Read properties
prop = (char*) xmlGetProp( node, (xmlChar*)"vertical" );
if (prop) _Vertical = convertBool((const char*)prop);
@ -606,13 +625,13 @@ namespace NLGUI
if (_Vertical)
{
_W = w;
if (!_Scale) _W = w;
_H = _Target->getMaxHReal();
}
else
{
_W = _Target->getMaxWReal();
_H = h;
if (!_Scale) _H = h;
}
CCtrlBase::updateCoords ();

View file

@ -46,6 +46,7 @@ namespace NLGUI
_BmpLeftW= _BmpMiddleW= _BmpRightW= _BmpH= 0;
_WMargin= 0;
_WMin= 0;
_HMin= 0;
_TextX= 0;
_TextY= 0;
_Setuped= false;
@ -124,6 +125,11 @@ namespace NLGUI
return toString( _WMin );
}
else
if( name == "hmin" )
{
return toString( _HMin );
}
else
if( name == "hardtext" )
{
if( _ViewText != NULL )
@ -296,6 +302,14 @@ namespace NLGUI
return;
}
else
if( name == "hmin" )
{
sint32 i;
if( fromString( value, i ) )
_HMin = i;
return;
}
else
if( name == "hardtext" )
{
if( _ViewText != NULL )
@ -469,6 +483,7 @@ namespace NLGUI
xmlNewProp( node, BAD_CAST "wmargin", BAD_CAST toString( _WMargin ).c_str() );
xmlNewProp( node, BAD_CAST "wmin", BAD_CAST toString( _WMin ).c_str() );
xmlNewProp( node, BAD_CAST "hmin", BAD_CAST toString( _HMin ).c_str() );
xmlNewProp( node, BAD_CAST "hardtext", BAD_CAST _ViewText->getText().toString().c_str() );
xmlNewProp( node, BAD_CAST "text_y", BAD_CAST toString( _TextY ).c_str() );
xmlNewProp( node, BAD_CAST "text_x", BAD_CAST toString( _TextX ).c_str() );
@ -519,7 +534,6 @@ namespace NLGUI
return false;
}
// *** Read Textures.
prop = (char*) xmlGetProp( cur, (xmlChar*)"tx_normal" );
if (prop)
@ -604,6 +618,15 @@ namespace NLGUI
// _WMin is at least the size of All W Bitmaps
_WMin= max(_WMin, _BmpLeftW + _BmpMiddleW + _BmpRightW);
// hmin
_HMin= 0;
prop = (char*) xmlGetProp( cur, (xmlChar*)"hmin" );
if (prop)
{
fromString((const char *) prop, _HMin);
}
_HMin= max(_HMin, _BmpH);
// TextY
_TextY= 0;
prop = (char*) xmlGetProp( cur, (xmlChar*)"text_y" );
@ -705,6 +728,43 @@ namespace NLGUI
return true;
}
// ***************************************************************************
void CCtrlTextButton::setTexture(const std::string &l, const std::string &m, const std::string &r, bool updateHeight)
{
nlctassert(NumTexture==3);
_TextureIdNormal[0].setTexture(l.c_str());
_TextureIdNormal[1].setTexture(m.c_str());
_TextureIdNormal[2].setTexture(r.c_str());
sint32 newH;
// Compute Bmp Sizes
CViewRenderer &rVR = *CViewRenderer::getInstance();
rVR.getTextureSizeFromId(_TextureIdNormal[0], _BmpLeftW, newH);
rVR.getTextureSizeFromId(_TextureIdNormal[1], _BmpMiddleW, newH);
rVR.getTextureSizeFromId(_TextureIdNormal[2], _BmpRightW, newH);
if (updateHeight) _BmpH = newH;
}
// ***************************************************************************
void CCtrlTextButton::setTexturePushed(const std::string &l, const std::string &m, const std::string &r)
{
nlctassert(NumTexture==3);
_TextureIdPushed[0].setTexture(l.c_str());
_TextureIdPushed[1].setTexture(m.c_str());
_TextureIdPushed[2].setTexture(r.c_str());
}
// ***************************************************************************
void CCtrlTextButton::setTextureOver(const std::string &l, const std::string &m, const std::string &r)
{
nlctassert(NumTexture==3);
_TextureIdOver[0].setTexture(l.c_str());
_TextureIdOver[1].setTexture(m.c_str());
_TextureIdOver[2].setTexture(r.c_str());
}
// ***************************************************************************
void CCtrlTextButton::draw ()
{
@ -899,7 +959,8 @@ namespace NLGUI
}
if (!(_SizeRef & 2))
{
_H= _BmpH;
_H= max(_BmpH, _ViewText->getH());
_H= max(_H, _HMin);
}
CViewBase::updateCoords();

View file

@ -473,6 +473,12 @@ namespace NLGUI
return _ViewText->getText();
}
// ***************************************************************************
CViewText *CDBGroupComboBox::getViewText()
{
return _ViewText;
}
// ***************************************************************************
std::string CDBGroupComboBox::getSelectionText() const
{
@ -633,6 +639,9 @@ namespace NLGUI
{
nlassert(groupMenu);
if (_ViewText)
groupMenu->setFontSize(_ViewText->getFontSize());
// Setup the menu with combo action.
groupMenu->reset();
for(uint i=0; i<getNumTexts(); i++)

View file

@ -702,9 +702,9 @@ namespace NLGUI
sint32 maxPos= max(_CursorPos, _SelectCursorPos) + (sint32)_Prompt.length();
// get its position on screen
sint cxMinPos, cyMinPos;
sint cxMaxPos, cyMaxPos;
sint height;
float cxMinPos, cyMinPos;
float cxMaxPos, cyMaxPos;
float height;
_ViewText->getCharacterPositionFromIndex(minPos, false, cxMinPos, cyMinPos, height);
_ViewText->getCharacterPositionFromIndex(maxPos, false, cxMaxPos, cyMaxPos, height);
@ -755,8 +755,8 @@ namespace NLGUI
if (_BlinkState) // is the cursor shown ?
{
// get its position on screen
sint cx, cy;
sint height;
float cx, cy;
float height;
_ViewText->getCharacterPositionFromIndex(_CursorPos + (sint)_Prompt.length(), _CursorAtPreviousLineEnd, cx, cy, height);
// display the cursor
// get the texture for the cursor
@ -1482,7 +1482,7 @@ namespace NLGUI
if (_ViewText->getWReal() > _WReal)
{
// Check if cursor visible
sint xCursVT, xCurs, yTmp, hTmp;
float xCursVT, xCurs, yTmp, hTmp;
// Get the cursor pos from the BL of the viewtext
_ViewText->getCharacterPositionFromIndex(_CursorPos+(sint)_Prompt.size(), false, xCursVT, yTmp, hTmp);
// Get the cursor pos from the BL of the edit box

View file

@ -382,6 +382,60 @@ namespace NLGUI
}
}
void CGroupHTML::setTextButtonStyle(CCtrlTextButton *ctrlButton, const CStyleParams &style)
{
// this will also set size for <a class="ryzom-ui-button"> treating it like "display: inline-block;"
if (style.Width > 0) ctrlButton->setWMin(_Style.Width);
if (style.Height > 0) ctrlButton->setHMin(_Style.Height);
CViewText *pVT = ctrlButton->getViewText();
if (pVT)
{
setTextStyle(pVT, _Style);
}
if (_Style.BackgroundColor.A > 0)
{
if (_Style.BackgroundColorOver.A == 0)
_Style.BackgroundColorOver = _Style.BackgroundColor;
ctrlButton->setColor(_Style.BackgroundColor);
ctrlButton->setColorOver(_Style.BackgroundColorOver);
ctrlButton->setTexture("", "blank.tga", "", false);
ctrlButton->setTextureOver("", "blank.tga", "");
ctrlButton->setProperty("force_text_over", "true");
}
else if (_Style.BackgroundColorOver.A > 0)
{
ctrlButton->setColorOver(_Style.BackgroundColorOver);
ctrlButton->setProperty("force_text_over", "true");
ctrlButton->setTextureOver("blank.tga", "blank.tga", "blank.tga");
}
}
void CGroupHTML::setTextStyle(CViewText *pVT, const CStyleParams &style)
{
if (pVT)
{
pVT->setFontSize(style.FontSize);
pVT->setColor(style.TextColor);
pVT->setColor(style.TextColor);
pVT->setFontName(style.FontFamily);
pVT->setFontSize(style.FontSize);
pVT->setEmbolden(style.FontWeight >= FONT_WEIGHT_BOLD);
pVT->setOblique(style.FontOblique);
pVT->setUnderlined(style.Underlined);
pVT->setStrikeThrough(style.StrikeThrough);
if (style.TextShadow.Enabled)
{
pVT->setShadow(true);
pVT->setShadowColor(style.TextShadow.Color);
pVT->setShadowOutline(style.TextShadow.Outline);
pVT->setShadowOffset(style.TextShadow.X, style.TextShadow.Y);
}
}
}
// Get an url and return the local filename with the path where the url image should be
string CGroupHTML::localImageName(const string &url)
{
@ -1489,6 +1543,13 @@ namespace NLGUI
// Paragraph ?
switch(element_number)
{
case HTML_HTML:
if (present[MY_HTML_HTML_STYLE] && value[MY_HTML_HTML_STYLE])
getStyleParams(value[MY_HTML_HTML_STYLE], _StyleDefault, _StyleDefault);
_Style = _StyleDefault;
setBackgroundColor(_Style.BackgroundColor);
break;
case HTML_HEAD:
_ReadingHeadTag = !_IgnoreHeadTag;
_IgnoreHeadTag = true;
@ -1549,9 +1610,13 @@ namespace NLGUI
_Style.TextColor = LinkColor;
_Style.Underlined = true;
_Style.GlobalColor = LinkColorGlobalColor;
_Style.BackgroundColor.A = 0;
_Style.BackgroundColorOver.A = 0;
_Style.Width = -1;
_Style.Height = -1;
if (present[HTML_A_STYLE] && value[HTML_A_STYLE])
getStyleParams(value[HTML_A_STYLE], _Style);
getStyleParams(value[HTML_A_STYLE], _Style, _StyleParams.back());
_A.push_back(true);
_Link.push_back ("");
@ -1604,7 +1669,7 @@ namespace NLGUI
style = value[MY_HTML_DIV_STYLE];
if (!style.empty())
getStyleParams(style, _Style);
getStyleParams(style, _Style, _StyleParams.back());
// use generic template system
if (_TrustedDomain && !instClass.empty() && instClass == "ryzom-ui-grouptemplate")
@ -1708,17 +1773,21 @@ namespace NLGUI
break;
case HTML_BODY:
{
if (present[HTML_BODY_BGCOLOR] && value[HTML_BODY_BGCOLOR])
{
CRGBA bgColor;
if (scanHTMLColor(value[HTML_BODY_BGCOLOR], bgColor))
setBackgroundColor (bgColor);
}
pushStyle();
string style;
if (present[HTML_BODY_STYLE] && value[HTML_BODY_STYLE])
style = value[HTML_BODY_STYLE];
if (!style.empty())
getStyleParams(style, _Style, _StyleParams.back());
CRGBA bgColor = _Style.BackgroundColor;
if (present[HTML_BODY_BGCOLOR] && value[HTML_BODY_BGCOLOR])
scanHTMLColor(value[HTML_BODY_BGCOLOR], bgColor);
if (bgColor != _Style.BackgroundColor)
setBackgroundColor(bgColor);
if (!style.empty())
{
@ -1743,10 +1812,6 @@ namespace NLGUI
image = image.substr(4, image.size()-5);
setBackground (image, scale, repeat);
}
// set default text style from <body>
getStyleParams(style, _StyleDefault);
_Style = _StyleDefault;
}
}
break;
@ -1776,7 +1841,7 @@ namespace NLGUI
_Style.TextColor = H1Color;
_Style.GlobalColor = H1ColorGlobalColor;
if (present[MY_HTML_H1_STYLE] && value[MY_HTML_H1_STYLE])
getStyleParams(value[MY_HTML_H1_STYLE], _Style);
getStyleParams(value[MY_HTML_H1_STYLE], _Style, _StyleParams.back());
}
break;
case HTML_H2:
@ -1788,7 +1853,7 @@ namespace NLGUI
_Style.TextColor = H2Color;
_Style.GlobalColor = H2ColorGlobalColor;
if (present[MY_HTML_H2_STYLE] && value[MY_HTML_H2_STYLE])
getStyleParams(value[MY_HTML_H2_STYLE], _Style);
getStyleParams(value[MY_HTML_H2_STYLE], _Style, _StyleParams.back());
}
break;
case HTML_H3:
@ -1800,7 +1865,7 @@ namespace NLGUI
_Style.TextColor = H3Color;
_Style.GlobalColor = H3ColorGlobalColor;
if (present[MY_HTML_H3_STYLE] && value[MY_HTML_H3_STYLE])
getStyleParams(value[MY_HTML_H3_STYLE], _Style);
getStyleParams(value[MY_HTML_H3_STYLE], _Style, _StyleParams.back());
}
break;
case HTML_H4:
@ -1812,7 +1877,7 @@ namespace NLGUI
_Style.TextColor = H4Color;
_Style.GlobalColor = H4ColorGlobalColor;
if (present[MY_HTML_H4_STYLE] && value[MY_HTML_H4_STYLE])
getStyleParams(value[MY_HTML_H4_STYLE], _Style);
getStyleParams(value[MY_HTML_H4_STYLE], _Style, _StyleParams.back());
}
break;
case HTML_H5:
@ -1824,7 +1889,7 @@ namespace NLGUI
_Style.TextColor = H5Color;
_Style.GlobalColor = H5ColorGlobalColor;
if (present[MY_HTML_H5_STYLE] && value[MY_HTML_H5_STYLE])
getStyleParams(value[MY_HTML_H5_STYLE], _Style);
getStyleParams(value[MY_HTML_H5_STYLE], _Style, _StyleParams.back());
}
break;
case HTML_H6:
@ -1836,7 +1901,7 @@ namespace NLGUI
_Style.TextColor = H6Color;
_Style.GlobalColor = H6ColorGlobalColor;
if (present[MY_HTML_H6_STYLE] && value[MY_HTML_H6_STYLE])
getStyleParams(value[MY_HTML_H6_STYLE], _Style);
getStyleParams(value[MY_HTML_H6_STYLE], _Style, _StyleParams.back());
}
break;
case HTML_IMG:
@ -1844,8 +1909,13 @@ namespace NLGUI
// Get the string name
if (present[MY_HTML_IMG_SRC] && value[MY_HTML_IMG_SRC])
{
CStyleParams style;
float tmpf;
std::string id;
CStyleParams style;
style.FontSize = _Style.FontSize;
if (present[MY_HTML_IMG_ID] && value[MY_HTML_IMG_ID])
id = value[MY_HTML_IMG_ID];
if (present[MY_HTML_IMG_WIDTH] && value[MY_HTML_IMG_WIDTH])
getPercentage(style.Width, tmpf, value[MY_HTML_IMG_WIDTH]);
@ -1858,7 +1928,7 @@ namespace NLGUI
// width, height from inline css
if (present[MY_HTML_IMG_STYLE] && value[MY_HTML_IMG_STYLE])
getStyleParams(value[MY_HTML_IMG_STYLE], style);
getStyleParams(value[MY_HTML_IMG_STYLE], style, _Style);
// Tooltip
const char *tooltip = NULL;
@ -1880,13 +1950,13 @@ namespace NLGUI
if (getA() && getParent () && getParent ()->getParent())
{
string params = "name=" + getId() + "|url=" + getLink ();
addButton(CCtrlButton::PushButton, value[MY_HTML_IMG_SRC], value[MY_HTML_IMG_SRC], value[MY_HTML_IMG_SRC],
addButton(CCtrlButton::PushButton, id, value[MY_HTML_IMG_SRC], value[MY_HTML_IMG_SRC],
overSrc, "browse", params.c_str(), tooltip, style);
}
else
if (tooltip || !overSrc.empty())
{
addButton(CCtrlButton::PushButton, value[MY_HTML_IMG_SRC], value[MY_HTML_IMG_SRC], value[MY_HTML_IMG_SRC],
addButton(CCtrlButton::PushButton, id, value[MY_HTML_IMG_SRC], value[MY_HTML_IMG_SRC],
overSrc, "", "", tooltip, style);
}
else
@ -1908,7 +1978,7 @@ namespace NLGUI
reloadImg = true;
}
addImage (value[MY_HTML_IMG_SRC], reloadImg, style);
addImage(id, value[MY_HTML_IMG_SRC], reloadImg, style);
}
}
}
@ -1920,6 +1990,10 @@ namespace NLGUI
// read general property
string templateName;
string minWidth;
string id;
if (present[MY_HTML_INPUT_ID] && value[MY_HTML_INPUT_ID])
id = value[MY_HTML_INPUT_ID];
// Widget template name
if (present[MY_HTML_INPUT_Z_BTN_TMPL] && value[MY_HTML_INPUT_Z_BTN_TMPL])
@ -1940,6 +2014,13 @@ namespace NLGUI
_Style.FontSize = TextFontSize;
_Style.FontWeight = FONT_WEIGHT_NORMAL;
_Style.FontOblique = false;
_Style.TextShadow = STextShadow(true);
_Style.Width = -1;
_Style.Height = -1;
// by default background texture is transparent,
// using alpha value to decide if to change it to 'blank.tga' for coloring
_Style.BackgroundColor.A = 0;
_Style.BackgroundColorOver.A = 0;
// Global color flag
if (present[MY_HTML_INPUT_GLOBAL_COLOR])
@ -1951,7 +2032,7 @@ namespace NLGUI
tooltip = value[MY_HTML_INPUT_ALT];
if (present[MY_HTML_INPUT_STYLE] && value[MY_HTML_INPUT_STYLE])
getStyleParams(value[MY_HTML_INPUT_STYLE], _Style);
getStyleParams(value[MY_HTML_INPUT_STYLE], _Style, _StyleParams.back());
string type = toLower(value[MY_HTML_INPUT_TYPE]);
if (type == "image")
@ -2042,6 +2123,8 @@ namespace NLGUI
}
ctrlButton->setText(ucstring::makeFromUtf8(text));
setTextButtonStyle(ctrlButton, _Style);
}
getParagraph()->addChild (buttonGroup);
paragraphChange ();
@ -2114,6 +2197,10 @@ namespace NLGUI
{
if (btnType == CCtrlButton::RadioButton)
{
// override with 'id' because radio buttons share same name
if (!id.empty())
checkbox->setId(id);
// group together buttons with same name
CForm &form = _Forms.back();
bool notfound = true;
@ -2184,7 +2271,7 @@ namespace NLGUI
if (present[HTML_SELECT_MULTIPLE] && value[HTML_SELECT_MULTIPLE])
multiple = true;
if (present[HTML_SELECT_STYLE] && value[HTML_SELECT_STYLE])
getStyleParams(value[HTML_SELECT_STYLE], style);
getStyleParams(value[HTML_SELECT_STYLE], style, _Style);
CGroupHTML::CForm::CEntry entry;
entry.Name = name;
@ -2205,6 +2292,7 @@ namespace NLGUI
sb->setMinH(style.Height);
sb->setMaxVisibleLine(size);
sb->setFontSize(style.FontSize);
}
entry.SelectBox = sb;
@ -2213,6 +2301,14 @@ namespace NLGUI
{
CDBGroupComboBox *cb = addComboBox(DefaultFormSelectGroup, name.c_str());
entry.ComboBox = cb;
if (cb)
{
// create view text
cb->updateCoords();
if (cb->getViewText())
setTextStyle(cb->getViewText(), style);
}
}
_Forms.back().Entries.push_back (entry);
}
@ -2259,7 +2355,7 @@ namespace NLGUI
pushStyle();
if (present[HTML_LI_STYLE] && value[HTML_LI_STYLE])
getStyleParams(value[HTML_LI_STYLE], _Style);
getStyleParams(value[HTML_LI_STYLE], _Style, _StyleParams.back());
ucstring str;
str.fromUtf8(_UL.back().getListMarkerText());
@ -2281,8 +2377,8 @@ namespace NLGUI
{
newParagraph(PBeginSpace);
pushStyle();
if (present[HTML_BLOCK_STYLE] && value[HTML_BLOCK_STYLE])
getStyleParams(value[HTML_BLOCK_STYLE], _Style);
if (present[MY_HTML_P_STYLE] && value[MY_HTML_P_STYLE])
getStyleParams(value[MY_HTML_P_STYLE], _Style, _StyleParams.back());
}
break;
case HTML_PRE:
@ -2291,7 +2387,7 @@ namespace NLGUI
_Style.FontFamily = "monospace";
if (present[HTML_PRE_STYLE] && value[HTML_PRE_STYLE])
getStyleParams(value[HTML_PRE_STYLE], _Style);
getStyleParams(value[HTML_PRE_STYLE], _Style, _StyleParams.back());
_PRE.push_back(true);
@ -2319,7 +2415,7 @@ namespace NLGUI
if (present[MY_HTML_TABLE_CELLPADDING] && value[MY_HTML_TABLE_CELLPADDING])
fromString(value[MY_HTML_TABLE_CELLPADDING], table->CellPadding);
if (present[MY_HTML_TABLE_STYLE] && value[MY_HTML_TABLE_STYLE])
getStyleParams(value[MY_HTML_TABLE_STYLE], _Style);
getStyleParams(value[MY_HTML_TABLE_STYLE], _Style, _StyleParams.back());
table->setMarginLeft(getIndent());
addHtmlGroup (table, 0);
@ -2349,7 +2445,7 @@ namespace NLGUI
}
if (present[MY_HTML_TD_STYLE] && value[MY_HTML_TD_STYLE])
getStyleParams(value[MY_HTML_TD_STYLE], _Style);
getStyleParams(value[MY_HTML_TD_STYLE], _Style, _StyleParams.back());
CGroupTable *table = getTable();
if (table)
@ -2439,9 +2535,12 @@ namespace NLGUI
_Style.FontOblique = false;
_Style.FontSize = TextFontSize;
_Style.TextShadow = STextShadow(true);
_Style.Width = -1;
_Style.Height = -1;
_Style.BackgroundColor.A = 0;
if (present[MY_HTML_TEXTAREA_STYLE] && value[MY_HTML_TEXTAREA_STYLE])
getStyleParams(value[MY_HTML_TEXTAREA_STYLE], _Style);
getStyleParams(value[MY_HTML_TEXTAREA_STYLE], _Style, _StyleParams.back());
// Got one form ?
if (!(_Forms.empty()))
@ -2497,7 +2596,7 @@ namespace NLGUI
pushStyle();
if (present[MY_HTML_TR_STYLE] && value[MY_HTML_TR_STYLE])
getStyleParams(value[MY_HTML_TR_STYLE], _Style);
getStyleParams(value[MY_HTML_TR_STYLE], _Style, _StyleParams.back());
}
break;
case HTML_UL:
@ -2514,7 +2613,7 @@ namespace NLGUI
pushStyle();
if (present[HTML_UL_STYLE] && value[HTML_UL_STYLE])
getStyleParams(value[HTML_UL_STYLE], _Style);
getStyleParams(value[HTML_UL_STYLE], _Style, _StyleParams.back());
break;
case HTML_OBJECT:
_ObjectType.clear();
@ -2537,7 +2636,7 @@ namespace NLGUI
pushStyle();
if (present[MY_HTML_SPAN_STYLE] && value[MY_HTML_SPAN_STYLE])
getStyleParams(value[MY_HTML_SPAN_STYLE], _Style);
getStyleParams(value[MY_HTML_SPAN_STYLE], _Style, _StyleParams.back());
}
break;
case HTML_DEL:
@ -2581,7 +2680,7 @@ namespace NLGUI
endParagraph();
pushStyle();
if (present[HTML_GEN_STYLE] && value[HTML_GEN_STYLE])
getStyleParams(value[HTML_GEN_STYLE], _Style);
getStyleParams(value[HTML_GEN_STYLE], _Style, _StyleParams.back());
}
break;
case HTML_DT:
@ -2604,7 +2703,7 @@ namespace NLGUI
pushStyle();
_Style.FontWeight = FONT_WEIGHT_BOLD;
if (present[HTML_GEN_STYLE] && value[HTML_GEN_STYLE])
getStyleParams(value[HTML_GEN_STYLE], _Style);
getStyleParams(value[HTML_GEN_STYLE], _Style, _StyleParams.back());
if (!_LI)
{
@ -2639,7 +2738,7 @@ namespace NLGUI
pushStyle();
if (present[HTML_GEN_STYLE] && value[HTML_GEN_STYLE])
getStyleParams(value[HTML_GEN_STYLE], _Style);
getStyleParams(value[HTML_GEN_STYLE], _Style, _StyleParams.back());
if (!_LI)
{
@ -2663,7 +2762,7 @@ namespace NLGUI
if (present[HTML_OL_TYPE] && value[HTML_OL_TYPE])
type = value[HTML_OL_TYPE];
if (present[HTML_OL_STYLE] && value[HTML_OL_STYLE])
getStyleParams(value[HTML_OL_STYLE], _Style);
getStyleParams(value[HTML_OL_STYLE], _Style, _StyleParams.back());
_UL.push_back(HTMLOListElement(start, type));
// if LI is already present
@ -2680,12 +2779,13 @@ namespace NLGUI
if (sep)
{
CStyleParams style;
style.FontSize = _Style.FontSize;
style.TextColor = CRGBA(120, 120, 120, 255);
style.Height = 0;
style.Width = 0;
if (present[HTML_HR_STYLE] && value[HTML_HR_STYLE])
getStyleParams(value[HTML_HR_STYLE], style);
getStyleParams(value[HTML_HR_STYLE], style, _Style);
CViewBitmap *bitmap = dynamic_cast<CViewBitmap*>(sep->getView("hr"));
if (bitmap)
@ -2725,6 +2825,9 @@ namespace NLGUI
case HTML_HEAD:
_ReadingHeadTag = false;
break;
case HTML_BODY:
popStyle();
break;
case HTML_FONT:
popStyle();
break;
@ -4469,6 +4572,8 @@ namespace NLGUI
// Translate the tooltip
ctrlButton->setDefaultContextHelp(ucstring::makeFromUtf8(getLinkTitle()));
ctrlButton->setText(tmpStr);
setTextButtonStyle(ctrlButton, _Style);
}
getParagraph()->addChild (buttonGroup);
paragraphChange ();
@ -4491,23 +4596,10 @@ namespace NLGUI
}
}
newLink->setText(tmpStr);
newLink->setColor(_Style.TextColor);
newLink->setFontName(_Style.FontFamily);
newLink->setFontSize(_Style.FontSize);
newLink->setEmbolden(embolden);
newLink->setOblique(_Style.FontOblique);
newLink->setUnderlined(_Style.Underlined);
newLink->setStrikeThrough(_Style.StrikeThrough);
newLink->setMultiLineSpace((uint)((float)(_Style.FontSize)*LineSpaceFontFactor));
newLink->setMultiLine(true);
newLink->setModulateGlobalColor(_Style.GlobalColor);
if (_Style.TextShadow.Enabled)
{
newLink->setShadow(true);
newLink->setShadowColor(_Style.TextShadow.Color);
newLink->setShadowOutline(_Style.TextShadow.Outline);
newLink->setShadowOffset(_Style.TextShadow.X, _Style.TextShadow.Y);
}
setTextStyle(newLink, _Style);
// newLink->setLineAtBottom (true);
registerAnchor(newLink);
@ -4528,7 +4620,7 @@ namespace NLGUI
// ***************************************************************************
void CGroupHTML::addImage(const char *img, bool reloadImg, const CStyleParams &style)
void CGroupHTML::addImage(const std::string &id, const char *img, bool reloadImg, const CStyleParams &style)
{
// In a paragraph ?
if (!_Paragraph)
@ -4544,6 +4636,7 @@ namespace NLGUI
// Not added ?
CViewBitmap *newImage = new CViewBitmap (TCtorParam());
newImage->setId(id);
//
// 1/ try to load the image with the old system (local files in bnp)
@ -4612,6 +4705,10 @@ namespace NLGUI
_CurrentViewLink = NULL;
{
// override cols/rows values from style
if (_Style.Width > 0) cols = _Style.Width / _Style.FontSize;
if (_Style.Height > 0) rows = _Style.Height / _Style.FontSize;
// Not added ?
std::vector<std::pair<std::string,std::string> > templateParams;
templateParams.push_back (std::pair<std::string,std::string> ("w", toString (cols*_Style.FontSize)));
@ -4649,7 +4746,18 @@ namespace NLGUI
// Set the content
CGroupEditBox *eb = dynamic_cast<CGroupEditBox*>(textArea->getGroup("eb"));
if (eb)
{
eb->setInputString(decodeHTMLEntities(content));
if (_Style.BackgroundColor.A > 0)
{
CViewBitmap *bg = dynamic_cast<CViewBitmap*>(eb->getView("bg"));
if (bg)
{
bg->setTexture("blank.tga");
bg->setColor(_Style.BackgroundColor);
}
}
}
textArea->invalidateCoords();
getParagraph()->addChild (textArea);
@ -4746,7 +4854,7 @@ namespace NLGUI
// ***************************************************************************
CCtrlButton *CGroupHTML::addButton(CCtrlButton::EType type, const std::string &/* name */, const std::string &normalBitmap, const std::string &pushedBitmap,
CCtrlButton *CGroupHTML::addButton(CCtrlButton::EType type, const std::string &name, const std::string &normalBitmap, const std::string &pushedBitmap,
const std::string &overBitmap, const char *actionHandler, const char *actionHandlerParams,
const char *tooltip, const CStyleParams &style)
{
@ -4759,6 +4867,10 @@ namespace NLGUI
// Add the ctrl button
CCtrlButton *ctrlButton = new CCtrlButton(TCtorParam());
if (!name.empty())
{
ctrlButton->setId(name);
}
// Load only tga files.. (conversion in dds filename is done in the lookup procedure)
string normal = normalBitmap.empty()?"":CFile::getPath(normalBitmap) + CFile::getFilenameWithoutExtension(normalBitmap) + ".tga";
@ -4898,9 +5010,7 @@ namespace NLGUI
_IgnoreBaseUrlTag = false;
// reset style
_StyleDefault = CStyleParams();
_Style = _StyleDefault;
_StyleParams.clear();
resetCssStyle();
// TR
@ -5043,6 +5153,25 @@ namespace NLGUI
}
}
void CGroupHTML::setTitle(const std::string &title)
{
ucstring uctitle;
uctitle.fromUtf8(title);
_TitleString.clear();
if(!_TitlePrefix.empty())
{
_TitleString = _TitlePrefix + " - ";
}
_TitleString += uctitle;
setTitle(_TitleString);
}
std::string CGroupHTML::getTitle() const {
return _TitleString.toUtf8();
};
// ***************************************************************************
bool CGroupHTML::lookupLocalFile (string &result, const char *url, bool isUrl)
@ -5572,6 +5701,22 @@ namespace NLGUI
// create html code with image url inside and do the request again
renderHtmlString("<html><head><title>"+_URL+"</title></head><body><img src=\"" + _URL + "\"></body></html>");
}
else if (_TrustedDomain && type.find("text/lua") == 0)
{
setTitle(_TitleString);
_LuaScript = "\nlocal __CURRENT_WINDOW__=\""+this->_Id+"\" \n"+content;
CLuaManager::getInstance().executeLuaScript(_LuaScript, true);
_LuaScript.clear();
_Browsing = false;
_Connecting = false;
// disable refresh button
clearRefresh();
// disable redo into this url
_AskedUrl.clear();
}
else
{
renderHtmlString(content);
@ -5770,6 +5915,13 @@ namespace NLGUI
return false;
}
// ***************************************************************************
void CGroupHTML::clearRefresh()
{
_URL.clear();
updateRefreshButton();
}
// ***************************************************************************
void CGroupHTML::clearUndoRedo()
{
@ -5809,7 +5961,9 @@ namespace NLGUI
return;
// push to redo, pop undo, and set current
if (!_AskedUrl.empty())
_BrowseRedo.push_front(_AskedUrl);
_AskedUrl= _BrowseUndo.back();
_BrowseUndo.pop_back();
@ -5856,7 +6010,7 @@ namespace NLGUI
{
CCtrlBaseButton *butRefresh = dynamic_cast<CCtrlBaseButton *>(CWidgetManager::getInstance()->getElementFromId(_BrowseRefreshButton));
bool enabled = !_Browsing && !_Connecting;
bool enabled = !_Browsing && !_Connecting && !_URL.empty();
if(butRefresh)
butRefresh->setFrozen(!enabled);
}
@ -5895,6 +6049,25 @@ namespace NLGUI
return true;
}
int CGroupHTML::luaClearRefresh(CLuaState &ls)
{
const char *funcName = "clearRefresh";
CLuaIHM::checkArgCount(ls, funcName, 0);
clearRefresh();
return 0;
}
int CGroupHTML::luaClearUndoRedo(CLuaState &ls)
{
const char *funcName = "clearUndoRedo";
CLuaIHM::checkArgCount(ls, funcName, 0);
clearUndoRedo();
return 0;
}
// ***************************************************************************
int CGroupHTML::luaBrowse(CLuaState &ls)
{
@ -6004,12 +6177,12 @@ namespace NLGUI
if (!url.empty())
{
string params = "name=" + getId() + "|url=" + getLink ();
addButton(CCtrlButton::PushButton, ls.toString(1), ls.toString(1), ls.toString(1),
addButton(CCtrlButton::PushButton, "", ls.toString(1), ls.toString(1),
"", "browse", params.c_str(), "", style);
}
else
{
addImage(ls.toString(1), false, style);
addImage("", ls.toString(1), false, style);
}
@ -6209,40 +6382,82 @@ namespace NLGUI
return uri.toString();
}
// ***************************************************************************
void CGroupHTML::resetCssStyle()
{
_StyleDefault = CStyleParams();
_StyleDefault.TextColor = TextColor;
_StyleDefault.FontSize = TextFontSize;
_StyleDefault.BackgroundColor = BgColor;
_Style = _StyleDefault;
_StyleParams.clear();
}
// ***************************************************************************
// CGroupHTML::CStyleParams style;
// style.FontSize; // font-size: 10px;
// style.TextColor; // color: #ABCDEF;
// style.Underlined; // text-decoration: underline; text-decoration-line: underline;
// style.StrikeThrough; // text-decoration: line-through; text-decoration-line: line-through;
void CGroupHTML::getStyleParams(const std::string &styleString, CStyleParams &style, bool inherit)
void CGroupHTML::getStyleParams(const std::string &styleString, CStyleParams &style, const CStyleParams &current)
{
const CStyleParams current = _Style;
if (inherit)
{
style.Underlined = current.Underlined;
style.StrikeThrough = current.StrikeThrough;
}
float tmpf;
TStyle styles = parseStyle(styleString);
TStyle::iterator it;
// first pass: get font-size for 'em' sizes
for (it=styles.begin(); it != styles.end(); ++it)
{
if (it->first == "font")
{
if (it->second == "inherit")
{
style.FontSize = current.FontSize;
style.FontFamily = current.FontFamily;
style.FontWeight = current.FontWeight;
style.FontOblique = current.FontOblique;
}
}
else
if (it->first == "font-size")
{
if (it->second == "inherit")
{
style.FontSize = current.FontSize;
}
else
{
float tmp;
sint size = 0;
getPercentage (size, tmp, it->second.c_str());
if (size > 0)
style.FontSize = size;
std::string unit;
if (getCssLength(tmpf, unit, it->second.c_str()))
{
if (unit == "rem")
style.FontSize = _StyleDefault.FontSize * tmpf;
else if (unit == "em")
style.FontSize = current.FontSize * tmpf;
else if (unit == "pt")
style.FontSize = tmpf / 0.75f;
else if (unit == "%")
style.FontSize = current.FontSize * tmpf / 100.f;
else
style.FontSize = tmpf;
}
}
}
}
// second pass: rest of style
for (it=styles.begin(); it != styles.end(); ++it)
{
if (it->first == "border")
{
sint32 b;
if (it->second == "none")
style.BorderWidth = 0;
else
if (fromString(it->second, b))
style.BorderWidth = b;
}
else
if (it->first == "font-style")
{
@ -6258,10 +6473,7 @@ namespace NLGUI
if (it->second == "inherit")
style.FontFamily = current.FontFamily;
else
if (it->second == "monospace")
style.FontFamily = "monospace";
else
style.FontFamily.clear();
style.FontFamily = it->second;
}
else
if (it->first == "font-weight")
@ -6425,16 +6637,68 @@ namespace NLGUI
}
else
if (it->first == "width")
getPercentage(style.Width, tmpf, it->second.c_str());
{
std::string unit;
if (getCssLength(tmpf, unit, it->second.c_str()))
{
if (unit == "rem")
style.Width = tmpf * _StyleDefault.FontSize;
else if (unit == "em")
style.Width = tmpf * style.FontSize;
else if (unit == "pt")
style.FontSize = tmpf / 0.75f;
else
style.Width = tmpf;
}
}
else
if (it->first == "height")
getPercentage(style.Height, tmpf, it->second.c_str());
{
std::string unit;
if (getCssLength(tmpf, unit, it->second.c_str()))
{
if (unit == "rem")
style.Height = tmpf * _StyleDefault.FontSize;
else if (unit == "em")
style.Height = tmpf * style.FontSize;
else if (unit == "pt")
style.FontSize = tmpf / 0.75f;
else
style.Height = tmpf;
}
}
else
if (it->first == "max-width")
getPercentage(style.MaxWidth, tmpf, it->second.c_str());
{
std::string unit;
if (getCssLength(tmpf, unit, it->second.c_str()))
{
if (unit == "rem")
style.MaxWidth = tmpf * _StyleDefault.FontSize;
else if (unit == "em")
style.MaxWidth = tmpf * style.FontSize;
else if (unit == "pt")
style.FontSize = tmpf / 0.75f;
else
style.MaxWidth = tmpf;
}
}
else
if (it->first == "max-height")
getPercentage(style.MaxHeight, tmpf, it->second.c_str());
{
std::string unit;
if (getCssLength(tmpf, unit, it->second.c_str()))
{
if (unit == "rem")
style.MaxHeight = tmpf * _StyleDefault.FontSize;
else if (unit == "em")
style.MaxHeight = tmpf * style.FontSize;
else if (unit == "pt")
style.FontSize = tmpf / 0.75f;
else
style.MaxHeight = tmpf;
}
}
else
if (it->first == "-ryzom-modulate-color")
{
@ -6445,7 +6709,31 @@ namespace NLGUI
if (fromString(it->second, b))
style.GlobalColor = b;
}
else
if (it->first == "background-color")
{
if (it->second == "inherit")
style.BackgroundColor = current.BackgroundColor;
else
scanHTMLColor(it->second.c_str(), style.BackgroundColor);
}
else
if (it->first == "-ryzom-background-color-over")
{
if (it->second == "inherit")
style.BackgroundColorOver = current.BackgroundColorOver;
else
scanHTMLColor(it->second.c_str(), style.BackgroundColorOver);
}
}
// if outer element has underline set, then inner element cannot remove it
if (current.Underlined)
style.Underlined = current.Underlined;
// if outer element has line-through set, then inner element cannot remove it
if (current.StrikeThrough)
style.StrikeThrough = current.StrikeThrough;
}
// ***************************************************************************

View file

@ -327,6 +327,7 @@ namespace NLGUI
nlwarning("html root node failed");
success = false;
}
xmlFreeDoc(parser->myDoc);
}
else
{

View file

@ -2561,6 +2561,12 @@ namespace NLGUI
}
}
// ------------------------------------------------------------------------------------------------
void CGroupMenu::setFontSize(uint fontSize)
{
_FontSize = fontSize;
}
// ------------------------------------------------------------------------------------------------
uint CGroupMenu::getNumLine() const
{

View file

@ -52,6 +52,7 @@ namespace NLGUI
_MinW= 0;
_MinH= 0;
_Over = false;
_TempOver = false;
_OverColor = CRGBA(255,255,255,32);
_OverElt = -1;
_LastW = 0;
@ -967,7 +968,7 @@ namespace NLGUI
// TEMP TEMP
//CViewRenderer &rVR = *CViewRenderer::getInstance();
//rVR.drawRotFlipBitmap _RenderLayer, (_XReal, _YReal, _WReal, _HReal, 0, false, rVR.getBlankTextureId(), CRGBA(0,255,0,255) );
if (_Over)
if (_Over || _TempOver)
{
CViewRenderer &rVR = *CViewRenderer::getInstance();
@ -1052,7 +1053,10 @@ namespace NLGUI
_OverElt = -1;
if (!isIn(eventDesc.getX(), eventDesc.getY()))
{
_TempOver = false;
return false;
}
for (uint32 i = 0; i < _Elements.size(); ++i)
if (_Elements[i].Element->getActive())

View file

@ -47,6 +47,15 @@ namespace NLGUI
#undef HTML_ATTR
#define HTML_ATTR(a,b) { (char*) #b }
HTAttr html_attr[] =
{
HTML_ATTR(HTML,DIR),
HTML_ATTR(HTML,LANG),
HTML_ATTR(HTML,VERSION),
HTML_ATTR(HTML,STYLE),
{ 0 }
};
HTAttr a_attr[] =
{
HTML_ATTR(A,ACCESSKEY),
@ -213,6 +222,7 @@ namespace NLGUI
HTML_ATTR(P,QUICK_HELP_EVENTS),
HTML_ATTR(P,QUICK_HELP_LINK),
HTML_ATTR(P,NAME),
HTML_ATTR(P,STYLE),
{ 0 }
};
@ -283,6 +293,37 @@ namespace NLGUI
};
// ***************************************************************************
bool getCssLength (float &value, std::string &unit, const std::string &str)
{
std::string::size_type pos = 0;
std::string::size_type len = str.size();
if (len == 1 && str[0] == '.')
{
return false;
}
while(pos < len)
{
bool isNumeric = (str[pos] >= '0' && str[pos] <= '9')
|| (pos == 0 && str[pos] == '.')
|| (pos > 0 && str[pos] == '.' && str[pos-1] >= '0' && str[pos-1] <= '9');
if (!isNumeric)
{
break;
}
pos++;
}
unit = toLower(str.substr(pos));
if (unit == "%" || unit == "rem" || unit == "em" || unit == "px" || unit == "pt")
{
std::string tmpstr = str.substr(0, pos);
return fromString(tmpstr, value);
}
return false;
}
// Read a width HTML parameter. "100" or "100%". Returns true if percent (0 ~ 1) else false
bool getPercentage (sint32 &width, float &percent, const char *str)
@ -468,6 +509,8 @@ namespace NLGUI
// Change the HTML DTD
SGML_dtd *HTML_DTD = HTML_dtd ();
HTML_DTD->tags[HTML_HTML].attributes = html_attr;
HTML_DTD->tags[HTML_HTML].number_of_attributes = sizeof(html_attr) / sizeof(HTAttr) - 1;
HTML_DTD->tags[HTML_TABLE].attributes = table_attr;
HTML_DTD->tags[HTML_TABLE].number_of_attributes = sizeof(table_attr) / sizeof(HTAttr) - 1;
HTML_DTD->tags[HTML_TR].attributes = tr_attr;

View file

@ -96,8 +96,23 @@ namespace NLGUI
if(w!=0 && h!=0)
{
_IsMinimized= false;
if (w != _ScreenW || h != _ScreenH)
{
_ScreenW = w;
_ScreenH = h;
updateInterfaceScale();
}
}
else
{
// Keep old coordinates (suppose resolution won't change, even if typically false wen we swithch from outgame to ingame)
_IsMinimized= true;
}
}
void CViewRenderer::updateInterfaceScale()
{
if(_ScreenW>0)
_OneOverScreenW = 1.0f / (float)_ScreenW;
else
@ -106,11 +121,27 @@ namespace NLGUI
_OneOverScreenH = 1.0f / (float)_ScreenH;
else
_OneOverScreenH = 1000;
_InterfaceScale = _InterfaceUserScale;
if (_InterfaceBaseW > 0 && _InterfaceBaseH > 0)
{
float wRatio = (float)_ScreenW / _InterfaceBaseW;
float rRatio = (float)_ScreenH / _InterfaceBaseH;
_InterfaceScale *= std::min(wRatio, rRatio);
}
if (_InterfaceScale != 1.0f)
{
_OneOverScreenW *= _InterfaceScale;
_OneOverScreenH *= _InterfaceScale;
_EffectiveScreenW = sint(_ScreenW / _InterfaceScale);
_EffectiveScreenH = sint(_ScreenH / _InterfaceScale);
}
else
{
// Keep old coordinates (suppose resolution won't change, even if typically false wen we swithch from outgame to ingame)
_IsMinimized= true;
_EffectiveScreenW = _ScreenW;
_EffectiveScreenH = _ScreenH;
}
}
@ -120,8 +151,8 @@ namespace NLGUI
*/
void CViewRenderer::getScreenSize (uint32 &w, uint32 &h)
{
w = _ScreenW;
h = _ScreenH;
w = _EffectiveScreenW;
h = _EffectiveScreenH;
}
/*
@ -133,6 +164,20 @@ namespace NLGUI
ooh= _OneOverScreenH;
}
void CViewRenderer::setInterfaceScale(float scale, sint32 width/*=0*/, sint32 height/*=0*/)
{
// prevent #div/0
if (sint(scale*100) > 0)
_InterfaceUserScale = scale;
else
_InterfaceUserScale = 1.0f;
_InterfaceBaseW = width;
_InterfaceBaseH = height;
updateInterfaceScale();
}
void CViewRenderer::setup()
{
_ClipX = _ClipY = 0;
@ -140,8 +185,10 @@ namespace NLGUI
_ClipH = 600;
_ScreenW = 800;
_ScreenH = 600;
_OneOverScreenW= 1.0f / (float)_ScreenW;
_OneOverScreenH= 1.0f / (float)_ScreenH;
_InterfaceScale = 1.0f;
_InterfaceUserScale = 1.0f;
_InterfaceBaseW = 0;
_InterfaceBaseH = 0;
_IsMinimized= false;
_WFigurTexture= 0;
_HFigurTexture= 0;
@ -157,6 +204,9 @@ namespace NLGUI
_EmptyLayer[i]= true;
}
_BlankGlobalTexture = NULL;
_Bilinear = false;
updateInterfaceScale();
}
@ -499,7 +549,7 @@ namespace NLGUI
/*
* drawBitmap
*/
void CViewRenderer::drawRotFlipBitmap (sint layerId, sint32 x, sint32 y, sint32 width, sint32 height,
void CViewRenderer::drawRotFlipBitmap (sint layerId, float x, float y, float width, float height,
uint8 rot, bool flipv, sint32 nTxId, const CRGBA &col)
{
if (width <= 0 || height <= 0) return;
@ -1283,7 +1333,7 @@ namespace NLGUI
_Material.setTexture(0, ite->Texture);
// Special Case if _WorldSpaceTransformation and _WorldSpaceScale, enable bilinear
if(_WorldSpaceTransformation && _WorldSpaceScale)
if(_Bilinear || (_WorldSpaceTransformation && _WorldSpaceScale))
ite->Texture->setFilterMode(UTexture::Linear, UTexture::LinearMipMapOff);
// draw quads and empty list
@ -1300,7 +1350,7 @@ namespace NLGUI
}
// Special Case if _WorldSpaceTransformation and _WorldSpaceScale, reset
if(_WorldSpaceTransformation && _WorldSpaceScale)
if(_Bilinear || (_WorldSpaceTransformation && _WorldSpaceScale))
ite->Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff);
}
if (!layer.FilteredAlphaBlendedQuads.empty() ||
@ -1942,6 +1992,25 @@ namespace NLGUI
void CViewRenderer::drawText (sint layerId, float x, float y, uint wordIndex, float xmin, float ymin, float xmax, float ymax, UTextContext &textContext)
{
xmin = xmin * _OneOverScreenW;
ymin = ymin * _OneOverScreenH;
xmax = xmax * _OneOverScreenW;
ymax = ymax * _OneOverScreenH;
if (_InterfaceScale != 1.0f && _InterfaceScale != 2.0f)
{
// align to screen pixel
x *= _OneOverScreenW * _ScreenW;
y *= _OneOverScreenH * _ScreenH;
x = floorf(x) * 1.f / (float) _ScreenW;
y = floorf(y) * 1.f / (float) _ScreenH;
}
else
{
x = floorf(x) * _OneOverScreenW;
y = floorf(y) * _OneOverScreenH;
}
if (_WorldSpaceTransformation)
{
textContext.printClipAtUnProjected(*getStringRenderBuffer(layerId), _CameraFrustum, _WorldSpaceMatrix, x, y, _CurrentZ, wordIndex, xmin, ymin, xmax, ymax);

File diff suppressed because it is too large Load diff

View file

@ -1850,7 +1850,7 @@ namespace NLGUI
class InvalidateTextVisitor : public CInterfaceElementVisitor
{
public:
InvalidateTextVisitor( bool reset )
InvalidateTextVisitor( bool reset)
{
this->reset = reset;
}
@ -1893,6 +1893,13 @@ namespace NLGUI
}
CViewRenderer::getInstance()->setClipWindow(0, 0, w, h);
bool scaleChanged = _InterfaceScale != CViewRenderer::getInstance()->getInterfaceScale();
if (scaleChanged)
{
_InterfaceScale = CViewRenderer::getInstance()->getInterfaceScale();
notifyInterfaceScaleWatchers();
}
// If all conditions are OK, move windows so they fit correctly with new screen size
// Do this work only InGame when Config is loaded
moveAllWindowsToNewScreenSize(w,h,true);
@ -1902,7 +1909,7 @@ namespace NLGUI
{
SMasterGroup &rMG = _MasterGroups[nMasterGroup];
InvalidateTextVisitor inv( false );
InvalidateTextVisitor inv( false);
rMG.Group->visitGroupAndChildren( &inv );
rMG.Group->invalidateCoords ();
@ -3688,6 +3695,42 @@ namespace NLGUI
}
// ------------------------------------------------------------------------------------------------
void CWidgetManager::notifyInterfaceScaleWatchers()
{
std::vector< IInterfaceScaleWatcher* >::iterator itr = scaleWatchers.begin();
while( itr != scaleWatchers.end() )
{
(*itr)->onInterfaceScaleChanged();
++itr;
}
}
// ------------------------------------------------------------------------------------------------
void CWidgetManager::registerInterfaceScaleWatcher( IInterfaceScaleWatcher *watcher )
{
std::vector< IInterfaceScaleWatcher* >::const_iterator itr
= std::find( scaleWatchers.begin(), scaleWatchers.end(), watcher );
if( itr != scaleWatchers.end() )
return;
scaleWatchers.push_back( watcher );
}
// ------------------------------------------------------------------------------------------------
void CWidgetManager::unregisterInterfaceScaleWatcher( IInterfaceScaleWatcher *watcher )
{
std::vector< IInterfaceScaleWatcher* >::iterator itr
= std::find( scaleWatchers.begin(), scaleWatchers.end(), watcher );
if( itr == scaleWatchers.end() )
return;
scaleWatchers.erase( itr );
}
// ------------------------------------------------------------------------------------------------
CWidgetManager::CWidgetManager()
{
LinkHack();
@ -3724,6 +3767,7 @@ namespace NLGUI
inGame = false;
setScreenWH(0, 0);
_InterfaceScale = 1.0f;
_GroupSelection = false;
multiSelection = false;

View file

@ -591,6 +591,22 @@ NLMISC_CATEGORISED_COMMAND(nel,stohr, "Convert a second number into an human rea
return true;
}
std::string toLower(const char *str)
{
if (!str) return "";
uint len = strlen(str);
string res;
res.reserve(len);
for(uint i = 0; i < len; i++)
{
if( (str[i] >= 'A') && (str[i] <= 'Z') )
res += str[i] - 'A' + 'a';
else
res += str[i];
}
return res;
}
std::string toLower(const std::string &str)
{
@ -1720,12 +1736,12 @@ static bool openDocWithExtension (const std::string &document, const std::string
const char *previousEnv = getenv("LD_LIBRARY_PATH");
// clear LD_LIBRARY_PATH to avoid problems with Steam Runtime
setenv("LD_LIBRARY_PATH", "", 1);
if (previousEnv) setenv("LD_LIBRARY_PATH", "", 1);
bool res = launchProgram(command, document);
// restore previous LD_LIBRARY_PATH
setenv("LD_LIBRARY_PATH", previousEnv, 1);
if (previousEnv) setenv("LD_LIBRARY_PATH", previousEnv, 1);
return res;
#endif // NL_OS_WINDOWS

View file

@ -185,7 +185,7 @@ bool CIFile::open(const std::string &path, bool text)
// Bigfile or xml pack access requested ?
string::size_type pos;
if ((pos = path.find('@')) != string::npos)
if (!CFile::fileExists(path) && (pos = path.find('@')) != string::npos)
{
// check for a double @ to identify XML pack file
if (pos+1 < path.size() && path[pos+1] == '@')

View file

@ -1,6 +1,10 @@
FILE(GLOB SRC *.cpp *.h)
FILE(GLOB HEADERS ../../include/nel/sound/*.h)
IF(NOT FFMPEG_FOUND)
LIST(REMOVE_ITEM SRC ${CMAKE_CURRENT_SOURCE_DIR}/audio_decoder_ffmpeg.cpp)
LIST(REMOVE_ITEM HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/../../include/nel/sound/audio_decoder_ffmpeg.h)
ENDIF()
FILE(GLOB ANIMATION
sound_anim_manager.cpp ../../include/nel/sound/sound_anim_manager.h
@ -58,6 +62,8 @@ FILE(GLOB STREAM
FILE(GLOB STREAM_FILE
audio_decoder.cpp ../../include/nel/sound/audio_decoder.h
audio_decoder_vorbis.cpp ../../include/nel/sound/audio_decoder_vorbis.h
audio_decoder_mp3.cpp ../../include/nel/sound/audio_decoder_mp3.h
audio_decoder_ffmpeg.cpp ../../include/nel/sound/audio_decoder_ffmpeg.h
stream_file_sound.cpp ../../include/nel/sound/stream_file_sound.h
stream_file_source.cpp ../../include/nel/sound/stream_file_source.h
)
@ -95,6 +101,12 @@ IF(WITH_STATIC)
TARGET_LINK_LIBRARIES(nelsound ${OGG_LIBRARY})
ENDIF()
IF(FFMPEG_FOUND)
ADD_DEFINITIONS(-DFFMPEG_ENABLED)
INCLUDE_DIRECTORIES(${FFMPEG_INCLUDE_DIRS})
TARGET_LINK_LIBRARIES(nelsound ${FFMPEG_LIBRARIES})
ENDIF()
INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR})

View file

@ -36,6 +36,11 @@
// Project includes
#include <nel/sound/audio_decoder_vorbis.h>
#include <nel/sound/audio_decoder_mp3.h>
#ifdef FFMPEG_ENABLED
#include <nel/sound/audio_decoder_ffmpeg.h>
#endif
using namespace std;
using namespace NLMISC;
@ -82,16 +87,32 @@ IAudioDecoder *IAudioDecoder::createAudioDecoder(const std::string &type, NLMISC
nlwarning("Stream is NULL");
return NULL;
}
#ifdef FFMPEG_ENABLED
try {
CAudioDecoderFfmpeg *decoder = new CAudioDecoderFfmpeg(stream, loop);
return static_cast<IAudioDecoder *>(decoder);
}
catch(const Exception &e)
{
nlwarning("Exception %s during ffmpeg setup", e.what());
return NULL;
}
#else
std::string type_lower = toLower(type);
if (type_lower == "ogg")
{
return new CAudioDecoderVorbis(stream, loop);
}
else if (type_lower == "mp3")
{
return new CAudioDecoderMP3(stream, loop);
}
else
{
nlwarning("Music file type unknown: '%s'", type_lower.c_str());
return NULL;
}
#endif
}
bool IAudioDecoder::getInfo(const std::string &filepath, std::string &artist, std::string &title, float &length)
@ -102,6 +123,14 @@ bool IAudioDecoder::getInfo(const std::string &filepath, std::string &artist, st
nlwarning("Music file %s does not exist!", filepath.c_str());
return false;
}
#ifdef FFMPEG_ENABLED
CIFile ifile;
ifile.setCacheFileOnOpen(false);
ifile.allowBNPCacheFileOnOpen(false);
if (ifile.open(lookup))
return CAudioDecoderFfmpeg::getInfo(&ifile, artist, title, length);
#else
std::string type = CFile::getExtension(filepath);
std::string type_lower = NLMISC::toLower(type);
@ -115,10 +144,21 @@ bool IAudioDecoder::getInfo(const std::string &filepath, std::string &artist, st
nlwarning("Unable to open: '%s'", filepath.c_str());
}
else if (type_lower == "mp3")
{
CIFile ifile;
ifile.setCacheFileOnOpen(false);
ifile.allowBNPCacheFileOnOpen(false);
if (ifile.open(lookup))
return CAudioDecoderMP3::getInfo(&ifile, artist, title, length);
nlwarning("Unable to open: '%s'", filepath.c_str());
}
else
{
nlwarning("Music file type unknown: '%s'", type_lower.c_str());
}
#endif
artist.clear(); title.clear();
return false;
@ -132,6 +172,15 @@ void IAudioDecoder::getMusicExtensions(std::vector<std::string> &extensions)
{
extensions.push_back("ogg");
}
if (std::find(extensions.begin(), extensions.end(), "mp3") == extensions.end())
{
extensions.push_back("mp3");
}
#ifdef FFMPEG_ENABLED
extensions.push_back("mp3");
extensions.push_back("flac");
extensions.push_back("aac");
#endif
// extensions.push_back("wav"); // TODO: Easy.
}
@ -139,7 +188,11 @@ void IAudioDecoder::getMusicExtensions(std::vector<std::string> &extensions)
/// Return if a music extension is supported by the nel sound library.
bool IAudioDecoder::isMusicExtensionSupported(const std::string &extension)
{
#ifdef FFMPEG_ENABLED
return (extension == "ogg" || extension == "mp3" || extension == "flac" || extension == "aac");
#else
return (extension == "ogg");
#endif
}
} /* namespace NLSOUND */

View file

@ -0,0 +1,431 @@
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2018 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdsound.h"
#include <nel/sound/audio_decoder_ffmpeg.h>
#define __STDC_CONSTANT_MACROS
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libavutil/opt.h>
}
using namespace std;
using namespace NLMISC;
using namespace NLSOUND;
// Visual Studio does not support AV_TIME_BASE_Q macro in C++
#undef AV_TIME_BASE_Q
static const AVRational AV_TIME_BASE_Q = {1, AV_TIME_BASE};
namespace {
const std::string av_err2string(sint err)
{
char buf[AV_ERROR_MAX_STRING_SIZE];
av_strerror(err, buf, AV_ERROR_MAX_STRING_SIZE);
return (std::string)buf;
}
void nel_logger(void *ptr, int level, const char *fmt, va_list vargs)
{
static char msg[1024];
const char *module = NULL;
// AV_LOG_DEBUG, AV_LOG_TRACE
if (level >= AV_LOG_DEBUG) return;
if (ptr)
{
AVClass *avc = *(AVClass**) ptr;
module = avc->item_name(ptr);
}
vsnprintf(msg, sizeof(msg), fmt, vargs);
msg[sizeof(msg)-1] = '\0';
switch(level)
{
case AV_LOG_PANIC:
// ffmpeg is about to crash so lets throw
nlerror("FFMPEG(P): (%s) %s", module, msg);
break;
case AV_LOG_FATAL:
// ffmpeg had unrecoverable error, corrupted stream or such
nlerrornoex("FFMPEG(F): (%s) %s", module, msg);
break;
case AV_LOG_ERROR:
nlwarning("FFMPEG(E): (%s) %s", module, msg);
break;
case AV_LOG_WARNING:
nlwarning("FFMPEG(W): (%s) %s", module, msg);
break;
case AV_LOG_INFO:
nlinfo("FFMPEG(I): (%s) %s", module, msg);
break;
case AV_LOG_VERBOSE:
nldebug("FFMPEG(V): (%s) %s", module, msg);
break;
case AV_LOG_DEBUG:
nldebug("FFMPEG(D): (%s) %s", module, msg);
break;
default:
nlinfo("FFMPEG: invalid log level:%d (%s) %s", level, module, msg);
break;
}
}
class CFfmpegInstance
{
public:
CFfmpegInstance()
{
av_log_set_level(AV_LOG_DEBUG);
av_log_set_callback(nel_logger);
av_register_all();
//avformat_network_init();
}
virtual ~CFfmpegInstance()
{
//avformat_network_deinit();
}
};
CFfmpegInstance ffmpeg;
// Send bytes to ffmpeg
int avio_read_packet(void *opaque, uint8 *buf, int buf_size)
{
NLSOUND::CAudioDecoderFfmpeg *decoder = static_cast<NLSOUND::CAudioDecoderFfmpeg *>(opaque);
NLMISC::IStream *stream = decoder->getStream();
nlassert(stream->isReading());
uint32 available = decoder->getStreamSize() - stream->getPos();
if (available == 0) return 0;
buf_size = FFMIN(buf_size, available);
stream->serialBuffer((uint8 *)buf, buf_size);
return buf_size;
}
sint64 avio_seek(void *opaque, sint64 offset, int whence)
{
NLSOUND::CAudioDecoderFfmpeg *decoder = static_cast<NLSOUND::CAudioDecoderFfmpeg *>(opaque);
NLMISC::IStream *stream = decoder->getStream();
nlassert(stream->isReading());
NLMISC::IStream::TSeekOrigin origin;
switch(whence)
{
case SEEK_SET:
origin = NLMISC::IStream::begin;
break;
case SEEK_CUR:
origin = NLMISC::IStream::current;
break;
case SEEK_END:
origin = NLMISC::IStream::end;
break;
case AVSEEK_SIZE:
return decoder->getStreamSize();
default:
return -1;
}
stream->seek((sint32) offset, origin);
return stream->getPos();
}
}//ns
namespace NLSOUND {
// swresample will convert audio to this format
#define FFMPEG_SAMPLE_RATE 44100
#define FFMPEG_CHANNELS 2
#define FFMPEG_CHANNEL_LAYOUT AV_CH_LAYOUT_STEREO
#define FFMPEG_BITS_PER_SAMPLE 16
#define FFMPEG_SAMPLE_FORMAT AV_SAMPLE_FMT_S16
CAudioDecoderFfmpeg::CAudioDecoderFfmpeg(NLMISC::IStream *stream, bool loop)
: IAudioDecoder(),
_Stream(stream), _Loop(loop), _IsMusicEnded(false), _StreamSize(0), _IsSupported(false),
_AvioContext(NULL), _FormatContext(NULL),
_AudioContext(NULL), _AudioStreamIndex(0),
_SwrContext(NULL)
{
_StreamOffset = stream->getPos();
stream->seek(0, NLMISC::IStream::end);
_StreamSize = stream->getPos();
stream->seek(_StreamOffset, NLMISC::IStream::begin);
try {
_FormatContext = avformat_alloc_context();
if (!_FormatContext)
throw Exception("Can't create AVFormatContext");
// avio_ctx_buffer can be reallocated by ffmpeg and assigned to avio_ctx->buffer
uint8 *avio_ctx_buffer = NULL;
size_t avio_ctx_buffer_size = 4096;
avio_ctx_buffer = static_cast<uint8 *>(av_malloc(avio_ctx_buffer_size));
if (!avio_ctx_buffer)
throw Exception("Can't allocate avio context buffer");
_AvioContext = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size, 0, this, &avio_read_packet, NULL, &avio_seek);
if (!_AvioContext)
throw Exception("Can't allocate avio context");
_FormatContext->pb = _AvioContext;
sint ret = avformat_open_input(&_FormatContext, NULL, NULL, NULL);
if (ret < 0)
throw Exception("avformat_open_input: %d", ret);
// find stream and then audio codec to see if ffmpeg supports this
_IsSupported = false;
if (avformat_find_stream_info(_FormatContext, NULL) >= 0)
{
_AudioStreamIndex = av_find_best_stream(_FormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
if (_AudioStreamIndex >= 0)
{
_AudioContext = _FormatContext->streams[_AudioStreamIndex]->codec;
av_opt_set_int(_AudioContext, "refcounted_frames", 1, 0);
AVCodec *codec = avcodec_find_decoder(_AudioContext->codec_id);
if (codec != NULL && avcodec_open2(_AudioContext, codec, NULL) >= 0)
{
_IsSupported = true;
}
}
}
}
catch(...)
{
release();
throw;
}
if (!_IsSupported)
{
nlwarning("FFMPEG: Decoder created, unknown stream format / codec");
}
}
CAudioDecoderFfmpeg::~CAudioDecoderFfmpeg()
{
release();
}
void CAudioDecoderFfmpeg::release()
{
if (_SwrContext)
swr_free(&_SwrContext);
if (_AudioContext)
avcodec_close(_AudioContext);
if (_FormatContext)
avformat_close_input(&_FormatContext);
if (_AvioContext && _AvioContext->buffer)
av_freep(&_AvioContext->buffer);
if (_AvioContext)
av_freep(&_AvioContext);
}
bool CAudioDecoderFfmpeg::isFormatSupported() const
{
return _IsSupported;
}
/// Get information on a music file.
bool CAudioDecoderFfmpeg::getInfo(NLMISC::IStream *stream, std::string &artist, std::string &title, float &length)
{
CAudioDecoderFfmpeg ffmpeg(stream, false);
if (!ffmpeg.isFormatSupported())
{
title.clear();
artist.clear();
length = 0.f;
return false;
}
AVDictionaryEntry *tag = NULL;
while((tag = av_dict_get(ffmpeg._FormatContext->metadata, "", tag, AV_DICT_IGNORE_SUFFIX)))
{
if (!strcmp(tag->key, "artist"))
{
artist = tag->value;
}
else if (!strcmp(tag->key, "title"))
{
title = tag->value;
}
}
length = ffmpeg.getLength();
return true;
}
uint32 CAudioDecoderFfmpeg::getRequiredBytes()
{
return 0; // no minimum requirement of bytes to buffer out
}
uint32 CAudioDecoderFfmpeg::getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum)
{
if (_IsMusicEnded) return 0;
nlassert(minimum <= maximum); // can't have this..
// TODO: CStreamFileSource::play() will stall when there is no frames on warmup
// supported can be set false if there is an issue creating converter
if (!_IsSupported)
{
_IsMusicEnded = true;
return 1;
}
uint32 bytes_read = 0;
AVFrame frame = {0};
AVPacket packet = {0};
if (!_SwrContext)
{
sint64 in_channel_layout = av_get_default_channel_layout(_AudioContext->channels);
_SwrContext = swr_alloc_set_opts(NULL,
// output
FFMPEG_CHANNEL_LAYOUT, FFMPEG_SAMPLE_FORMAT, FFMPEG_SAMPLE_RATE,
// input
in_channel_layout, _AudioContext->sample_fmt, _AudioContext->sample_rate,
0, NULL);
swr_init(_SwrContext);
}
sint ret;
while(bytes_read < minimum)
{
// read packet from stream
if ((ret = av_read_frame(_FormatContext, &packet)) < 0)
{
_IsMusicEnded = true;
// TODO: looping
break;
}
if (packet.stream_index == _AudioStreamIndex)
{
// packet can contain multiple frames
AVPacket first = packet;
int got_frame = 0;
do {
got_frame = 0;
ret = avcodec_decode_audio4(_AudioContext, &frame, &got_frame, &packet);
if (ret < 0)
{
nlwarning("FFMPEG: error decoding audio frame: %s", av_err2string(ret).c_str());
break;
}
packet.size -= ret;
packet.data += ret;
if (got_frame)
{
uint32 out_bps = av_get_bytes_per_sample(FFMPEG_SAMPLE_FORMAT) * FFMPEG_CHANNELS;
uint32 max_samples = (maximum - bytes_read) / out_bps;
uint32 out_samples = av_rescale_rnd(swr_get_delay(_SwrContext, _AudioContext->sample_rate) + frame.nb_samples,
FFMPEG_SAMPLE_RATE, _AudioContext->sample_rate, AV_ROUND_UP);
if (max_samples > out_samples)
max_samples = out_samples;
uint32 converted = swr_convert(_SwrContext, &buffer, max_samples, (const uint8 **)frame.extended_data, frame.nb_samples);
uint32 size = out_bps * converted;
bytes_read += size;
buffer += size;
av_frame_unref(&frame);
}
} while (got_frame && packet.size > 0);
av_packet_unref(&first);
}
else
{
ret = 0;
av_packet_unref(&packet);
}
}
return bytes_read;
}
uint8 CAudioDecoderFfmpeg::getChannels()
{
return FFMPEG_CHANNELS;
}
uint CAudioDecoderFfmpeg::getSamplesPerSec()
{
return FFMPEG_SAMPLE_RATE;
}
uint8 CAudioDecoderFfmpeg::getBitsPerSample()
{
return FFMPEG_BITS_PER_SAMPLE;
}
bool CAudioDecoderFfmpeg::isMusicEnded()
{
return _IsMusicEnded;
}
float CAudioDecoderFfmpeg::getLength()
{
float length = 0.f;
if (_FormatContext->duration != AV_NOPTS_VALUE)
{
length = _FormatContext->duration * av_q2d(AV_TIME_BASE_Q);
}
else if (_FormatContext->streams[_AudioStreamIndex]->duration != AV_NOPTS_VALUE)
{
length = _FormatContext->streams[_AudioStreamIndex]->duration * av_q2d(_FormatContext->streams[_AudioStreamIndex]->time_base);
}
return length;
}
void CAudioDecoderFfmpeg::setLooping(bool loop)
{
_Loop = loop;
}
} /* namespace NLSOUND */
/* end of file */

View file

@ -0,0 +1,224 @@
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2018 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdsound.h"
#include <nel/sound/audio_decoder_mp3.h>
#define DR_MP3_IMPLEMENTATION
#include <nel/sound/decoder/dr_mp3.h>
using namespace std;
using namespace NLMISC;
using namespace NLSOUND;
namespace NLSOUND {
// callback for drmp3
static size_t drmp3_read(void* pUserData, void* pBufferOut, size_t bytesToRead)
{
NLSOUND::CAudioDecoderMP3 *decoder = static_cast<NLSOUND::CAudioDecoderMP3 *>(pUserData);
NLMISC::IStream *stream = decoder->getStream();
nlassert(stream->isReading());
uint32 available = decoder->getStreamSize() - stream->getPos();
if (available == 0)
return 0;
if (bytesToRead > available)
bytesToRead = available;
stream->serialBuffer((uint8 *)pBufferOut, bytesToRead);
return bytesToRead;
}
// callback for drmp3
static drmp3_bool32 drmp3_seek(void* pUserData, int offset, drmp3_seek_origin origin)
{
NLSOUND::CAudioDecoderMP3 *decoder = static_cast<NLSOUND::CAudioDecoderMP3 *>(pUserData);
NLMISC::IStream *stream = decoder->getStream();
nlassert(stream->isReading());
NLMISC::IStream::TSeekOrigin seekOrigin;
if (origin == drmp3_seek_origin_start)
seekOrigin = NLMISC::IStream::begin;
else if (origin == drmp3_seek_origin_current)
seekOrigin = NLMISC::IStream::current;
else
return false;
stream->seek((sint32) offset, seekOrigin);
return true;
}
// these should always be 44100Hz/16bit/2ch
#define MP3_SAMPLE_RATE 44100
#define MP3_BITS_PER_SAMPLE 16
#define MP3_CHANNELS 2
CAudioDecoderMP3::CAudioDecoderMP3(NLMISC::IStream *stream, bool loop)
: IAudioDecoder(),
_Stream(stream), _Loop(loop), _IsMusicEnded(false), _StreamSize(0), _IsSupported(false), _PCMFrameCount(0)
{
_StreamOffset = stream->getPos();
stream->seek(0, NLMISC::IStream::end);
_StreamSize = stream->getPos();
stream->seek(_StreamOffset, NLMISC::IStream::begin);
drmp3_config config;
config.outputChannels = MP3_CHANNELS;
config.outputSampleRate = MP3_SAMPLE_RATE;
_IsSupported = drmp3_init(&_Decoder, &drmp3_read, &drmp3_seek, this, &config);
if (!_IsSupported)
{
nlwarning("MP3: Decoder failed to read stream");
}
}
CAudioDecoderMP3::~CAudioDecoderMP3()
{
if (_IsSupported)
{
drmp3_uninit(&_Decoder);
}
}
bool CAudioDecoderMP3::isFormatSupported() const
{
return _IsSupported;
}
/// Get information on a music file.
bool CAudioDecoderMP3::getInfo(NLMISC::IStream *stream, std::string &artist, std::string &title, float &length)
{
CAudioDecoderMP3 mp3(stream, false);
if (!mp3.isFormatSupported())
{
title.clear();
artist.clear();
length = 0.f;
return false;
}
length = mp3.getLength();
// ID3v1
stream->seek(-128, NLMISC::IStream::end);
{
uint8 buf[128];
stream->serialBuffer(buf, 128);
if(buf[0] == 'T' && buf[1] == 'A' && buf[2] == 'G')
{
uint i;
for(i = 0; i < 30; ++i) if (buf[3+i] == '\0') break;
artist.assign((char *)&buf[3], i);
for(i = 0; i < 30; ++i) if (buf[33+i] == '\0') break;
title.assign((char *)&buf[33], i);
}
}
return true;
}
uint32 CAudioDecoderMP3::getRequiredBytes()
{
return 0; // no minimum requirement of bytes to buffer out
}
uint32 CAudioDecoderMP3::getNextBytes(uint8 *buffer, uint32 minimum, uint32 maximum)
{
if (_IsMusicEnded) return 0;
nlassert(minimum <= maximum); // can't have this..
// TODO: CStreamFileSource::play() will stall when there is no frames on warmup
// supported can be set false if there is an issue creating converter
if (!_IsSupported)
{
_IsMusicEnded = true;
return 1;
}
sint16 *pFrameBufferOut = (sint16 *)buffer;
uint32 bytesPerFrame = MP3_BITS_PER_SAMPLE / 8 * _Decoder.channels;
uint32 totalFramesRead = 0;
uint32 framesToRead = minimum / bytesPerFrame;
while(framesToRead > 0)
{
float tempBuffer[4096];
uint64 tempFrames = drmp3_countof(tempBuffer) / _Decoder.channels;
if (tempFrames > framesToRead)
tempFrames = framesToRead;
tempFrames = drmp3_read_pcm_frames_f32(&_Decoder, tempFrames, tempBuffer);
if (tempFrames == 0)
break;
drmp3dec_f32_to_s16(tempBuffer, pFrameBufferOut, tempFrames * _Decoder.channels);
pFrameBufferOut += tempFrames * _Decoder.channels;
framesToRead -= tempFrames;
totalFramesRead += tempFrames;
}
_IsMusicEnded = (framesToRead > 0);
return totalFramesRead * bytesPerFrame;
}
uint8 CAudioDecoderMP3::getChannels()
{
return _Decoder.channels;
}
uint CAudioDecoderMP3::getSamplesPerSec()
{
return _Decoder.sampleRate;
}
uint8 CAudioDecoderMP3::getBitsPerSample()
{
return MP3_BITS_PER_SAMPLE;
}
bool CAudioDecoderMP3::isMusicEnded()
{
return _IsMusicEnded;
}
float CAudioDecoderMP3::getLength()
{
// cached because drmp3_get_pcm_frame_count is reading full file
if (_PCMFrameCount == 0)
{
_PCMFrameCount = drmp3_get_pcm_frame_count(&_Decoder);
}
return _PCMFrameCount / (float) _Decoder.sampleRate;
}
void CAudioDecoderMP3::setLooping(bool loop)
{
_Loop = loop;
}
} /* namespace NLSOUND */
/* end of file */

View file

@ -2759,7 +2759,7 @@ void CAudioMixerUser::setEventMusicVolume(float gain)
bool CAudioMixerUser::isEventMusicEnded()
{
if (_MusicChannelFaders[EventMusicChannel].isInitOk())
_MusicChannelFaders[EventMusicChannel].isEnded();
return _MusicChannelFaders[EventMusicChannel].isEnded();
return true;
}

View file

@ -704,7 +704,9 @@ void CComplexSource::checkup()
for (; first != last; ++first)
{
USource *source = *first;
if (source != NULL && source->getSound()->getLooping() && !source->isPlaying())
if (source == NULL)
continue;
if (source->getSound()->getLooping() && !source->isPlaying())
source->play();
if (source->getSound()->getSoundType() != CSound::SOUND_SIMPLE)
static_cast<CSourceCommon*>(source)->checkup();

View file

@ -121,6 +121,11 @@ void CSourceAL::setStreaming(bool streaming)
alTestError();
_Buffer = NULL;
_IsStreaming = streaming;
if (_IsStreaming)
{
// make sure looping is disabled on OpenAL side
setLooping(false);
}
}
/* Set the buffer that will be played (no streaming)
@ -194,7 +199,11 @@ uint CSourceAL::countStreamingBuffers() const
// a bit ugly here, but makes a much easier/simpler implementation on both drivers
ALint buffersProcessed;
alGetSourcei(_Source, AL_BUFFERS_PROCESSED, &buffersProcessed);
while (buffersProcessed)
if (buffersProcessed && _QueuedBuffers.empty())
{
nlwarning("AL: QueuedBuffers is empty, but OpenAL buffers processed > 0");
}
while (buffersProcessed && !_QueuedBuffers.empty())
{
ALuint bufferName = _QueuedBuffers.front()->bufferName();
alSourceUnqueueBuffers(_Source, 1, &bufferName);

View file

@ -82,15 +82,16 @@ void CSampleBankManager::init(NLGEORGES::UFormElm *mixerConfig)
TFilteredBank fb;
std::string bankName;
NLGEORGES::UFormElm *realBank = NULL;
realBank->getArrayNode(&realBank, j);
realBanks->getArrayNode(&realBank, j);
if (realBank != 0)
{
realBank->getValueByName(bankName, ".SampleBank");
fb.BankName = CStringMapper::map(bankName);
realBank->getValueByName(fb.Filter, ".Filter");
vfb.push_back(fb);
}
}
}
if (!vfb.empty())
{

View file

@ -45,7 +45,7 @@ using namespace std;
namespace NLSOUND {
CStreamFileSource::CStreamFileSource(CStreamFileSound *streamFileSound, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster, CGroupController *groupController)
: CStreamSource(streamFileSound, spawn, cb, cbUserParam, cluster, groupController), m_AudioDecoder(NULL), m_Paused(false)
: CStreamSource(streamFileSound, spawn, cb, cbUserParam, cluster, groupController), m_AudioDecoder(NULL), m_Paused(false), m_DecodingEnded(false)
{
m_Thread = NLMISC::IThread::create(this);
}
@ -244,7 +244,7 @@ void CStreamFileSource::resume()
bool CStreamFileSource::isEnded()
{
return (!m_Thread->isRunning() && !_Playing && !m_WaitingForPlay && !m_Paused);
return m_DecodingEnded || (!m_Thread->isRunning() && !_Playing && !m_WaitingForPlay && !m_Paused);
}
float CStreamFileSource::getLength()
@ -319,6 +319,7 @@ void CStreamFileSource::run()
this->getRecommendedBufferSize(samples, bytes);
uint32 recSleep = 40;
uint32 doSleep = 10;
m_DecodingEnded = false;
while (_Playing || m_WaitingForPlay)
{
if (!m_AudioDecoder->isMusicEnded())
@ -369,6 +370,9 @@ void CStreamFileSource::run()
{
delete m_AudioDecoder;
m_AudioDecoder = NULL;
// _Playing cannot be used to detect play state because its required in cleanup
// Using m_AudioDecoder in isEnded() may result race condition (decoder is only created after thread is started)
m_DecodingEnded = !m_WaitingForPlay;
}
// drop buffers
m_FreeBuffers = 3;

View file

@ -92,7 +92,7 @@ void CStreamSource::releasePhysicalSource()
// free the track
pSource->stop();
pSource->setStreaming(false);
mixer->freeTrack(m_Track);
if (mixer) mixer->freeTrack(m_Track);
m_Track = NULL;
}
}

View file

@ -214,6 +214,7 @@ int main(int argc, char **argv)
args.addArg("x", "extract", "", "Extract all interface elements from <output_filename> to <input_path>.");
args.addAdditionalArg("output_filename", "PNG or TGA file to generate", true);
args.addAdditionalArg("input_path", "Path that containts interfaces elements", false);
args.addArg("", "no-border", "", "Disable border duplication. Enabled by default");
if (!args.parse(argc, argv)) return 1;
@ -227,6 +228,13 @@ int main(int argc, char **argv)
existingUVfilename = args.getArg("s").front();
}
//
uint borderSize = 1;
if (args.haveLongArg("no-border"))
{
borderSize = 0;
}
// extract all interface elements
bool extractElements = args.haveArg("x");
@ -407,6 +415,27 @@ int main(int argc, char **argv)
pBtmp->convertToType(CBitmap::RGBA);
}
// duplicate icon border
if (borderSize > 0)
{
NLMISC::CBitmap *tmp = new NLMISC::CBitmap;
tmp->resize(pBtmp->getWidth(), pBtmp->getHeight());
tmp->blit(pBtmp, 0, 0);
// corners
tmp->resample(tmp->getWidth() + borderSize * 2, tmp->getHeight() + borderSize * 2);
// top, bottom
tmp->blit(pBtmp, borderSize, 0);
tmp->blit(pBtmp, borderSize, borderSize*2);
// left, right
tmp->blit(pBtmp, 0, borderSize);
tmp->blit(pBtmp, borderSize*2, borderSize);
// center
tmp->blit(pBtmp, borderSize, borderSize);
delete pBtmp;
pBtmp = tmp;
}
AllMaps[i] = pBtmp;
}
catch (const NLMISC::Exception &e)
@ -461,10 +490,10 @@ int main(int argc, char **argv)
putIn (AllMaps[i], &GlobalTexture, x, y);
putIn (AllMaps[i], &GlobalMask, x, y, false);
UVMin[i].U = (float)x;
UVMin[i].V = (float)y;
UVMax[i].U = (float)x+AllMaps[i]->getWidth();
UVMax[i].V = (float)y+AllMaps[i]->getHeight();
UVMin[i].U = (float)x + borderSize;
UVMin[i].V = (float)y + borderSize;
UVMax[i].U = (float)x + AllMaps[i]->getWidth() - borderSize;
UVMax[i].V = (float)y + AllMaps[i]->getHeight() - borderSize;
#if 0
// Do not remove this is useful for debugging

View file

@ -41,20 +41,19 @@ Gamma_max = 1.0;
Application = { "Lirria", "./client_ryzom_r.exe", "./" };
BackgroundDownloader = 0;
PatchWanted = 0;
PatchWanted = 1;
PatchUrl = "http://lirria.khaganat.net:43435";
RingReleaseNotePath = "http://lirria.khaganat.net/patch/index.php";
SignUpURL = "";
StartupHost = "lirria.khaganat.net:40916";
StartupPage = "/login/r2_login.php";
InstallStatsUrl = "https://lirria.khaganat.net:50000/stats/stats.php";
CreateAccountURL = "https://lirria.khaganat.net/ams/?page=register";
EditAccountURL = "https://lirria.khaganat.net/";
ForgetPwdURL = "https://lirria.khaganat.net/ams/index.php?page=forgot_password";
//FreeTrialURL = "https://lirria.khaganat.net/";
LoginSupportURL = "https://www.khaganat.net/irc";
ConditionsTermsURL = "khaganat.net/wikhan/fr:charte";
InstallStatsUrl = "http://lirria.khaganat.net:50000/stats/stats.php";
CreateAccountURL = "http://lirria.khaganat.net/ams/?page=register";
EditAccountURL = "http://lirria.khaganat.net/ams/?page=settings";
ConditionsTermsURL = "http://www.gnu.org/licenses/agpl-3.0.html";
ForgetPwdURL = "http://lirria.khaganat.net/ams/?page=forgot_password";
LoginSupportURL = "https://lirria.khaganat.net/";
InstallWebPage = "";
// Full path and filename where cURL can find certificate bundle file
// cacert.pem file can be downloaded from https://curl.haxx.se/docs/caextract.html
@ -67,7 +66,7 @@ ConditionsTermsURL = "khaganat.net/wikhan/fr:charte";
////////////////
// the language to use as in ISO 639-2
LanguageCode = "fr"; // english
LanguageCode = "en"; // english
XMLInputFile = "input_config_v3.xml";
@ -236,7 +235,7 @@ FXAA_ps3 = 1;
AnisotropicFilter = 0;
Bloom = 1;
Bloom = 0;
Bloom_ps0 = 0;
Bloom_ps1 = 1;
Bloom_ps2 = 1;
@ -325,12 +324,22 @@ CameraDistance = 3.0; // Camera Distance(in meter) from the user (for the Third
CameraDistStep = 1.0;
CameraDistMin = 1.0;
CameraDistMax = 25.0;
CameraDistMax = 250.0;
CameraAccel = 5.0;
CameraSpeedMin = 2.0;
CameraSpeedMax = 100.0;
CameraResetSpeed = 10.0; // Speed in radian/s
// Values for UI Scale
InterfaceScale = 1.0;
InterfaceScale_min = 0.8;
InterfaceScale_max = 2.0;
InterfaceScale_step = 0.05;
// Enable biliner filtering for UI textures
// Texture atlas needs to be generated with border duplication
// or there will be visible texture bleeding
BilinearUI = 1;
// Default values for map
MaxMapScale = 2.0;
R2EDMaxMapScale = 8.0;
@ -440,7 +449,7 @@ PrintfCommandsFreeTrial = {
DisplayMissingAnimFile = 0;
LoadingStringCount = 54;
LoadingStringCount = 0;
// Some R2 parameters ...
@ -573,7 +582,7 @@ ScenarioSavePath = "./my_scenarios/";
// note : we add a dot in the name to be sure that there cannot be a conflict with character keyset name
BuiltInKeySets =
{
"", // default khanat keyboard layout
"", // default ryzom keyboard layout
"bi.zqsd", // european keyboard fps displacement style (NB : don't change this layout name, ryzom will automatically select it if keyboard is french or belgian)
"bi.wasd", // english keyboard fps displacement style (NB : don't change this layout name, ryzom will automatically select it if keyboard is not french nor belgian)
"bi.wow_alike" // 'world of warcraft' like keyboard style. (NB : not available for ring)
@ -587,19 +596,21 @@ ScenarioLanguages = {"fr","de","en","other_lang"};
// Map each language to a forum help page
HelpPages =
{
"fr=https://khaganat.net/wikhan/fr:error",
"en=https://khaganat.net/wikhan/fr:error",
"wk=https://khaganat.net/wikhan/fr:error",
"de=https://khaganat.net/wikhan/fr:error",
"es=https://khaganat.net/wikhan/fr:error",
"ru=https://khaganat.net/wikhan/fr:error"
"fr=http://forums.khaganat.net/",
"en=http://forums.lirria.khaganat.net",
"wk=http://forums.lirria.khaganat.net",
"de=http://forums.lirria.khaganat.net",
"es=http://forums.lirria.khaganat.net",
"ru=http://forums.lirria.khaganat.net"
};
// interval in minutes for webig notify thread to run
WebIgNotifInterval = 10;
WebIgMainDomain = "app.lirria.khaganat.net";
WebIgTrustedDomains = {
"api.lirria.khaganat.net", "app.lirria.khaganat.net", "lirria.khaganat.net"
"api.lirria.khaganat.net", "app.lirria.khaganat.net"
};
//PatchletUrl = "";
SelectedSlot = 0;

View file

@ -78,17 +78,11 @@
<command name="self" action="self_target" params="" />
<command name="target" action="target" params="entity=$" />
<command name="tar" action="target" params="entity=$" />
<command name="target" action="target" params="entity=+" ctrlchar="false" />
<command name="tar" action="target" params="entity=+" ctrlchar="false" />
<command name="target_quiet" action="target" params="entity=$|quiet=true" />
<command name="tarq" action="target" params="entity=$|quiet=true" />
<command name="target" action="target" params="entity=$|prefer_complete_match=$" />
<command name="tar" action="target" params="entity=$|prefer_complete_match=$" />
<command name="target_quiet" action="target" params="entity=$|prefer_complete_match=$|quiet=true" />
<command name="tarq" action="target" params="entity=$|prefer_complete_match=$|quiet=true" />
<command name="target_quiet" action="target" params="entity=+|quiet=true" ctrlchar="false" />
<command name="tarq" action="target" params="entity=+|quiet=true" ctrlchar="false" />
<command name="target" action="no_target" params="" />
<command name="tar" action="no_target" params="" />
@ -122,6 +116,8 @@
<command name="loot" action="inv_temp_all" params="" />
<command name="setuiscale" action="set_ui_scale" params="scale=$"/>
<command name="mapsearch" action="proc" params="map_search_show_set|+" />
<command name="mapsearch" action="proc" params="map_search_show" />

View file

@ -11,6 +11,15 @@
<variable entry="UI:SAVE:MP3_VOLUME"
type="sint64"
value="255" />
<variable entry="UI:SAVE:MP3_REPEAT"
type="sint32"
value="1" />
<variable entry="UI:SAVE:MP3_SHUFFLE"
type="sint32"
value="0" />
<variable entry="UI:TEMP:MP3_PLAYING"
type="sint32"
value="0" />
<!--******************-->
<!--* MP3 PLAYER *-->
<!--******************-->
@ -33,11 +42,28 @@
global_color_normal="true" global_color_pushed="true" global_color_over="true"
ondblclick_l="music_player" params_dblclick_l="song=#index" />
<view type="bitmap" id="bg" posref="MM MM" sizeref="wh" color="200 200 200 50" texture="blank.tga" scale="true" active="false" />
<view type="text" id="title" posref="ML ML" x="0" line_maxw="#line_maxw" fontsize="8" shadow="true" multi_line="true" multi_line_space="0" />
<view type="text" id="duration" posref="MR MR" x="0" fontsize="8" shadow="true" color="255 255 255 128" />
<view type="text" id="duration" posref="MR MR" x="0" fontsize="8" shadow="true" color="255 255 255 150" />
</group>
</template>
<proc id="proc_mp3_toggle_shuffle">
<action handler="set" params="dblink=UI:SAVE:MP3_SHUFFLE|value=not(@UI:SAVE:MP3_SHUFFLE)" />
<action handler="music_player" params="update_playlist" />
</proc>
<proc id="proc_mp3_update_tooltip">
<action handler="lua" params="getUI('ui:interface:mp3_player:controls:but_play').tooltip = i18n.get('uiMP3Play')" cond="eq(@UI:TEMP:MP3_PLAYING,0)" />
<action handler="lua" params="getUI('ui:interface:mp3_player:controls:but_play').tooltip = i18n.get('uiMP3Pause')" cond="eq(@UI:TEMP:MP3_PLAYING,1)" />
</proc>
<proc id="proc_mp3_but_play">
<action handler="set" params="dblink=UI:TEMP:MP3_PLAYING|value=not(@UI:TEMP:MP3_PLAYING)" />
<action handler="music_player" params="play" cond="eq(@UI:TEMP:MP3_PLAYING,1)" />
<action handler="music_player" params="pause" cond="eq(@UI:TEMP:MP3_PLAYING,0)" />
</proc>
<group type="modal"
id="playlist"
posparent="mp3_player"
@ -82,6 +108,30 @@
hardtext="uiPlaylistRefresh"
onclick_l="music_player"
params_l="play_songs" />
<ctrl style="text_button_header"
button_type="toggle_button"
id="shuffle"
posparent="refresh"
posref="ML MR"
x="-4"
y="0"
hardtext="S"
onclick_l="proc"
params_l="proc_mp3_toggle_shuffle" />
<ctrl style="text_button_header"
button_type="toggle_button"
id="repeat"
posparent="shuffle"
posref="ML MR"
x="-4"
y="0"
hardtext="R"
onclick_l="set"
params_l="dblink=UI:SAVE:MP3_REPEAT|value=not(@UI:SAVE:MP3_REPEAT)" />
<!-- change button state when variable chaanged -->
<link expr="@UI:SAVE:MP3_REPEAT" target="repeat:pushed" />
<link expr="@UI:SAVE:MP3_SHUFFLE" target="shuffle:pushed" />
<group id="content" x="8" y="-24" over="true" sizeref="w" posref="TL TL" child_resize_h="true" child_resize_hmargin="4">
<group id="songs" x="10" y="0" sizeref="w" posref="TL TL" child_resize_h="true" max_h="215">
@ -167,26 +217,30 @@
tooltip="uiMP3Prev" />
<ctrl type="button"
id="but_play"
button_type="push_button"
button_type="toggle_button"
posref="MM MM"
posparent="slot2"
tx_normal="mp3_button_play.tga"
tx_pushed="mp3_button_play.tga"
tx_pushed="mp3_button_pause.tga"
tx_over="mp3_button_over.tga"
onclick_l="music_player"
params_l="play"
onclick_l="proc"
params_l="proc_mp3_but_play"
tooltip="uiMP3Play" />
<!-- TODO: but_stop is using placeholder texture -->
<ctrl type="button"
id="but_pause"
id="but_stop"
button_type="push_button"
posref="MM MM"
posparent="slot3"
tx_normal="mp3_button_pause.tga"
tx_pushed="mp3_button_pause.tga"
tx_normal="w_button_10_choice.tga"
tx_pushed="w_button_10_choice.tga"
tx_over="mp3_button_over.tga"
scale="true"
w="20"
h="16"
onclick_l="music_player"
params_l="pause"
tooltip="uiMP3Pause" />
params_l="stop"
tooltip="uiMP3Stop" />
<ctrl type="button"
id="but_next"
button_type="push_button"
@ -276,6 +330,8 @@
posref="TL TL"
w="48"
h="20" />
<link expr="@UI:TEMP:MP3_PLAYING" target="but_play:pushed" action="proc" params="proc_mp3_update_tooltip" />
</group>
<!-- SCREEN -->
<group id="screen"

View file

@ -876,10 +876,17 @@
posparent="lum"
x="0"
y="-2" />
<instance template="tgcw_scrollbarfloat"
id="scale"
text="uiInterfaceScale"
posref="BL TL"
posparent="gam"
x="0"
y="-2" />
<instance template="tgcw_checkbox"
id="waitvbl"
text="uiWaitVBL"
posparent="gam"
posparent="scale"
posref="BL TL"
x="-20"
y="-28" />
@ -3091,6 +3098,13 @@
realtime="true"
widget="sbfloat"
link="Gamma" />
<param ui="general:scale:c"
type="cfg"
realtime="false"
ui_view="general:scale:c_res"
ui_decimal="2"
widget="sbfloat"
link="InterfaceScale" />
<param ui="general:waitvbl:c"
type="cfg"
realtime="true"

View file

@ -1042,7 +1042,9 @@
h="24"
posref="TL TL"
x="0"
y="-4">
y="-4"
max_w="0"
max_sizeref="w">
<ctrl style="tab_button_new"
id="tab0"
x="0"
@ -1183,6 +1185,22 @@
texture="blank.tga"
color="166 166 166 255" />
</group>
<!-- scroll target element will be set from c++ code: it does not exist on template build time -->
<ctrl type="scroll"
id="channel_scroll"
vertical="false"
align="L"
y="-1"
h="4"
scale="true"
posref="BL TL"
posparent="header_opened"
tx_bottomleft="skin_scroll_l.tga"
tx_middle="skin_scroll_h.tga"
tx_topright="skin_scroll_r.tga"
target="" />
<group id="content"
x="0"
y="0"
@ -3155,4 +3173,13 @@
params="game:chatUrlBrowse()" />
</group>
<group type="menu"
id="chat_copy_action_menu"
extends="base_menu">
<action id="copy"
name="uiCopy"
handler="copy_chat"
params="from_modal" />
</group>
</interface_config>

View file

@ -0,0 +1,42 @@
--
-- custom maps
--
if (game==nil) then
game= {};
end
-- alternative textures for maps
game.mapTextures = {}
-- game.mapTextures["zorai_map.tga"] = "tryker_map.tga"
-- register alternative texture for map
function game:setAltMap(mapName, altMap)
self.mapTextures[mapName] = altMap
end
-- remove alternative map texture
function game:removeAltMap(mapName)
self.mapTextures[mapName] = nil
end
-- map = getUI("ui:interface:map:content:map_content:actual_map")
function game:onLoadMap(map)
-- debugInfo("onLoadMap(id=".. map.id ..", texture=".. map.texture ..")");
-- if alt view not enabled
if getDbProp("UI:VARIABLES:SHOW_ALT_MAP") == 0 or map:isIsland() then
return
end
local texture = map.texture
if self.mapTextures[texture] ~= nil then
-- debugInfo("-- using ".. self.mapTextures[texture] .." for " .. texture)
return self.mapTextures[texture]
end
end
-- register map overrride
-- game:setAltMap("fyros_map.tga", "fyros_map_sp.tga")

View file

@ -2,6 +2,10 @@
<root id="interface" x="0" y="0" w="800" h="600" active="true" />
<lua file="map.lua" />
<!-- flag for game:onLoadMap() handler to override map texture or not -->
<variable entry="UI:VARIABLES:SHOW_ALT_MAP" type="bool" value="0" />
<!-- base menu of landmark -->
<group type="menu" id="land_mark_menu" extends="base_menu">

View file

@ -2625,6 +2625,7 @@
<!-- * EDIT BOX WIDGET * -->
<!-- ********************* -->
<template name="edit_box_widget"
active="true"
posref="TL TL"
text_x="0"
text_y="0"
@ -2669,8 +2670,10 @@
max_float_prec="5"
tooltip=""
tooltip_parent=""
negative_filter="">
negative_filter=""
render_layer="0">
<group id="#id"
active="#active"
posref="#posref"
x="#x"
y="#y"
@ -2679,7 +2682,8 @@
child_resize_hmargin="#child_resize_hmargin"
sizeref="#sizeref"
w="#w"
h="#h">
h="#h"
render_layer="#render_layer">
<group type="edit_box"
sizeref="w"
w="-8"
@ -2708,7 +2712,8 @@
max_float_prec="#max_float_prec"
tooltip="#tooltip"
tooltip_parent="#tooltip_parent"
negative_filter="#negative_filter">
negative_filter="#negative_filter"
render_layer="#render_layer">
<view type="bitmap"
id="bg"
scale="true"
@ -2716,7 +2721,8 @@
h="0"
w="0"
texture="#bg_texture"
inherit_gc_alpha="true" />
inherit_gc_alpha="true"
render_layer="#render_layer" />
<view id="edit_text"
type="text"
continuous_update="#continuous_text_update"
@ -2733,7 +2739,8 @@
shadow_color="#shadow_color"
shadow_outline="#shadow_outline"
hardtext=""
global_color="false" />
global_color="false"
render_layer="#render_layer" />
</group>
<!-- border around the list -->
<view type="bitmap"
@ -2741,25 +2748,29 @@
texture="W_box_top_left.tga"
posparent="eb"
posref="TL BR"
inherit_gc_alpha="true" />
inherit_gc_alpha="true"
render_layer="#render_layer" />
<view type="bitmap"
id="trb"
texture="W_box_top_right.tga"
posparent="eb"
posref="TR BL"
inherit_gc_alpha="true" />
inherit_gc_alpha="true"
render_layer="#render_layer" />
<view type="bitmap"
id="brb"
texture="W_box_bot_right.tga"
posparent="eb"
posref="BR TL"
inherit_gc_alpha="true" />
inherit_gc_alpha="true"
render_layer="#render_layer" />
<view type="bitmap"
id="blb"
texture="W_box_bot_left.tga"
posparent="eb"
posref="BL TR"
inherit_gc_alpha="true" />
inherit_gc_alpha="true"
render_layer="#render_layer" />
<view type="bitmap"
id="tb"
texture="W_box_top.tga"
@ -2769,7 +2780,8 @@
sizeref="w"
w="0"
h="4"
inherit_gc_alpha="true" />
inherit_gc_alpha="true"
render_layer="#render_layer" />
<view type="bitmap"
id="bb"
texture="W_box_bot.tga"
@ -2779,7 +2791,8 @@
sizeref="w"
w="0"
h="4"
inherit_gc_alpha="true" />
inherit_gc_alpha="true"
render_layer="#render_layer" />
<view type="bitmap"
id="lb"
texture="W_box_left.tga"
@ -2789,7 +2802,8 @@
sizeref="h"
h="0"
w="4"
inherit_gc_alpha="true" />
inherit_gc_alpha="true"
render_layer="#render_layer" />
<view type="bitmap"
id="rb"
texture="W_box_right.tga"
@ -2799,7 +2813,8 @@
sizeref="h"
h="0"
w="4"
inherit_gc_alpha="true" />
inherit_gc_alpha="true"
render_layer="#render_layer" />
</group>
</template>
<!-- ******************************* -->
@ -6337,6 +6352,7 @@
x="0"
y="0"
posref="BL BL"
posparent=""
dblink=""
texture=""
tooltip=""
@ -6345,6 +6361,7 @@
id="but_#id"
button_type="toggle_button"
posref="#posref"
posparent="but_#posparent"
x="#x"
y="#y"
tx_normal="w_button_filter_off.tga"
@ -6371,6 +6388,7 @@
x="0"
y="0"
posref="BL BL"
posparent=""
dblink=""
texture=""
tooltip=""
@ -6379,6 +6397,7 @@
id="but_#id"
button_type="toggle_button"
posref="#posref"
posparent="but_#posparent"
x="#x"
y="#y"
tx_normal="w_button_filter_off.tga"
@ -6553,83 +6572,109 @@
texture="W_line_hor.tga" />
<link expr="eq(@UI:SAVE:#inv_type:ICON_LIST, 0)"
target="bag_list:active,sep2:active" />
<!-- details -->
<instance template="tinv_item_list_icon_swap"
id="detail"
x="-2"
y="10"
posref="BR MR"
dblink="UI:SAVE:#inv_type:ICON_LIST"
texture="details_on.tga"
tooltip="uittDetail" />
<link expr="ifthenelse(@UI:SAVE:#inv_type:ICON_LIST, 'details_off.tga', 'details_on.tga')"
target="bit_detail:texture" />
<!-- filter -->
<instance template="tinv_item_list_filter"
id="filter_tp"
x="-35"
y="1"
posref="BR BR"
x="-8"
posref="ML MR"
posparent="detail"
inv_type="#inv_type"
dblink="UI:SAVE:#inv_type:FILTER_TP"
texture="filter_tp.tga"
tooltip="uittFilterTP" />
<instance template="tinv_item_list_filter"
id="filter_missmp"
x="-60"
y="1"
posref="BR BR"
x="-2"
posref="ML MR"
posparent="filter_tp"
inv_type="#inv_type"
dblink="UI:SAVE:#inv_type:FILTER_MISSMP"
texture="filter_mission.tga"
tooltip="uittFilterMissMP" />
<instance template="tinv_item_list_filter"
id="filter_mp"
x="-85"
y="1"
posref="BR BR"
x="-2"
posref="ML MR"
posparent="filter_missmp"
inv_type="#inv_type"
dblink="UI:SAVE:#inv_type:FILTER_MP"
texture="filter_mps.tga"
tooltip="uittFilterMP" />
<instance template="tinv_item_list_filter"
id="filter_tool"
x="-110"
y="1"
posref="BR BR"
x="-2"
posref="ML MR"
posparent="filter_mp"
inv_type="#inv_type"
dblink="UI:SAVE:#inv_type:FILTER_TOOL"
texture="filter_tools.tga"
tooltip="uittFilterTool" />
<instance template="tinv_item_list_filter"
id="filter_weapon"
x="-135"
y="1"
posref="BR BR"
x="-2"
posref="ML MR"
posparent="filter_tool"
inv_type="#inv_type"
dblink="UI:SAVE:#inv_type:FILTER_WEAPON"
texture="filter_weapon.tga"
tooltip="uittFilterWeapon" />
<instance template="tinv_item_list_filter"
id="filter_armor"
x="-160"
y="1"
posref="BR BR"
x="-2"
posref="ML MR"
posparent="filter_weapon"
inv_type="#inv_type"
dblink="UI:SAVE:#inv_type:FILTER_ARMOR"
texture="filter_armor.tga"
tooltip="uittFilterArmor" />
<!-- search -->
<ctrl type="button"
id="but_inv_search"
button_type="toggle_button"
x="-2"
posref="ML MR"
posparent="but_filter_armor"
tx_normal="w_button_filter_off.tga"
tx_pushed="w_button_filter_on.tga"
tx_over=""
tooltip="uittInventorySearch"
onclick_l="inv_search_button"
params_l="inv_query_eb" />
<view type="bitmap"
id="bit_inv_search"
posref="MM MM"
posparent="but_inv_search"
texture="w_help_1.tga"
global_color="false"
color="255 255 255 150" />
<instance template="edit_box_widget"
id="inv_query_eb"
posref="BR BR"
x="-190"
y="1"
active="false"
posref="TR BR"
posparent="but_detail"
x="0"
y="8"
w="100"
render_layer="9"
clear_on_escape="true"
enter_recover_focus="false"
max_num_chars="20"
max_historic="0"
onenter="inv_set_search"
onchange="inv_set_search" onchange_params="#inv_type" />
<!-- details -->
<instance template="tinv_item_list_icon_swap"
id="detail"
x="-4"
y="1"
posref="BR BR"
dblink="UI:SAVE:#inv_type:ICON_LIST"
texture="details_on.tga"
tooltip="uittDetail" />
<link expr="ifthenelse(@UI:SAVE:#inv_type:ICON_LIST, 'details_off.tga', 'details_on.tga')"
target="bit_detail:texture" />
onchange="inv_set_search"
on_focus_lost="inv_search_unfocus"
on_focus_lost_params="but_inv_search" />
</group>
</template>
<!-- Link to trig brick action message. don't play when 0 (avoid flying text at startup) -->

View file

@ -300,6 +300,12 @@ CClientConfig::CClientConfig()
Luminosity = 0.f; // Default Monitor Luminosity.
Gamma = 0.f; // Default Monitor Gamma.
InterfaceScale = 1.0f; // Resize UI
InterfaceScale_min = 0.8f;
InterfaceScale_max = 2.0f;
InterfaceScale_step = 0.05;
BilinearUI = true;
VREnable = false;
VRDisplayDevice = "Auto";
VRDisplayDeviceId = "";
@ -424,6 +430,7 @@ CClientConfig::CClientConfig()
WebIgMainDomain = "atys.ryzom.com";
WebIgTrustedDomains.push_back(WebIgMainDomain);
WebIgNotifInterval = 10; // time in minutes
CurlMaxConnections = 2;
CurlCABundle.clear();
@ -834,6 +841,13 @@ void CClientConfig::setValues()
READ_FLOAT_FV(Luminosity)
// Gamma
READ_FLOAT_FV(Gamma)
// UI scaling
READ_FLOAT_FV(InterfaceScale);
READ_FLOAT_FV(InterfaceScale_min);
READ_FLOAT_FV(InterfaceScale_max);
READ_FLOAT_FV(InterfaceScale_step);
clamp(ClientCfg.InterfaceScale, ClientCfg.InterfaceScale_min, ClientCfg.InterfaceScale_max);
READ_BOOL_FV(BilinearUI);
// 3D Driver
varPtr = ClientCfg.ConfigFile.getVarPtr ("Driver3D");
if (varPtr)
@ -1074,6 +1088,7 @@ void CClientConfig::setValues()
// WEBIG //
READ_STRING_FV(WebIgMainDomain);
READ_STRINGVECTOR_FV(WebIgTrustedDomains);
READ_INT_FV(WebIgNotifInterval);
READ_INT_FV(CurlMaxConnections);
if (ClientCfg.CurlMaxConnections < 0)
ClientCfg.CurlMaxConnections = 2;

View file

@ -46,7 +46,6 @@ using NLMISC::CVector;
using NLMISC::CRGBA;
using std::string;
//---------------------------------------------------
// CClientConfig :
// Struct to manage a config file for the client.
@ -146,6 +145,13 @@ struct CClientConfig
/// Monitor Gamma [-1 ~ 1], default 0
float Gamma;
// UI scaling
float InterfaceScale;
float InterfaceScale_min;
float InterfaceScale_max;
float InterfaceScale_step;
bool BilinearUI;
// VR
bool VREnable;
std::string VRDisplayDevice;
@ -306,6 +312,7 @@ struct CClientConfig
std::string WebIgMainDomain;
std::vector<string> WebIgTrustedDomains;
uint WebIgNotifInterval; // value in minutes for notification thread
sint32 CurlMaxConnections;
string CurlCABundle;

View file

@ -475,14 +475,17 @@ bool randomFromString(std::string const& str, sint16& val, sint16 min = -32768,
return false;
}
NLMISC_COMMAND(random, "Roll a dice and say the result around","[<min>] <max>")
NLMISC_COMMAND(random, "Roll a dice and say the result around","[<min>] <max> [h|ide]")
{
// Check parameters.
if (args.size()<1 || args.size()>2)
if (args.size() < 1 || args.size() > 3)
return false;
sint16 min = 1;
sint16 max;
bool hide = args[args.size()-1][0] == 'h';
if (!randomFromString(args[0], max))
{
CInterfaceManager *pIM = CInterfaceManager::getInstance();
@ -491,13 +494,13 @@ NLMISC_COMMAND(random, "Roll a dice and say the result around","[<min>] <max>")
pIM->displaySystemInfo(msg);
return false;
}
if (args.size()==2)
if (args.size() > 1 && args[1][0] != 'h')
{
if (!randomFromString(args[1], min))
{
CInterfaceManager *pIM = CInterfaceManager::getInstance();
ucstring msg = CI18N::get("uiRandomBadParameter");
strFindReplace(msg, "%s", args[0] );
strFindReplace(msg, "%s", args[1] );
pIM->displaySystemInfo(msg);
return false;
}
@ -506,7 +509,7 @@ NLMISC_COMMAND(random, "Roll a dice and say the result around","[<min>] <max>")
std::swap(min, max);
if (UserEntity != NULL)
UserEntity->rollDice(min, max);
UserEntity->rollDice(min, max, hide);
return true;
}

View file

@ -227,6 +227,9 @@ void connectionRestoreVideoMode ()
SetMouseCursor ();
SetMouseSpeed (ClientCfg.CursorSpeed);
SetMouseAcceleration (ClientCfg.CursorAcceleration);
// Restore user UI scaling
CViewRenderer::getInstance()->setInterfaceScale(ClientCfg.InterfaceScale);
}
@ -262,6 +265,8 @@ void setOutGameFullScreen()
InitMouseWithCursor(ClientCfg.HardwareCursor && !StereoDisplayAttached);
}
// Enable auto scaling in login window
CViewRenderer::getInstance()->setInterfaceScale(1.0f, 1024, 768);
}
@ -427,6 +432,9 @@ bool connection (const string &cookie, const string &fsaddr)
firstConnection = false;
// Restore user UI scaling
CViewRenderer::getInstance()->setInterfaceScale(ClientCfg.InterfaceScale);
// Disable inputs
Actions.enable(false);
EditActions.enable(false);
@ -558,6 +566,9 @@ bool reconnection()
InterfaceState = globalMenu();
}
// Restore user UI scaling
CViewRenderer::getInstance()->setInterfaceScale(ClientCfg.InterfaceScale);
// Disable inputs
Actions.enable(false);
EditActions.enable(false);

View file

@ -488,8 +488,177 @@ CContinent *CContinentManager::get(const std::string &contName)
return NULL;
}
void CContinentManager::serialUserLandMarks(NLMISC::IStream &f)
void CContinentManager::writeTo(xmlNodePtr node) const
{
// <landmarks continent="bagne" type="user" />
// <landmarks continent="tryker" type="user">
// <landmark type="0" x="0" y="0" title="landmark title"/>
// ...
// </landmarks>
for(TContinents::const_iterator it = _Continents.begin(); it != _Continents.end(); ++it)
{
std::string name = it->first;
xmlNodePtr contNode = xmlNewChild(node, NULL, (const xmlChar*)"landmarks", NULL);
xmlSetProp(contNode, (const xmlChar*)"continent", (const xmlChar*)name.c_str());
xmlSetProp(contNode, (const xmlChar*)"type", (const xmlChar*)"user");
if (it->second && it->second->UserLandMarks.size() > 0)
{
for(uint i = 0; i< it->second->UserLandMarks.size(); ++i)
{
const CUserLandMark& lm = it->second->UserLandMarks[i];
xmlNodePtr lmNode = xmlNewChild(contNode, NULL, (const xmlChar*)"landmark", NULL);
xmlSetProp(lmNode, (const xmlChar*)"type", (const xmlChar*)toString("%d", (uint32)lm.Type).c_str());
xmlSetProp(lmNode, (const xmlChar*)"x", (const xmlChar*)toString("%.2f", lm.Pos.x).c_str());
xmlSetProp(lmNode, (const xmlChar*)"y", (const xmlChar*)toString("%.2f", lm.Pos.y).c_str());
// sanitize ascii control chars
// libxml will encode other special chars itself
std::string title = lm.Title.toUtf8();
for(uint i = 0; i< title.size(); i++)
{
if (title[i] >= '\0' && title[i] < ' ' && title[i] != '\n' && title[i] != '\t')
{
title[i] = '?';
}
}
xmlSetProp(lmNode, (const xmlChar*)"title", (const xmlChar*)title.c_str());
}
}
}
}
void CContinentManager::readFrom(xmlNodePtr node)
{
CXMLAutoPtr prop;
// <landmarks continent="bagne" type="user">
// <landmark type="0" x="0" y="0" title="landmark title" />
// ...
// </landmarks>
std::string continent;
prop = xmlGetProp(node, (xmlChar*)"continent");
if (!prop)
{
nlwarning("Ignore landmarks group 'continent' attribute.");
return;
}
continent = (const char*)prop;
TContinents::iterator itContinent = _Continents.find(continent);
if (itContinent == _Continents.end() || !itContinent->second)
{
nlwarning("Ignore landmarks group with unknown 'continent' '%s'", continent.c_str());
return;
}
std::string lmtype;
prop = xmlGetProp(node, (xmlChar*)"type");
if (!prop)
{
nlwarning("Ignore landmarks group without 'type' attribute.");
return;
}
lmtype = toLower((const char*)prop);
if (lmtype != "user")
{
nlwarning("Ignore landmarks group with type '%s', expected 'user'.", lmtype.c_str());
return;
}
node = node->children;
while(node)
{
if (stricmp((char*)node->name, "landmark") != 0)
{
nlwarning("Ignore invalid node '%s' under landmarks group", (const char*)node->name);
node = node->next;
continue;
}
bool add = true;
CUserLandMark lm;
prop = xmlGetProp(node, (xmlChar*)"type");
if (prop)
fromString((const char*)prop, lm.Type);
else
nlwarning("Using default value for landmark type");
prop = xmlGetProp(node, (xmlChar*)"x");
if (prop)
{
fromString((const char*)prop, lm.Pos.x);
}
else
{
nlwarning("Landmark missing 'x' attribute");
add = false;
}
prop = xmlGetProp(node, (xmlChar*)"y");
if (prop)
{
fromString((const char*)prop, lm.Pos.y);
}
else
{
nlwarning("Landmark missing 'y' attribute");
add = false;
}
prop = xmlGetProp(node, (xmlChar*)"title");
if (prop)
{
lm.Title.fromUtf8((const char*)prop);
}
else
{
nlwarning("Landmark missing 'title' attribute");
add = false;
}
if (add)
{
// before adding, check for duplicate
// duplicates might be read from .icfg before .xml is read
add = true;
for(uint i = 0; i< itContinent->second->UserLandMarks.size(); ++i)
{
const CUserLandMark& test = itContinent->second->UserLandMarks[i];
uint xdiff = abs(test.Pos.x - lm.Pos.x) * 100;
uint ydiff = abs(test.Pos.y - lm.Pos.y) * 100;
if (xdiff == 0 && ydiff == 0)
{
add = false;
break;
}
}
if (add)
{
itContinent->second->UserLandMarks.push_back(lm);
}
else
{
nlwarning("Ignore landmark with duplicate pos (continent:'%s', x:%.2f, y:%.2f, type:%d, title:'%s')", continent.c_str(), lm.Pos.x, lm.Pos.y, (uint8)lm.Type, lm.Title.toUtf8().c_str());
}
}
else
{
nlwarning("Landmark not added");
}
node = node->next;
}
}
uint32 CContinentManager::serialUserLandMarks(NLMISC::IStream &f)
{
uint32 totalLandmarks = 0;
f.serialVersion(1);
if (!f.isReading())
{
@ -502,6 +671,7 @@ void CContinentManager::serialUserLandMarks(NLMISC::IStream &f)
if (it->second)
{
f.serialCont(it->second->UserLandMarks);
totalLandmarks += it->second->UserLandMarks.size();
}
else
{
@ -522,6 +692,7 @@ void CContinentManager::serialUserLandMarks(NLMISC::IStream &f)
if (it != _Continents.end() && it->second)
{
f.serialCont(it->second->UserLandMarks);
totalLandmarks += it->second->UserLandMarks.size();
}
else
{
@ -530,6 +701,8 @@ void CContinentManager::serialUserLandMarks(NLMISC::IStream &f)
}
}
}
return totalLandmarks;
}

View file

@ -109,8 +109,13 @@ public:
const std::string &getCurrentContinentSelectName();
// load / save all user landmarks in xml format
void writeTo(xmlNodePtr node) const;
void readFrom(xmlNodePtr node);
// load / saves all user landMarks
void serialUserLandMarks(NLMISC::IStream &f);
// \return number of landmarks loaded or saved
uint32 serialUserLandMarks(NLMISC::IStream &f);
// rebuild visible landmarks on current map
void updateUserLandMarks();

View file

@ -2334,7 +2334,63 @@ CEntityCL *CEntityManager::getEntityByName (uint32 stringId) const
}
//-----------------------------------------------
CEntityCL *CEntityManager::getEntityByKeywords (const std::vector<ucstring> &keywords, bool onlySelectable) const
{
if (keywords.empty()) return NULL;
std::vector<ucstring> lcKeywords;
lcKeywords.resize(keywords.size());
for(uint k = 0; k < keywords.size(); k++)
{
lcKeywords[k] = toLower(keywords[k]);
}
const NLMISC::CVectorD &userPosD = UserEntity->pos();
const uint count = (uint)_Entities.size();
uint selectedEntityId = 0;
float selectedEntityDist = FLT_MAX;
for(uint i = 0; i < count; ++i)
{
if (!_Entities[i]) continue;
if (onlySelectable && !_Entities[i]->properties().selectable()) continue;
ucstring lcName;
lcName = toLower(_Entities[i]->getDisplayName());
if (lcName.empty()) continue;
bool match = true;
for (uint k = 0; k < lcKeywords.size(); ++k)
{
if (lcName.find(lcKeywords[k]) == ucstring::npos)
{
match = false;
break;
}
}
if (match)
{
const NLMISC::CVectorD &targetPosD = _Entities[i]->pos();
float deltaX = (float) targetPosD.x - (float) userPosD.x;
float deltaY = (float) targetPosD.y - (float) userPosD.y;
float dist = (float)sqrt(deltaX * deltaX + deltaY * deltaY);
if (dist < selectedEntityDist)
{
selectedEntityDist = dist;
selectedEntityId = i;
}
}
}
if (selectedEntityDist != FLT_MAX)
return _Entities[selectedEntityId];
else
return NULL;
}
//-----------------------------------------------
CEntityCL *CEntityManager::getEntityByName (const ucstring &name, bool caseSensitive, bool complete) const
{
ucstring source = name;

View file

@ -302,6 +302,12 @@ public:
* \param complete : if true, the name must match the full name of the entity.
*/
CEntityCL *getEntityByName (const ucstring &name, bool caseSensitive, bool complete) const;
/**
* Case insensitive match against entity name. All listed keywords must match.
* \param keywords to match
* \param onlySelectable : if true, match only entity that can be selected
*/
CEntityCL *getEntityByKeywords (const std::vector<ucstring> &keywords, bool onlySelectable) const;
CEntityCL *getEntityBySheetName (const std::string &sheet) const;
/// Get an entity by dataset index. Returns NULL if the entity is not found.
CEntityCL *getEntityByCompressedIndex(TDataSetIndex compressedIndex) const;

View file

@ -1298,6 +1298,7 @@ void CFarTP::sendReady()
pIM->loadKeys();
CWidgetManager::getInstance()->hideAllWindows();
pIM->loadInterfaceConfig();
pIM->loadLandmarks();
}
else
{

View file

@ -1096,19 +1096,17 @@ void prelogInit()
UDriver::CMode mode;
bool forceWindowed1024x768 = true;
if (Driver->getCurrentScreenMode(mode))
{
// if screen mode lower than 1024x768, use same mode in fullscreen
if (mode.Width <= 1024 && mode.Height <= 768)
// use current mode if its smaller than 1024x768
// mode should be windowed already, but incase its not, use the mode as is
if (mode.Windowed && (mode.Width > 1024 && mode.Height > 768))
{
mode.Windowed = false;
forceWindowed1024x768 = false;
mode.Width = 1024;
mode.Height = 768;
}
}
if (forceWindowed1024x768)
else
{
mode.Width = 1024;
mode.Height = 768;
@ -1332,6 +1330,8 @@ void prelogInit()
CInterfaceManager::getInstance();
CViewRenderer::getInstance()->setInterfaceScale(1.0f, 1024, 768);
CViewRenderer::getInstance()->setBilinearFiltering(ClientCfg.BilinearUI);
// Yoyo: initialize NOW the InputHandler for Event filtering.
CInputHandlerManager *InputHandlerManager = CInputHandlerManager::getInstance();

View file

@ -84,9 +84,6 @@ bool InitMouseWithCursor (bool hardware)
MouseHardware = hardware;
CViewPointer::setHWMouse( hardware );
// Update mouse information
UpdateMouse ();
if (InitMouseFirstTime)
{
InitMouseFirstTime = false;
@ -129,16 +126,6 @@ bool IsMouseCursorHardware ()
return MouseHardware;
}
// *********************************************************************************
// Set the mouse mode. Call this method once per frame to update window size
void UpdateMouse ()
{
if (!Driver->isSystemCursorCaptured())
{
DownMouseButtons = 0;
}
}
// *********************************************************************************
// Use this method to toggle the mouse (freelook <- cursor)
void SetMouseFreeLook ()
@ -160,7 +147,6 @@ void SetMouseFreeLook ()
pointer->show (false);
}
}
UpdateMouse ();
}
}
@ -179,9 +165,9 @@ void SetMouseCursor (bool updatePos)
// Get the last cursor
float x = 0.5f, y = 0.5f;
// Window size
uint width = Driver->getWindowWidth();
uint height = Driver->getWindowHeight();
// Screen size
uint width, height;
CViewRenderer::getInstance()->getScreenSize(width, height);
// Update the interface pointer
CInterfaceManager *instance = CInterfaceManager::getInstance();
@ -202,7 +188,6 @@ void SetMouseCursor (bool updatePos)
}
MouseFreeLook = false;
UpdateMouse ();
// Integer coordinates
sint ix = (sint)(x*(float)width+0.5f);
@ -253,7 +238,6 @@ void SetMouseCursor (bool updatePos)
void SetMouseSpeed (float speed)
{
MouseCursorSpeed = speed;
UpdateMouse ();
}
// *********************************************************************************
@ -261,12 +245,27 @@ void SetMouseSpeed (float speed)
void SetMouseAcceleration (uint accel)
{
MouseCursorAcceleration = accel;
UpdateMouse ();
}
// *********************************************************************************
void HandleSystemCursorCapture(const CEvent &event)
{
static bool mouseCaptured = false;
// capture on first move event after button is held down or free look is activated
if (event == EventMouseMoveId && !mouseCaptured && (MouseFreeLook || DownMouseButtons != 0))
{
mouseCaptured = true;
Driver->setCapture(true);
}
// release when button is released and not in free look
if (mouseCaptured && !MouseFreeLook && DownMouseButtons == 0)
{
mouseCaptured = false;
Driver->setCapture(false);
}
if (event == EventMouseDownId)
{
CEventMouseDown &em = (CEventMouseDown &) event;
@ -279,8 +278,6 @@ void HandleSystemCursorCapture(const CEvent &event)
cursor->setPointerMiddleDown(em.Button == middleButton);
cursor->setPointerRightDown(em.Button == rightButton);
}
Driver->setCapture(true);
}
if (event == EventMouseUpId)
@ -297,7 +294,6 @@ void HandleSystemCursorCapture(const CEvent &event)
cursor->setPointerMiddleDown(false);
cursor->setPointerRightDown(false);
}
Driver->setCapture(false);
}
}

View file

@ -44,9 +44,6 @@ bool InitMouseWithCursor (bool hardware);
// Is mouse cursor hardware ?
bool IsMouseCursorHardware ();
// Set the mouse mode. Call this method once per frame to update window size
void UpdateMouse ();
// Use this method to toggle the mouse (freelook <- cursor)
void SetMouseFreeLook ();

View file

@ -383,10 +383,10 @@ class CAHEditPreviousLine : public CAHEdit
// .. so do nothing
return;
}
sint cx, cy;
sint height;
float cx, cy;
float height;
_GroupEdit->getViewText()->getCharacterPositionFromIndex(cursorPosInText, _GroupEdit->isCursorAtPreviousLineEnd(), cx, cy, height);
cy += height + _GroupEdit->getViewText()->getMultiLineSpace();
cy += _GroupEdit->getViewText()->getLineHeight();
uint newCharIndex;
bool newLineEnd;
_GroupEdit->getViewText()->getCharacterIndexFromPosition(cx, cy, newCharIndex, newLineEnd);
@ -401,8 +401,8 @@ class CAHEditPreviousLine : public CAHEdit
}
_GroupEdit->setCursorAtPreviousLineEnd(false);
// takes character whose X is closer to the current X
sint cx0, cx1;
sint cy0, cy1;
float cx0, cx1;
float cy0, cy1;
_GroupEdit->getViewText()->getCharacterPositionFromIndex(newCharIndex, _GroupEdit->isCursorAtPreviousLineEnd(), cx0, cy0, height);
_GroupEdit->getViewText()->getCharacterPositionFromIndex(newCharIndex + 1, _GroupEdit->isCursorAtPreviousLineEnd(), cx1, cy1, height);
if (abs(cx0 - cx) < abs(cx1 - cx) || cy0 != cy1)
@ -446,8 +446,8 @@ class CAHEditNextLine : public CAHEdit
}
else if (_GroupEdit->getViewText()->getMultiLine())
{
sint cx, cy;
sint height;
float cx, cy;
float height;
_GroupEdit->getViewText()->getCharacterPositionFromIndex(_GroupEdit->getCursorPos() + (sint)_GroupEdit->getPrompt().length(), _GroupEdit->isCursorAtPreviousLineEnd(), cx, cy, height);
if (cy != 0)
{
@ -466,8 +466,8 @@ class CAHEditNextLine : public CAHEdit
}
_GroupEdit->setCursorAtPreviousLineEnd(false);
// takes character whose X is closer to the current X
sint cx0, cx1;
sint cy0, cy1;
float cx0, cx1;
float cy0, cy1;
_GroupEdit->getViewText()->getCharacterPositionFromIndex(newCharIndex, _GroupEdit->isCursorAtPreviousLineEnd(), cx0, cy0, height);
_GroupEdit->getViewText()->getCharacterPositionFromIndex(newCharIndex + 1, _GroupEdit->isCursorAtPreviousLineEnd(), cx1, cy1, height);
if (abs(cx0 - cx) < abs(cx1 - cx) || cy0 != cy1)

View file

@ -1532,6 +1532,7 @@ public:
virtual void execute (CCtrlBase * /* pCaller */, const string &Params)
{
// free with no confirm
if (!UserEntity->isBusy())
beastOrder ("free", Params, false);
}
};
@ -2415,14 +2416,35 @@ class CAHTarget : public IActionHandler
{
virtual void execute (CCtrlBase * /* pCaller */, const string &Params)
{
// Get the entity name to target
ucstring entityName;
entityName.fromUtf8 (getParam (Params, "entity"));
bool preferCompleteMatch = (getParam (Params, "prefer_complete_match") != "0");
entityName.fromUtf8(getParam(Params, "entity"));
if (entityName.empty()) return;
string completeMatch = getParam(Params, "prefer_complete_match");
bool quiet = (getParam (Params, "quiet") == "true");
if (!entityName.empty())
vector<ucstring> keywords;
NLMISC::splitUCString(entityName, ucstring(" "), keywords);
if (!keywords.empty() && keywords[0].size() > 0 && keywords[0][0] == (ucchar)'"')
{
// entity name is in quotes, do old style match with 'starts with' filter
// search for optional second parameter from old command for prefer_complete_match param
keywords.clear();
ucstring::size_type lastOf = entityName.rfind(ucstring("\""));
if (lastOf == 0)
lastOf = ucstring::npos;
// override the value only when there is no 'prefer_complete_match' parameter set
if (completeMatch.empty() && lastOf < entityName.size())
completeMatch = trim(entityName.substr(lastOf+1).toUtf8());
entityName = entityName.substr(1, lastOf-1);
}
// late check because only possible if doing 'starts-with' search
bool preferCompleteMatch = (completeMatch != "0");
CEntityCL *entity = NULL;
if (preferCompleteMatch)
{
@ -2430,10 +2452,15 @@ class CAHTarget : public IActionHandler
entity = EntitiesMngr.getEntityByName (entityName, false, true);
}
if (entity == NULL && !keywords.empty())
{
entity = EntitiesMngr.getEntityByKeywords(keywords, true);
}
if (entity == NULL)
{
// Get the entity with a partial match
entity = EntitiesMngr.getEntityByName (entityName, false, false);
// Get the entity with a partial match using 'starts with' search
entity = EntitiesMngr.getEntityByName(entityName, false, false);
}
if (entity == NULL)
@ -2442,43 +2469,13 @@ class CAHTarget : public IActionHandler
entity = EntitiesMngr.getEntityBySheetName(entityName.toUtf8());
}
if (entity)
if (entity && entity->properties().selectable() && !entity->getDisplayName().empty())
{
CCharacterCL *character = dynamic_cast<CCharacterCL*>(entity);
if (character != NULL)
{
if(character->isSelectableBySpace())
{
nldebug("isSelectableBySpace");
}
else
{
nldebug("is not isSelectableBySpace");
}
}
if(entity->properties().selectable())
{
nldebug("is prop selectable");
}
else
{
// to avoid campfire selection exploit #316
nldebug("is not prop selectable");
CInterfaceManager *pIM= CInterfaceManager::getInstance();
if(!quiet)
pIM->displaySystemInfo(CI18N::get("uiTargetErrorCmd"));
return;
}
// Select the entity
UserEntity->selection(entity->slot());
}
else
else if (!quiet)
{
CInterfaceManager *pIM= CInterfaceManager::getInstance();
if(!quiet)
pIM->displaySystemInfo(CI18N::get("uiTargetErrorCmd"));
}
CInterfaceManager::getInstance()->displaySystemInfo(CI18N::get("uiTargetErrorCmd"));
}
}
};
@ -3732,6 +3729,34 @@ class CHandlerGameConfigChangeScreenRatioCustom : public IActionHandler
};
REGISTER_ACTION_HANDLER (CHandlerGameConfigChangeScreenRatioCustom, "game_config_change_screen_ratio_custom");
// ***************************************************************************
class CHandlerSetInterfaceScale : public IActionHandler
{
virtual void execute (CCtrlBase *pCaller, const string &Params)
{
std::string s;
s = getParam(Params, "scale");
if (!s.empty()) {
float scale;
if (fromString(s, scale))
{
if (scale >= ClientCfg.InterfaceScale_min && scale <= ClientCfg.InterfaceScale_max)
{
ClientCfg.InterfaceScale = scale;
ClientCfg.writeDouble("InterfaceScale", ClientCfg.InterfaceScale);
ClientCfg.IsInvalidated = true;
return;
}
}
}
ucstring help("/setuiscale "+toString("%.1f .. %.1f", ClientCfg.InterfaceScale_min, ClientCfg.InterfaceScale_max));
CInterfaceManager::getInstance()->displaySystemInfo(help);
}
};
REGISTER_ACTION_HANDLER (CHandlerSetInterfaceScale, "set_ui_scale");
// ***************************************************************************
class CHandlerGameMissionAbandon : public IActionHandler
@ -4209,7 +4234,7 @@ public:
string fileName = getParam(sParams, "music");
// don't play if db is in init stage
if (IngameDbMngr.initInProgress()) return;
if (!ClientCfg.Local && IngameDbMngr.initInProgress()) return;
if(SoundMngr)
SoundMngr->playEventMusic(fileName, xFade, loop);
@ -4231,7 +4256,7 @@ public:
string fileName= getParam(sParams, "music");
// don't play if db is in init stage
if (IngameDbMngr.initInProgress()) return;
if (!ClientCfg.Local && IngameDbMngr.initInProgress()) return;
if(SoundMngr)
SoundMngr->stopEventMusic(fileName, xFade);

View file

@ -28,6 +28,9 @@ using namespace NLMISC;
CChatTextManager* CChatTextManager::_Instance = NULL;
// last selected chat from 'copy_chat_popup' action handler
static std::string LastSelectedChat;
//=================================================================================
CChatTextManager::CChatTextManager() :
_TextFontSize(NULL),
@ -402,6 +405,10 @@ CViewBase *CChatTextManager::createMsgTextComplex(const ucstring &msg, NLMISC::C
para->setSizeRef("w");
para->setResizeFromChildH(true);
// use right click because left click might be used to activate chat window
para->setRightClickHandler("copy_chat_popup");
para->setRightClickHandlerParams(msg.toUtf8());
if (plaintext)
{
CViewBase *vt = createMsgTextSimple(msg, col, justified, NULL);
@ -522,3 +529,41 @@ void CChatTextManager::reset ()
_TextShadowed = NULL;
_ShowTimestamps = NULL;
}
// ***************************************************************************
// Called when we right click on a chat line
class CHandlerCopyChatPopup: public IActionHandler
{
public:
virtual void execute(CCtrlBase *pCaller, const string &params )
{
if (pCaller == NULL) return;
LastSelectedChat = params;
CGroupParagraph *pGP = dynamic_cast<CGroupParagraph *>(pCaller);
if (pGP) pGP->enableTempOver();
CWidgetManager::getInstance()->enableModalWindow (pCaller, "ui:interface:chat_copy_action_menu");
}
};
REGISTER_ACTION_HANDLER( CHandlerCopyChatPopup, "copy_chat_popup");
// ***************************************************************************
// Called when we right click on a chat line and choose 'copy' from context menu
class CHandlerCopyChat: public IActionHandler
{
public:
virtual void execute(CCtrlBase *pCaller, const string &params )
{
if (pCaller == NULL) return;
CGroupParagraph *pGP = dynamic_cast<CGroupParagraph *>(pCaller);
if (pGP) pGP->disableTempOver();
CAHManager::getInstance()->runActionHandler("copy_to_clipboard", NULL, LastSelectedChat);
CWidgetManager::getInstance()->disableModalWindow();
}
};
REGISTER_ACTION_HANDLER( CHandlerCopyChat, "copy_chat");

View file

@ -1142,6 +1142,15 @@ CChatWindow *CChatWindowManager::createChatGroupWindow(const CChatWindowDesc &de
if (!desc.HeaderColor.empty())
w->setHeaderColor(desc.HeaderColor);
// because root group was created from template, element from scrollbar target attribute was not created yet
CInterfaceGroup *pIG = w->getContainer()->getGroup("header_opened:channel_select");
if (pIG)
{
CCtrlScroll *sb = dynamic_cast<CCtrlScroll*>(w->getContainer()->getCtrl("channel_scroll"));
if (sb) sb->setTarget(pIG);
}
return w;
}
else

View file

@ -18,6 +18,7 @@
#include "group_html_webig.h"
#include "nel/misc/xml_auto_ptr.h"
#include "nel/gui/lua_manager.h"
#include "../client_cfg.h"
#include "../user_entity.h"
#include "../entities.h"
@ -54,7 +55,7 @@ REGISTER_ACTION_HANDLER( CHandlerBrowseHome, "browse_home");
static string getWebAuthKey()
{
if(!UserEntity) return "";
if(!UserEntity || !NetMngr.getLoginCookie().isValid()) return "";
// authkey = <sharid><name><cid><cookie>
uint32 cid = NetMngr.getLoginCookie().getUserId() * 16 + PlayerSelectedSlot;
@ -149,13 +150,21 @@ size_t writeDataFromCurl(void *buffer, size_t size, size_t nmemb, void *pcl)
return size*nmemb;
}
struct CWebigNotificationThread : public NLMISC::IRunnable
class CWebigNotificationThread : public NLMISC::IRunnable
{
private:
CURL *Curl;
bool _Running;
IThread *_Thread;
public:
CWebigNotificationThread()
{
_Running = false;
_Thread = NULL;
curl_global_init(CURL_GLOBAL_ALL);
Curl = curl_easy_init();
if(!Curl) return;
curl_easy_setopt(Curl, CURLOPT_COOKIEFILE, "");
@ -173,6 +182,12 @@ struct CWebigNotificationThread : public NLMISC::IRunnable
curl_easy_cleanup(Curl);
Curl = 0;
}
if (_Thread)
{
_Thread->terminate();
delete _Thread;
_Thread = NULL;
}
}
void get(const std::string &url)
@ -186,61 +201,24 @@ struct CWebigNotificationThread : public NLMISC::IRunnable
curl_easy_getinfo(Curl, CURLINFO_RESPONSE_CODE, &r);
//nlwarning("result : '%s'", curlresult.c_str());
vector<string> notifs;
explode(curlresult, string("|"), notifs);
char *ch;
std::string contentType;
res = curl_easy_getinfo(Curl, CURLINFO_CONTENT_TYPE, &ch);
if (res == CURLE_OK && ch != NULL)
{
contentType = ch;
}
// Update the mail notification icon
uint32 nbmail = 0;
if(!notifs.empty() && fromString(notifs[0], nbmail))
// "text/lua; charset=utf8"
if (contentType.find("text/lua") == 0)
{
//nlinfo("nb mail is a number %d", nbmail);
CInterfaceManager *pIM = CInterfaceManager::getInstance();
if(pIM)
{
CCDBNodeLeaf *_CheckMailNode = NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:MAIL_WAITING");
if(_CheckMailNode)
{
_CheckMailNode->setValue32(nbmail==0?0:1);
CInterfaceElement *elm = CWidgetManager::getInstance()->getElementFromId("ui:interface:compass:mail:mail_nb");
if (elm)
{
CViewText *vt = dynamic_cast<CViewText*>(elm);
vt->setText(toString("%d", nbmail));
}
}
}
std::string script;
script = "\nlocal __WEBIG_NOTIF__= true\n" + curlresult;
CInterfaceManager::getInstance()->queueLuaScript(script);
}
else
{
nlwarning("this is not a number '%s'", curlresult.c_str());
}
// Update the forum notification icon
uint32 nbforum = 0;
if(notifs.size() > 1 && fromString(notifs[1], nbforum))
{
//nlinfo("nb forum this is a number %d", nbforum);
CInterfaceManager *pIM = CInterfaceManager::getInstance();
if(pIM)
{
CCDBNodeLeaf *_CheckForumNode = NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:FORUM_UPDATED");
if(_CheckForumNode)
{
_CheckForumNode->setValue32(nbforum==0?0:1);
CInterfaceElement *elm = CWidgetManager::getInstance()->getElementFromId("ui:interface:compass:forum:forum_nb");
if (elm)
{
CViewText *vt = dynamic_cast<CViewText*>(elm);
vt->setText(toString("%d", nbforum));
}
}
}
}
else
{
nlwarning("this is not a number '%s'", curlresult.c_str());
nlwarning("Invalid content-type '%s', expected 'text/lua'", contentType.c_str());
}
}
@ -257,30 +235,93 @@ struct CWebigNotificationThread : public NLMISC::IRunnable
void run()
{
// first time, we wait a small amount of time to be sure everything is initialized
nlSleep(1*60*1000);
while (true)
if (ClientCfg.WebIgNotifInterval == 0)
{
string url = "http://"+ClientCfg.WebIgMainDomain+"/index.php?app=notif&rnd="+randomString();
_Running = false;
nlwarning("ClientCfg.WebIgNotifInterval == 0, notification thread not running");
return;
}
std::string domain = ClientCfg.WebIgMainDomain;
uint32 ms = ClientCfg.WebIgNotifInterval*60*1000;
_Running = true;
// first time, we wait a small amount of time to be sure everything is initialized
nlSleep(30*1000);
uint c = 0;
while (_Running)
{
string url = "https://"+domain+"/index.php?app=notif&format=lua&rnd="+randomString();
addWebIGParams(url, true);
get(url);
nlSleep(10*60*1000);
sleepLoop(ms);
}
}
void sleepLoop(uint ms)
{
// use smaller sleep time so stopThread() will not block too long
// tick == 100ms
uint32 ticks = ms / 100;
while (_Running && ticks > 0) {
nlSleep(100);
ticks--;
}
}
void startThread()
{
if (!_Thread)
{
_Thread = IThread::create(this);
nlassert(_Thread != NULL);
_Thread->start();
nlwarning("WebIgNotification thread started");
}
else
{
nlwarning("WebIgNotification thread already started");
}
}
void stopThread()
{
_Running = false;
if (_Thread)
{
_Thread->wait();
delete _Thread;
_Thread = NULL;
nlwarning("WebIgNotification thread stopped");
}
else
{
nlwarning("WebIgNotification thread already stopped");
}
}
bool isRunning() const
{
return _Running;
}
};
void startWebigNotificationThread()
static CWebigNotificationThread webigThread;
void startWebIgNotificationThread()
{
static bool startedWebigNotificationThread = false;
if(!startedWebigNotificationThread)
if (!webigThread.isRunning())
{
curl_global_init(CURL_GLOBAL_ALL);
//nlinfo("startStatThread");
CWebigNotificationThread *webigThread = new CWebigNotificationThread();
IThread *thread = IThread::create (webigThread);
nlassert (thread != NULL);
thread->start ();
startedWebigNotificationThread = true;
webigThread.startThread();
}
}
void stopWebIgNotificationThread()
{
if (webigThread.isRunning())
{
webigThread.stopThread();
}
}
@ -304,6 +345,8 @@ CGroupHTMLAuth::~CGroupHTMLAuth()
void CGroupHTMLAuth::addHTTPGetParams (string &url, bool trustedDomain)
{
if(!UserEntity || !NetMngr.getLoginCookie().isValid()) return;
addWebIGParams(url, trustedDomain);
}
@ -311,7 +354,7 @@ void CGroupHTMLAuth::addHTTPGetParams (string &url, bool trustedDomain)
void CGroupHTMLAuth::addHTTPPostParams (SFormFields &formfields, bool trustedDomain)
{
if(!UserEntity) return;
if(!UserEntity || !NetMngr.getLoginCookie().isValid()) return;
uint32 cid = NetMngr.getLoginCookie().getUserId() * 16 + PlayerSelectedSlot;
formfields.add("shardid", toString(CharacterHomeSessionId));
@ -349,7 +392,6 @@ NLMISC_REGISTER_OBJECT(CViewBase, CGroupHTMLWebIG, std::string, "webig_html");
CGroupHTMLWebIG::CGroupHTMLWebIG(const TCtorParam &param)
: CGroupHTMLAuth(param)
{
startWebigNotificationThread();
}
// ***************************************************************************

View file

@ -20,6 +20,9 @@
#include "nel/misc/types_nl.h"
#include "nel/gui/group_html.h"
void startWebIgNotificationThread();
void stopWebIgNotificationThread();
/**
* Auth HTML group
*/

View file

@ -86,8 +86,10 @@ void CGroupInScene::computeWindowPos(sint32 &newX, sint32 &newY, CVector &newPro
tmp = pVR.getFrustum().projectZ (tmp);
// Get the width and height
tmp.x *= (float)CViewRenderer::getInstance()->getDriver()->getWindowWidth();
tmp.y *= (float)CViewRenderer::getInstance()->getDriver()->getWindowHeight();
uint32 width, height;
CViewRenderer::getInstance()->getScreenSize(width, height);
tmp.x *= width;
tmp.y *= height;
// position without offset, in float
newProjCenter.x= tmp.x;

View file

@ -680,9 +680,8 @@ CGroupInSceneBubbleManager::CPopupContext *CGroupInSceneBubbleManager::buildCont
if (target)
{
// Find a position
NL3D::UDriver *Driver = CViewRenderer::getInstance()->getDriver();
const uint width = Driver->getWindowWidth();
const uint height = Driver->getWindowHeight();
uint32 width, height;
CViewRenderer::getInstance()->getScreenSize(width, height);
h = (target->getXReal() < ((sint)width-target->getXReal()-target->getWReal()))?"l":"r";
v = (target->getYReal() < ((sint)height-target->getYReal()-target->getHReal()))?"b":"t";
target->setActive(true);

View file

@ -38,6 +38,7 @@
#include "../sheet_manager.h" // for MaxNumPeopleInTeam
#include "../global.h"
#include "nel/gui/ctrl_quad.h"
#include "nel/gui/lua_ihm.h"
//
#include "nel/misc/xml_auto_ptr.h"
#include "game_share/mission_desc.h"
@ -403,6 +404,7 @@ CGroupMap::CGroupMap(const TCtorParam &param)
_MaxH = 2000;
//_MinW = 50;
_MapTF = NULL;
_MapTexture.clear();
_PlayerPosMaterial = NULL;
_PlayerPosTF = NULL;
_MapTexW = 0;
@ -462,6 +464,8 @@ CGroupMap::CGroupMap(const TCtorParam &param)
_PanStartDateInMs = 0;
_DeltaTimeBeforePanInMs = 0;
_DeltaPosBeforePan = 0;
//
_LuaLoadMapEntered = false;
}
//============================================================================================================
@ -2070,16 +2074,62 @@ void CGroupMap::loadPlayerPos()
_PlayerPosMaterial.setTexture(_PlayerPosTF);
}
//============================================================================================================
void CGroupMap::reload()
{
if (!_CurMap || !getActive()) return;
SMap* current = _CurMap;
_CurMap = NULL;
setMap(current);
}
//============================================================================================================
void CGroupMap::loadMap()
{
_MapLoadFailure = true;
if (!_CurMap) return;
const std::string &mapName = _CurMap->BitmapName;
std::string fullName = NLMISC::CPath::lookup(mapName, false, false);
_MapTexture = _CurMap->BitmapName;
// call lua game:onLoadMap() function if present
// avoid deadlock if called recursively
if (!_LuaLoadMapEntered)
{
_LuaLoadMapEntered = true;
CLuaState *ls = CLuaManager::getInstance().getLuaState();
CLuaStackRestorer lsr(ls, ls->getTop());
ls->pushGlobalTable();
CLuaObject game(*ls);
game = game["game"];
if (!game["onLoadMap"].isNil())
{
uint numArg = 1;
uint numResult = 1;
CLuaIHM::pushReflectableOnStack(*ls, this);
if (game.callMethodByNameNoThrow("onLoadMap", numArg, numResult))
{
if (ls->isString(1))
{
if (!NLMISC::CPath::lookup(ls->toString(1), false, false).empty())
_MapTexture = ls->toString(1);
else
nlwarning("Custom map texture not found '%s' for map '%s'", ls->toString(1), _MapTexture.c_str());
}
}
}
_LuaLoadMapEntered = false;
}
std::string fullName = NLMISC::CPath::lookup(_MapTexture, false, false);
if (fullName.empty())
{
nlwarning("Can't find map %s", mapName.c_str());
nlwarning("Can't find map %s", _MapTexture.c_str());
return;
}
uint32 w, h;
@ -2098,7 +2148,7 @@ void CGroupMap::loadMap()
}
else
{
nlwarning("Can't open map %s", mapName.c_str());
nlwarning("Can't open map %s", _MapTexture.c_str());
return;
}
_MapTF = Driver->createTextureFile(fullName);
@ -2665,7 +2715,7 @@ CCtrlButton *CGroupMap::addUserLandMark(const NLMISC::CVector2f &pos, const ucst
addLandMark(_UserLM, pos, title, getUserLandMarkOptions((uint32)_CurContinent->UserLandMarks.size() - 1));
// Save the config file each time a user landmark is created
CInterfaceManager::getInstance()->saveConfig();
CInterfaceManager::getInstance()->saveLandmarks();
return _UserLM.back();
}
@ -2689,6 +2739,7 @@ void CGroupMap::removeUserLandMark(CCtrlButton *button)
updateUserLandMarks();
}
CInterfaceManager::getInstance()->saveLandmarks();
return;
}
}
@ -2708,6 +2759,8 @@ void CGroupMap::updateUserLandMark(CCtrlButton *button, const ucstring &newTitle
updateLandMarkButton(_UserLM[k], getUserLandMarkOptions(k));
button->setDefaultContextHelp(newTitle);
CInterfaceManager::getInstance()->saveLandmarks();
return;
}
}
@ -3322,6 +3375,36 @@ SMap *CGroupMap::getParentMap(SMap *map)
return NULL;
}
//=========================================================================================================
std::string CGroupMap::getContinentName() const
{
if (_CurMap == NULL) return "";
return toLower(_CurMap->ContinentName);
}
//=========================================================================================================
std::string CGroupMap::getMapTexture() const
{
return toLower(_MapTexture);
}
//=========================================================================================================
int CGroupMap::luaReload(CLuaState &ls)
{
CLuaIHM::checkArgCount(ls, "reload", 0);
reload();
return 0;
}
//=========================================================================================================
int CGroupMap::luaIsIsland(CLuaState &ls)
{
CLuaIHM::checkArgCount(ls, "isIsland", 0);
ls.push(_IsIsland);
return 1;
}
/////////////////////
// ACTION HANDLERS //

View file

@ -114,6 +114,17 @@ public:
*/
virtual void onUpdate(CGroupMap &/* owner */) {}
};
REFLECT_EXPORT_START(CGroupMap, CInterfaceGroup)
REFLECT_STRING("continent", getContinentName, dummySet);
REFLECT_STRING("texture", getMapTexture, dummySet);
REFLECT_LUA_METHOD("isIsland", luaIsIsland);
REFLECT_LUA_METHOD("reload", luaReload);
REFLECT_EXPORT_END
int luaReload(CLuaState &ls);
int luaIsIsland(CLuaState &ls);
public:
CGroupMap(const TCtorParam &param);
virtual ~CGroupMap();
@ -134,6 +145,14 @@ public:
void setMap(const std::string &mapName);
void setMap(SMap *map);
// return current continent
std::string getContinentName() const;
// return currently displayed map texture
std::string getMapTexture() const;
// reload current map texture
void reload();
// pan the map of the given number of pixels
void pan(sint32 dx, sint32 dy);
@ -323,6 +342,7 @@ private:
CContinent *_CurContinent; // the last continent for which the map was displayed (can be NULL if world)
NLMISC::CVector2f _MapMinCorner; // In world coordinates
NLMISC::CVector2f _MapMaxCorner;
std::string _MapTexture; // currently displayed map texture
bool _IsIsland; // true if current map is an island (island bitmap need not to be raised to the next
// power of 2
@ -499,6 +519,8 @@ private:
// r2 islands
std::vector<SMap> _Islands;
// guard against recursive calls
bool _LuaLoadMapEntered;
private:
void loadPlayerPos();

View file

@ -297,6 +297,49 @@ void CGroupQuickHelp::beginElement (uint element_number, const std::vector<bool>
}
}
// ***************************************************************************
std::string CGroupQuickHelp::getLanguageUrl(const std::string &href, std::string lang) const
{
std::string uri = href;
if (uri.size() < 5 || uri.substr(0, 5) == "http://" || uri.substr(0, 6) == "https://")
{
return uri;
}
// modify uri such that '_??.html' ending contains current user language
if (uri.substr(uri.size()-5) == ".html")
{
if (uri.rfind("_") == uri.size() - 8)
{
uri = uri.substr(0, uri.size() - 8);
}
else
{
uri = uri.substr(0, uri.size() - 5);
}
uri += "_" + lang + ".html";
// files inside bnp (file:/gamedev.bnp@help_en.html) will always match with CPath::lookup()
std::string fname;
size_t pos = uri.find("@");
if (pos != std::string::npos)
{
fname = uri.substr(pos+1);
}
else
{
fname = uri;
}
if (CPath::lookup(fname, false) == "" && lang != "en")
{
uri = getLanguageUrl(href, "en");
}
}
return uri;
}
// ***************************************************************************
void CGroupQuickHelp::browse (const char *url)
@ -307,12 +350,7 @@ void CGroupQuickHelp::browse (const char *url)
_IsQuickHelp = false;
string completeURL = url;
if (completeURL.substr(completeURL.size()-5, 5) == ".html")
{
completeURL = completeURL.substr(0, completeURL.size()-5); // Substract the ".html"
completeURL += "_" + ClientCfg.getHtmlLanguageCode() + ".html";
}
string completeURL = getLanguageUrl(url, ClientCfg.getHtmlLanguageCode());
CGroupHTML::browse (completeURL.c_str());
}
@ -321,9 +359,7 @@ void CGroupQuickHelp::browse (const char *url)
std::string CGroupQuickHelp::home()
{
string completeURL = Home;
completeURL = completeURL.substr(0, completeURL.size()-5); // Substract the ".html"
completeURL += "_" + ClientCfg.getHtmlLanguageCode() + ".html";
string completeURL = getLanguageUrl(Home, ClientCfg.getHtmlLanguageCode());
return completeURL;
}

View file

@ -53,6 +53,11 @@ private:
virtual void browse (const char *url);
virtual std::string home();
// Modify uri with '.html' or '_??.html' ending to have current user language,
// If the uri is not found locally, then try "en" as fallback language
// ie. 'help_ru.html' does not exists, return 'help_en.html'
std::string getLanguageUrl(const std::string &href, std::string lang) const;
// Init parsing value
void initParameters();

View file

@ -133,6 +133,8 @@ using namespace NLGUI;
#include "../global.h"
#include "user_agent.h"
#include "group_html_webig.h"
using namespace NLMISC;
namespace NLGUI
@ -457,6 +459,7 @@ CInterfaceManager::CInterfaceManager()
parser->addModule( "command", new CCommandParser() );
parser->addModule( "key", new CKeyParser() );
parser->addModule( "macro", new CMacroParser() );
parser->addModule( "landmarks", new CLandmarkParser() );
parser->setCacheUIParsing( ClientCfg.CacheUIParsing );
CViewRenderer::setDriver( Driver );
@ -483,6 +486,8 @@ CInterfaceManager::CInterfaceManager()
interfaceLinkUpdater = new CInterfaceLink::CInterfaceLinkUpdater();
_ScreenW = _ScreenH = 0;
_LastInGameScreenW = _LastInGameScreenH = 0;
_InterfaceScaleChanged = false;
_InterfaceScale = 1.0f;
_DescTextTarget = NULL;
_ConfigLoaded = false;
_LogState = false;
@ -979,6 +984,9 @@ void CInterfaceManager::initInGame()
// Interface config
loadInterfaceConfig();
//Load user landmarks
loadLandmarks();
// Must do extra init for people interaction after load
PeopleInterraction.initAfterLoad();
@ -1043,6 +1051,8 @@ void CInterfaceManager::initInGame()
{
displaySystemInfo(CI18N::get("uiLogTurnedOff"));
}
startWebIgNotificationThread();
}
// ------------------------------------------------------------------------------------------------
@ -1214,6 +1224,22 @@ void CInterfaceManager::configureQuitDialogBox()
}
}
// ------------------------------------------------------------------------------------------------
//
std::string CInterfaceManager::getSaveFileName(const std::string &module, const std::string &ext, bool useShared) const
{
string filename = "save/" + module + "_" + PlayerSelectedFileName + "." + ext;
if (useShared && !CFile::fileExists(filename))
{
string sharedFile = "save/shared_" + module + "." + ext;
if (CFile::fileExists(sharedFile))
{
return sharedFile;
}
}
return filename;
}
// ------------------------------------------------------------------------------------------------
void CInterfaceManager::loadKeys()
{
@ -1225,26 +1251,19 @@ void CInterfaceManager::loadKeys()
vector<string> xmlFilesToParse;
// Does the keys file exist ?
string userKeyFileName = "save/keys_"+PlayerSelectedFileName+".xml";
string userKeyFileName = getSaveFileName("keys", "xml");
if (CFile::fileExists(userKeyFileName) && CFile::getFileSize(userKeyFileName) > 0)
{
// Load the user key file
xmlFilesToParse.push_back (userKeyFileName);
}
else
{
string filename = "save/shared_keys.xml";
if(CFile::fileExists(filename) && CFile::getFileSize(filename) > 0)
{
xmlFilesToParse.push_back(filename);
}
}
// Load the default key (but don't replace existings bounds, see keys.xml "key_def_no_replace")
xmlFilesToParse.push_back ("keys.xml");
if (!parseInterface (xmlFilesToParse, true))
{
badXMLParseMessageBox();
createFileBackup("Error loading keys", userKeyFileName);
}
_KeysLoaded = true;
@ -1257,10 +1276,7 @@ void CInterfaceManager::loadInterfaceConfig()
if (ClientCfg.R2EDEnabled) // in R2ED mode the CEditor class deals with it
return;
string filename = "save/interface_" + PlayerSelectedFileName + ".icfg";
if (!CFile::fileExists(filename))
filename = "save/shared_interface.icfg";
string filename = getSaveFileName("interface", "icfg");
loadConfig(filename); // Invalidate coords of changed groups
_ConfigLoaded = true;
@ -1290,6 +1306,7 @@ void CInterfaceManager::uninitInGame0 ()
// ------------------------------------------------------------------------------------------------
void CInterfaceManager::uninitInGame1 ()
{
stopWebIgNotificationThread();
// release Bar Manager (ChaScore1, ChaScore2 etc... Bars)
CBarManager::getInstance()->releaseInGame();
@ -1363,6 +1380,8 @@ void CInterfaceManager::uninitInGame1 ()
reset();
CInterfaceLink::removeAllLinks();
CWidgetManager::getInstance()->setPointer( NULL );
// Release DDX manager, before DB remove
CDDXManager::getInstance()->release();
@ -1451,6 +1470,9 @@ void CInterfaceManager::updateFrameEvents()
H_AUTO_USE ( RZ_Client_Update_Frame_Events )
// lua scripts from different thread
flushScriptQueue();
flushDebugWindow();
// Handle anims done in 2 times because some AH can add or remove anims
@ -1675,6 +1697,7 @@ bool CInterfaceManager::loadConfig (const string &filename)
uint32 nNbMode;
CInterfaceConfig ic;
bool lastInGameScreenResLoaded= false;
uint32 nbLandmarks = 0;
try
{
sint ver = f.serialVersion(ICFG_STREAM_VERSION);
@ -1770,7 +1793,7 @@ bool CInterfaceManager::loadConfig (const string &filename)
}
// Load user landmarks
ContinentMngr.serialUserLandMarks(f);
nbLandmarks = ContinentMngr.serialUserLandMarks(f);
CCDBNodeLeaf *pNL = NLGUI::CDBManager::getInstance()->getDbProp( "SERVER:INTERFACES:NB_BONUS_LANDMARKS" );
if ( pNL )
@ -1804,10 +1827,9 @@ bool CInterfaceManager::loadConfig (const string &filename)
catch(const NLMISC::EStream &)
{
f.close();
string sFileNameBackup = sFileName+"backup";
if (CFile::fileExists(sFileNameBackup))
CFile::deleteFile(sFileNameBackup);
CFile::moveFile(sFileNameBackup, sFileName);
createFileBackup("Config loading failed", sFileName);
nlwarning("Config loading failed : restore default");
vector<string> v;
if (!ClientCfg.R2EDEnabled)
@ -1818,6 +1840,35 @@ bool CInterfaceManager::loadConfig (const string &filename)
}
f.close();
if (nbLandmarks > 0)
{
// use copy for backup so on save proper shared/player icfg file is used
createFileBackup("Landmarks will be migrated to xml", sFileName, true);
// if icfg is interface_player.icfg, then landmarks must also be loaded/saved to player file
if (nlstricmp(sFileName.substr(0, 12), "save/shared_") != 0)
{
string lmfile = getSaveFileName("landmarks", "xml", false);
if (!CFile::fileExists(lmfile))
{
// create placeholder player landmarks file so saveLandmarks will use it
// even if shared landmarks file exists
COFile f;
if (f.open(lmfile, false, false, true))
{
std::string xml;
xml = "<?xml version=\"1.0\"?>\n<interface_config />";
f.serialBuffer((uint8 *)xml.c_str(), xml.size());
f.close();
}
}
}
// merge .icfg landmarks with landmarks.xml
loadLandmarks();
saveLandmarks();
}
// *** If saved resolution is different from the current one setuped, must fix positions in _Modes
if(lastInGameScreenResLoaded)
{
@ -1825,8 +1876,12 @@ bool CInterfaceManager::loadConfig (const string &filename)
CWidgetManager::getInstance()->setScreenWH(_LastInGameScreenW, _LastInGameScreenH);
// NB: we are typically InGame here (even though the _InGame flag is not yet set)
// Use the screen size of the config file. Don't update current UI, just _Modes
CWidgetManager::getInstance()->moveAllWindowsToNewScreenSize(ClientCfg.Width, ClientCfg.Height, false);
updateDesktops( ClientCfg.Width, ClientCfg.Height );
//
// ClientCfg has W/H set to screen size, but interface expects scaled size
sint32 scaledW = ClientCfg.Width / ClientCfg.InterfaceScale;
sint32 scaledH = ClientCfg.Height / ClientCfg.InterfaceScale;
CWidgetManager::getInstance()->moveAllWindowsToNewScreenSize(scaledW, scaledH, false);
updateDesktops(scaledW, scaledH);
}
// *** apply the current mode
@ -1868,6 +1923,88 @@ public:
}
};
bool CInterfaceManager::saveLandmarks(bool verbose) const
{
bool ret = true;
if (!ClientCfg.R2EDEnabled)
{
uint8 currMode = getMode();
string filename = getSaveFileName("landmarks", "xml");
if (verbose) CInterfaceManager::getInstance()->displaySystemInfo("Saving " + filename);
ret = saveLandmarks(filename);
}
return ret;
}
bool CInterfaceManager::loadLandmarks()
{
// Does the keys file exist ?
string filename = getSaveFileName("landmarks", "xml");
CIFile f;
string sFileName;
sFileName = NLMISC::CPath::lookup (filename, false);
if (sFileName.empty() || !f.open(sFileName))
return false;
bool ret = false;
vector<string> xmlFilesToParse;
xmlFilesToParse.push_back (filename);
//ContinentMngr.serialUserLandMarks(node);
if (!parseInterface (xmlFilesToParse, true))
{
f.close();
createFileBackup("Error while loading landmarks", filename);
ret = false;
}
return ret;
}
bool CInterfaceManager::saveLandmarks(const std::string &filename) const
{
nlinfo( "Saving landmarks : %s", filename.c_str() );
bool ret = false;
try
{
COFile f;
// using temporary file, so no f.close() unless its a success
if (f.open(filename, false, false, true))
{
COXml xmlStream;
xmlStream.init (&f);
xmlDocPtr doc = xmlStream.getDocument ();
xmlNodePtr node = xmlNewDocNode(doc, NULL, (const xmlChar*)"interface_config", NULL);
xmlDocSetRootElement (doc, node);
ContinentMngr.writeTo(node);
// Flush the stream
xmlStream.flush();
// Close the stream
f.close ();
ret = true;
}
}
catch (const Exception &e)
{
nlwarning ("Error while writing the file %s : %s.", filename.c_str(), e.what ());
}
return ret;
}
// ------------------------------------------------------------------------------------------------
//
bool CInterfaceManager::saveConfig (bool verbose)
@ -1878,9 +2015,7 @@ bool CInterfaceManager::saveConfig (bool verbose)
{
uint8 currMode = getMode();
string filename = "save/interface_" + PlayerSelectedFileName + ".icfg";
if (!CFile::fileExists(filename) && CFile::fileExists("save/shared_interface.icfg"))
filename = "save/shared_interface.icfg";
string filename = getSaveFileName("interface", "icfg");
if (verbose) CInterfaceManager::getInstance()->displaySystemInfo("Saving " + filename);
ret = saveConfig(filename);
@ -1995,8 +2130,13 @@ bool CInterfaceManager::saveConfig (const string &filename)
CTaskBarManager *pTBM= CTaskBarManager::getInstance();
pTBM->serial(f);
// Save user landmarks
ContinentMngr.serialUserLandMarks(f);
//ContinentMngr.serialUserLandMarks(f);
// empty landmarks block for compatibility
{
f.serialVersion(1);
uint32 numCont = 0;
f.serial(numCont);
}
// Info Windows position.
CInterfaceHelp::serialInfoWindows(f);
@ -2047,6 +2187,13 @@ void CInterfaceManager::drawViews(NL3D::UCamera camera)
_CurrentPlayerCharac[i] = node ? node->getValue32() : 0;
}
// scale must be updated right before widget manager checks it
if (_InterfaceScaleChanged)
{
CViewRenderer::getInstance()->setInterfaceScale(_InterfaceScale);
_InterfaceScaleChanged = false;
}
CWidgetManager::getInstance()->drawViews( camera );
// flush obs
@ -2935,7 +3082,6 @@ NLMISC_COMMAND(loadui, "Load an interface file", "<loadui [all]/interface.xml>")
return result;
}
// ***************************************************************************
void CInterfaceManager::displayWebWindow(const string & name, const string & url)
{
@ -3344,6 +3490,24 @@ void CInterfaceManager::notifyForumUpdated()
_CheckForumNode->setValue32(1);
}
void CInterfaceManager::queueLuaScript(const std::string &script)
{
CAutoMutex<CMutex> autoMutex(_ScriptQueueMutex);
_ScriptQueue.push(script);
}
void CInterfaceManager::flushScriptQueue()
{
CAutoMutex<CMutex> autoMutex(_ScriptQueueMutex);
while(!_ScriptQueue.empty())
{
CLuaManager::getInstance().executeLuaScript(_ScriptQueue.front());
_ScriptQueue.pop();
}
}
// ***************************************************************************
void CInterfaceManager::resetTextIndex()
@ -4120,3 +4284,40 @@ bool CInterfaceManager::parseTokens(ucstring& ucstr)
ucstr = str;
return true;;
}
std::string CInterfaceManager::getNextBackupName(std::string filename)
{
std::string ts = getTimestampHuman("%Y-%m-%d");
if (!ts.empty())
{
std::string::size_type pos = filename.find_last_of('.');
if (pos == std::string::npos)
filename = filename + "_" + ts + "_";
else
filename = filename.substr(0, pos) + "_" + ts + "_" + filename.substr(pos);
}
// filename_YYYY-MM-DD_000.ext
return CFile::findNewFile(filename);
}
void CInterfaceManager::createFileBackup(const std::string &message, const std::string &filename, bool useCopy)
{
std::string backupName = getNextBackupName(filename);
nlwarning("%s: '%s'.", message.c_str(), filename.c_str());
if (!backupName.empty())
{
if (useCopy)
{
nlwarning("Backup copy saved as '%s'", backupName.c_str());
CFile::copyFile(backupName, filename);
}
else
{
nlwarning("File renamed to '%s'", backupName.c_str());
CFile::moveFile(backupName, filename);
}
}
}

View file

@ -19,8 +19,11 @@
#ifndef NL_INTERFACE_MANAGER_H
#define NL_INTERFACE_MANAGER_H
#include <queue>
#include "nel/misc/types_nl.h"
#include "nel/misc/cdb_manager.h"
#include "nel/misc/mutex.h"
#include "nel/3d/u_texture.h"
#include "nel/3d/u_text_context.h"
#include "nel/gui/interface_group.h"
@ -203,6 +206,19 @@ public:
/// Load a set of xml files
bool parseInterface (const std::vector<std::string> &xmlFileNames, bool reload, bool isFilename = true);
/// return new filename that can be used to backup original file
std::string getNextBackupName(std::string filename);
/// copy/rename filename for backup and show error in log
void createFileBackup(const std::string &message, const std::string &filename, bool useCopy = false);
/// select player/shared file name from 'save' folder'
std::string getSaveFileName(const std::string &module, const std::string &ext, bool useShared = true) const;
/// Load / save user landmarks in .xml format
bool loadLandmarks ();
bool saveLandmarks (bool verbose = false) const;
bool saveLandmarks (const std::string &filename) const;
// Load/Save position, size, etc.. of windows
bool loadConfig (const std::string &filename);
// Save config to default location, if verbose is true, display message in game sysinfo
@ -414,6 +430,10 @@ public:
void notifyMailAvailable();
void notifyForumUpdated();
/** Queue up lua script to be run on next frame update
*/
void queueLuaScript(const std::string &script);
/** Return true if 12-hour clock should be used
*/
static bool use12hClock();
@ -549,6 +569,7 @@ public:
NLMISC::CCDBNodeLeaf *_DB_UI_DUMMY_FACTION_TYPE;
void updateDesktops( uint32 newScreenW, uint32 newScreenH );
void setInterfaceScale( float scale ) { _InterfaceScaleChanged = true; _InterfaceScale = scale; }
private:
@ -556,6 +577,12 @@ private:
NLMISC::CCDBNodeLeaf *_CheckForumNode;
sint64 _UpdateWeatherTime;
// WebIG notify thread is pushing lua code here
std::queue<std::string> _ScriptQueue;
NLMISC::CMutex _ScriptQueueMutex;
void flushScriptQueue();
// @}
/** This is the GLOBAL Action counter used to synchronize some systems (including INVENTORY) with the server.
@ -587,6 +614,8 @@ private:
uint32 _ScreenW, _ScreenH; // Change res detection
sint32 _LastInGameScreenW, _LastInGameScreenH; // Resolution used for last InGame interface
float _InterfaceScale;
bool _InterfaceScaleChanged;
// Modes
std::vector<CInterfaceConfig::CDesktopImage> _Modes;

View file

@ -2003,6 +2003,9 @@ bool SBagOptions::parse(xmlNodePtr cur, CInterfaceGroup * /* parentGroup */)
prop = xmlGetProp (cur, (xmlChar*)"filter_tool");
if (prop) DbFilterTool = NLGUI::CDBManager::getInstance()->getDbProp(prop.str());
prop = xmlGetProp (cur, (xmlChar*)"filter_pet");
if (prop) DbFilterPet = NLGUI::CDBManager::getInstance()->getDbProp(prop.str());
prop = xmlGetProp (cur, (xmlChar*)"filter_mp");
if (prop) DbFilterMP = NLGUI::CDBManager::getInstance()->getDbProp(prop.str());
@ -2079,6 +2082,13 @@ bool SBagOptions::isSomethingChanged()
LastDbFilterTool = (DbFilterTool->getValue8() != 0);
}
if (DbFilterPet != NULL)
if ((DbFilterPet->getValue8() != 0) != LastDbFilterPet)
{
bRet = true;
LastDbFilterPet = (DbFilterPet->getValue8() != 0);
}
if (DbFilterMP != NULL)
if ((DbFilterMP->getValue8() != 0) != LastDbFilterMP)
{
@ -2117,6 +2127,7 @@ bool SBagOptions::canDisplay(CDBCtrlSheet *pCS) const
bool bFilterArmor = getFilterArmor();
bool bFilterWeapon = getFilterWeapon();
bool bFilterTool = getFilterTool();
bool bFilterPet = getFilterPet();
bool bFilterMP = getFilterMP();
bool bFilterMissMP = getFilterMissMP();
bool bFilterTP = getFilterTP();
@ -2169,10 +2180,13 @@ bool SBagOptions::canDisplay(CDBCtrlSheet *pCS) const
(pIS->Family == ITEMFAMILY::HARVEST_TOOL) ||
(pIS->Family == ITEMFAMILY::TAMING_TOOL) ||
(pIS->Family == ITEMFAMILY::TRAINING_TOOL) ||
(pIS->Family == ITEMFAMILY::BAG) ||
(pIS->Family == ITEMFAMILY::PET_ANIMAL_TICKET) )
(pIS->Family == ITEMFAMILY::BAG))
if (!bFilterTool) bDisplay = false;
// Pet
if (pIS->Family == ITEMFAMILY::PET_ANIMAL_TICKET)
if (!bFilterPet) bDisplay = false;
// MP
if ((pIS->Family == ITEMFAMILY::RAW_MATERIAL) && pIS->canBuildSomeItemPart())
if (!bFilterMP) bDisplay = false;
@ -2529,25 +2543,92 @@ class CHandlerInvDrag : public IActionHandler
};
REGISTER_ACTION_HANDLER( CHandlerInvDrag, "inv_drag" );
// **********************************************************************************************************
class CHandlerInvSetSearch : public IActionHandler
// ***************************************************************************
// show/hide edit box, set keyboard focus if 'show'
class CHandlerInvSearchButton : public IActionHandler
{
void execute (CCtrlBase *pCaller, const std::string &sParams)
virtual void execute (CCtrlBase *pCaller, const string &sParams)
{
if (sParams.empty())
{
nlwarning("inv_search_button: missing edit box shortid");
return;
}
CCtrlBaseButton* btn = dynamic_cast<CCtrlBaseButton *>(pCaller);
if (!btn)
{
nlwarning("inv_search_button pCaller == NULL, caller must be CCtrlBaseButton with 'toggle_button' type");
return;
}
ucstring filter;
std::string id = btn->getParent()->getId() + ":" + sParams + ":eb";
CGroupEditBox *eb = dynamic_cast<CGroupEditBox*>(CWidgetManager::getInstance()->getElementFromId(id));
if (!eb)
{
nlwarning("inv_search_button: editbox (%s) not found\n", id.c_str());
return;
}
eb->getParent()->setActive(btn->getPushed());
if (eb->getParent()->getActive())
{
CWidgetManager::getInstance()->setCaptureKeyboard(eb);
eb->setSelectionAll();
filter = eb->getInputString();
}
CDBGroupListSheetBag *pList = dynamic_cast<CDBGroupListSheetBag*>(CWidgetManager::getInstance()->getElementFromId(btn->getParent()->getId() + ":bag_list"));
if (pList != NULL) pList->setSearchFilter(filter);
CDBGroupIconListBag *pIcons = dynamic_cast<CDBGroupIconListBag*>(CWidgetManager::getInstance()->getElementFromId(btn->getParent()->getId() + ":bag_icons"));
if (pIcons != NULL) pIcons->setSearchFilter(filter);
}
};
REGISTER_ACTION_HANDLER( CHandlerInvSearchButton, "inv_search_button" );
// ***************************************************************************
// if :eb is empty then hide edit box, unpush search button
class CHandlerInvSearchUnfocus : public IActionHandler
{
virtual void execute (CCtrlBase *pCaller, const string &sParams)
{
if (!pCaller) return;
CGroupEditBox *eb = dynamic_cast<CGroupEditBox *>(pCaller);
if (!eb) return;
CInterfaceManager *pIM = CInterfaceManager::getInstance();
if (!eb || !eb->getInputString().empty()) return;
// ui:interface:inventory:content:bag:iil:inv_query_eb:eb
string invId = pCaller->getParent()->getParent()->getId();
std::string id = pCaller->getParent()->getParent()->getId() + ":" + sParams;
CCtrlBaseButton *btn = dynamic_cast<CCtrlBaseButton*>(CWidgetManager::getInstance()->getElementFromId(id));
if (btn) btn->setPushed(false);
CDBGroupListSheetBag *pList = dynamic_cast<CDBGroupListSheetBag*>(CWidgetManager::getInstance()->getElementFromId(invId + ":bag_list"));
// hide :inv_query_eb
pCaller->getParent()->setActive(false);
// clear filter
CAHManager::getInstance()->runActionHandler("inv_set_search", pCaller, "");
}
};
REGISTER_ACTION_HANDLER( CHandlerInvSearchUnfocus, "inv_search_unfocus" );
// **********************************************************************************************************
// set inventory search string
class CHandlerInvSetSearch : public IActionHandler
{
void execute (CCtrlBase *pCaller, const std::string &sParams)
{
CGroupEditBox *eb = dynamic_cast<CGroupEditBox *>(pCaller);
if (!eb) return;
// ui:interface:inventory:content:bag:iil:inv_query_eb:eb
std::string id = pCaller->getParent()->getParent()->getId();
CDBGroupListSheetBag *pList = dynamic_cast<CDBGroupListSheetBag*>(CWidgetManager::getInstance()->getElementFromId(id + ":bag_list"));
if (pList != NULL) pList->setSearchFilter(eb->getInputString());
CDBGroupIconListBag *pIcons = dynamic_cast<CDBGroupIconListBag*>(CWidgetManager::getInstance()->getElementFromId(invId + ":bag_icons"));
CDBGroupIconListBag *pIcons = dynamic_cast<CDBGroupIconListBag*>(CWidgetManager::getInstance()->getElementFromId(id + ":bag_icons"));
if (pIcons != NULL) pIcons->setSearchFilter(eb->getInputString());
}
};

View file

@ -509,6 +509,7 @@ struct SBagOptions
NLMISC::CCDBNodeLeaf *DbFilterArmor;
NLMISC::CCDBNodeLeaf *DbFilterWeapon;
NLMISC::CCDBNodeLeaf *DbFilterTool;
NLMISC::CCDBNodeLeaf *DbFilterPet;
NLMISC::CCDBNodeLeaf *DbFilterMP;
NLMISC::CCDBNodeLeaf *DbFilterMissMP;
NLMISC::CCDBNodeLeaf *DbFilterTP;
@ -516,6 +517,7 @@ struct SBagOptions
bool LastDbFilterArmor;
bool LastDbFilterWeapon;
bool LastDbFilterTool;
bool LastDbFilterPet;
bool LastDbFilterMP;
bool LastDbFilterMissMP;
bool LastDbFilterTP;
@ -529,8 +531,8 @@ struct SBagOptions
SBagOptions()
{
InvType = CInventoryManager::InvUnknown;
DbFilterArmor = DbFilterWeapon = DbFilterTool = DbFilterMP = DbFilterMissMP = DbFilterTP = NULL;
LastDbFilterArmor = LastDbFilterWeapon = LastDbFilterTool = LastDbFilterMP = LastDbFilterMissMP = LastDbFilterTP = false;
DbFilterArmor = DbFilterWeapon = DbFilterTool = DbFilterPet = DbFilterMP = DbFilterMissMP = DbFilterTP = NULL;
LastDbFilterArmor = LastDbFilterWeapon = LastDbFilterTool = LastDbFilterPet = LastDbFilterMP = LastDbFilterMissMP = LastDbFilterTP = false;
SearchFilterChanged = false;
SearchQualityMin = 0;
SearchQualityMax = 999;
@ -561,6 +563,12 @@ struct SBagOptions
return (DbFilterTool->getValue8()!=0);
}
bool getFilterPet() const
{
if (DbFilterPet == NULL) return true;
return (DbFilterPet->getValue8()!=0);
}
bool getFilterMP() const
{
if (DbFilterMP == NULL) return true;

View file

@ -540,6 +540,7 @@ void CLuaIHMRyzom::RegisterRyzomFunctions(NLGUI::CLuaState &ls)
LUABIND_FUNC(getClientCfg),
LUABIND_FUNC(sendMsgToServer),
LUABIND_FUNC(sendMsgToServerPvpTag),
LUABIND_FUNC(sendMsgToServerUseItem),
LUABIND_FUNC(isGuildQuitAvailable),
LUABIND_FUNC(sortGuildMembers),
LUABIND_FUNC(getNbGuildMembers),
@ -1687,7 +1688,13 @@ int CLuaIHMRyzom::getWeatherValue(CLuaState &ls)
CLuaIHM::checkArgCount(ls, funcName, 0);
uint64 currDay = RT.getRyzomDay();
float currHour = (float) RT.getRyzomTime();
ls.push(::getBlendedWeather(currDay, currHour, *WeatherFunctionParams, ContinentMngr.cur()->WeatherFunction));
float weather = 0.f;
if (ContinentMngr.cur())
{
weather = ::getBlendedWeather(currDay, currHour, *WeatherFunctionParams, ContinentMngr.cur()->WeatherFunction);
}
ls.push(weather);
return 1;
}
@ -3326,6 +3333,16 @@ void CLuaIHMRyzom::sendMsgToServerPvpTag(bool pvpTag)
::sendMsgToServer("PVP:PVP_TAG", tag);
}
// ***************************************************************************
void CLuaIHMRyzom::sendMsgToServerUseItem(sint32 slot)
{
//H_AUTO(Lua_CLuaIHM_sendMsgToServerUseItem)
uint8 u8n1 = (uint8)((uint16)slot >> 8);
uint8 u8n2 = (uint8)((uint16)slot & 0x00FF);
::sendMsgToServer("ITEM:USE_ITEM", u8n1, u8n2);
}
// ***************************************************************************
bool CLuaIHMRyzom::isGuildQuitAvailable()
{

View file

@ -198,6 +198,7 @@ private:
static std::string getClientCfg(const std::string &varName);
static void sendMsgToServer(const std::string &msgName);
static void sendMsgToServerPvpTag(bool pvpTag);
static void sendMsgToServerUseItem(sint32 slot);
static bool isGuildQuitAvailable();
static void sortGuildMembers();
static sint32 getNbGuildMembers();

View file

@ -39,6 +39,9 @@ extern UDriver *Driver;
#define TEMPLATE_PLAYLIST_SONG "playlist_song"
#define TEMPLATE_PLAYLIST_SONG_TITLE "title"
#define TEMPLATE_PLAYLIST_SONG_DURATION "duration"
// ui state
#define MP3_SAVE_SHUFFLE "UI:SAVE:MP3_SHUFFLE"
#define MP3_SAVE_REPEAT "UI:SAVE:MP3_REPEAT"
static const std::string MediaPlayerDirectory("music/");
@ -48,8 +51,20 @@ CMusicPlayer MusicPlayer;
CMusicPlayer::CMusicPlayer ()
{
_CurrentSong = 0;
_CurrentSongIndex = 0;
_State = Stopped;
_PlayStart = 0;
_PauseTime = 0;
}
bool CMusicPlayer::isRepeatEnabled() const
{
return (NLGUI::CDBManager::getInstance()->getDbProp(MP3_SAVE_REPEAT)->getValue32() == 1);
}
bool CMusicPlayer::isShuffleEnabled() const
{
return (NLGUI::CDBManager::getInstance()->getDbProp(MP3_SAVE_SHUFFLE)->getValue32() == 1);
}
@ -59,16 +74,61 @@ void CMusicPlayer::playSongs (const std::vector<CSongs> &songs)
_Songs = songs;
// reset song index if out of bounds
if (_CurrentSong > _Songs.size())
_CurrentSong = 0;
if (_CurrentSongIndex > _Songs.size())
_CurrentSongIndex = 0;
if (isShuffleEnabled())
shuffleAndRebuildPlaylist();
else
rebuildPlaylist();
// If pause, stop, else play will resume
if (_State == Paused)
_State = Stopped;
}
// ***************************************************************************
void CMusicPlayer::updatePlaylist(sint prevIndex)
{
CInterfaceElement *pIE;
std::string rowId;
if (prevIndex >= 0 && prevIndex < _Songs.size())
{
rowId = toString("%s:s%d:bg", MP3_PLAYER_PLAYLIST_LIST, prevIndex);
pIE = dynamic_cast<CInterfaceElement*>(CWidgetManager::getInstance()->getElementFromId(rowId));
if (pIE) pIE->setActive(false);
}
rowId = toString("%s:s%d:bg", MP3_PLAYER_PLAYLIST_LIST, _CurrentSongIndex);
pIE = dynamic_cast<CInterfaceElement*>(CWidgetManager::getInstance()->getElementFromId(rowId));
if (pIE) pIE->setActive(true);
}
// ***************************************************************************
void CMusicPlayer::shuffleAndRebuildPlaylist()
{
std::random_shuffle(_Songs.begin(), _Songs.end());
rebuildPlaylist();
}
// ***************************************************************************
void CMusicPlayer::rebuildPlaylist()
{
CGroupList *pList = dynamic_cast<CGroupList *>(CWidgetManager::getInstance()->getElementFromId(MP3_PLAYER_PLAYLIST_LIST));
if (pList)
{
pList->clearGroups();
pList->setDynamicDisplaySize(true);
bool found = _CurrentSong.Filename.empty();
for (uint i=0; i < _Songs.size(); ++i)
{
if (!found && _CurrentSong.Filename == _Songs[i].Filename)
{
found = true;
_CurrentSongIndex = i;
}
uint min = (sint32)(_Songs[i].Length / 60) % 60;
uint sec = (sint32)(_Songs[i].Length) % 60;
uint hour = _Songs[i].Length / 3600;
@ -103,9 +163,7 @@ void CMusicPlayer::playSongs (const std::vector<CSongs> &songs)
pList->invalidateCoords();
}
// If pause, stop, else play will resume
if (_State == Paused)
_State = Stopped;
updatePlaylist();
}
@ -116,31 +174,43 @@ void CMusicPlayer::play (sint index)
if(!SoundMngr)
return;
sint prevSongIndex = _CurrentSongIndex;
if (index >= 0 && index < (sint)_Songs.size())
{
if (_State == Paused)
{
stop();
}
_CurrentSong = index;
_CurrentSongIndex = index;
_PauseTime = 0;
}
if (!_Songs.empty())
{
nlassert (_CurrentSong<_Songs.size());
nlassert (_CurrentSongIndex<_Songs.size());
/* If the player is paused, resume, else, play the current song */
if (_State == Paused)
{
SoundMngr->resumeMusic();
}
else
SoundMngr->playMusic(_Songs[_CurrentSong].Filename, 0, true, false, false);
{
SoundMngr->playMusic(_Songs[_CurrentSongIndex].Filename, 0, true, false, false);
_PauseTime = 0;
}
_State = Playing;
_PlayStart = CTime::getLocalTime() - _PauseTime;
/* Show the song title */
CInterfaceManager *pIM = CInterfaceManager::getInstance();
CViewText *pVT = dynamic_cast<CViewText*>(CWidgetManager::getInstance()->getElementFromId("ui:interface:mp3_player:screen:text"));
if (pVT)
pVT->setText (ucstring::makeFromUtf8(_Songs[_CurrentSong].Title));
_CurrentSong = _Songs[_CurrentSongIndex];
updatePlaylist(prevSongIndex);
NLGUI::CDBManager::getInstance()->getDbProp("UI:TEMP:MP3_PLAYING")->setValueBool(true);
}
}
@ -150,11 +220,17 @@ void CMusicPlayer::pause ()
{
if(!SoundMngr)
return;
// pause the music only if we are really playing (else risk to pause a background music!)
if(_State==Playing)
{
SoundMngr->pauseMusic();
_State = Paused;
if (_PlayStart > 0)
_PauseTime = CTime::getLocalTime() - _PlayStart;
NLGUI::CDBManager::getInstance()->getDbProp("UI:TEMP:MP3_PLAYING")->setValueBool(false);
}
}
@ -164,9 +240,14 @@ void CMusicPlayer::stop ()
{
if(!SoundMngr)
return;
// stop the music only if we are really playing (else risk to stop a background music!)
SoundMngr->stopMusic(0);
_State = Stopped;
_PlayStart = 0;
_PauseTime = 0;
NLGUI::CDBManager::getInstance()->getDbProp("UI:TEMP:MP3_PLAYING")->setValueBool(false);
}
// ***************************************************************************
@ -176,12 +257,13 @@ void CMusicPlayer::previous ()
if (!_Songs.empty())
{
// Point the previous song
if (_CurrentSong == 0)
_CurrentSong = (uint)_Songs.size()-1;
sint index;
if (_CurrentSongIndex == 0)
index = (uint)_Songs.size()-1;
else
_CurrentSong--;
index = _CurrentSongIndex-1;
play ();
play(index);
}
}
@ -191,9 +273,11 @@ void CMusicPlayer::next ()
{
if (!_Songs.empty())
{
_CurrentSong++;
_CurrentSong%=_Songs.size();
play ();
sint index = _CurrentSongIndex+1;
if (index == _Songs.size())
index = 0;
play(index);
}
}
@ -205,17 +289,35 @@ void CMusicPlayer::update ()
return;
if (_State == Playing)
{
CViewText *pVT = dynamic_cast<CViewText*>(CWidgetManager::getInstance()->getElementFromId("ui:interface:mp3_player:screen:text"));
if (pVT)
{
TTime dur = (CTime::getLocalTime() - _PlayStart) / 1000;
uint min = (dur / 60) % 60;
uint sec = dur % 60;
uint hour = dur / 3600;
std::string title(toString("%02d:%02d", min, sec));
if (hour > 0) title = toString("%02d:", hour) + title;
title += " " + _CurrentSong.Title;
pVT->setText(ucstring::makeFromUtf8(title));
}
if (SoundMngr->isMusicEnded ())
{
// Point the next song
_CurrentSong++;
_CurrentSong%=_Songs.size();
// End of the playlist ?
if (_CurrentSong != 0)
// select next song from playlist
sint index = _CurrentSongIndex + 1;
if (isRepeatEnabled() || index < _Songs.size())
{
// No, play the next song
play ();
if (index == _Songs.size())
{
index = 0;
if (isShuffleEnabled())
shuffleAndRebuildPlaylist();
}
play(index);
}
else
{
@ -284,22 +386,10 @@ public:
// no format supported
if (extensions.empty()) return;
bool oggSupported = false;
bool mp3Supported = false;
std::string message;
for(uint i = 0; i < extensions.size(); ++i)
{
if (extensions[i] == "ogg")
{
oggSupported = true;
message += " ogg";
}
else if (extensions[i] == "mp3")
{
mp3Supported = true;
message += " mp3";
}
message += " " + extensions[i];
}
message += " m3u m3u8";
nlinfo("Media player supports: '%s'", message.substr(1).c_str());
@ -316,14 +406,8 @@ public:
for (i = 0; i < filesToProcess.size(); ++i)
{
std::string ext = toLower(CFile::getExtension(filesToProcess[i]));
if (ext == "ogg")
if (std::find(extensions.begin(), extensions.end(), ext) != extensions.end())
{
if (oggSupported)
filenames.push_back(filesToProcess[i]);
}
else if (ext == "mp3" || ext == "mp2" || ext == "mp1")
{
if (mp3Supported)
filenames.push_back(filesToProcess[i]);
}
else if (ext == "m3u" || ext == "m3u8")
@ -345,14 +429,6 @@ public:
std::vector<CMusicPlayer::CSongs> songs;
for (i=0; i<filenames.size(); i++)
{
// '@' in filenames are reserved for .bnp files
// and sound system fails to open such file
if (filenames[i].find("@") != string::npos)
{
nlwarning("Ignore media file containing '@' in name: '%s'", filenames[i].c_str());
continue;
}
if (!CFile::fileExists(filenames[i])) {
nlwarning("Ignore non-existing file '%s'", filenames[i].c_str());
continue;
@ -360,16 +436,27 @@ public:
CMusicPlayer::CSongs song;
song.Filename = filenames[i];
// TODO: cache the result for next refresh
SoundMngr->getMixer()->getSongTitle(filenames[i], song.Title, song.Length);
if (song.Length > 0)
songs.push_back (song);
}
MusicPlayer.playSongs(songs);
}
else if (Params == "update_playlist")
{
if (MusicPlayer.isShuffleEnabled())
MusicPlayer.shuffleAndRebuildPlaylist();
MusicPlayer.rebuildPlaylist();
}
else if (Params == "previous")
MusicPlayer.previous();
else if (Params == "play")
MusicPlayer.play();
else if (Params == "stop")
MusicPlayer.stop();
else if (Params == "pause")
MusicPlayer.pause();
else if (Params == "next")

View file

@ -55,14 +55,28 @@ public:
void update ();
bool isRepeatEnabled() const;
bool isShuffleEnabled() const;
// Build playlist UI from songs
void rebuildPlaylist();
// Randomize playlist and rebuild the ui
void shuffleAndRebuildPlaylist();
// Update playlist active row
void updatePlaylist(sint prevIndex = -1);
private:
// The playlist
uint _CurrentSong; // If (!_Songs.empty()) must always be <_Songs.size()
CSongs _CurrentSong;
uint _CurrentSongIndex; // If (!_Songs.empty()) must always be <_Songs.size()
std::vector<CSongs> _Songs;
// State
enum TState { Stopped, Playing, Paused } _State;
TTime _PlayStart;
TTime _PauseTime;
};
extern CMusicPlayer MusicPlayer;

View file

@ -24,6 +24,7 @@
#include "../commands.h"
#include "interface_3d_scene.h"
#include "nel/misc/i_xml.h"
#include "../continent_manager.h"
using namespace NLMISC;
@ -31,6 +32,8 @@ using namespace NLMISC;
#include "../client_cfg.h"
#endif
extern CContinentManager ContinentMngr;
CIF3DSceneParser::CIF3DSceneParser()
{
parsingStage |= ( Resolved | GroupChildren );
@ -529,3 +532,21 @@ bool CMacroParser::parse( xmlNodePtr cur, NLGUI::CInterfaceGroup *parentGroup )
return true;
}
CLandmarkParser::CLandmarkParser()
{
parsingStage |= Unresolved;
}
CLandmarkParser::~CLandmarkParser()
{
}
bool CLandmarkParser::parse( xmlNodePtr cur, NLGUI::CInterfaceGroup *parentGroup )
{
H_AUTO(parseLandmark)
ContinentMngr.readFrom(cur);
return true;
}

View file

@ -76,4 +76,13 @@ public:
bool parse( xmlNodePtr cur, CInterfaceGroup *parentGroup );
};
class CLandmarkParser : public CInterfaceParser::IParserModule
{
public:
CLandmarkParser();
~CLandmarkParser();
bool parse( xmlNodePtr cur, CInterfaceGroup *parentGroup );
};
#endif

View file

@ -34,6 +34,7 @@ void registerInterfaceElements()
CViewPointerRyzom::forceLinking();
REGISTER_REFLECTABLE_CLASS(CViewRadar, CViewBase);
REGISTER_REFLECTABLE_CLASS(CGroupMap, CInterfaceGroup);
REGISTER_REFLECTABLE_CLASS(CDBCtrlSheet, CCtrlDraggable);
REGISTER_REFLECTABLE_CLASS(IListSheetBase, CInterfaceGroup);
REGISTER_REFLECTABLE_CLASS(CInterface3DScene, CInterfaceGroup);

View file

@ -3419,6 +3419,22 @@ void displayDebugClusters()
}
NLMISC_COMMAND(dumpFontTexture, "Write font texture to file", "")
{
CInterfaceManager *im = CInterfaceManager::getInstance();
if (TextContext)
{
std::string fname = CFile::findNewFile("font-texture.tga");
TextContext->dumpCacheTexture(fname.c_str());
im->displaySystemInfo(ucstring(fname + " created"), "SYS");
}
else
{
im->displaySystemInfo(ucstring("Error: TextContext == NULL"), "SYS");
}
return true;
}
// ***************************************************************************
void inGamePatchUncompleteWarning()

View file

@ -36,6 +36,7 @@
#include "input.h"
#include "sound_manager.h"
#include "camera.h"
#include "interface_v3/interface_manager.h"
using namespace NLMISC;
using namespace NL3D;
@ -100,6 +101,12 @@ void updateFromClientCfg()
Driver->forceTextureResize(1);
}
if (ClientCfg.InterfaceScale != LastClientCfg.InterfaceScale)
CInterfaceManager::getInstance()->setInterfaceScale(ClientCfg.InterfaceScale);
if (ClientCfg.BilinearUI != LastClientCfg.BilinearUI)
CViewRenderer::getInstance()->setBilinearFiltering(ClientCfg.BilinearUI);
//---------------------------------------------------
if (ClientCfg.WaitVBL != LastClientCfg.WaitVBL)
{

Some files were not shown because too many files have changed in this diff Show more